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