aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/diagnostics
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src/diagnostics')
-rw-r--r--crates/ide/src/diagnostics/inactive_code.rs117
-rw-r--r--crates/ide/src/diagnostics/macro_error.rs163
-rw-r--r--crates/ide/src/diagnostics/unresolved_macro_call.rs12
-rw-r--r--crates/ide/src/diagnostics/unresolved_proc_macro.rs30
4 files changed, 322 insertions, 0 deletions
diff --git a/crates/ide/src/diagnostics/inactive_code.rs b/crates/ide/src/diagnostics/inactive_code.rs
new file mode 100644
index 000000000..afe333204
--- /dev/null
+++ b/crates/ide/src/diagnostics/inactive_code.rs
@@ -0,0 +1,117 @@
1use cfg::DnfExpr;
2use stdx::format_to;
3
4use crate::{
5 diagnostics::{Diagnostic, DiagnosticsContext},
6 Severity,
7};
8
9// Diagnostic: inactive-code
10//
11// This diagnostic is shown for code with inactive `#[cfg]` attributes.
12pub(super) fn inactive_code(
13 ctx: &DiagnosticsContext<'_>,
14 d: &hir::InactiveCode,
15) -> Option<Diagnostic> {
16 // If there's inactive code somewhere in a macro, don't propagate to the call-site.
17 if d.node.file_id.expansion_info(ctx.sema.db).is_some() {
18 return None;
19 }
20
21 let inactive = DnfExpr::new(d.cfg.clone()).why_inactive(&d.opts);
22 let mut message = "code is inactive due to #[cfg] directives".to_string();
23
24 if let Some(inactive) = inactive {
25 format_to!(message, ": {}", inactive);
26 }
27
28 let res = Diagnostic::new(
29 "inactive-code",
30 message,
31 ctx.sema.diagnostics_display_range(d.node.clone()).range,
32 )
33 .severity(Severity::WeakWarning)
34 .with_unused(true);
35 Some(res)
36}
37
38#[cfg(test)]
39mod tests {
40 use crate::diagnostics::tests::check_diagnostics_with_inactive_code;
41
42 #[test]
43 fn cfg_diagnostics() {
44 check_diagnostics_with_inactive_code(
45 r#"
46fn f() {
47 // The three g̶e̶n̶d̶e̶r̶s̶ statements:
48
49 #[cfg(a)] fn f() {} // Item statement
50 //^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
51 #[cfg(a)] {} // Expression statement
52 //^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
53 #[cfg(a)] let x = 0; // let statement
54 //^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
55
56 abc(#[cfg(a)] 0);
57 //^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
58 let x = Struct {
59 #[cfg(a)] f: 0,
60 //^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
61 };
62 match () {
63 () => (),
64 #[cfg(a)] () => (),
65 //^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
66 }
67
68 #[cfg(a)] 0 // Trailing expression of block
69 //^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
70}
71 "#,
72 true,
73 );
74 }
75
76 #[test]
77 fn inactive_item() {
78 // Additional tests in `cfg` crate. This only tests disabled cfgs.
79
80 check_diagnostics_with_inactive_code(
81 r#"
82 #[cfg(no)] pub fn f() {}
83 //^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
84
85 #[cfg(no)] #[cfg(no2)] mod m;
86 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no and no2 are disabled
87
88 #[cfg(all(not(a), b))] enum E {}
89 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: b is disabled
90
91 #[cfg(feature = "std")] use std;
92 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: feature = "std" is disabled
93"#,
94 true,
95 );
96 }
97
98 /// Tests that `cfg` attributes behind `cfg_attr` is handled properly.
99 #[test]
100 fn inactive_via_cfg_attr() {
101 cov_mark::check!(cfg_attr_active);
102 check_diagnostics_with_inactive_code(
103 r#"
104 #[cfg_attr(not(never), cfg(no))] fn f() {}
105 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
106
107 #[cfg_attr(not(never), cfg(not(no)))] fn f() {}
108
109 #[cfg_attr(never, cfg(no))] fn g() {}
110
111 #[cfg_attr(not(never), inline, cfg(no))] fn h() {}
112 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
113"#,
114 true,
115 );
116 }
117}
diff --git a/crates/ide/src/diagnostics/macro_error.rs b/crates/ide/src/diagnostics/macro_error.rs
new file mode 100644
index 000000000..8cc8cfb48
--- /dev/null
+++ b/crates/ide/src/diagnostics/macro_error.rs
@@ -0,0 +1,163 @@
1use crate::diagnostics::{Diagnostic, DiagnosticsContext};
2
3// Diagnostic: macro-error
4//
5// This diagnostic is shown for macro expansion errors.
6pub(super) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic {
7 Diagnostic::new(
8 "macro-error",
9 d.message.clone(),
10 ctx.sema.diagnostics_display_range(d.node.clone()).range,
11 )
12 .experimental()
13}
14
15#[cfg(test)]
16mod tests {
17 use crate::diagnostics::tests::{check_diagnostics, check_no_diagnostics};
18
19 #[test]
20 fn builtin_macro_fails_expansion() {
21 check_diagnostics(
22 r#"
23#[rustc_builtin_macro]
24macro_rules! include { () => {} }
25
26 include!("doesntexist");
27//^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `doesntexist`
28 "#,
29 );
30 }
31
32 #[test]
33 fn include_macro_should_allow_empty_content() {
34 check_diagnostics(
35 r#"
36//- /lib.rs
37#[rustc_builtin_macro]
38macro_rules! include { () => {} }
39
40include!("foo/bar.rs");
41//- /foo/bar.rs
42// empty
43"#,
44 );
45 }
46
47 #[test]
48 fn good_out_dir_diagnostic() {
49 check_diagnostics(
50 r#"
51#[rustc_builtin_macro]
52macro_rules! include { () => {} }
53#[rustc_builtin_macro]
54macro_rules! env { () => {} }
55#[rustc_builtin_macro]
56macro_rules! concat { () => {} }
57
58 include!(concat!(env!("OUT_DIR"), "/out.rs"));
59//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix
60"#,
61 );
62 }
63
64 #[test]
65 fn register_attr_and_tool() {
66 cov_mark::check!(register_attr);
67 cov_mark::check!(register_tool);
68 check_no_diagnostics(
69 r#"
70#![register_tool(tool)]
71#![register_attr(attr)]
72
73#[tool::path]
74#[attr]
75struct S;
76"#,
77 );
78 // NB: we don't currently emit diagnostics here
79 }
80
81 #[test]
82 fn macro_diag_builtin() {
83 check_diagnostics(
84 r#"
85#[rustc_builtin_macro]
86macro_rules! env {}
87
88#[rustc_builtin_macro]
89macro_rules! include {}
90
91#[rustc_builtin_macro]
92macro_rules! compile_error {}
93
94#[rustc_builtin_macro]
95macro_rules! format_args { () => {} }
96
97fn main() {
98 // Test a handful of built-in (eager) macros:
99
100 include!(invalid);
101 //^^^^^^^^^^^^^^^^^ could not convert tokens
102 include!("does not exist");
103 //^^^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `does not exist`
104
105 env!(invalid);
106 //^^^^^^^^^^^^^ could not convert tokens
107
108 env!("OUT_DIR");
109 //^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix
110
111 compile_error!("compile_error works");
112 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ compile_error works
113
114 // Lazy:
115
116 format_args!();
117 //^^^^^^^^^^^^^^ no rule matches input tokens
118}
119"#,
120 );
121 }
122
123 #[test]
124 fn macro_rules_diag() {
125 check_diagnostics(
126 r#"
127macro_rules! m {
128 () => {};
129}
130fn f() {
131 m!();
132
133 m!(hi);
134 //^^^^^^ leftover tokens
135}
136 "#,
137 );
138 }
139 #[test]
140 fn dollar_crate_in_builtin_macro() {
141 check_diagnostics(
142 r#"
143#[macro_export]
144#[rustc_builtin_macro]
145macro_rules! format_args {}
146
147#[macro_export]
148macro_rules! arg { () => {} }
149
150#[macro_export]
151macro_rules! outer {
152 () => {
153 $crate::format_args!( "", $crate::arg!(1) )
154 };
155}
156
157fn f() {
158 outer!();
159} //^^^^^^^^ leftover tokens
160"#,
161 )
162 }
163}
diff --git a/crates/ide/src/diagnostics/unresolved_macro_call.rs b/crates/ide/src/diagnostics/unresolved_macro_call.rs
index a3af332a4..15b6a2730 100644
--- a/crates/ide/src/diagnostics/unresolved_macro_call.rs
+++ b/crates/ide/src/diagnostics/unresolved_macro_call.rs
@@ -35,6 +35,18 @@ mod tests {
35 use crate::diagnostics::tests::check_diagnostics; 35 use crate::diagnostics::tests::check_diagnostics;
36 36
37 #[test] 37 #[test]
38 fn unresolved_macro_diag() {
39 check_diagnostics(
40 r#"
41fn f() {
42 m!();
43} //^ unresolved macro `m!`
44
45"#,
46 );
47 }
48
49 #[test]
38 fn test_unresolved_macro_range() { 50 fn test_unresolved_macro_range() {
39 check_diagnostics( 51 check_diagnostics(
40 r#" 52 r#"
diff --git a/crates/ide/src/diagnostics/unresolved_proc_macro.rs b/crates/ide/src/diagnostics/unresolved_proc_macro.rs
new file mode 100644
index 000000000..3dc6ab451
--- /dev/null
+++ b/crates/ide/src/diagnostics/unresolved_proc_macro.rs
@@ -0,0 +1,30 @@
1use crate::{
2 diagnostics::{Diagnostic, DiagnosticsContext},
3 Severity,
4};
5
6// Diagnostic: unresolved-proc-macro
7//
8// This diagnostic is shown when a procedural macro can not be found. This usually means that
9// procedural macro support is simply disabled (and hence is only a weak hint instead of an error),
10// but can also indicate project setup problems.
11//
12// If you are seeing a lot of "proc macro not expanded" warnings, you can add this option to the
13// `rust-analyzer.diagnostics.disabled` list to prevent them from showing. Alternatively you can
14// enable support for procedural macros (see `rust-analyzer.procMacro.enable`).
15pub(super) fn unresolved_proc_macro(
16 ctx: &DiagnosticsContext<'_>,
17 d: &hir::UnresolvedProcMacro,
18) -> Diagnostic {
19 // Use more accurate position if available.
20 let display_range = d
21 .precise_location
22 .unwrap_or_else(|| ctx.sema.diagnostics_display_range(d.node.clone()).range);
23 // FIXME: it would be nice to tell the user whether proc macros are currently disabled
24 let message = match &d.macro_name {
25 Some(name) => format!("proc macro `{}` not expanded", name),
26 None => "proc macro not expanded".to_string(),
27 };
28
29 Diagnostic::new("unresolved-proc-macro", message, display_range).severity(Severity::WeakWarning)
30}