ide_diagnostics/handlers/
macro_error.rs1use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity};
2
3pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_, '_>, d: &hir::MacroError) -> Diagnostic {
15 let display_range = ctx.sema.diagnostics_display_range_for_range(d.range);
17 Diagnostic::new(
18 DiagnosticCode::Ra(d.kind, if d.error { Severity::Error } else { Severity::WeakWarning }),
19 d.message.clone(),
20 display_range,
21 )
22 .stable()
23}
24
25pub(crate) fn macro_def_error(
29 ctx: &DiagnosticsContext<'_, '_>,
30 d: &hir::MacroDefError,
31) -> Diagnostic {
32 let display_range = match d.name {
34 Some(name) => ctx.sema.diagnostics_display_range_for_range(d.node.with_value(name)),
35 None => ctx.sema.diagnostics_display_range(d.node.map(|it| it.syntax_node_ptr())),
36 };
37 Diagnostic::new(
38 DiagnosticCode::Ra("macro-def-error", Severity::Error),
39 d.message.clone(),
40 display_range,
41 )
42 .stable()
43}
44
45#[cfg(test)]
46mod tests {
47 use crate::{
48 DiagnosticsConfig,
49 tests::{check_diagnostics, check_diagnostics_with_config},
50 };
51
52 #[test]
53 fn builtin_macro_fails_expansion() {
54 check_diagnostics(
55 r#"
56#[rustc_builtin_macro]
57macro_rules! include { () => {} }
58
59#[rustc_builtin_macro]
60macro_rules! compile_error { () => {} }
61
62 include!("doesntexist");
63 //^^^^^^^^^^^^^ error: failed to load file `doesntexist`
64
65 compile_error!("compile_error macro works");
66//^^^^^^^^^^^^^ error: compile_error macro works
67
68 compile_error! { "compile_error macro braced works" }
69//^^^^^^^^^^^^^ error: compile_error macro braced works
70 "#,
71 );
72 }
73
74 #[test]
75 fn eager_macro_concat() {
76 check_diagnostics(
77 r#"
78//- /lib.rs crate:lib deps:core
79use core::{panic, concat};
80
81mod private {
82 pub use core::concat;
83}
84
85macro_rules! m {
86 () => {
87 panic!(concat!($crate::private::concat!("")));
88 };
89}
90
91fn f() {
92 m!();
93}
94
95//- /core.rs crate:core
96#[macro_export]
97#[rustc_builtin_macro]
98macro_rules! concat { () => {} }
99
100pub macro panic {
101 ($msg:expr) => (
102 $crate::panicking::panic_str($msg)
103 ),
104}
105 "#,
106 );
107 }
108
109 #[test]
110 fn include_macro_should_allow_empty_content() {
111 let mut config = DiagnosticsConfig::test_sample();
112
113 config.disabled.insert("unlinked-file".to_owned());
116
117 check_diagnostics_with_config(
118 config,
119 r#"
120//- /lib.rs
121#[rustc_builtin_macro]
122macro_rules! include { () => {} }
123
124include!("foo/bar.rs");
125//- /foo/bar.rs
126// empty
127"#,
128 );
129 }
130
131 #[test]
132 fn good_out_dir_diagnostic() {
133 check_diagnostics(
135 r#"
136#[rustc_builtin_macro]
137macro_rules! include { () => {} }
138#[rustc_builtin_macro]
139macro_rules! env { () => {} }
140#[rustc_builtin_macro]
141macro_rules! concat { () => {} }
142
143 include!(concat!(
144 // ^^^^^^ error: `OUT_DIR` not set, build scripts may have failed to run
145 env!(
146 //^^^ error: `OUT_DIR` not set, build scripts may have failed to run
147 "OUT_DIR"), "/out.rs"));
148 //^^^^^^^^^ error: `OUT_DIR` not set, build scripts may have failed to run
149"#,
150 );
151 }
152
153 #[test]
154 fn register_tool() {
155 cov_mark::check!(register_tool);
156 check_diagnostics(
157 r#"
158#![register_tool(tool)]
159
160#[tool::path]
161struct S;
162"#,
163 );
164 }
166
167 #[test]
168 fn macro_diag_builtin() {
169 check_diagnostics(
170 r#"
171//- minicore: fmt
172#[rustc_builtin_macro]
173macro_rules! env {}
174
175#[rustc_builtin_macro]
176macro_rules! include {}
177
178#[rustc_builtin_macro]
179macro_rules! compile_error {}
180#[rustc_builtin_macro]
181macro_rules! concat {}
182
183fn main() {
184 // Test a handful of built-in (eager) macros:
185
186 include!(invalid);
187 //^^^^^^^ error: expected string literal
188 include!("does not exist");
189 //^^^^^^^^^^^^^^^^ error: failed to load file `does not exist`
190
191 include!(concat!("does ", "not ", "exist"));
192 // ^^^^^^^^^^^^^^^^^^^^^^^^ error: failed to load file `does not exist`
193
194 env!(invalid);
195 //^^^^^^^ error: expected string literal
196
197 env!("OUT_DIR");
198 //^^^^^^^^^ error: `OUT_DIR` not set, build scripts may have failed to run
199
200 compile_error!("compile_error works");
201 //^^^^^^^^^^^^^ error: compile_error works
202
203 // Lazy:
204
205 format_args!();
206 //^^^^^^^^^^^ error: Syntax Error in Expansion: expected expression
207}
208"#,
209 );
210 }
211
212 #[test]
213 fn macro_rules_diag() {
214 check_diagnostics(
215 r#"
216macro_rules! m {
217 () => {};
218}
219fn f() {
220 m!();
221
222 m!(hi);
223 //^ error: leftover tokens
224}
225 "#,
226 );
227 }
228
229 #[test]
230 fn dollar_crate_in_builtin_macro() {
231 check_diagnostics(
232 r#"
233#[macro_export]
234#[rustc_builtin_macro]
235macro_rules! format_args {}
236
237#[macro_export]
238macro_rules! arg { () => {} }
239
240#[macro_export]
241macro_rules! outer {
242 () => {
243 $crate::format_args!( "", $crate::arg!(1) )
244 };
245}
246
247fn f() {
248 outer!();
249} //^^^^^^ error: leftover tokens
250 //^^^^^^ error: Syntax Error in Expansion: expected expression
251"#,
252 )
253 }
254
255 #[test]
256 fn def_diagnostic() {
257 check_diagnostics(
258 r#"
259macro_rules! foo {
260 //^^^ error: expected subtree
261 f => {};
262}
263
264fn f() {
265 foo!();
266 //^^^ error: macro definition has parse errors
267
268}
269"#,
270 )
271 }
272
273 #[test]
274 fn expansion_syntax_diagnostic() {
275 check_diagnostics(
276 r#"
277macro_rules! foo {
278 () => { struct; };
279}
280
281fn f() {
282 foo!();
283 //^^^ error: Syntax Error in Expansion: expected a name
284}
285"#,
286 )
287 }
288
289 #[test]
290 fn include_does_not_break_diagnostics() {
291 check_diagnostics(
292 r#"
293//- minicore: include
294//- /lib.rs crate:lib
295include!("include-me.rs");
296//- /include-me.rs
297/// long doc that pushes the diagnostic range beyond the first file's text length
298 #[err]
299 // ^^^ error: unresolved macro `err`
300mod prim_never {}
301"#,
302 );
303 }
304
305 #[test]
306 fn no_stack_overflow_for_missing_binding() {
307 check_diagnostics(
308 r#"
309#[macro_export]
310macro_rules! boom {
311 (
312 $($code:literal),+,
313 $(param: $param:expr,)?
314 ) => {{
315 let _ = $crate::boom!(@param $($param)*);
316 }};
317 (@param) => { () };
318 (@param $param:expr) => { $param };
319}
320
321fn it_works() {
322 // NOTE: there is an error, but RA crashes before showing it
323 boom!("RAND", param: c7.clone());
324 // ^^^^^ error: expected literal
325}
326
327 "#,
328 );
329 }
330}