diff options
330 files changed, 14039 insertions, 11266 deletions
diff --git a/Cargo.lock b/Cargo.lock index 505263c64..e47b87964 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -245,9 +245,9 @@ dependencies = [ | |||
245 | 245 | ||
246 | [[package]] | 246 | [[package]] |
247 | name = "cov-mark" | 247 | name = "cov-mark" |
248 | version = "1.1.0" | 248 | version = "2.0.0-pre.1" |
249 | source = "registry+https://github.com/rust-lang/crates.io-index" | 249 | source = "registry+https://github.com/rust-lang/crates.io-index" |
250 | checksum = "9ffa3d3e0138386cd4361f63537765cac7ee40698028844635a54495a92f67f3" | 250 | checksum = "0d48d8f76bd9331f19fe2aaf3821a9f9fb32c3963e1e3d6ce82a8c09cef7444a" |
251 | 251 | ||
252 | [[package]] | 252 | [[package]] |
253 | name = "crc32fast" | 253 | name = "crc32fast" |
@@ -482,6 +482,7 @@ dependencies = [ | |||
482 | "hir_ty", | 482 | "hir_ty", |
483 | "itertools", | 483 | "itertools", |
484 | "log", | 484 | "log", |
485 | "once_cell", | ||
485 | "profile", | 486 | "profile", |
486 | "rustc-hash", | 487 | "rustc-hash", |
487 | "smallvec", | 488 | "smallvec", |
@@ -765,9 +766,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" | |||
765 | 766 | ||
766 | [[package]] | 767 | [[package]] |
767 | name = "libc" | 768 | name = "libc" |
768 | version = "0.2.95" | 769 | version = "0.2.97" |
769 | source = "registry+https://github.com/rust-lang/crates.io-index" | 770 | source = "registry+https://github.com/rust-lang/crates.io-index" |
770 | checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36" | 771 | checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" |
771 | 772 | ||
772 | [[package]] | 773 | [[package]] |
773 | name = "libloading" | 774 | name = "libloading" |
@@ -1158,6 +1159,15 @@ dependencies = [ | |||
1158 | [[package]] | 1159 | [[package]] |
1159 | name = "proc_macro_test" | 1160 | name = "proc_macro_test" |
1160 | version = "0.0.0" | 1161 | version = "0.0.0" |
1162 | dependencies = [ | ||
1163 | "cargo_metadata", | ||
1164 | "proc_macro_test_impl", | ||
1165 | "toolchain", | ||
1166 | ] | ||
1167 | |||
1168 | [[package]] | ||
1169 | name = "proc_macro_test_impl" | ||
1170 | version = "0.0.0" | ||
1161 | 1171 | ||
1162 | [[package]] | 1172 | [[package]] |
1163 | name = "profile" | 1173 | name = "profile" |
@@ -1170,6 +1180,7 @@ dependencies = [ | |||
1170 | "once_cell", | 1180 | "once_cell", |
1171 | "perf-event", | 1181 | "perf-event", |
1172 | "tikv-jemalloc-ctl", | 1182 | "tikv-jemalloc-ctl", |
1183 | "winapi", | ||
1173 | ] | 1184 | ] |
1174 | 1185 | ||
1175 | [[package]] | 1186 | [[package]] |
@@ -1792,9 +1803,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" | |||
1792 | 1803 | ||
1793 | [[package]] | 1804 | [[package]] |
1794 | name = "ungrammar" | 1805 | name = "ungrammar" |
1795 | version = "1.13.0" | 1806 | version = "1.14.0" |
1796 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1807 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1797 | checksum = "76760314176cc2b94047af2f921b92c39f11a34dc05c43a3c2b0fc91cb22959f" | 1808 | checksum = "50ef6d7335c77ec3e4a7c4be74c2b9e4642569e94a4004c836f8cca71fede3a7" |
1798 | 1809 | ||
1799 | [[package]] | 1810 | [[package]] |
1800 | name = "unicase" | 1811 | name = "unicase" |
diff --git a/Cargo.toml b/Cargo.toml index 32ba3923b..4d6908fa9 100644 --- a/Cargo.toml +++ b/Cargo.toml | |||
@@ -1,6 +1,7 @@ | |||
1 | [workspace] | 1 | [workspace] |
2 | resolver = "2" | 2 | resolver = "2" |
3 | members = ["xtask/", "lib/*", "crates/*"] | 3 | members = ["xtask/", "lib/*", "crates/*"] |
4 | exclude = ["crates/proc_macro_test/imp"] | ||
4 | 5 | ||
5 | [profile.dev] | 6 | [profile.dev] |
6 | # Disabling debug info speeds up builds a bunch, | 7 | # Disabling debug info speeds up builds a bunch, |
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs index 69ceba735..da4afb5eb 100644 --- a/crates/base_db/src/fixture.rs +++ b/crates/base_db/src/fixture.rs | |||
@@ -190,7 +190,7 @@ impl From<Fixture> for FileMeta { | |||
190 | edition: f | 190 | edition: f |
191 | .edition | 191 | .edition |
192 | .as_ref() | 192 | .as_ref() |
193 | .map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap()), | 193 | .map_or(Edition::Edition2018, |v| Edition::from_str(v).unwrap()), |
194 | env: f.env.into_iter().collect(), | 194 | env: f.env.into_iter().collect(), |
195 | introduce_new_source_root: f.introduce_new_source_root, | 195 | introduce_new_source_root: f.introduce_new_source_root, |
196 | } | 196 | } |
diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs index 62bf2a4b2..d26f8f180 100644 --- a/crates/base_db/src/lib.rs +++ b/crates/base_db/src/lib.rs | |||
@@ -42,7 +42,7 @@ pub struct FilePosition { | |||
42 | pub offset: TextSize, | 42 | pub offset: TextSize, |
43 | } | 43 | } |
44 | 44 | ||
45 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] | 45 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] |
46 | pub struct FileRange { | 46 | pub struct FileRange { |
47 | pub file_id: FileId, | 47 | pub file_id: FileId, |
48 | pub range: TextRange, | 48 | pub range: TextRange, |
@@ -120,6 +120,7 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> { | |||
120 | } | 120 | } |
121 | 121 | ||
122 | fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> { | 122 | fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> { |
123 | let _p = profile::span("relevant_crates"); | ||
123 | let source_root = self.0.file_source_root(file_id); | 124 | let source_root = self.0.file_source_root(file_id); |
124 | self.0.source_root_crates(source_root) | 125 | self.0.source_root_crates(source_root) |
125 | } | 126 | } |
diff --git a/crates/cfg/src/cfg_expr.rs b/crates/cfg/src/cfg_expr.rs index 069fc01d0..8a1a51e6e 100644 --- a/crates/cfg/src/cfg_expr.rs +++ b/crates/cfg/src/cfg_expr.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! The condition expression used in `#[cfg(..)]` attributes. | 1 | //! The condition expression used in `#[cfg(..)]` attributes. |
2 | //! | 2 | //! |
3 | //! See: https://doc.rust-lang.org/reference/conditional-compilation.html#conditional-compilation | 3 | //! See: <https://doc.rust-lang.org/reference/conditional-compilation.html#conditional-compilation> |
4 | 4 | ||
5 | use std::{fmt, slice::Iter as SliceIter}; | 5 | use std::{fmt, slice::Iter as SliceIter}; |
6 | 6 | ||
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs index 59fd38880..03b8dd767 100644 --- a/crates/cfg/src/lib.rs +++ b/crates/cfg/src/lib.rs | |||
@@ -22,7 +22,7 @@ pub use dnf::DnfExpr; | |||
22 | /// `foo` and `bar` are both enabled. And here, we store key-value options as a set of tuple | 22 | /// `foo` and `bar` are both enabled. And here, we store key-value options as a set of tuple |
23 | /// of key and value in `key_values`. | 23 | /// of key and value in `key_values`. |
24 | /// | 24 | /// |
25 | /// See: https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options | 25 | /// See: <https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options> |
26 | #[derive(Debug, Clone, PartialEq, Eq, Default)] | 26 | #[derive(Debug, Clone, PartialEq, Eq, Default)] |
27 | pub struct CfgOptions { | 27 | pub struct CfgOptions { |
28 | enabled: FxHashSet<CfgAtom>, | 28 | enabled: FxHashSet<CfgAtom>, |
diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml index 560b15238..7c148fd40 100644 --- a/crates/hir/Cargo.toml +++ b/crates/hir/Cargo.toml | |||
@@ -16,6 +16,7 @@ either = "1.5.3" | |||
16 | arrayvec = "0.7" | 16 | arrayvec = "0.7" |
17 | itertools = "0.10.0" | 17 | itertools = "0.10.0" |
18 | smallvec = "1.4.0" | 18 | smallvec = "1.4.0" |
19 | once_cell = "1" | ||
19 | 20 | ||
20 | stdx = { path = "../stdx", version = "0.0.0" } | 21 | stdx = { path = "../stdx", version = "0.0.0" } |
21 | syntax = { path = "../syntax", version = "0.0.0" } | 22 | syntax = { path = "../syntax", version = "0.0.0" } |
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 2cdbd172a..b4c505898 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs | |||
@@ -3,251 +3,152 @@ | |||
3 | //! | 3 | //! |
4 | //! This probably isn't the best way to do this -- ideally, diagnistics should | 4 | //! This probably isn't the best way to do this -- ideally, diagnistics should |
5 | //! be expressed in terms of hir types themselves. | 5 | //! be expressed in terms of hir types themselves. |
6 | use std::any::Any; | 6 | use cfg::{CfgExpr, CfgOptions}; |
7 | 7 | use either::Either; | |
8 | use cfg::{CfgExpr, CfgOptions, DnfExpr}; | ||
9 | use hir_def::path::ModPath; | 8 | use hir_def::path::ModPath; |
10 | use hir_expand::{HirFileId, InFile}; | 9 | use hir_expand::{name::Name, HirFileId, InFile}; |
11 | use stdx::format_to; | ||
12 | use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; | 10 | use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; |
13 | 11 | ||
14 | pub use hir_ty::{ | 12 | macro_rules! diagnostics { |
15 | diagnostics::{ | 13 | ($($diag:ident,)*) => { |
16 | IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, | 14 | pub enum AnyDiagnostic {$( |
17 | MissingOkOrSomeInTailExpr, NoSuchField, RemoveThisSemicolon, | 15 | $diag(Box<$diag>), |
18 | ReplaceFilterMapNextWithFindMap, | 16 | )*} |
19 | }, | 17 | |
20 | diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder}, | 18 | $( |
21 | }; | 19 | impl From<$diag> for AnyDiagnostic { |
22 | 20 | fn from(d: $diag) -> AnyDiagnostic { | |
23 | // Diagnostic: unresolved-module | 21 | AnyDiagnostic::$diag(Box::new(d)) |
24 | // | 22 | } |
25 | // This diagnostic is triggered if rust-analyzer is unable to discover referred module. | 23 | } |
24 | )* | ||
25 | }; | ||
26 | } | ||
27 | |||
28 | diagnostics![ | ||
29 | BreakOutsideOfLoop, | ||
30 | InactiveCode, | ||
31 | IncorrectCase, | ||
32 | MacroError, | ||
33 | MismatchedArgCount, | ||
34 | MissingFields, | ||
35 | MissingMatchArms, | ||
36 | MissingOkOrSomeInTailExpr, | ||
37 | MissingUnsafe, | ||
38 | NoSuchField, | ||
39 | RemoveThisSemicolon, | ||
40 | ReplaceFilterMapNextWithFindMap, | ||
41 | UnimplementedBuiltinMacro, | ||
42 | UnresolvedExternCrate, | ||
43 | UnresolvedImport, | ||
44 | UnresolvedMacroCall, | ||
45 | UnresolvedModule, | ||
46 | UnresolvedProcMacro, | ||
47 | ]; | ||
48 | |||
26 | #[derive(Debug)] | 49 | #[derive(Debug)] |
27 | pub struct UnresolvedModule { | 50 | pub struct UnresolvedModule { |
28 | pub file: HirFileId, | 51 | pub decl: InFile<AstPtr<ast::Module>>, |
29 | pub decl: AstPtr<ast::Module>, | ||
30 | pub candidate: String, | 52 | pub candidate: String, |
31 | } | 53 | } |
32 | 54 | ||
33 | impl Diagnostic for UnresolvedModule { | ||
34 | fn code(&self) -> DiagnosticCode { | ||
35 | DiagnosticCode("unresolved-module") | ||
36 | } | ||
37 | fn message(&self) -> String { | ||
38 | "unresolved module".to_string() | ||
39 | } | ||
40 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
41 | InFile::new(self.file, self.decl.clone().into()) | ||
42 | } | ||
43 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
44 | self | ||
45 | } | ||
46 | } | ||
47 | |||
48 | // Diagnostic: unresolved-extern-crate | ||
49 | // | ||
50 | // This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate. | ||
51 | #[derive(Debug)] | 55 | #[derive(Debug)] |
52 | pub struct UnresolvedExternCrate { | 56 | pub struct UnresolvedExternCrate { |
53 | pub file: HirFileId, | 57 | pub decl: InFile<AstPtr<ast::ExternCrate>>, |
54 | pub item: AstPtr<ast::ExternCrate>, | ||
55 | } | ||
56 | |||
57 | impl Diagnostic for UnresolvedExternCrate { | ||
58 | fn code(&self) -> DiagnosticCode { | ||
59 | DiagnosticCode("unresolved-extern-crate") | ||
60 | } | ||
61 | fn message(&self) -> String { | ||
62 | "unresolved extern crate".to_string() | ||
63 | } | ||
64 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
65 | InFile::new(self.file, self.item.clone().into()) | ||
66 | } | ||
67 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
68 | self | ||
69 | } | ||
70 | } | 58 | } |
71 | 59 | ||
72 | #[derive(Debug)] | 60 | #[derive(Debug)] |
73 | pub struct UnresolvedImport { | 61 | pub struct UnresolvedImport { |
74 | pub file: HirFileId, | 62 | pub decl: InFile<AstPtr<ast::UseTree>>, |
75 | pub node: AstPtr<ast::UseTree>, | 63 | } |
76 | } | 64 | |
77 | |||
78 | impl Diagnostic for UnresolvedImport { | ||
79 | fn code(&self) -> DiagnosticCode { | ||
80 | DiagnosticCode("unresolved-import") | ||
81 | } | ||
82 | fn message(&self) -> String { | ||
83 | "unresolved import".to_string() | ||
84 | } | ||
85 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
86 | InFile::new(self.file, self.node.clone().into()) | ||
87 | } | ||
88 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
89 | self | ||
90 | } | ||
91 | fn is_experimental(&self) -> bool { | ||
92 | // This currently results in false positives in the following cases: | ||
93 | // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly) | ||
94 | // - `core::arch` (we don't handle `#[path = "../<path>"]` correctly) | ||
95 | // - proc macros and/or proc macro generated code | ||
96 | true | ||
97 | } | ||
98 | } | ||
99 | |||
100 | // Diagnostic: unresolved-macro-call | ||
101 | // | ||
102 | // This diagnostic is triggered if rust-analyzer is unable to resolve the path to a | ||
103 | // macro in a macro invocation. | ||
104 | #[derive(Debug, Clone, Eq, PartialEq)] | 65 | #[derive(Debug, Clone, Eq, PartialEq)] |
105 | pub struct UnresolvedMacroCall { | 66 | pub struct UnresolvedMacroCall { |
106 | pub file: HirFileId, | 67 | pub macro_call: InFile<AstPtr<ast::MacroCall>>, |
107 | pub node: AstPtr<ast::MacroCall>, | ||
108 | pub path: ModPath, | 68 | pub path: ModPath, |
109 | } | 69 | } |
110 | 70 | ||
111 | impl Diagnostic for UnresolvedMacroCall { | ||
112 | fn code(&self) -> DiagnosticCode { | ||
113 | DiagnosticCode("unresolved-macro-call") | ||
114 | } | ||
115 | fn message(&self) -> String { | ||
116 | format!("unresolved macro `{}!`", self.path) | ||
117 | } | ||
118 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
119 | InFile::new(self.file, self.node.clone().into()) | ||
120 | } | ||
121 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
122 | self | ||
123 | } | ||
124 | fn is_experimental(&self) -> bool { | ||
125 | true | ||
126 | } | ||
127 | } | ||
128 | |||
129 | // Diagnostic: inactive-code | ||
130 | // | ||
131 | // This diagnostic is shown for code with inactive `#[cfg]` attributes. | ||
132 | #[derive(Debug, Clone, Eq, PartialEq)] | 71 | #[derive(Debug, Clone, Eq, PartialEq)] |
133 | pub struct InactiveCode { | 72 | pub struct InactiveCode { |
134 | pub file: HirFileId, | 73 | pub node: InFile<SyntaxNodePtr>, |
135 | pub node: SyntaxNodePtr, | ||
136 | pub cfg: CfgExpr, | 74 | pub cfg: CfgExpr, |
137 | pub opts: CfgOptions, | 75 | pub opts: CfgOptions, |
138 | } | 76 | } |
139 | 77 | ||
140 | impl Diagnostic for InactiveCode { | ||
141 | fn code(&self) -> DiagnosticCode { | ||
142 | DiagnosticCode("inactive-code") | ||
143 | } | ||
144 | fn message(&self) -> String { | ||
145 | let inactive = DnfExpr::new(self.cfg.clone()).why_inactive(&self.opts); | ||
146 | let mut buf = "code is inactive due to #[cfg] directives".to_string(); | ||
147 | |||
148 | if let Some(inactive) = inactive { | ||
149 | format_to!(buf, ": {}", inactive); | ||
150 | } | ||
151 | |||
152 | buf | ||
153 | } | ||
154 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
155 | InFile::new(self.file, self.node.clone()) | ||
156 | } | ||
157 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
158 | self | ||
159 | } | ||
160 | } | ||
161 | |||
162 | // Diagnostic: unresolved-proc-macro | ||
163 | // | ||
164 | // This diagnostic is shown when a procedural macro can not be found. This usually means that | ||
165 | // procedural macro support is simply disabled (and hence is only a weak hint instead of an error), | ||
166 | // but can also indicate project setup problems. | ||
167 | // | ||
168 | // If you are seeing a lot of "proc macro not expanded" warnings, you can add this option to the | ||
169 | // `rust-analyzer.diagnostics.disabled` list to prevent them from showing. Alternatively you can | ||
170 | // enable support for procedural macros (see `rust-analyzer.procMacro.enable`). | ||
171 | #[derive(Debug, Clone, Eq, PartialEq)] | 78 | #[derive(Debug, Clone, Eq, PartialEq)] |
172 | pub struct UnresolvedProcMacro { | 79 | pub struct UnresolvedProcMacro { |
173 | pub file: HirFileId, | 80 | pub node: InFile<SyntaxNodePtr>, |
174 | pub node: SyntaxNodePtr, | ||
175 | /// If the diagnostic can be pinpointed more accurately than via `node`, this is the `TextRange` | 81 | /// If the diagnostic can be pinpointed more accurately than via `node`, this is the `TextRange` |
176 | /// to use instead. | 82 | /// to use instead. |
177 | pub precise_location: Option<TextRange>, | 83 | pub precise_location: Option<TextRange>, |
178 | pub macro_name: Option<String>, | 84 | pub macro_name: Option<String>, |
179 | } | 85 | } |
180 | 86 | ||
181 | impl Diagnostic for UnresolvedProcMacro { | 87 | #[derive(Debug, Clone, Eq, PartialEq)] |
182 | fn code(&self) -> DiagnosticCode { | 88 | pub struct MacroError { |
183 | DiagnosticCode("unresolved-proc-macro") | 89 | pub node: InFile<SyntaxNodePtr>, |
184 | } | 90 | pub message: String, |
91 | } | ||
185 | 92 | ||
186 | fn message(&self) -> String { | 93 | #[derive(Debug)] |
187 | match &self.macro_name { | 94 | pub struct UnimplementedBuiltinMacro { |
188 | Some(name) => format!("proc macro `{}` not expanded", name), | 95 | pub node: InFile<SyntaxNodePtr>, |
189 | None => "proc macro not expanded".to_string(), | 96 | } |
190 | } | ||
191 | } | ||
192 | 97 | ||
193 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | 98 | #[derive(Debug)] |
194 | InFile::new(self.file, self.node.clone()) | 99 | pub struct NoSuchField { |
195 | } | 100 | pub field: InFile<AstPtr<ast::RecordExprField>>, |
101 | } | ||
196 | 102 | ||
197 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | 103 | #[derive(Debug)] |
198 | self | 104 | pub struct BreakOutsideOfLoop { |
199 | } | 105 | pub expr: InFile<AstPtr<ast::Expr>>, |
200 | } | 106 | } |
201 | 107 | ||
202 | // Diagnostic: macro-error | 108 | #[derive(Debug)] |
203 | // | 109 | pub struct MissingUnsafe { |
204 | // This diagnostic is shown for macro expansion errors. | 110 | pub expr: InFile<AstPtr<ast::Expr>>, |
205 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
206 | pub struct MacroError { | ||
207 | pub file: HirFileId, | ||
208 | pub node: SyntaxNodePtr, | ||
209 | pub message: String, | ||
210 | } | 111 | } |
211 | 112 | ||
212 | impl Diagnostic for MacroError { | 113 | #[derive(Debug)] |
213 | fn code(&self) -> DiagnosticCode { | 114 | pub struct MissingFields { |
214 | DiagnosticCode("macro-error") | 115 | pub file: HirFileId, |
215 | } | 116 | pub field_list_parent: Either<AstPtr<ast::RecordExpr>, AstPtr<ast::RecordPat>>, |
216 | fn message(&self) -> String { | 117 | pub field_list_parent_path: Option<AstPtr<ast::Path>>, |
217 | self.message.clone() | 118 | pub missed_fields: Vec<Name>, |
218 | } | ||
219 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
220 | InFile::new(self.file, self.node.clone()) | ||
221 | } | ||
222 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
223 | self | ||
224 | } | ||
225 | fn is_experimental(&self) -> bool { | ||
226 | // Newly added and not very well-tested, might contain false positives. | ||
227 | true | ||
228 | } | ||
229 | } | 119 | } |
230 | 120 | ||
231 | #[derive(Debug)] | 121 | #[derive(Debug)] |
232 | pub struct UnimplementedBuiltinMacro { | 122 | pub struct ReplaceFilterMapNextWithFindMap { |
233 | pub file: HirFileId, | 123 | pub file: HirFileId, |
234 | pub node: SyntaxNodePtr, | 124 | /// This expression is the whole method chain up to and including `.filter_map(..).next()`. |
125 | pub next_expr: AstPtr<ast::Expr>, | ||
235 | } | 126 | } |
236 | 127 | ||
237 | impl Diagnostic for UnimplementedBuiltinMacro { | 128 | #[derive(Debug)] |
238 | fn code(&self) -> DiagnosticCode { | 129 | pub struct MismatchedArgCount { |
239 | DiagnosticCode("unimplemented-builtin-macro") | 130 | pub call_expr: InFile<AstPtr<ast::Expr>>, |
240 | } | 131 | pub expected: usize, |
132 | pub found: usize, | ||
133 | } | ||
241 | 134 | ||
242 | fn message(&self) -> String { | 135 | #[derive(Debug)] |
243 | "unimplemented built-in macro".to_string() | 136 | pub struct RemoveThisSemicolon { |
244 | } | 137 | pub expr: InFile<AstPtr<ast::Expr>>, |
138 | } | ||
245 | 139 | ||
246 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | 140 | #[derive(Debug)] |
247 | InFile::new(self.file, self.node.clone()) | 141 | pub struct MissingOkOrSomeInTailExpr { |
248 | } | 142 | pub expr: InFile<AstPtr<ast::Expr>>, |
143 | // `Some` or `Ok` depending on whether the return type is Result or Option | ||
144 | pub required: String, | ||
145 | } | ||
249 | 146 | ||
250 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | 147 | #[derive(Debug)] |
251 | self | 148 | pub struct MissingMatchArms { |
252 | } | 149 | pub file: HirFileId, |
150 | pub match_expr: AstPtr<ast::Expr>, | ||
151 | pub arms: AstPtr<ast::MatchArmList>, | ||
253 | } | 152 | } |
153 | |||
154 | pub use hir_ty::diagnostics::IncorrectCase; | ||
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index d3ef29db4..5bc0b2338 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -15,7 +15,7 @@ | |||
15 | //! | 15 | //! |
16 | //! `hir` is what insulates the "we don't know how to actually write an incremental compiler" | 16 | //! `hir` is what insulates the "we don't know how to actually write an incremental compiler" |
17 | //! from the ide with completions, hovers, etc. It is a (soft, internal) boundary: | 17 | //! from the ide with completions, hovers, etc. It is a (soft, internal) boundary: |
18 | //! https://www.tedinski.com/2018/02/06/system-boundaries.html. | 18 | //! <https://www.tedinski.com/2018/02/06/system-boundaries.html>. |
19 | 19 | ||
20 | #![recursion_limit = "512"] | 20 | #![recursion_limit = "512"] |
21 | 21 | ||
@@ -35,14 +35,10 @@ use std::{iter, sync::Arc}; | |||
35 | 35 | ||
36 | use arrayvec::ArrayVec; | 36 | use arrayvec::ArrayVec; |
37 | use base_db::{CrateDisplayName, CrateId, Edition, FileId}; | 37 | use base_db::{CrateDisplayName, CrateId, Edition, FileId}; |
38 | use diagnostics::{ | ||
39 | InactiveCode, MacroError, UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, | ||
40 | UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro, | ||
41 | }; | ||
42 | use either::Either; | 38 | use either::Either; |
43 | use hir_def::{ | 39 | use hir_def::{ |
44 | adt::{ReprKind, VariantData}, | 40 | adt::{ReprKind, VariantData}, |
45 | body::BodyDiagnostic, | 41 | body::{BodyDiagnostic, SyntheticSyntax}, |
46 | expr::{BindingAnnotation, LabelId, Pat, PatId}, | 42 | expr::{BindingAnnotation, LabelId, Pat, PatId}, |
47 | item_tree::ItemTreeNode, | 43 | item_tree::ItemTreeNode, |
48 | lang_item::LangItemTarget, | 44 | lang_item::LangItemTarget, |
@@ -50,7 +46,6 @@ use hir_def::{ | |||
50 | per_ns::PerNs, | 46 | per_ns::PerNs, |
51 | resolver::{HasResolver, Resolver}, | 47 | resolver::{HasResolver, Resolver}, |
52 | src::HasSource as _, | 48 | src::HasSource as _, |
53 | type_ref::TraitRef, | ||
54 | AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, | 49 | AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, |
55 | DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId, | 50 | DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId, |
56 | LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, | 51 | LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, |
@@ -61,8 +56,8 @@ use hir_ty::{ | |||
61 | autoderef, | 56 | autoderef, |
62 | consteval::ConstExt, | 57 | consteval::ConstExt, |
63 | could_unify, | 58 | could_unify, |
64 | diagnostics_sink::DiagnosticSink, | 59 | diagnostics::BodyValidationDiagnostic, |
65 | method_resolution::{self, def_crates, TyFingerprint}, | 60 | method_resolution::{self, TyFingerprint}, |
66 | primitive::UintTy, | 61 | primitive::UintTy, |
67 | subst_prefix, | 62 | subst_prefix, |
68 | traits::FnTrait, | 63 | traits::FnTrait, |
@@ -73,6 +68,7 @@ use hir_ty::{ | |||
73 | }; | 68 | }; |
74 | use itertools::Itertools; | 69 | use itertools::Itertools; |
75 | use nameres::diagnostics::DefDiagnosticKind; | 70 | use nameres::diagnostics::DefDiagnosticKind; |
71 | use once_cell::unsync::Lazy; | ||
76 | use rustc_hash::FxHashSet; | 72 | use rustc_hash::FxHashSet; |
77 | use stdx::{format_to, impl_from}; | 73 | use stdx::{format_to, impl_from}; |
78 | use syntax::{ | 74 | use syntax::{ |
@@ -85,6 +81,13 @@ use crate::db::{DefDatabase, HirDatabase}; | |||
85 | 81 | ||
86 | pub use crate::{ | 82 | pub use crate::{ |
87 | attrs::{HasAttrs, Namespace}, | 83 | attrs::{HasAttrs, Namespace}, |
84 | diagnostics::{ | ||
85 | AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, MacroError, | ||
86 | MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, | ||
87 | MissingUnsafe, NoSuchField, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap, | ||
88 | UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, | ||
89 | UnresolvedModule, UnresolvedProcMacro, | ||
90 | }, | ||
88 | has_source::HasSource, | 91 | has_source::HasSource, |
89 | semantics::{PathResolution, Semantics, SemanticsScope}, | 92 | semantics::{PathResolution, Semantics, SemanticsScope}, |
90 | }; | 93 | }; |
@@ -192,6 +195,7 @@ impl Crate { | |||
192 | db: &dyn DefDatabase, | 195 | db: &dyn DefDatabase, |
193 | query: import_map::Query, | 196 | query: import_map::Query, |
194 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | 197 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { |
198 | let _p = profile::span("query_external_importables"); | ||
195 | import_map::search_dependencies(db, self.into(), query).into_iter().map(|item| match item { | 199 | import_map::search_dependencies(db, self.into(), query).into_iter().map(|item| match item { |
196 | ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()), | 200 | ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()), |
197 | ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()), | 201 | ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()), |
@@ -332,7 +336,7 @@ impl ModuleDef { | |||
332 | } | 336 | } |
333 | } | 337 | } |
334 | 338 | ||
335 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | 339 | pub fn diagnostics(self, db: &dyn HirDatabase) -> Vec<AnyDiagnostic> { |
336 | let id = match self { | 340 | let id = match self { |
337 | ModuleDef::Adt(it) => match it { | 341 | ModuleDef::Adt(it) => match it { |
338 | Adt::Struct(it) => it.id.into(), | 342 | Adt::Struct(it) => it.id.into(), |
@@ -345,15 +349,19 @@ impl ModuleDef { | |||
345 | ModuleDef::Module(it) => it.id.into(), | 349 | ModuleDef::Module(it) => it.id.into(), |
346 | ModuleDef::Const(it) => it.id.into(), | 350 | ModuleDef::Const(it) => it.id.into(), |
347 | ModuleDef::Static(it) => it.id.into(), | 351 | ModuleDef::Static(it) => it.id.into(), |
348 | _ => return, | 352 | _ => return Vec::new(), |
349 | }; | 353 | }; |
350 | 354 | ||
351 | let module = match self.module(db) { | 355 | let module = match self.module(db) { |
352 | Some(it) => it, | 356 | Some(it) => it, |
353 | None => return, | 357 | None => return Vec::new(), |
354 | }; | 358 | }; |
355 | 359 | ||
356 | hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id, sink) | 360 | let mut acc = Vec::new(); |
361 | for diag in hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id) { | ||
362 | acc.push(diag.into()) | ||
363 | } | ||
364 | acc | ||
357 | } | 365 | } |
358 | } | 366 | } |
359 | 367 | ||
@@ -442,10 +450,10 @@ impl Module { | |||
442 | } | 450 | } |
443 | 451 | ||
444 | pub fn visibility_of(self, db: &dyn HirDatabase, def: &ModuleDef) -> Option<Visibility> { | 452 | pub fn visibility_of(self, db: &dyn HirDatabase, def: &ModuleDef) -> Option<Visibility> { |
445 | self.id.def_map(db.upcast())[self.id.local_id].scope.visibility_of(def.clone().into()) | 453 | self.id.def_map(db.upcast())[self.id.local_id].scope.visibility_of((*def).into()) |
446 | } | 454 | } |
447 | 455 | ||
448 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | 456 | pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) { |
449 | let _p = profile::span("Module::diagnostics").detail(|| { | 457 | let _p = profile::span("Module::diagnostics").detail(|| { |
450 | format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string())) | 458 | format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string())) |
451 | }); | 459 | }); |
@@ -458,18 +466,22 @@ impl Module { | |||
458 | match &diag.kind { | 466 | match &diag.kind { |
459 | DefDiagnosticKind::UnresolvedModule { ast: declaration, candidate } => { | 467 | DefDiagnosticKind::UnresolvedModule { ast: declaration, candidate } => { |
460 | let decl = declaration.to_node(db.upcast()); | 468 | let decl = declaration.to_node(db.upcast()); |
461 | sink.push(UnresolvedModule { | 469 | acc.push( |
462 | file: declaration.file_id, | 470 | UnresolvedModule { |
463 | decl: AstPtr::new(&decl), | 471 | decl: InFile::new(declaration.file_id, AstPtr::new(&decl)), |
464 | candidate: candidate.clone(), | 472 | candidate: candidate.clone(), |
465 | }) | 473 | } |
474 | .into(), | ||
475 | ) | ||
466 | } | 476 | } |
467 | DefDiagnosticKind::UnresolvedExternCrate { ast } => { | 477 | DefDiagnosticKind::UnresolvedExternCrate { ast } => { |
468 | let item = ast.to_node(db.upcast()); | 478 | let item = ast.to_node(db.upcast()); |
469 | sink.push(UnresolvedExternCrate { | 479 | acc.push( |
470 | file: ast.file_id, | 480 | UnresolvedExternCrate { |
471 | item: AstPtr::new(&item), | 481 | decl: InFile::new(ast.file_id, AstPtr::new(&item)), |
472 | }); | 482 | } |
483 | .into(), | ||
484 | ); | ||
473 | } | 485 | } |
474 | 486 | ||
475 | DefDiagnosticKind::UnresolvedImport { id, index } => { | 487 | DefDiagnosticKind::UnresolvedImport { id, index } => { |
@@ -478,25 +490,30 @@ impl Module { | |||
478 | let import = &item_tree[id.value]; | 490 | let import = &item_tree[id.value]; |
479 | 491 | ||
480 | let use_tree = import.use_tree_to_ast(db.upcast(), file_id, *index); | 492 | let use_tree = import.use_tree_to_ast(db.upcast(), file_id, *index); |
481 | sink.push(UnresolvedImport { file: file_id, node: AstPtr::new(&use_tree) }); | 493 | acc.push( |
494 | UnresolvedImport { decl: InFile::new(file_id, AstPtr::new(&use_tree)) } | ||
495 | .into(), | ||
496 | ); | ||
482 | } | 497 | } |
483 | 498 | ||
484 | DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => { | 499 | DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => { |
485 | let item = ast.to_node(db.upcast()); | 500 | let item = ast.to_node(db.upcast()); |
486 | sink.push(InactiveCode { | 501 | acc.push( |
487 | file: ast.file_id, | 502 | InactiveCode { |
488 | node: AstPtr::new(&item).into(), | 503 | node: ast.with_value(AstPtr::new(&item).into()), |
489 | cfg: cfg.clone(), | 504 | cfg: cfg.clone(), |
490 | opts: opts.clone(), | 505 | opts: opts.clone(), |
491 | }); | 506 | } |
507 | .into(), | ||
508 | ); | ||
492 | } | 509 | } |
493 | 510 | ||
494 | DefDiagnosticKind::UnresolvedProcMacro { ast } => { | 511 | DefDiagnosticKind::UnresolvedProcMacro { ast } => { |
495 | let mut precise_location = None; | 512 | let mut precise_location = None; |
496 | let (file, ast, name) = match ast { | 513 | let (node, name) = match ast { |
497 | MacroCallKind::FnLike { ast_id, .. } => { | 514 | MacroCallKind::FnLike { ast_id, .. } => { |
498 | let node = ast_id.to_node(db.upcast()); | 515 | let node = ast_id.to_node(db.upcast()); |
499 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None) | 516 | (ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node))), None) |
500 | } | 517 | } |
501 | MacroCallKind::Derive { ast_id, derive_name, .. } => { | 518 | MacroCallKind::Derive { ast_id, derive_name, .. } => { |
502 | let node = ast_id.to_node(db.upcast()); | 519 | let node = ast_id.to_node(db.upcast()); |
@@ -529,71 +546,84 @@ impl Module { | |||
529 | } | 546 | } |
530 | 547 | ||
531 | ( | 548 | ( |
532 | ast_id.file_id, | 549 | ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node))), |
533 | SyntaxNodePtr::from(AstPtr::new(&node)), | ||
534 | Some(derive_name.clone()), | 550 | Some(derive_name.clone()), |
535 | ) | 551 | ) |
536 | } | 552 | } |
553 | MacroCallKind::Attr { ast_id, invoc_attr_index, attr_name, .. } => { | ||
554 | let node = ast_id.to_node(db.upcast()); | ||
555 | let attr = | ||
556 | node.attrs().nth((*invoc_attr_index) as usize).unwrap_or_else( | ||
557 | || panic!("cannot find attribute #{}", invoc_attr_index), | ||
558 | ); | ||
559 | ( | ||
560 | ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&attr))), | ||
561 | Some(attr_name.clone()), | ||
562 | ) | ||
563 | } | ||
537 | }; | 564 | }; |
538 | sink.push(UnresolvedProcMacro { | 565 | acc.push( |
539 | file, | 566 | UnresolvedProcMacro { node, precise_location, macro_name: name }.into(), |
540 | node: ast, | 567 | ); |
541 | precise_location, | ||
542 | macro_name: name, | ||
543 | }); | ||
544 | } | 568 | } |
545 | 569 | ||
546 | DefDiagnosticKind::UnresolvedMacroCall { ast, path } => { | 570 | DefDiagnosticKind::UnresolvedMacroCall { ast, path } => { |
547 | let node = ast.to_node(db.upcast()); | 571 | let node = ast.to_node(db.upcast()); |
548 | sink.push(UnresolvedMacroCall { | 572 | acc.push( |
549 | file: ast.file_id, | 573 | UnresolvedMacroCall { |
550 | node: AstPtr::new(&node), | 574 | macro_call: InFile::new(ast.file_id, AstPtr::new(&node)), |
551 | path: path.clone(), | 575 | path: path.clone(), |
552 | }); | 576 | } |
577 | .into(), | ||
578 | ); | ||
553 | } | 579 | } |
554 | 580 | ||
555 | DefDiagnosticKind::MacroError { ast, message } => { | 581 | DefDiagnosticKind::MacroError { ast, message } => { |
556 | let (file, ast) = match ast { | 582 | let node = match ast { |
557 | MacroCallKind::FnLike { ast_id, .. } => { | 583 | MacroCallKind::FnLike { ast_id, .. } => { |
558 | let node = ast_id.to_node(db.upcast()); | 584 | let node = ast_id.to_node(db.upcast()); |
559 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) | 585 | ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node))) |
560 | } | 586 | } |
561 | MacroCallKind::Derive { ast_id, .. } => { | 587 | MacroCallKind::Derive { ast_id, .. } |
588 | | MacroCallKind::Attr { ast_id, .. } => { | ||
589 | // FIXME: point to the attribute instead, this creates very large diagnostics | ||
562 | let node = ast_id.to_node(db.upcast()); | 590 | let node = ast_id.to_node(db.upcast()); |
563 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) | 591 | ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node))) |
564 | } | 592 | } |
565 | }; | 593 | }; |
566 | sink.push(MacroError { file, node: ast, message: message.clone() }); | 594 | acc.push(MacroError { node, message: message.clone() }.into()); |
567 | } | 595 | } |
568 | 596 | ||
569 | DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => { | 597 | DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => { |
570 | let node = ast.to_node(db.upcast()); | 598 | let node = ast.to_node(db.upcast()); |
571 | // Must have a name, otherwise we wouldn't emit it. | 599 | // Must have a name, otherwise we wouldn't emit it. |
572 | let name = node.name().expect("unimplemented builtin macro with no name"); | 600 | let name = node.name().expect("unimplemented builtin macro with no name"); |
573 | let ptr = SyntaxNodePtr::from(AstPtr::new(&name)); | 601 | acc.push( |
574 | sink.push(UnimplementedBuiltinMacro { file: ast.file_id, node: ptr }); | 602 | UnimplementedBuiltinMacro { |
603 | node: ast.with_value(SyntaxNodePtr::from(AstPtr::new(&name))), | ||
604 | } | ||
605 | .into(), | ||
606 | ); | ||
575 | } | 607 | } |
576 | } | 608 | } |
577 | } | 609 | } |
578 | for decl in self.declarations(db) { | 610 | for decl in self.declarations(db) { |
579 | match decl { | 611 | match decl { |
580 | crate::ModuleDef::Function(f) => f.diagnostics(db, sink), | 612 | ModuleDef::Function(f) => f.diagnostics(db, acc), |
581 | crate::ModuleDef::Module(m) => { | 613 | ModuleDef::Module(m) => { |
582 | // Only add diagnostics from inline modules | 614 | // Only add diagnostics from inline modules |
583 | if def_map[m.id.local_id].origin.is_inline() { | 615 | if def_map[m.id.local_id].origin.is_inline() { |
584 | m.diagnostics(db, sink) | 616 | m.diagnostics(db, acc) |
585 | } | 617 | } |
586 | } | 618 | } |
587 | _ => { | 619 | _ => acc.extend(decl.diagnostics(db)), |
588 | decl.diagnostics(db, sink); | ||
589 | } | ||
590 | } | 620 | } |
591 | } | 621 | } |
592 | 622 | ||
593 | for impl_def in self.impl_defs(db) { | 623 | for impl_def in self.impl_defs(db) { |
594 | for item in impl_def.items(db) { | 624 | for item in impl_def.items(db) { |
595 | if let AssocItem::Function(f) = item { | 625 | if let AssocItem::Function(f) = item { |
596 | f.diagnostics(db, sink); | 626 | f.diagnostics(db, acc); |
597 | } | 627 | } |
598 | } | 628 | } |
599 | } | 629 | } |
@@ -995,41 +1025,191 @@ impl Function { | |||
995 | db.function_data(self.id).is_async() | 1025 | db.function_data(self.id).is_async() |
996 | } | 1026 | } |
997 | 1027 | ||
998 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | 1028 | pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) { |
999 | let krate = self.module(db).id.krate(); | 1029 | let krate = self.module(db).id.krate(); |
1000 | 1030 | ||
1001 | let source_map = db.body_with_source_map(self.id.into()).1; | 1031 | let source_map = db.body_with_source_map(self.id.into()).1; |
1002 | for diag in source_map.diagnostics() { | 1032 | for diag in source_map.diagnostics() { |
1003 | match diag { | 1033 | match diag { |
1004 | BodyDiagnostic::InactiveCode { node, cfg, opts } => sink.push(InactiveCode { | 1034 | BodyDiagnostic::InactiveCode { node, cfg, opts } => acc.push( |
1005 | file: node.file_id, | 1035 | InactiveCode { node: node.clone(), cfg: cfg.clone(), opts: opts.clone() } |
1006 | node: node.value.clone(), | 1036 | .into(), |
1007 | cfg: cfg.clone(), | 1037 | ), |
1008 | opts: opts.clone(), | 1038 | BodyDiagnostic::MacroError { node, message } => acc.push( |
1009 | }), | 1039 | MacroError { |
1010 | BodyDiagnostic::MacroError { node, message } => sink.push(MacroError { | 1040 | node: node.clone().map(|it| it.into()), |
1011 | file: node.file_id, | 1041 | message: message.to_string(), |
1012 | node: node.value.clone().into(), | 1042 | } |
1013 | message: message.to_string(), | 1043 | .into(), |
1014 | }), | 1044 | ), |
1015 | BodyDiagnostic::UnresolvedProcMacro { node } => sink.push(UnresolvedProcMacro { | 1045 | BodyDiagnostic::UnresolvedProcMacro { node } => acc.push( |
1016 | file: node.file_id, | 1046 | UnresolvedProcMacro { |
1017 | node: node.value.clone().into(), | 1047 | node: node.clone().map(|it| it.into()), |
1018 | precise_location: None, | 1048 | precise_location: None, |
1019 | macro_name: None, | 1049 | macro_name: None, |
1020 | }), | 1050 | } |
1021 | BodyDiagnostic::UnresolvedMacroCall { node, path } => { | 1051 | .into(), |
1022 | sink.push(UnresolvedMacroCall { | 1052 | ), |
1023 | file: node.file_id, | 1053 | BodyDiagnostic::UnresolvedMacroCall { node, path } => acc.push( |
1024 | node: node.value.clone(), | 1054 | UnresolvedMacroCall { macro_call: node.clone(), path: path.clone() }.into(), |
1025 | path: path.clone(), | 1055 | ), |
1026 | }) | 1056 | } |
1057 | } | ||
1058 | |||
1059 | let infer = db.infer(self.id.into()); | ||
1060 | let source_map = Lazy::new(|| db.body_with_source_map(self.id.into()).1); | ||
1061 | for d in &infer.diagnostics { | ||
1062 | match d { | ||
1063 | hir_ty::InferenceDiagnostic::NoSuchField { expr } => { | ||
1064 | let field = source_map.field_syntax(*expr); | ||
1065 | acc.push(NoSuchField { field }.into()) | ||
1066 | } | ||
1067 | hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr } => { | ||
1068 | let expr = source_map | ||
1069 | .expr_syntax(*expr) | ||
1070 | .expect("break outside of loop in synthetic syntax"); | ||
1071 | acc.push(BreakOutsideOfLoop { expr }.into()) | ||
1072 | } | ||
1073 | } | ||
1074 | } | ||
1075 | |||
1076 | for expr in hir_ty::diagnostics::missing_unsafe(db, self.id.into()) { | ||
1077 | match source_map.expr_syntax(expr) { | ||
1078 | Ok(expr) => acc.push(MissingUnsafe { expr }.into()), | ||
1079 | Err(SyntheticSyntax) => { | ||
1080 | // FIXME: Here and eslwhere in this file, the `expr` was | ||
1081 | // desugared, report or assert that this doesn't happen. | ||
1082 | } | ||
1083 | } | ||
1084 | } | ||
1085 | |||
1086 | for diagnostic in BodyValidationDiagnostic::collect(db, self.id.into()) { | ||
1087 | match diagnostic { | ||
1088 | BodyValidationDiagnostic::RecordMissingFields { | ||
1089 | record, | ||
1090 | variant, | ||
1091 | missed_fields, | ||
1092 | } => { | ||
1093 | let variant_data = variant.variant_data(db.upcast()); | ||
1094 | let missed_fields = missed_fields | ||
1095 | .into_iter() | ||
1096 | .map(|idx| variant_data.fields()[idx].name.clone()) | ||
1097 | .collect(); | ||
1098 | |||
1099 | match record { | ||
1100 | Either::Left(record_expr) => match source_map.expr_syntax(record_expr) { | ||
1101 | Ok(source_ptr) => { | ||
1102 | let root = source_ptr.file_syntax(db.upcast()); | ||
1103 | if let ast::Expr::RecordExpr(record_expr) = | ||
1104 | &source_ptr.value.to_node(&root) | ||
1105 | { | ||
1106 | if let Some(_) = record_expr.record_expr_field_list() { | ||
1107 | acc.push( | ||
1108 | MissingFields { | ||
1109 | file: source_ptr.file_id, | ||
1110 | field_list_parent: Either::Left(AstPtr::new( | ||
1111 | record_expr, | ||
1112 | )), | ||
1113 | field_list_parent_path: record_expr | ||
1114 | .path() | ||
1115 | .map(|path| AstPtr::new(&path)), | ||
1116 | missed_fields, | ||
1117 | } | ||
1118 | .into(), | ||
1119 | ) | ||
1120 | } | ||
1121 | } | ||
1122 | } | ||
1123 | Err(SyntheticSyntax) => (), | ||
1124 | }, | ||
1125 | Either::Right(record_pat) => match source_map.pat_syntax(record_pat) { | ||
1126 | Ok(source_ptr) => { | ||
1127 | if let Some(expr) = source_ptr.value.as_ref().left() { | ||
1128 | let root = source_ptr.file_syntax(db.upcast()); | ||
1129 | if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) { | ||
1130 | if let Some(_) = record_pat.record_pat_field_list() { | ||
1131 | acc.push( | ||
1132 | MissingFields { | ||
1133 | file: source_ptr.file_id, | ||
1134 | field_list_parent: Either::Right(AstPtr::new( | ||
1135 | &record_pat, | ||
1136 | )), | ||
1137 | field_list_parent_path: record_pat | ||
1138 | .path() | ||
1139 | .map(|path| AstPtr::new(&path)), | ||
1140 | missed_fields, | ||
1141 | } | ||
1142 | .into(), | ||
1143 | ) | ||
1144 | } | ||
1145 | } | ||
1146 | } | ||
1147 | } | ||
1148 | Err(SyntheticSyntax) => (), | ||
1149 | }, | ||
1150 | } | ||
1151 | } | ||
1152 | BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap { method_call_expr } => { | ||
1153 | if let Ok(next_source_ptr) = source_map.expr_syntax(method_call_expr) { | ||
1154 | acc.push( | ||
1155 | ReplaceFilterMapNextWithFindMap { | ||
1156 | file: next_source_ptr.file_id, | ||
1157 | next_expr: next_source_ptr.value, | ||
1158 | } | ||
1159 | .into(), | ||
1160 | ); | ||
1161 | } | ||
1162 | } | ||
1163 | BodyValidationDiagnostic::MismatchedArgCount { call_expr, expected, found } => { | ||
1164 | match source_map.expr_syntax(call_expr) { | ||
1165 | Ok(source_ptr) => acc.push( | ||
1166 | MismatchedArgCount { call_expr: source_ptr, expected, found }.into(), | ||
1167 | ), | ||
1168 | Err(SyntheticSyntax) => (), | ||
1169 | } | ||
1170 | } | ||
1171 | BodyValidationDiagnostic::RemoveThisSemicolon { expr } => { | ||
1172 | match source_map.expr_syntax(expr) { | ||
1173 | Ok(expr) => acc.push(RemoveThisSemicolon { expr }.into()), | ||
1174 | Err(SyntheticSyntax) => (), | ||
1175 | } | ||
1176 | } | ||
1177 | BodyValidationDiagnostic::MissingOkOrSomeInTailExpr { expr, required } => { | ||
1178 | match source_map.expr_syntax(expr) { | ||
1179 | Ok(expr) => acc.push(MissingOkOrSomeInTailExpr { expr, required }.into()), | ||
1180 | Err(SyntheticSyntax) => (), | ||
1181 | } | ||
1182 | } | ||
1183 | BodyValidationDiagnostic::MissingMatchArms { match_expr } => { | ||
1184 | match source_map.expr_syntax(match_expr) { | ||
1185 | Ok(source_ptr) => { | ||
1186 | let root = source_ptr.file_syntax(db.upcast()); | ||
1187 | if let ast::Expr::MatchExpr(match_expr) = | ||
1188 | &source_ptr.value.to_node(&root) | ||
1189 | { | ||
1190 | if let (Some(match_expr), Some(arms)) = | ||
1191 | (match_expr.expr(), match_expr.match_arm_list()) | ||
1192 | { | ||
1193 | acc.push( | ||
1194 | MissingMatchArms { | ||
1195 | file: source_ptr.file_id, | ||
1196 | match_expr: AstPtr::new(&match_expr), | ||
1197 | arms: AstPtr::new(&arms), | ||
1198 | } | ||
1199 | .into(), | ||
1200 | ) | ||
1201 | } | ||
1202 | } | ||
1203 | } | ||
1204 | Err(SyntheticSyntax) => (), | ||
1205 | } | ||
1027 | } | 1206 | } |
1028 | } | 1207 | } |
1029 | } | 1208 | } |
1030 | 1209 | ||
1031 | hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink); | 1210 | for diag in hir_ty::diagnostics::validate_module_item(db, krate, self.id.into()) { |
1032 | hir_ty::diagnostics::validate_body(db, self.id.into(), sink); | 1211 | acc.push(diag.into()) |
1212 | } | ||
1033 | } | 1213 | } |
1034 | 1214 | ||
1035 | /// Whether this function declaration has a definition. | 1215 | /// Whether this function declaration has a definition. |
@@ -1331,6 +1511,7 @@ impl MacroDef { | |||
1331 | MacroDefKind::Declarative(_) => MacroKind::Declarative, | 1511 | MacroDefKind::Declarative(_) => MacroKind::Declarative, |
1332 | MacroDefKind::BuiltIn(_, _) | MacroDefKind::BuiltInEager(_, _) => MacroKind::BuiltIn, | 1512 | MacroDefKind::BuiltIn(_, _) | MacroDefKind::BuiltInEager(_, _) => MacroKind::BuiltIn, |
1333 | MacroDefKind::BuiltInDerive(_, _) => MacroKind::Derive, | 1513 | MacroDefKind::BuiltInDerive(_, _) => MacroKind::Derive, |
1514 | MacroDefKind::BuiltInAttr(_, _) => MacroKind::Attr, | ||
1334 | MacroDefKind::ProcMacro(_, base_db::ProcMacroKind::CustomDerive, _) => { | 1515 | MacroDefKind::ProcMacro(_, base_db::ProcMacroKind::CustomDerive, _) => { |
1335 | MacroKind::Derive | 1516 | MacroKind::Derive |
1336 | } | 1517 | } |
@@ -1338,6 +1519,13 @@ impl MacroDef { | |||
1338 | MacroDefKind::ProcMacro(_, base_db::ProcMacroKind::FuncLike, _) => MacroKind::ProcMacro, | 1519 | MacroDefKind::ProcMacro(_, base_db::ProcMacroKind::FuncLike, _) => MacroKind::ProcMacro, |
1339 | } | 1520 | } |
1340 | } | 1521 | } |
1522 | |||
1523 | pub fn is_fn_like(&self) -> bool { | ||
1524 | match self.kind() { | ||
1525 | MacroKind::Declarative | MacroKind::BuiltIn | MacroKind::ProcMacro => true, | ||
1526 | MacroKind::Attr | MacroKind::Derive => false, | ||
1527 | } | ||
1528 | } | ||
1341 | } | 1529 | } |
1342 | 1530 | ||
1343 | /// Invariant: `inner.as_assoc_item(db).is_some()` | 1531 | /// Invariant: `inner.as_assoc_item(db).is_some()` |
@@ -1429,6 +1617,20 @@ impl AssocItem { | |||
1429 | _ => None, | 1617 | _ => None, |
1430 | } | 1618 | } |
1431 | } | 1619 | } |
1620 | |||
1621 | pub fn containing_trait_impl(self, db: &dyn HirDatabase) -> Option<Trait> { | ||
1622 | match self.container(db) { | ||
1623 | AssocItemContainer::Impl(i) => i.trait_(db), | ||
1624 | _ => None, | ||
1625 | } | ||
1626 | } | ||
1627 | |||
1628 | pub fn containing_trait_or_trait_impl(self, db: &dyn HirDatabase) -> Option<Trait> { | ||
1629 | match self.container(db) { | ||
1630 | AssocItemContainer::Trait(t) => Some(t), | ||
1631 | AssocItemContainer::Impl(i) => i.trait_(db), | ||
1632 | } | ||
1633 | } | ||
1432 | } | 1634 | } |
1433 | 1635 | ||
1434 | impl HasVisibility for AssocItem { | 1636 | impl HasVisibility for AssocItem { |
@@ -1726,7 +1928,7 @@ impl Impl { | |||
1726 | } | 1928 | } |
1727 | 1929 | ||
1728 | pub fn all_for_type(db: &dyn HirDatabase, Type { krate, ty, .. }: Type) -> Vec<Impl> { | 1930 | pub fn all_for_type(db: &dyn HirDatabase, Type { krate, ty, .. }: Type) -> Vec<Impl> { |
1729 | let def_crates = match def_crates(db, &ty, krate) { | 1931 | let def_crates = match method_resolution::def_crates(db, &ty, krate) { |
1730 | Some(def_crates) => def_crates, | 1932 | Some(def_crates) => def_crates, |
1731 | None => return Vec::new(), | 1933 | None => return Vec::new(), |
1732 | }; | 1934 | }; |
@@ -1783,9 +1985,11 @@ impl Impl { | |||
1783 | } | 1985 | } |
1784 | 1986 | ||
1785 | // FIXME: the return type is wrong. This should be a hir version of | 1987 | // FIXME: the return type is wrong. This should be a hir version of |
1786 | // `TraitRef` (ie, resolved `TypeRef`). | 1988 | // `TraitRef` (to account for parameters and qualifiers) |
1787 | pub fn trait_(self, db: &dyn HirDatabase) -> Option<TraitRef> { | 1989 | pub fn trait_(self, db: &dyn HirDatabase) -> Option<Trait> { |
1788 | db.impl_data(self.id).target_trait.as_deref().cloned() | 1990 | let trait_ref = db.impl_trait(self.id)?.skip_binders().clone(); |
1991 | let id = hir_ty::from_chalk_trait_id(trait_ref.trait_id); | ||
1992 | Some(Trait { id }) | ||
1789 | } | 1993 | } |
1790 | 1994 | ||
1791 | pub fn self_ty(self, db: &dyn HirDatabase) -> Type { | 1995 | pub fn self_ty(self, db: &dyn HirDatabase) -> Type { |
@@ -2130,7 +2334,7 @@ impl Type { | |||
2130 | krate: Crate, | 2334 | krate: Crate, |
2131 | mut callback: impl FnMut(AssocItem) -> Option<T>, | 2335 | mut callback: impl FnMut(AssocItem) -> Option<T>, |
2132 | ) -> Option<T> { | 2336 | ) -> Option<T> { |
2133 | for krate in def_crates(db, &self.ty, krate.id)? { | 2337 | for krate in method_resolution::def_crates(db, &self.ty, krate.id)? { |
2134 | let impls = db.inherent_impls_in_crate(krate); | 2338 | let impls = db.inherent_impls_in_crate(krate); |
2135 | 2339 | ||
2136 | for impl_def in impls.for_self_ty(&self.ty) { | 2340 | for impl_def in impls.for_self_ty(&self.ty) { |
@@ -2162,6 +2366,7 @@ impl Type { | |||
2162 | name: Option<&Name>, | 2366 | name: Option<&Name>, |
2163 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | 2367 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, |
2164 | ) -> Option<T> { | 2368 | ) -> Option<T> { |
2369 | let _p = profile::span("iterate_method_candidates"); | ||
2165 | // There should be no inference vars in types passed here | 2370 | // There should be no inference vars in types passed here |
2166 | // FIXME check that? | 2371 | // FIXME check that? |
2167 | // FIXME replace Unknown by bound vars here | 2372 | // FIXME replace Unknown by bound vars here |
@@ -2195,6 +2400,7 @@ impl Type { | |||
2195 | name: Option<&Name>, | 2400 | name: Option<&Name>, |
2196 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, | 2401 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, |
2197 | ) -> Option<T> { | 2402 | ) -> Option<T> { |
2403 | let _p = profile::span("iterate_path_candidates"); | ||
2198 | let canonical = hir_ty::replace_errors_with_variables(&self.ty); | 2404 | let canonical = hir_ty::replace_errors_with_variables(&self.ty); |
2199 | 2405 | ||
2200 | let env = self.env.clone(); | 2406 | let env = self.env.clone(); |
@@ -2232,6 +2438,7 @@ impl Type { | |||
2232 | &'a self, | 2438 | &'a self, |
2233 | db: &'a dyn HirDatabase, | 2439 | db: &'a dyn HirDatabase, |
2234 | ) -> impl Iterator<Item = Trait> + 'a { | 2440 | ) -> impl Iterator<Item = Trait> + 'a { |
2441 | let _p = profile::span("applicable_inherent_traits"); | ||
2235 | self.autoderef(db) | 2442 | self.autoderef(db) |
2236 | .filter_map(|derefed_type| derefed_type.ty.dyn_trait()) | 2443 | .filter_map(|derefed_type| derefed_type.ty.dyn_trait()) |
2237 | .flat_map(move |dyn_trait_id| hir_ty::all_super_traits(db.upcast(), dyn_trait_id)) | 2444 | .flat_map(move |dyn_trait_id| hir_ty::all_super_traits(db.upcast(), dyn_trait_id)) |
@@ -2304,13 +2511,13 @@ impl Type { | |||
2304 | match ty.kind(&Interner) { | 2511 | match ty.kind(&Interner) { |
2305 | TyKind::Adt(_, substs) => { | 2512 | TyKind::Adt(_, substs) => { |
2306 | cb(type_.derived(ty.clone())); | 2513 | cb(type_.derived(ty.clone())); |
2307 | walk_substs(db, type_, &substs, cb); | 2514 | walk_substs(db, type_, substs, cb); |
2308 | } | 2515 | } |
2309 | TyKind::AssociatedType(_, substs) => { | 2516 | TyKind::AssociatedType(_, substs) => { |
2310 | if let Some(_) = ty.associated_type_parent_trait(db) { | 2517 | if let Some(_) = ty.associated_type_parent_trait(db) { |
2311 | cb(type_.derived(ty.clone())); | 2518 | cb(type_.derived(ty.clone())); |
2312 | } | 2519 | } |
2313 | walk_substs(db, type_, &substs, cb); | 2520 | walk_substs(db, type_, substs, cb); |
2314 | } | 2521 | } |
2315 | TyKind::OpaqueType(_, subst) => { | 2522 | TyKind::OpaqueType(_, subst) => { |
2316 | if let Some(bounds) = ty.impl_trait_bounds(db) { | 2523 | if let Some(bounds) = ty.impl_trait_bounds(db) { |
@@ -2350,7 +2557,7 @@ impl Type { | |||
2350 | TyKind::FnDef(_, substs) | 2557 | TyKind::FnDef(_, substs) |
2351 | | TyKind::Tuple(_, substs) | 2558 | | TyKind::Tuple(_, substs) |
2352 | | TyKind::Closure(.., substs) => { | 2559 | | TyKind::Closure(.., substs) => { |
2353 | walk_substs(db, type_, &substs, cb); | 2560 | walk_substs(db, type_, substs, cb); |
2354 | } | 2561 | } |
2355 | TyKind::Function(hir_ty::FnPointer { substitution, .. }) => { | 2562 | TyKind::Function(hir_ty::FnPointer { substitution, .. }) => { |
2356 | walk_substs(db, type_, &substitution.0, cb); | 2563 | walk_substs(db, type_, &substitution.0, cb); |
@@ -2481,6 +2688,18 @@ impl ScopeDef { | |||
2481 | 2688 | ||
2482 | items | 2689 | items |
2483 | } | 2690 | } |
2691 | |||
2692 | pub fn is_value_def(&self) -> bool { | ||
2693 | matches!( | ||
2694 | self, | ||
2695 | ScopeDef::ModuleDef(ModuleDef::Function(_)) | ||
2696 | | ScopeDef::ModuleDef(ModuleDef::Variant(_)) | ||
2697 | | ScopeDef::ModuleDef(ModuleDef::Const(_)) | ||
2698 | | ScopeDef::ModuleDef(ModuleDef::Static(_)) | ||
2699 | | ScopeDef::GenericParam(GenericParam::ConstParam(_)) | ||
2700 | | ScopeDef::Local(_) | ||
2701 | ) | ||
2702 | } | ||
2484 | } | 2703 | } |
2485 | 2704 | ||
2486 | impl From<ItemInNs> for ScopeDef { | 2705 | impl From<ItemInNs> for ScopeDef { |
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index c7f2c02e4..613266e07 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs | |||
@@ -17,7 +17,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; | |||
17 | use syntax::{ | 17 | use syntax::{ |
18 | algo::find_node_at_offset, | 18 | algo::find_node_at_offset, |
19 | ast::{self, GenericParamsOwner, LoopBodyOwner}, | 19 | ast::{self, GenericParamsOwner, LoopBodyOwner}, |
20 | match_ast, AstNode, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextSize, | 20 | match_ast, AstNode, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize, |
21 | }; | 21 | }; |
22 | 22 | ||
23 | use crate::{ | 23 | use crate::{ |
@@ -35,8 +35,9 @@ pub enum PathResolution { | |||
35 | Def(ModuleDef), | 35 | Def(ModuleDef), |
36 | /// A local binding (only value namespace) | 36 | /// A local binding (only value namespace) |
37 | Local(Local), | 37 | Local(Local), |
38 | /// A generic parameter | 38 | /// A type parameter |
39 | TypeParam(TypeParam), | 39 | TypeParam(TypeParam), |
40 | /// A const parameter | ||
40 | ConstParam(ConstParam), | 41 | ConstParam(ConstParam), |
41 | SelfType(Impl), | 42 | SelfType(Impl), |
42 | Macro(MacroDef), | 43 | Macro(MacroDef), |
@@ -117,6 +118,16 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
117 | pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { | 118 | pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { |
118 | self.imp.expand(macro_call) | 119 | self.imp.expand(macro_call) |
119 | } | 120 | } |
121 | |||
122 | /// If `item` has an attribute macro attached to it, expands it. | ||
123 | pub fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> { | ||
124 | self.imp.expand_attr_macro(item) | ||
125 | } | ||
126 | |||
127 | pub fn is_attr_macro_call(&self, item: &ast::Item) -> bool { | ||
128 | self.imp.is_attr_macro_call(item) | ||
129 | } | ||
130 | |||
120 | pub fn speculative_expand( | 131 | pub fn speculative_expand( |
121 | &self, | 132 | &self, |
122 | actual_macro_call: &ast::MacroCall, | 133 | actual_macro_call: &ast::MacroCall, |
@@ -181,7 +192,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
181 | node: &SyntaxNode, | 192 | node: &SyntaxNode, |
182 | offset: TextSize, | 193 | offset: TextSize, |
183 | ) -> Option<N> { | 194 | ) -> Option<N> { |
184 | if let Some(it) = find_node_at_offset(&node, offset) { | 195 | if let Some(it) = find_node_at_offset(node, offset) { |
185 | return Some(it); | 196 | return Some(it); |
186 | } | 197 | } |
187 | 198 | ||
@@ -332,6 +343,22 @@ impl<'db> SemanticsImpl<'db> { | |||
332 | Some(node) | 343 | Some(node) |
333 | } | 344 | } |
334 | 345 | ||
346 | fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> { | ||
347 | let sa = self.analyze(item.syntax()); | ||
348 | let src = InFile::new(sa.file_id, item.clone()); | ||
349 | let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src))?; | ||
350 | let file_id = macro_call_id.as_file(); | ||
351 | let node = self.db.parse_or_expand(file_id)?; | ||
352 | self.cache(node.clone(), file_id); | ||
353 | Some(node) | ||
354 | } | ||
355 | |||
356 | fn is_attr_macro_call(&self, item: &ast::Item) -> bool { | ||
357 | let sa = self.analyze(item.syntax()); | ||
358 | let src = InFile::new(sa.file_id, item.clone()); | ||
359 | self.with_ctx(|ctx| ctx.item_to_macro_call(src).is_some()) | ||
360 | } | ||
361 | |||
335 | fn speculative_expand( | 362 | fn speculative_expand( |
336 | &self, | 363 | &self, |
337 | actual_macro_call: &ast::MacroCall, | 364 | actual_macro_call: &ast::MacroCall, |
@@ -362,25 +389,65 @@ impl<'db> SemanticsImpl<'db> { | |||
362 | 389 | ||
363 | let token = successors(Some(InFile::new(sa.file_id, token)), |token| { | 390 | let token = successors(Some(InFile::new(sa.file_id, token)), |token| { |
364 | self.db.unwind_if_cancelled(); | 391 | self.db.unwind_if_cancelled(); |
365 | let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?; | 392 | |
366 | let tt = macro_call.token_tree()?; | 393 | for node in token.value.ancestors() { |
367 | if !tt.syntax().text_range().contains_range(token.value.text_range()) { | 394 | match_ast! { |
368 | return None; | 395 | match node { |
369 | } | 396 | ast::MacroCall(macro_call) => { |
370 | let file_id = sa.expand(self.db, token.with_value(¯o_call))?; | 397 | let tt = macro_call.token_tree()?; |
371 | let token = self | 398 | let l_delim = match tt.left_delimiter_token() { |
372 | .expansion_info_cache | 399 | Some(it) => it.text_range().end(), |
373 | .borrow_mut() | 400 | None => tt.syntax().text_range().start() |
374 | .entry(file_id) | 401 | }; |
375 | .or_insert_with(|| file_id.expansion_info(self.db.upcast())) | 402 | let r_delim = match tt.right_delimiter_token() { |
376 | .as_ref()? | 403 | Some(it) => it.text_range().start(), |
377 | .map_token_down(token.as_ref())?; | 404 | None => tt.syntax().text_range().end() |
378 | 405 | }; | |
379 | if let Some(parent) = token.value.parent() { | 406 | if !TextRange::new(l_delim, r_delim).contains_range(token.value.text_range()) { |
380 | self.cache(find_root(&parent), token.file_id); | 407 | return None; |
408 | } | ||
409 | let file_id = sa.expand(self.db, token.with_value(¯o_call))?; | ||
410 | let token = self | ||
411 | .expansion_info_cache | ||
412 | .borrow_mut() | ||
413 | .entry(file_id) | ||
414 | .or_insert_with(|| file_id.expansion_info(self.db.upcast())) | ||
415 | .as_ref()? | ||
416 | .map_token_down(token.as_ref())?; | ||
417 | |||
418 | if let Some(parent) = token.value.parent() { | ||
419 | self.cache(find_root(&parent), token.file_id); | ||
420 | } | ||
421 | |||
422 | return Some(token); | ||
423 | }, | ||
424 | ast::Item(item) => { | ||
425 | match self.with_ctx(|ctx| ctx.item_to_macro_call(token.with_value(item))) { | ||
426 | Some(call_id) => { | ||
427 | let file_id = call_id.as_file(); | ||
428 | let token = self | ||
429 | .expansion_info_cache | ||
430 | .borrow_mut() | ||
431 | .entry(file_id) | ||
432 | .or_insert_with(|| file_id.expansion_info(self.db.upcast())) | ||
433 | .as_ref()? | ||
434 | .map_token_down(token.as_ref())?; | ||
435 | |||
436 | if let Some(parent) = token.value.parent() { | ||
437 | self.cache(find_root(&parent), token.file_id); | ||
438 | } | ||
439 | |||
440 | return Some(token); | ||
441 | } | ||
442 | None => {} | ||
443 | } | ||
444 | }, | ||
445 | _ => {} | ||
446 | } | ||
447 | } | ||
381 | } | 448 | } |
382 | 449 | ||
383 | Some(token) | 450 | None |
384 | }) | 451 | }) |
385 | .last() | 452 | .last() |
386 | .unwrap(); | 453 | .unwrap(); |
@@ -677,7 +744,7 @@ impl<'db> SemanticsImpl<'db> { | |||
677 | return None; | 744 | return None; |
678 | } | 745 | } |
679 | 746 | ||
680 | let func = self.resolve_method_call(&method_call_expr).map(Function::from)?; | 747 | let func = self.resolve_method_call(method_call_expr).map(Function::from)?; |
681 | let res = match func.self_param(self.db)?.access(self.db) { | 748 | let res = match func.self_param(self.db)?.access(self.db) { |
682 | Access::Shared | Access::Exclusive => true, | 749 | Access::Shared | Access::Exclusive => true, |
683 | Access::Owned => false, | 750 | Access::Owned => false, |
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index 9a5a2255f..e8c2ed48e 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs | |||
@@ -10,7 +10,7 @@ use hir_def::{ | |||
10 | ImplId, LifetimeParamId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, | 10 | ImplId, LifetimeParamId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, |
11 | UnionId, VariantId, | 11 | UnionId, VariantId, |
12 | }; | 12 | }; |
13 | use hir_expand::{name::AsName, AstId, MacroDefKind}; | 13 | use hir_expand::{name::AsName, AstId, MacroCallId, MacroDefKind}; |
14 | use rustc_hash::FxHashMap; | 14 | use rustc_hash::FxHashMap; |
15 | use smallvec::SmallVec; | 15 | use smallvec::SmallVec; |
16 | use stdx::impl_from; | 16 | use stdx::impl_from; |
@@ -145,16 +145,25 @@ impl SourceToDefCtx<'_, '_> { | |||
145 | Some((container, label_id)) | 145 | Some((container, label_id)) |
146 | } | 146 | } |
147 | 147 | ||
148 | pub(super) fn item_to_macro_call(&mut self, src: InFile<ast::Item>) -> Option<MacroCallId> { | ||
149 | let map = self.dyn_map(src.as_ref())?; | ||
150 | map[keys::ATTR_MACRO].get(&src).copied() | ||
151 | } | ||
152 | |||
148 | fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>( | 153 | fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>( |
149 | &mut self, | 154 | &mut self, |
150 | src: InFile<Ast>, | 155 | src: InFile<Ast>, |
151 | key: Key<Ast, ID>, | 156 | key: Key<Ast, ID>, |
152 | ) -> Option<ID> { | 157 | ) -> Option<ID> { |
153 | let container = self.find_container(src.as_ref().map(|it| it.syntax()))?; | 158 | self.dyn_map(src.as_ref())?[key].get(&src).copied() |
159 | } | ||
160 | |||
161 | fn dyn_map<Ast: AstNode + 'static>(&mut self, src: InFile<&Ast>) -> Option<&DynMap> { | ||
162 | let container = self.find_container(src.map(|it| it.syntax()))?; | ||
154 | let db = self.db; | 163 | let db = self.db; |
155 | let dyn_map = | 164 | let dyn_map = |
156 | &*self.cache.entry(container).or_insert_with(|| container.child_by_source(db)); | 165 | &*self.cache.entry(container).or_insert_with(|| container.child_by_source(db)); |
157 | dyn_map[key].get(&src).copied() | 166 | Some(dyn_map) |
158 | } | 167 | } |
159 | 168 | ||
160 | pub(super) fn type_param_to_def(&mut self, src: InFile<ast::TypeParam>) -> Option<TypeParamId> { | 169 | pub(super) fn type_param_to_def(&mut self, src: InFile<ast::TypeParam>) -> Option<TypeParamId> { |
@@ -202,62 +211,68 @@ impl SourceToDefCtx<'_, '_> { | |||
202 | 211 | ||
203 | pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> { | 212 | pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> { |
204 | for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) { | 213 | for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) { |
205 | let res: ChildContainer = match_ast! { | 214 | if let Some(res) = self.container_to_def(container) { |
206 | match (container.value) { | 215 | return Some(res); |
207 | ast::Module(it) => { | 216 | } |
208 | let def = self.module_to_def(container.with_value(it))?; | ||
209 | def.into() | ||
210 | }, | ||
211 | ast::Trait(it) => { | ||
212 | let def = self.trait_to_def(container.with_value(it))?; | ||
213 | def.into() | ||
214 | }, | ||
215 | ast::Impl(it) => { | ||
216 | let def = self.impl_to_def(container.with_value(it))?; | ||
217 | def.into() | ||
218 | }, | ||
219 | ast::Fn(it) => { | ||
220 | let def = self.fn_to_def(container.with_value(it))?; | ||
221 | DefWithBodyId::from(def).into() | ||
222 | }, | ||
223 | ast::Struct(it) => { | ||
224 | let def = self.struct_to_def(container.with_value(it))?; | ||
225 | VariantId::from(def).into() | ||
226 | }, | ||
227 | ast::Enum(it) => { | ||
228 | let def = self.enum_to_def(container.with_value(it))?; | ||
229 | def.into() | ||
230 | }, | ||
231 | ast::Union(it) => { | ||
232 | let def = self.union_to_def(container.with_value(it))?; | ||
233 | VariantId::from(def).into() | ||
234 | }, | ||
235 | ast::Static(it) => { | ||
236 | let def = self.static_to_def(container.with_value(it))?; | ||
237 | DefWithBodyId::from(def).into() | ||
238 | }, | ||
239 | ast::Const(it) => { | ||
240 | let def = self.const_to_def(container.with_value(it))?; | ||
241 | DefWithBodyId::from(def).into() | ||
242 | }, | ||
243 | ast::TypeAlias(it) => { | ||
244 | let def = self.type_alias_to_def(container.with_value(it))?; | ||
245 | def.into() | ||
246 | }, | ||
247 | ast::Variant(it) => { | ||
248 | let def = self.enum_variant_to_def(container.with_value(it))?; | ||
249 | VariantId::from(def).into() | ||
250 | }, | ||
251 | _ => continue, | ||
252 | } | ||
253 | }; | ||
254 | return Some(res); | ||
255 | } | 217 | } |
256 | 218 | ||
257 | let def = self.file_to_def(src.file_id.original_file(self.db.upcast())).get(0).copied()?; | 219 | let def = self.file_to_def(src.file_id.original_file(self.db.upcast())).get(0).copied()?; |
258 | Some(def.into()) | 220 | Some(def.into()) |
259 | } | 221 | } |
260 | 222 | ||
223 | fn container_to_def(&mut self, container: InFile<SyntaxNode>) -> Option<ChildContainer> { | ||
224 | let cont = match_ast! { | ||
225 | match (container.value) { | ||
226 | ast::Module(it) => { | ||
227 | let def = self.module_to_def(container.with_value(it))?; | ||
228 | def.into() | ||
229 | }, | ||
230 | ast::Trait(it) => { | ||
231 | let def = self.trait_to_def(container.with_value(it))?; | ||
232 | def.into() | ||
233 | }, | ||
234 | ast::Impl(it) => { | ||
235 | let def = self.impl_to_def(container.with_value(it))?; | ||
236 | def.into() | ||
237 | }, | ||
238 | ast::Fn(it) => { | ||
239 | let def = self.fn_to_def(container.with_value(it))?; | ||
240 | DefWithBodyId::from(def).into() | ||
241 | }, | ||
242 | ast::Struct(it) => { | ||
243 | let def = self.struct_to_def(container.with_value(it))?; | ||
244 | VariantId::from(def).into() | ||
245 | }, | ||
246 | ast::Enum(it) => { | ||
247 | let def = self.enum_to_def(container.with_value(it))?; | ||
248 | def.into() | ||
249 | }, | ||
250 | ast::Union(it) => { | ||
251 | let def = self.union_to_def(container.with_value(it))?; | ||
252 | VariantId::from(def).into() | ||
253 | }, | ||
254 | ast::Static(it) => { | ||
255 | let def = self.static_to_def(container.with_value(it))?; | ||
256 | DefWithBodyId::from(def).into() | ||
257 | }, | ||
258 | ast::Const(it) => { | ||
259 | let def = self.const_to_def(container.with_value(it))?; | ||
260 | DefWithBodyId::from(def).into() | ||
261 | }, | ||
262 | ast::TypeAlias(it) => { | ||
263 | let def = self.type_alias_to_def(container.with_value(it))?; | ||
264 | def.into() | ||
265 | }, | ||
266 | ast::Variant(it) => { | ||
267 | let def = self.enum_variant_to_def(container.with_value(it))?; | ||
268 | VariantId::from(def).into() | ||
269 | }, | ||
270 | _ => return None, | ||
271 | } | ||
272 | }; | ||
273 | Some(cont) | ||
274 | } | ||
275 | |||
261 | fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<GenericDefId> { | 276 | fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<GenericDefId> { |
262 | for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) { | 277 | for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) { |
263 | let res: GenericDefId = match_ast! { | 278 | let res: GenericDefId = match_ast! { |
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 37a050415..c9744d81d 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs | |||
@@ -222,7 +222,7 @@ impl SourceAnalyzer { | |||
222 | Pat::Path(path) => path, | 222 | Pat::Path(path) => path, |
223 | _ => return None, | 223 | _ => return None, |
224 | }; | 224 | }; |
225 | let res = resolve_hir_path(db, &self.resolver, &path)?; | 225 | let res = resolve_hir_path(db, &self.resolver, path)?; |
226 | match res { | 226 | match res { |
227 | PathResolution::Def(def) => Some(def), | 227 | PathResolution::Def(def) => Some(def), |
228 | _ => None, | 228 | _ => None, |
@@ -329,7 +329,7 @@ impl SourceAnalyzer { | |||
329 | 329 | ||
330 | let (variant, missing_fields, _exhaustive) = | 330 | let (variant, missing_fields, _exhaustive) = |
331 | record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?; | 331 | record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?; |
332 | let res = self.missing_fields(db, krate, &substs, variant, missing_fields); | 332 | let res = self.missing_fields(db, krate, substs, variant, missing_fields); |
333 | Some(res) | 333 | Some(res) |
334 | } | 334 | } |
335 | 335 | ||
@@ -347,7 +347,7 @@ impl SourceAnalyzer { | |||
347 | 347 | ||
348 | let (variant, missing_fields, _exhaustive) = | 348 | let (variant, missing_fields, _exhaustive) = |
349 | record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?; | 349 | record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?; |
350 | let res = self.missing_fields(db, krate, &substs, variant, missing_fields); | 350 | let res = self.missing_fields(db, krate, substs, variant, missing_fields); |
351 | Some(res) | 351 | Some(res) |
352 | } | 352 | } |
353 | 353 | ||
diff --git a/crates/hir_def/Cargo.toml b/crates/hir_def/Cargo.toml index 43324d8d9..bb86f6a73 100644 --- a/crates/hir_def/Cargo.toml +++ b/crates/hir_def/Cargo.toml | |||
@@ -10,7 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = { version = "1.1", features = ["thread-local"] } | 13 | cov-mark = "2.0.0-pre.1" |
14 | dashmap = { version = "4.0.2", features = ["raw-api"] } | 14 | dashmap = { version = "4.0.2", features = ["raw-api"] } |
15 | log = "0.4.8" | 15 | log = "0.4.8" |
16 | once_cell = "1.3.1" | 16 | once_cell = "1.3.1" |
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index 385ba8c80..d07adb084 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -36,6 +36,10 @@ use crate::{ | |||
36 | pub struct Documentation(String); | 36 | pub struct Documentation(String); |
37 | 37 | ||
38 | impl Documentation { | 38 | impl Documentation { |
39 | pub fn new(s: String) -> Self { | ||
40 | Documentation(s) | ||
41 | } | ||
42 | |||
39 | pub fn as_str(&self) -> &str { | 43 | pub fn as_str(&self) -> &str { |
40 | &self.0 | 44 | &self.0 |
41 | } | 45 | } |
@@ -102,7 +106,9 @@ impl RawAttrs { | |||
102 | ) -> Self { | 106 | ) -> Self { |
103 | let entries = collect_attrs(owner) | 107 | let entries = collect_attrs(owner) |
104 | .flat_map(|(id, attr)| match attr { | 108 | .flat_map(|(id, attr)| match attr { |
105 | Either::Left(attr) => Attr::from_src(db, attr, hygiene, id), | 109 | Either::Left(attr) => { |
110 | attr.meta().and_then(|meta| Attr::from_src(db, meta, hygiene, id)) | ||
111 | } | ||
106 | Either::Right(comment) => comment.doc_comment().map(|doc| Attr { | 112 | Either::Right(comment) => comment.doc_comment().map(|doc| Attr { |
107 | id, | 113 | id, |
108 | input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))), | 114 | input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))), |
@@ -168,10 +174,9 @@ impl RawAttrs { | |||
168 | let index = attr.id; | 174 | let index = attr.id; |
169 | let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| { | 175 | let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| { |
170 | let tree = Subtree { delimiter: None, token_trees: attr.to_vec() }; | 176 | let tree = Subtree { delimiter: None, token_trees: attr.to_vec() }; |
171 | let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; | ||
172 | // FIXME hygiene | 177 | // FIXME hygiene |
173 | let hygiene = Hygiene::new_unhygienic(); | 178 | let hygiene = Hygiene::new_unhygienic(); |
174 | Attr::from_src(db, attr, &hygiene, index) | 179 | Attr::from_tt(db, &tree, &hygiene, index) |
175 | }); | 180 | }); |
176 | 181 | ||
177 | let cfg_options = &crate_graph[krate].cfg_options; | 182 | let cfg_options = &crate_graph[krate].cfg_options; |
@@ -578,13 +583,13 @@ impl AttrSourceMap { | |||
578 | .get(id.ast_index as usize) | 583 | .get(id.ast_index as usize) |
579 | .unwrap_or_else(|| panic!("cannot find doc comment at index {:?}", id)) | 584 | .unwrap_or_else(|| panic!("cannot find doc comment at index {:?}", id)) |
580 | .clone() | 585 | .clone() |
581 | .map(|attr| Either::Right(attr)) | 586 | .map(Either::Right) |
582 | } else { | 587 | } else { |
583 | self.attrs | 588 | self.attrs |
584 | .get(id.ast_index as usize) | 589 | .get(id.ast_index as usize) |
585 | .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", id)) | 590 | .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", id)) |
586 | .clone() | 591 | .clone() |
587 | .map(|attr| Either::Left(attr)) | 592 | .map(Either::Left) |
588 | } | 593 | } |
589 | } | 594 | } |
590 | } | 595 | } |
@@ -601,7 +606,7 @@ pub struct DocsRangeMap { | |||
601 | impl DocsRangeMap { | 606 | impl DocsRangeMap { |
602 | pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> { | 607 | pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> { |
603 | let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?; | 608 | let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?; |
604 | let (line_docs_range, idx, original_line_src_range) = self.mapping[found].clone(); | 609 | let (line_docs_range, idx, original_line_src_range) = self.mapping[found]; |
605 | if !line_docs_range.contains_range(range) { | 610 | if !line_docs_range.contains_range(range) { |
606 | return None; | 611 | return None; |
607 | } | 612 | } |
@@ -660,7 +665,7 @@ impl fmt::Display for AttrInput { | |||
660 | impl Attr { | 665 | impl Attr { |
661 | fn from_src( | 666 | fn from_src( |
662 | db: &dyn DefDatabase, | 667 | db: &dyn DefDatabase, |
663 | ast: ast::Attr, | 668 | ast: ast::Meta, |
664 | hygiene: &Hygiene, | 669 | hygiene: &Hygiene, |
665 | id: AttrId, | 670 | id: AttrId, |
666 | ) -> Option<Attr> { | 671 | ) -> Option<Attr> { |
@@ -679,6 +684,19 @@ impl Attr { | |||
679 | Some(Attr { id, path, input }) | 684 | Some(Attr { id, path, input }) |
680 | } | 685 | } |
681 | 686 | ||
687 | fn from_tt( | ||
688 | db: &dyn DefDatabase, | ||
689 | tt: &tt::Subtree, | ||
690 | hygiene: &Hygiene, | ||
691 | id: AttrId, | ||
692 | ) -> Option<Attr> { | ||
693 | let (parse, _) = | ||
694 | mbe::token_tree_to_syntax_node(tt, hir_expand::FragmentKind::MetaItem).ok()?; | ||
695 | let ast = ast::Meta::cast(parse.syntax_node())?; | ||
696 | |||
697 | Self::from_src(db, ast, hygiene, id) | ||
698 | } | ||
699 | |||
682 | /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths | 700 | /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths |
683 | /// to derive macros. | 701 | /// to derive macros. |
684 | /// | 702 | /// |
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index da1fdac33..bed4c4994 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -1000,18 +1000,18 @@ impl From<ast::LiteralKind> for Literal { | |||
1000 | // FIXME: these should have actual values filled in, but unsure on perf impact | 1000 | // FIXME: these should have actual values filled in, but unsure on perf impact |
1001 | LiteralKind::IntNumber(lit) => { | 1001 | LiteralKind::IntNumber(lit) => { |
1002 | if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) { | 1002 | if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) { |
1003 | return Literal::Float(Default::default(), builtin); | 1003 | Literal::Float(Default::default(), builtin) |
1004 | } else if let builtin @ Some(_) = | 1004 | } else if let builtin @ Some(_) = |
1005 | lit.suffix().and_then(|it| BuiltinInt::from_suffix(&it)) | 1005 | lit.suffix().and_then(|it| BuiltinInt::from_suffix(it)) |
1006 | { | 1006 | { |
1007 | Literal::Int(lit.value().unwrap_or(0) as i128, builtin) | 1007 | Literal::Int(lit.value().unwrap_or(0) as i128, builtin) |
1008 | } else { | 1008 | } else { |
1009 | let builtin = lit.suffix().and_then(|it| BuiltinUint::from_suffix(&it)); | 1009 | let builtin = lit.suffix().and_then(|it| BuiltinUint::from_suffix(it)); |
1010 | Literal::Uint(lit.value().unwrap_or(0), builtin) | 1010 | Literal::Uint(lit.value().unwrap_or(0), builtin) |
1011 | } | 1011 | } |
1012 | } | 1012 | } |
1013 | LiteralKind::FloatNumber(lit) => { | 1013 | LiteralKind::FloatNumber(lit) => { |
1014 | let ty = lit.suffix().and_then(|it| BuiltinFloat::from_suffix(&it)); | 1014 | let ty = lit.suffix().and_then(|it| BuiltinFloat::from_suffix(it)); |
1015 | Literal::Float(Default::default(), ty) | 1015 | Literal::Float(Default::default(), ty) |
1016 | } | 1016 | } |
1017 | LiteralKind::ByteString(bs) => { | 1017 | LiteralKind::ByteString(bs) => { |
diff --git a/crates/hir_def/src/body/scope.rs b/crates/hir_def/src/body/scope.rs index 6764de3a7..58a1fc81c 100644 --- a/crates/hir_def/src/body/scope.rs +++ b/crates/hir_def/src/body/scope.rs | |||
@@ -198,7 +198,7 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope | |||
198 | } | 198 | } |
199 | Expr::Lambda { args, body: body_expr, .. } => { | 199 | Expr::Lambda { args, body: body_expr, .. } => { |
200 | let scope = scopes.new_scope(scope); | 200 | let scope = scopes.new_scope(scope); |
201 | scopes.add_params_bindings(body, scope, &args); | 201 | scopes.add_params_bindings(body, scope, args); |
202 | compute_expr_scopes(*body_expr, body, scopes, scope); | 202 | compute_expr_scopes(*body_expr, body, scopes, scope); |
203 | } | 203 | } |
204 | Expr::Match { expr, arms } => { | 204 | Expr::Match { expr, arms } => { |
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs index d4fae05a6..27d837d47 100644 --- a/crates/hir_def/src/body/tests.rs +++ b/crates/hir_def/src/body/tests.rs | |||
@@ -3,7 +3,7 @@ mod block; | |||
3 | use base_db::{fixture::WithFixture, SourceDatabase}; | 3 | use base_db::{fixture::WithFixture, SourceDatabase}; |
4 | use expect_test::Expect; | 4 | use expect_test::Expect; |
5 | 5 | ||
6 | use crate::{test_db::TestDB, ModuleDefId}; | 6 | use crate::ModuleDefId; |
7 | 7 | ||
8 | use super::*; | 8 | use super::*; |
9 | 9 | ||
@@ -28,11 +28,6 @@ fn lower(ra_fixture: &str) -> Arc<Body> { | |||
28 | db.body(fn_def.unwrap().into()) | 28 | db.body(fn_def.unwrap().into()) |
29 | } | 29 | } |
30 | 30 | ||
31 | fn check_diagnostics(ra_fixture: &str) { | ||
32 | let db: TestDB = TestDB::with_files(ra_fixture); | ||
33 | db.check_diagnostics(); | ||
34 | } | ||
35 | |||
36 | fn block_def_map_at(ra_fixture: &str) -> String { | 31 | fn block_def_map_at(ra_fixture: &str) -> String { |
37 | let (db, position) = crate::test_db::TestDB::with_position(ra_fixture); | 32 | let (db, position) = crate::test_db::TestDB::with_position(ra_fixture); |
38 | 33 | ||
@@ -57,7 +52,7 @@ fn check_at(ra_fixture: &str, expect: Expect) { | |||
57 | fn your_stack_belongs_to_me() { | 52 | fn your_stack_belongs_to_me() { |
58 | cov_mark::check!(your_stack_belongs_to_me); | 53 | cov_mark::check!(your_stack_belongs_to_me); |
59 | lower( | 54 | lower( |
60 | " | 55 | r#" |
61 | macro_rules! n_nuple { | 56 | macro_rules! n_nuple { |
62 | ($e:tt) => (); | 57 | ($e:tt) => (); |
63 | ($($rest:tt)*) => {{ | 58 | ($($rest:tt)*) => {{ |
@@ -65,7 +60,7 @@ macro_rules! n_nuple { | |||
65 | }}; | 60 | }}; |
66 | } | 61 | } |
67 | fn main() { n_nuple!(1,2,3); } | 62 | fn main() { n_nuple!(1,2,3); } |
68 | ", | 63 | "#, |
69 | ); | 64 | ); |
70 | } | 65 | } |
71 | 66 | ||
@@ -73,7 +68,7 @@ fn main() { n_nuple!(1,2,3); } | |||
73 | fn macro_resolve() { | 68 | fn macro_resolve() { |
74 | // Regression test for a path resolution bug introduced with inner item handling. | 69 | // Regression test for a path resolution bug introduced with inner item handling. |
75 | lower( | 70 | lower( |
76 | r" | 71 | r#" |
77 | macro_rules! vec { | 72 | macro_rules! vec { |
78 | () => { () }; | 73 | () => { () }; |
79 | ($elem:expr; $n:expr) => { () }; | 74 | ($elem:expr; $n:expr) => { () }; |
@@ -84,140 +79,6 @@ mod m { | |||
84 | let _ = vec![FileSet::default(); self.len()]; | 79 | let _ = vec![FileSet::default(); self.len()]; |
85 | } | 80 | } |
86 | } | 81 | } |
87 | ", | 82 | "#, |
88 | ); | ||
89 | } | ||
90 | |||
91 | #[test] | ||
92 | fn cfg_diagnostics() { | ||
93 | check_diagnostics( | ||
94 | r" | ||
95 | fn f() { | ||
96 | // The three g̶e̶n̶d̶e̶r̶s̶ statements: | ||
97 | |||
98 | #[cfg(a)] fn f() {} // Item statement | ||
99 | //^^^^^^^^^^^^^^^^^^^ InactiveCode | ||
100 | #[cfg(a)] {} // Expression statement | ||
101 | //^^^^^^^^^^^^ InactiveCode | ||
102 | #[cfg(a)] let x = 0; // let statement | ||
103 | //^^^^^^^^^^^^^^^^^^^^ InactiveCode | ||
104 | |||
105 | abc(#[cfg(a)] 0); | ||
106 | //^^^^^^^^^^^ InactiveCode | ||
107 | let x = Struct { | ||
108 | #[cfg(a)] f: 0, | ||
109 | //^^^^^^^^^^^^^^ InactiveCode | ||
110 | }; | ||
111 | match () { | ||
112 | () => (), | ||
113 | #[cfg(a)] () => (), | ||
114 | //^^^^^^^^^^^^^^^^^^ InactiveCode | ||
115 | } | ||
116 | |||
117 | #[cfg(a)] 0 // Trailing expression of block | ||
118 | //^^^^^^^^^^^ InactiveCode | ||
119 | } | ||
120 | ", | ||
121 | ); | ||
122 | } | ||
123 | |||
124 | #[test] | ||
125 | fn macro_diag_builtin() { | ||
126 | check_diagnostics( | ||
127 | r#" | ||
128 | #[rustc_builtin_macro] | ||
129 | macro_rules! env {} | ||
130 | |||
131 | #[rustc_builtin_macro] | ||
132 | macro_rules! include {} | ||
133 | |||
134 | #[rustc_builtin_macro] | ||
135 | macro_rules! compile_error {} | ||
136 | |||
137 | #[rustc_builtin_macro] | ||
138 | macro_rules! format_args { | ||
139 | () => {} | ||
140 | } | ||
141 | |||
142 | fn f() { | ||
143 | // Test a handful of built-in (eager) macros: | ||
144 | |||
145 | include!(invalid); | ||
146 | //^^^^^^^^^^^^^^^^^ could not convert tokens | ||
147 | include!("does not exist"); | ||
148 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `does not exist` | ||
149 | |||
150 | env!(invalid); | ||
151 | //^^^^^^^^^^^^^ could not convert tokens | ||
152 | |||
153 | env!("OUT_DIR"); | ||
154 | //^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix | ||
155 | |||
156 | compile_error!("compile_error works"); | ||
157 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ compile_error works | ||
158 | |||
159 | // Lazy: | ||
160 | |||
161 | format_args!(); | ||
162 | //^^^^^^^^^^^^^^ no rule matches input tokens | ||
163 | } | ||
164 | "#, | ||
165 | ); | ||
166 | } | ||
167 | |||
168 | #[test] | ||
169 | fn macro_rules_diag() { | ||
170 | check_diagnostics( | ||
171 | r#" | ||
172 | macro_rules! m { | ||
173 | () => {}; | ||
174 | } | ||
175 | fn f() { | ||
176 | m!(); | ||
177 | |||
178 | m!(hi); | ||
179 | //^^^^^^ leftover tokens | ||
180 | } | ||
181 | "#, | ||
182 | ); | 83 | ); |
183 | } | 84 | } |
184 | |||
185 | #[test] | ||
186 | fn unresolved_macro_diag() { | ||
187 | check_diagnostics( | ||
188 | r#" | ||
189 | fn f() { | ||
190 | m!(); | ||
191 | //^^^^ UnresolvedMacroCall | ||
192 | } | ||
193 | "#, | ||
194 | ); | ||
195 | } | ||
196 | |||
197 | #[test] | ||
198 | fn dollar_crate_in_builtin_macro() { | ||
199 | check_diagnostics( | ||
200 | r#" | ||
201 | #[macro_export] | ||
202 | #[rustc_builtin_macro] | ||
203 | macro_rules! format_args {} | ||
204 | |||
205 | #[macro_export] | ||
206 | macro_rules! arg { | ||
207 | () => {} | ||
208 | } | ||
209 | |||
210 | #[macro_export] | ||
211 | macro_rules! outer { | ||
212 | () => { | ||
213 | $crate::format_args!( "", $crate::arg!(1) ) | ||
214 | }; | ||
215 | } | ||
216 | |||
217 | fn f() { | ||
218 | outer!(); | ||
219 | //^^^^^^^^ leftover tokens | ||
220 | } | ||
221 | "#, | ||
222 | ) | ||
223 | } | ||
diff --git a/crates/hir_def/src/body/tests/block.rs b/crates/hir_def/src/body/tests/block.rs index bc3d0f138..15c10d053 100644 --- a/crates/hir_def/src/body/tests/block.rs +++ b/crates/hir_def/src/body/tests/block.rs | |||
@@ -163,14 +163,14 @@ fn legacy_macro_items() { | |||
163 | // correctly. | 163 | // correctly. |
164 | check_at( | 164 | check_at( |
165 | r#" | 165 | r#" |
166 | macro_rules! hit { | 166 | macro_rules! mark { |
167 | () => { | 167 | () => { |
168 | struct Hit {} | 168 | struct Hit {} |
169 | } | 169 | } |
170 | } | 170 | } |
171 | 171 | ||
172 | fn f() { | 172 | fn f() { |
173 | hit!(); | 173 | mark!(); |
174 | $0 | 174 | $0 |
175 | } | 175 | } |
176 | "#, | 176 | "#, |
@@ -193,20 +193,20 @@ use core::cov_mark; | |||
193 | 193 | ||
194 | fn f() { | 194 | fn f() { |
195 | fn nested() { | 195 | fn nested() { |
196 | cov_mark::hit!(Hit); | 196 | cov_mark::mark!(Hit); |
197 | $0 | 197 | $0 |
198 | } | 198 | } |
199 | } | 199 | } |
200 | //- /core.rs crate:core | 200 | //- /core.rs crate:core |
201 | pub mod cov_mark { | 201 | pub mod cov_mark { |
202 | #[macro_export] | 202 | #[macro_export] |
203 | macro_rules! _hit { | 203 | macro_rules! _mark { |
204 | ($name:ident) => { | 204 | ($name:ident) => { |
205 | struct $name {} | 205 | struct $name {} |
206 | } | 206 | } |
207 | } | 207 | } |
208 | 208 | ||
209 | pub use crate::_hit as hit; | 209 | pub use crate::_mark as mark; |
210 | } | 210 | } |
211 | "#, | 211 | "#, |
212 | expect![[r#" | 212 | expect![[r#" |
diff --git a/crates/hir_def/src/builtin_attr.rs b/crates/hir_def/src/builtin_attr.rs index d5d7f0f47..39c7f84f7 100644 --- a/crates/hir_def/src/builtin_attr.rs +++ b/crates/hir_def/src/builtin_attr.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | //! | 2 | //! |
3 | //! The actual definitions were copied from rustc's `compiler/rustc_feature/src/builtin_attrs.rs`. | 3 | //! The actual definitions were copied from rustc's `compiler/rustc_feature/src/builtin_attrs.rs`. |
4 | //! | 4 | //! |
5 | //! It was last synchronized with upstream commit 2225ee1b62ff089917434aefd9b2bf509cfa087f. | 5 | //! It was last synchronized with upstream commit 835150e70288535bc57bb624792229b9dc94991d. |
6 | //! | 6 | //! |
7 | //! The macros were adjusted to only expand to the attribute name, since that is all we need to do | 7 | //! The macros were adjusted to only expand to the attribute name, since that is all we need to do |
8 | //! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to | 8 | //! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to |
@@ -58,7 +58,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ | |||
58 | ungated!(reexport_test_harness_main, Normal, template!(NameValueStr: "name")), | 58 | ungated!(reexport_test_harness_main, Normal, template!(NameValueStr: "name")), |
59 | 59 | ||
60 | // Macros: | 60 | // Macros: |
61 | ungated!(derive, Normal, template!(List: "Trait1, Trait2, ...")), | ||
62 | ungated!(automatically_derived, Normal, template!(Word)), | 61 | ungated!(automatically_derived, Normal, template!(Word)), |
63 | // FIXME(#14407) | 62 | // FIXME(#14407) |
64 | ungated!(macro_use, Normal, template!(Word, List: "name1, name2, ...")), | 63 | ungated!(macro_use, Normal, template!(Word, List: "name1, name2, ...")), |
@@ -98,8 +97,8 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ | |||
98 | template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#), | 97 | template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#), |
99 | ), | 98 | ), |
100 | ungated!(link_name, AssumedUsed, template!(NameValueStr: "name")), | 99 | ungated!(link_name, AssumedUsed, template!(NameValueStr: "name")), |
101 | ungated!(no_link, Normal, template!(Word)), | 100 | ungated!(no_link, AssumedUsed, template!(Word)), |
102 | ungated!(repr, Normal, template!(List: "C")), | 101 | ungated!(repr, AssumedUsed, template!(List: "C")), |
103 | ungated!(export_name, AssumedUsed, template!(NameValueStr: "name")), | 102 | ungated!(export_name, AssumedUsed, template!(NameValueStr: "name")), |
104 | ungated!(link_section, AssumedUsed, template!(NameValueStr: "name")), | 103 | ungated!(link_section, AssumedUsed, template!(NameValueStr: "name")), |
105 | ungated!(no_mangle, AssumedUsed, template!(Word)), | 104 | ungated!(no_mangle, AssumedUsed, template!(Word)), |
@@ -112,6 +111,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ | |||
112 | const_eval_limit, CrateLevel, template!(NameValueStr: "N"), const_eval_limit, | 111 | const_eval_limit, CrateLevel, template!(NameValueStr: "N"), const_eval_limit, |
113 | experimental!(const_eval_limit) | 112 | experimental!(const_eval_limit) |
114 | ), | 113 | ), |
114 | gated!( | ||
115 | move_size_limit, CrateLevel, template!(NameValueStr: "N"), large_assignments, | ||
116 | experimental!(move_size_limit) | ||
117 | ), | ||
115 | 118 | ||
116 | // Entry point: | 119 | // Entry point: |
117 | ungated!(main, Normal, template!(Word)), | 120 | ungated!(main, Normal, template!(Word)), |
@@ -140,6 +143,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ | |||
140 | template!(List: "address, memory, thread"), | 143 | template!(List: "address, memory, thread"), |
141 | experimental!(no_sanitize) | 144 | experimental!(no_sanitize) |
142 | ), | 145 | ), |
146 | gated!(no_coverage, AssumedUsed, template!(Word), experimental!(no_coverage)), | ||
143 | 147 | ||
144 | // FIXME: #14408 assume docs are used since rustdoc looks at them. | 148 | // FIXME: #14408 assume docs are used since rustdoc looks at them. |
145 | ungated!(doc, AssumedUsed, template!(List: "hidden|inline|...", NameValueStr: "string")), | 149 | ungated!(doc, AssumedUsed, template!(List: "hidden|inline|...", NameValueStr: "string")), |
@@ -151,11 +155,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ | |||
151 | // Linking: | 155 | // Linking: |
152 | gated!(naked, AssumedUsed, template!(Word), naked_functions, experimental!(naked)), | 156 | gated!(naked, AssumedUsed, template!(Word), naked_functions, experimental!(naked)), |
153 | gated!( | 157 | gated!( |
154 | link_args, Normal, template!(NameValueStr: "args"), | ||
155 | "the `link_args` attribute is experimental and not portable across platforms, \ | ||
156 | it is recommended to use `#[link(name = \"foo\")] instead", | ||
157 | ), | ||
158 | gated!( | ||
159 | link_ordinal, AssumedUsed, template!(List: "ordinal"), raw_dylib, | 158 | link_ordinal, AssumedUsed, template!(List: "ordinal"), raw_dylib, |
160 | experimental!(link_ordinal) | 159 | experimental!(link_ordinal) |
161 | ), | 160 | ), |
@@ -172,7 +171,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ | |||
172 | "custom test frameworks are an unstable feature", | 171 | "custom test frameworks are an unstable feature", |
173 | ), | 172 | ), |
174 | // RFC #1268 | 173 | // RFC #1268 |
175 | gated!(marker, Normal, template!(Word), marker_trait_attr, experimental!(marker)), | 174 | gated!(marker, AssumedUsed, template!(Word), marker_trait_attr, experimental!(marker)), |
176 | gated!( | 175 | gated!( |
177 | thread_local, AssumedUsed, template!(Word), | 176 | thread_local, AssumedUsed, template!(Word), |
178 | "`#[thread_local]` is an experimental feature, and does not currently handle destructors", | 177 | "`#[thread_local]` is an experimental feature, and does not currently handle destructors", |
@@ -291,7 +290,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ | |||
291 | // Internal attributes, Macro related: | 290 | // Internal attributes, Macro related: |
292 | // ========================================================================== | 291 | // ========================================================================== |
293 | 292 | ||
294 | rustc_attr!(rustc_builtin_macro, AssumedUsed, template!(Word), IMPL_DETAIL), | 293 | rustc_attr!(rustc_builtin_macro, AssumedUsed, template!(Word, NameValueStr: "name"), IMPL_DETAIL), |
295 | rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), INTERNAL_UNSTABLE), | 294 | rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), INTERNAL_UNSTABLE), |
296 | rustc_attr!( | 295 | rustc_attr!( |
297 | rustc_macro_transparency, AssumedUsed, | 296 | rustc_macro_transparency, AssumedUsed, |
@@ -319,7 +318,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ | |||
319 | // ========================================================================== | 318 | // ========================================================================== |
320 | 319 | ||
321 | rustc_attr!(rustc_promotable, AssumedUsed, template!(Word), IMPL_DETAIL), | 320 | rustc_attr!(rustc_promotable, AssumedUsed, template!(Word), IMPL_DETAIL), |
322 | rustc_attr!(rustc_args_required_const, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE), | 321 | rustc_attr!(rustc_legacy_const_generics, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE), |
323 | 322 | ||
324 | // ========================================================================== | 323 | // ========================================================================== |
325 | // Internal attributes, Layout related: | 324 | // Internal attributes, Layout related: |
@@ -380,6 +379,15 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ | |||
380 | rustc_specialization_trait, Normal, template!(Word), | 379 | rustc_specialization_trait, Normal, template!(Word), |
381 | "the `#[rustc_specialization_trait]` attribute is used to check specializations" | 380 | "the `#[rustc_specialization_trait]` attribute is used to check specializations" |
382 | ), | 381 | ), |
382 | rustc_attr!( | ||
383 | rustc_main, Normal, template!(Word), | ||
384 | "the `#[rustc_main]` attribute is used internally to specify test entry point function", | ||
385 | ), | ||
386 | rustc_attr!( | ||
387 | rustc_skip_array_during_method_dispatch, Normal, template!(Word), | ||
388 | "the `#[rustc_skip_array_during_method_dispatch]` attribute is used to exclude a trait \ | ||
389 | from method dispatch when the receiver is an array, for compatibility in editions < 2021." | ||
390 | ), | ||
383 | 391 | ||
384 | // ========================================================================== | 392 | // ========================================================================== |
385 | // Internal attributes, Testing: | 393 | // Internal attributes, Testing: |
@@ -387,6 +395,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ | |||
387 | 395 | ||
388 | rustc_attr!(TEST, rustc_outlives, Normal, template!(Word)), | 396 | rustc_attr!(TEST, rustc_outlives, Normal, template!(Word)), |
389 | rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word)), | 397 | rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word)), |
398 | rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word)), | ||
390 | rustc_attr!(TEST, rustc_variance, Normal, template!(Word)), | 399 | rustc_attr!(TEST, rustc_variance, Normal, template!(Word)), |
391 | rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ...")), | 400 | rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ...")), |
392 | rustc_attr!(TEST, rustc_regions, Normal, template!(Word)), | 401 | rustc_attr!(TEST, rustc_regions, Normal, template!(Word)), |
@@ -395,13 +404,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ | |||
395 | template!(Word, List: "delay_span_bug_from_inside_query") | 404 | template!(Word, List: "delay_span_bug_from_inside_query") |
396 | ), | 405 | ), |
397 | rustc_attr!(TEST, rustc_dump_user_substs, AssumedUsed, template!(Word)), | 406 | rustc_attr!(TEST, rustc_dump_user_substs, AssumedUsed, template!(Word)), |
407 | rustc_attr!(TEST, rustc_evaluate_where_clauses, AssumedUsed, template!(Word)), | ||
398 | rustc_attr!(TEST, rustc_if_this_changed, AssumedUsed, template!(Word, List: "DepNode")), | 408 | rustc_attr!(TEST, rustc_if_this_changed, AssumedUsed, template!(Word, List: "DepNode")), |
399 | rustc_attr!(TEST, rustc_then_this_would_need, AssumedUsed, template!(List: "DepNode")), | 409 | rustc_attr!(TEST, rustc_then_this_would_need, AssumedUsed, template!(List: "DepNode")), |
400 | rustc_attr!( | 410 | rustc_attr!( |
401 | TEST, rustc_dirty, AssumedUsed, | ||
402 | template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#), | ||
403 | ), | ||
404 | rustc_attr!( | ||
405 | TEST, rustc_clean, AssumedUsed, | 411 | TEST, rustc_clean, AssumedUsed, |
406 | template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#), | 412 | template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#), |
407 | ), | 413 | ), |
diff --git a/crates/hir_def/src/child_by_source.rs b/crates/hir_def/src/child_by_source.rs index f2e809ca9..f22383e22 100644 --- a/crates/hir_def/src/child_by_source.rs +++ b/crates/hir_def/src/child_by_source.rs | |||
@@ -85,6 +85,10 @@ impl ChildBySource for ItemScope { | |||
85 | res[keys::CONST].insert(src, konst); | 85 | res[keys::CONST].insert(src, konst); |
86 | }); | 86 | }); |
87 | self.impls().for_each(|imp| add_impl(db, res, imp)); | 87 | self.impls().for_each(|imp| add_impl(db, res, imp)); |
88 | self.attr_macro_invocs().for_each(|(ast_id, call_id)| { | ||
89 | let item = ast_id.with_value(ast_id.to_node(db.upcast())); | ||
90 | res[keys::ATTR_MACRO].insert(item, call_id); | ||
91 | }); | ||
88 | 92 | ||
89 | fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) { | 93 | fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) { |
90 | match item { | 94 | match item { |
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs index d2bb381be..52cb7777b 100644 --- a/crates/hir_def/src/data.rs +++ b/crates/hir_def/src/data.rs | |||
@@ -143,6 +143,10 @@ pub struct TraitData { | |||
143 | pub is_auto: bool, | 143 | pub is_auto: bool, |
144 | pub is_unsafe: bool, | 144 | pub is_unsafe: bool, |
145 | pub visibility: RawVisibility, | 145 | pub visibility: RawVisibility, |
146 | /// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore | ||
147 | /// method calls to this trait's methods when the receiver is an array and the crate edition is | ||
148 | /// 2015 or 2018. | ||
149 | pub skip_array_during_method_dispatch: bool, | ||
146 | } | 150 | } |
147 | 151 | ||
148 | impl TraitData { | 152 | impl TraitData { |
@@ -157,6 +161,10 @@ impl TraitData { | |||
157 | let container = AssocContainerId::TraitId(tr); | 161 | let container = AssocContainerId::TraitId(tr); |
158 | let visibility = item_tree[tr_def.visibility].clone(); | 162 | let visibility = item_tree[tr_def.visibility].clone(); |
159 | let mut expander = Expander::new(db, tr_loc.id.file_id(), module_id); | 163 | let mut expander = Expander::new(db, tr_loc.id.file_id(), module_id); |
164 | let skip_array_during_method_dispatch = item_tree | ||
165 | .attrs(db, tr_loc.container.krate(), ModItem::from(tr_loc.id.value).into()) | ||
166 | .by_key("rustc_skip_array_during_method_dispatch") | ||
167 | .exists(); | ||
160 | 168 | ||
161 | let items = collect_items( | 169 | let items = collect_items( |
162 | db, | 170 | db, |
@@ -168,7 +176,14 @@ impl TraitData { | |||
168 | 100, | 176 | 100, |
169 | ); | 177 | ); |
170 | 178 | ||
171 | Arc::new(TraitData { name, items, is_auto, is_unsafe, visibility }) | 179 | Arc::new(TraitData { |
180 | name, | ||
181 | items, | ||
182 | is_auto, | ||
183 | is_unsafe, | ||
184 | visibility, | ||
185 | skip_array_during_method_dispatch, | ||
186 | }) | ||
172 | } | 187 | } |
173 | 188 | ||
174 | pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ { | 189 | pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ { |
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs index 7eadc8e0d..c977971cd 100644 --- a/crates/hir_def/src/db.rs +++ b/crates/hir_def/src/db.rs | |||
@@ -51,6 +51,9 @@ pub trait InternDatabase: SourceDatabase { | |||
51 | 51 | ||
52 | #[salsa::query_group(DefDatabaseStorage)] | 52 | #[salsa::query_group(DefDatabaseStorage)] |
53 | pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | 53 | pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { |
54 | #[salsa::input] | ||
55 | fn enable_proc_attr_macros(&self) -> bool; | ||
56 | |||
54 | #[salsa::invoke(ItemTree::file_item_tree_query)] | 57 | #[salsa::invoke(ItemTree::file_item_tree_query)] |
55 | fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>; | 58 | fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>; |
56 | 59 | ||
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs index 44d22b918..6933f6e3c 100644 --- a/crates/hir_def/src/generics.rs +++ b/crates/hir_def/src/generics.rs | |||
@@ -280,7 +280,7 @@ impl GenericParams { | |||
280 | sm.type_params.insert(param_id, Either::Right(type_param.clone())); | 280 | sm.type_params.insert(param_id, Either::Right(type_param.clone())); |
281 | 281 | ||
282 | let type_ref = TypeRef::Path(name.into()); | 282 | let type_ref = TypeRef::Path(name.into()); |
283 | self.fill_bounds(&lower_ctx, &type_param, Either::Left(type_ref)); | 283 | self.fill_bounds(lower_ctx, &type_param, Either::Left(type_ref)); |
284 | } | 284 | } |
285 | for lifetime_param in params.lifetime_params() { | 285 | for lifetime_param in params.lifetime_params() { |
286 | let name = | 286 | let name = |
@@ -289,7 +289,7 @@ impl GenericParams { | |||
289 | let param_id = self.lifetimes.alloc(param); | 289 | let param_id = self.lifetimes.alloc(param); |
290 | sm.lifetime_params.insert(param_id, lifetime_param.clone()); | 290 | sm.lifetime_params.insert(param_id, lifetime_param.clone()); |
291 | let lifetime_ref = LifetimeRef::new_name(name); | 291 | let lifetime_ref = LifetimeRef::new_name(name); |
292 | self.fill_bounds(&lower_ctx, &lifetime_param, Either::Right(lifetime_ref)); | 292 | self.fill_bounds(lower_ctx, &lifetime_param, Either::Right(lifetime_ref)); |
293 | } | 293 | } |
294 | for const_param in params.const_params() { | 294 | for const_param in params.const_params() { |
295 | let name = const_param.name().map_or_else(Name::missing, |it| it.as_name()); | 295 | let name = const_param.name().map_or_else(Name::missing, |it| it.as_name()); |
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs index 960cabb5f..404e3e153 100644 --- a/crates/hir_def/src/import_map.rs +++ b/crates/hir_def/src/import_map.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! A map of all publicly exported items in a crate. | 1 | //! A map of all publicly exported items in a crate. |
2 | 2 | ||
3 | use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc}; | 3 | use std::{fmt, hash::BuildHasherDefault, sync::Arc}; |
4 | 4 | ||
5 | use base_db::CrateId; | 5 | use base_db::CrateId; |
6 | use fst::{self, Streamer}; | 6 | use fst::{self, Streamer}; |
@@ -69,81 +69,11 @@ pub struct ImportMap { | |||
69 | impl ImportMap { | 69 | impl ImportMap { |
70 | pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> { | 70 | pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> { |
71 | let _p = profile::span("import_map_query"); | 71 | let _p = profile::span("import_map_query"); |
72 | let def_map = db.crate_def_map(krate); | ||
73 | let mut import_map = Self::default(); | ||
74 | |||
75 | // We look only into modules that are public(ly reexported), starting with the crate root. | ||
76 | let empty = ImportPath { segments: vec![] }; | ||
77 | let root = def_map.module_id(def_map.root()); | ||
78 | let mut worklist = vec![(root, empty)]; | ||
79 | while let Some((module, mod_path)) = worklist.pop() { | ||
80 | let ext_def_map; | ||
81 | let mod_data = if module.krate == krate { | ||
82 | &def_map[module.local_id] | ||
83 | } else { | ||
84 | // The crate might reexport a module defined in another crate. | ||
85 | ext_def_map = module.def_map(db); | ||
86 | &ext_def_map[module.local_id] | ||
87 | }; | ||
88 | |||
89 | let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| { | ||
90 | let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public); | ||
91 | if per_ns.is_none() { | ||
92 | None | ||
93 | } else { | ||
94 | Some((name, per_ns)) | ||
95 | } | ||
96 | }); | ||
97 | |||
98 | for (name, per_ns) in visible_items { | ||
99 | let mk_path = || { | ||
100 | let mut path = mod_path.clone(); | ||
101 | path.segments.push(name.clone()); | ||
102 | path | ||
103 | }; | ||
104 | |||
105 | for item in per_ns.iter_items() { | ||
106 | let path = mk_path(); | ||
107 | let path_len = path.len(); | ||
108 | let import_info = | ||
109 | ImportInfo { path, container: module, is_trait_assoc_item: false }; | ||
110 | |||
111 | if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() { | ||
112 | import_map.collect_trait_assoc_items( | ||
113 | db, | ||
114 | tr, | ||
115 | matches!(item, ItemInNs::Types(_)), | ||
116 | &import_info, | ||
117 | ); | ||
118 | } | ||
119 | 72 | ||
120 | match import_map.map.entry(item) { | 73 | let mut import_map = collect_import_map(db, krate); |
121 | Entry::Vacant(entry) => { | ||
122 | entry.insert(import_info); | ||
123 | } | ||
124 | Entry::Occupied(mut entry) => { | ||
125 | // If the new path is shorter, prefer that one. | ||
126 | if path_len < entry.get().path.len() { | ||
127 | *entry.get_mut() = import_info; | ||
128 | } else { | ||
129 | continue; | ||
130 | } | ||
131 | } | ||
132 | } | ||
133 | |||
134 | // If we've just added a path to a module, descend into it. We might traverse | ||
135 | // modules multiple times, but only if the new path to it is shorter than the | ||
136 | // first (else we `continue` above). | ||
137 | if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { | ||
138 | worklist.push((mod_id, mk_path())); | ||
139 | } | ||
140 | } | ||
141 | } | ||
142 | } | ||
143 | 74 | ||
144 | let mut importables = import_map.map.iter().collect::<Vec<_>>(); | 75 | let mut importables = import_map.map.iter().collect::<Vec<_>>(); |
145 | 76 | importables.sort_by_cached_key(|(_, import_info)| fst_path(&import_info.path)); | |
146 | importables.sort_by(cmp); | ||
147 | 77 | ||
148 | // Build the FST, taking care not to insert duplicate values. | 78 | // Build the FST, taking care not to insert duplicate values. |
149 | 79 | ||
@@ -151,13 +81,13 @@ impl ImportMap { | |||
151 | let mut last_batch_start = 0; | 81 | let mut last_batch_start = 0; |
152 | 82 | ||
153 | for idx in 0..importables.len() { | 83 | for idx in 0..importables.len() { |
154 | if let Some(next_item) = importables.get(idx + 1) { | 84 | let key = fst_path(&importables[last_batch_start].1.path); |
155 | if cmp(&importables[last_batch_start], next_item) == Ordering::Equal { | 85 | if let Some((_, next_import_info)) = importables.get(idx + 1) { |
86 | if key == fst_path(&next_import_info.path) { | ||
156 | continue; | 87 | continue; |
157 | } | 88 | } |
158 | } | 89 | } |
159 | 90 | ||
160 | let key = fst_path(&importables[last_batch_start].1.path); | ||
161 | builder.insert(key, last_batch_start as u64).unwrap(); | 91 | builder.insert(key, last_batch_start as u64).unwrap(); |
162 | 92 | ||
163 | last_batch_start = idx + 1; | 93 | last_batch_start = idx + 1; |
@@ -185,6 +115,7 @@ impl ImportMap { | |||
185 | is_type_in_ns: bool, | 115 | is_type_in_ns: bool, |
186 | original_import_info: &ImportInfo, | 116 | original_import_info: &ImportInfo, |
187 | ) { | 117 | ) { |
118 | let _p = profile::span("collect_trait_assoc_items"); | ||
188 | for (assoc_item_name, item) in &db.trait_data(tr).items { | 119 | for (assoc_item_name, item) in &db.trait_data(tr).items { |
189 | let module_def_id = match item { | 120 | let module_def_id = match item { |
190 | AssocItemId::FunctionId(f) => ModuleDefId::from(*f), | 121 | AssocItemId::FunctionId(f) => ModuleDefId::from(*f), |
@@ -210,6 +141,84 @@ impl ImportMap { | |||
210 | } | 141 | } |
211 | } | 142 | } |
212 | 143 | ||
144 | fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap { | ||
145 | let _p = profile::span("collect_import_map"); | ||
146 | |||
147 | let def_map = db.crate_def_map(krate); | ||
148 | let mut import_map = ImportMap::default(); | ||
149 | |||
150 | // We look only into modules that are public(ly reexported), starting with the crate root. | ||
151 | let empty = ImportPath { segments: vec![] }; | ||
152 | let root = def_map.module_id(def_map.root()); | ||
153 | let mut worklist = vec![(root, empty)]; | ||
154 | while let Some((module, mod_path)) = worklist.pop() { | ||
155 | let ext_def_map; | ||
156 | let mod_data = if module.krate == krate { | ||
157 | &def_map[module.local_id] | ||
158 | } else { | ||
159 | // The crate might reexport a module defined in another crate. | ||
160 | ext_def_map = module.def_map(db); | ||
161 | &ext_def_map[module.local_id] | ||
162 | }; | ||
163 | |||
164 | let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| { | ||
165 | let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public); | ||
166 | if per_ns.is_none() { | ||
167 | None | ||
168 | } else { | ||
169 | Some((name, per_ns)) | ||
170 | } | ||
171 | }); | ||
172 | |||
173 | for (name, per_ns) in visible_items { | ||
174 | let mk_path = || { | ||
175 | let mut path = mod_path.clone(); | ||
176 | path.segments.push(name.clone()); | ||
177 | path | ||
178 | }; | ||
179 | |||
180 | for item in per_ns.iter_items() { | ||
181 | let path = mk_path(); | ||
182 | let path_len = path.len(); | ||
183 | let import_info = | ||
184 | ImportInfo { path, container: module, is_trait_assoc_item: false }; | ||
185 | |||
186 | if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() { | ||
187 | import_map.collect_trait_assoc_items( | ||
188 | db, | ||
189 | tr, | ||
190 | matches!(item, ItemInNs::Types(_)), | ||
191 | &import_info, | ||
192 | ); | ||
193 | } | ||
194 | |||
195 | match import_map.map.entry(item) { | ||
196 | Entry::Vacant(entry) => { | ||
197 | entry.insert(import_info); | ||
198 | } | ||
199 | Entry::Occupied(mut entry) => { | ||
200 | // If the new path is shorter, prefer that one. | ||
201 | if path_len < entry.get().path.len() { | ||
202 | *entry.get_mut() = import_info; | ||
203 | } else { | ||
204 | continue; | ||
205 | } | ||
206 | } | ||
207 | } | ||
208 | |||
209 | // If we've just added a path to a module, descend into it. We might traverse | ||
210 | // modules multiple times, but only if the new path to it is shorter than the | ||
211 | // first (else we `continue` above). | ||
212 | if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { | ||
213 | worklist.push((mod_id, mk_path())); | ||
214 | } | ||
215 | } | ||
216 | } | ||
217 | } | ||
218 | |||
219 | import_map | ||
220 | } | ||
221 | |||
213 | impl PartialEq for ImportMap { | 222 | impl PartialEq for ImportMap { |
214 | fn eq(&self, other: &Self) -> bool { | 223 | fn eq(&self, other: &Self) -> bool { |
215 | // `fst` and `importables` are built from `map`, so we don't need to compare them. | 224 | // `fst` and `importables` are built from `map`, so we don't need to compare them. |
@@ -240,17 +249,12 @@ impl fmt::Debug for ImportMap { | |||
240 | } | 249 | } |
241 | 250 | ||
242 | fn fst_path(path: &ImportPath) -> String { | 251 | fn fst_path(path: &ImportPath) -> String { |
252 | let _p = profile::span("fst_path"); | ||
243 | let mut s = path.to_string(); | 253 | let mut s = path.to_string(); |
244 | s.make_ascii_lowercase(); | 254 | s.make_ascii_lowercase(); |
245 | s | 255 | s |
246 | } | 256 | } |
247 | 257 | ||
248 | fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) -> Ordering { | ||
249 | let lhs_str = fst_path(&lhs.path); | ||
250 | let rhs_str = fst_path(&rhs.path); | ||
251 | lhs_str.cmp(&rhs_str) | ||
252 | } | ||
253 | |||
254 | #[derive(Debug, Eq, PartialEq, Hash)] | 258 | #[derive(Debug, Eq, PartialEq, Hash)] |
255 | pub enum ImportKind { | 259 | pub enum ImportKind { |
256 | Module, | 260 | Module, |
@@ -338,6 +342,7 @@ impl Query { | |||
338 | } | 342 | } |
339 | 343 | ||
340 | fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool { | 344 | fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool { |
345 | let _p = profile::span("import_map::Query::import_matches"); | ||
341 | if import.is_trait_assoc_item { | 346 | if import.is_trait_assoc_item { |
342 | if self.exclude_import_kinds.contains(&ImportKind::AssociatedItem) { | 347 | if self.exclude_import_kinds.contains(&ImportKind::AssociatedItem) { |
343 | return false; | 348 | return false; |
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs index 9014468ea..08407ebfa 100644 --- a/crates/hir_def/src/item_scope.rs +++ b/crates/hir_def/src/item_scope.rs | |||
@@ -4,11 +4,11 @@ | |||
4 | use std::collections::hash_map::Entry; | 4 | use std::collections::hash_map::Entry; |
5 | 5 | ||
6 | use base_db::CrateId; | 6 | use base_db::CrateId; |
7 | use hir_expand::name::Name; | 7 | use hir_expand::{name::Name, AstId, MacroCallId, MacroDefKind}; |
8 | use hir_expand::MacroDefKind; | ||
9 | use once_cell::sync::Lazy; | 8 | use once_cell::sync::Lazy; |
10 | use rustc_hash::{FxHashMap, FxHashSet}; | 9 | use rustc_hash::{FxHashMap, FxHashSet}; |
11 | use stdx::format_to; | 10 | use stdx::format_to; |
11 | use syntax::ast; | ||
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
14 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, ImplId, | 14 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, ImplId, |
@@ -53,12 +53,13 @@ pub struct ItemScope { | |||
53 | // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will | 53 | // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will |
54 | // be all resolved to the last one defined if shadowing happens. | 54 | // be all resolved to the last one defined if shadowing happens. |
55 | legacy_macros: FxHashMap<Name, MacroDefId>, | 55 | legacy_macros: FxHashMap<Name, MacroDefId>, |
56 | attr_macros: FxHashMap<AstId<ast::Item>, MacroCallId>, | ||
56 | } | 57 | } |
57 | 58 | ||
58 | pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { | 59 | pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { |
59 | BuiltinType::ALL | 60 | BuiltinType::ALL |
60 | .iter() | 61 | .iter() |
61 | .map(|(name, ty)| (name.clone(), PerNs::types(ty.clone().into(), Visibility::Public))) | 62 | .map(|(name, ty)| (name.clone(), PerNs::types((*ty).into(), Visibility::Public))) |
62 | .collect() | 63 | .collect() |
63 | }); | 64 | }); |
64 | 65 | ||
@@ -169,6 +170,16 @@ impl ItemScope { | |||
169 | self.legacy_macros.insert(name, mac); | 170 | self.legacy_macros.insert(name, mac); |
170 | } | 171 | } |
171 | 172 | ||
173 | pub(crate) fn add_attr_macro_invoc(&mut self, item: AstId<ast::Item>, call: MacroCallId) { | ||
174 | self.attr_macros.insert(item, call); | ||
175 | } | ||
176 | |||
177 | pub(crate) fn attr_macro_invocs( | ||
178 | &self, | ||
179 | ) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ { | ||
180 | self.attr_macros.iter().map(|(k, v)| (*k, *v)) | ||
181 | } | ||
182 | |||
172 | pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> { | 183 | pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> { |
173 | self.unnamed_trait_imports.get(&tr).copied() | 184 | self.unnamed_trait_imports.get(&tr).copied() |
174 | } | 185 | } |
@@ -307,6 +318,7 @@ impl ItemScope { | |||
307 | unnamed_consts, | 318 | unnamed_consts, |
308 | unnamed_trait_imports, | 319 | unnamed_trait_imports, |
309 | legacy_macros, | 320 | legacy_macros, |
321 | attr_macros, | ||
310 | } = self; | 322 | } = self; |
311 | types.shrink_to_fit(); | 323 | types.shrink_to_fit(); |
312 | values.shrink_to_fit(); | 324 | values.shrink_to_fit(); |
@@ -317,6 +329,7 @@ impl ItemScope { | |||
317 | unnamed_consts.shrink_to_fit(); | 329 | unnamed_consts.shrink_to_fit(); |
318 | unnamed_trait_imports.shrink_to_fit(); | 330 | unnamed_trait_imports.shrink_to_fit(); |
319 | legacy_macros.shrink_to_fit(); | 331 | legacy_macros.shrink_to_fit(); |
332 | attr_macros.shrink_to_fit(); | ||
320 | } | 333 | } |
321 | } | 334 | } |
322 | 335 | ||
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index 6208facd5..3f90bda74 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -130,7 +130,7 @@ impl<'a> Ctx<'a> { | |||
130 | ast::Item::ExternBlock(ast) => self.lower_extern_block(ast).into(), | 130 | ast::Item::ExternBlock(ast) => self.lower_extern_block(ast).into(), |
131 | }; | 131 | }; |
132 | 132 | ||
133 | self.add_attrs(item.into(), attrs.clone()); | 133 | self.add_attrs(item.into(), attrs); |
134 | 134 | ||
135 | Some(item) | 135 | Some(item) |
136 | } | 136 | } |
@@ -276,10 +276,11 @@ impl<'a> Ctx<'a> { | |||
276 | let visibility = self.lower_visibility(enum_); | 276 | let visibility = self.lower_visibility(enum_); |
277 | let name = enum_.name()?.as_name(); | 277 | let name = enum_.name()?.as_name(); |
278 | let generic_params = self.lower_generic_params(GenericsOwner::Enum, enum_); | 278 | let generic_params = self.lower_generic_params(GenericsOwner::Enum, enum_); |
279 | let variants = match &enum_.variant_list() { | 279 | let variants = |
280 | Some(variant_list) => self.lower_variants(variant_list), | 280 | self.with_inherited_visibility(visibility, |this| match &enum_.variant_list() { |
281 | None => IdRange::new(self.next_variant_idx()..self.next_variant_idx()), | 281 | Some(variant_list) => this.lower_variants(variant_list), |
282 | }; | 282 | None => IdRange::new(this.next_variant_idx()..this.next_variant_idx()), |
283 | }); | ||
283 | let ast_id = self.source_ast_id_map.ast_id(enum_); | 284 | let ast_id = self.source_ast_id_map.ast_id(enum_); |
284 | let res = Enum { name, visibility, generic_params, variants, ast_id }; | 285 | let res = Enum { name, visibility, generic_params, variants, ast_id }; |
285 | Some(id(self.data().enums.alloc(res))) | 286 | Some(id(self.data().enums.alloc(res))) |
@@ -822,7 +823,7 @@ fn is_intrinsic_fn_unsafe(name: &Name) -> bool { | |||
822 | known::type_name, | 823 | known::type_name, |
823 | known::variant_count, | 824 | known::variant_count, |
824 | ] | 825 | ] |
825 | .contains(&name) | 826 | .contains(name) |
826 | } | 827 | } |
827 | 828 | ||
828 | fn lower_abi(abi: ast::Abi) -> Interned<str> { | 829 | fn lower_abi(abi: ast::Abi) -> Interned<str> { |
@@ -854,7 +855,7 @@ impl UseTreeLowering<'_> { | |||
854 | // E.g. `use something::{inner}` (prefix is `None`, path is `something`) | 855 | // E.g. `use something::{inner}` (prefix is `None`, path is `something`) |
855 | // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`) | 856 | // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`) |
856 | Some(path) => { | 857 | Some(path) => { |
857 | match ModPath::from_src(self.db, path, &self.hygiene) { | 858 | match ModPath::from_src(self.db, path, self.hygiene) { |
858 | Some(it) => Some(it), | 859 | Some(it) => Some(it), |
859 | None => return None, // FIXME: report errors somewhere | 860 | None => return None, // FIXME: report errors somewhere |
860 | } | 861 | } |
@@ -873,7 +874,7 @@ impl UseTreeLowering<'_> { | |||
873 | } else { | 874 | } else { |
874 | let is_glob = tree.star_token().is_some(); | 875 | let is_glob = tree.star_token().is_some(); |
875 | let path = match tree.path() { | 876 | let path = match tree.path() { |
876 | Some(path) => Some(ModPath::from_src(self.db, path, &self.hygiene)?), | 877 | Some(path) => Some(ModPath::from_src(self.db, path, self.hygiene)?), |
877 | None => None, | 878 | None => None, |
878 | }; | 879 | }; |
879 | let alias = tree.rename().map(|a| { | 880 | let alias = tree.rename().map(|a| { |
diff --git a/crates/hir_def/src/item_tree/pretty.rs b/crates/hir_def/src/item_tree/pretty.rs index cc9944a22..b1e1b70d0 100644 --- a/crates/hir_def/src/item_tree/pretty.rs +++ b/crates/hir_def/src/item_tree/pretty.rs | |||
@@ -426,7 +426,7 @@ impl<'a> Printer<'a> { | |||
426 | w!(self, " {{"); | 426 | w!(self, " {{"); |
427 | self.indented(|this| { | 427 | self.indented(|this| { |
428 | for item in &**items { | 428 | for item in &**items { |
429 | this.print_mod_item((*item).into()); | 429 | this.print_mod_item(*item); |
430 | } | 430 | } |
431 | }); | 431 | }); |
432 | wln!(self, "}}"); | 432 | wln!(self, "}}"); |
diff --git a/crates/hir_def/src/item_tree/tests.rs b/crates/hir_def/src/item_tree/tests.rs index b362add5c..57686dc6e 100644 --- a/crates/hir_def/src/item_tree/tests.rs +++ b/crates/hir_def/src/item_tree/tests.rs | |||
@@ -359,3 +359,41 @@ trait Tr<'a, T: 'a>: Super {} | |||
359 | "#]], | 359 | "#]], |
360 | ) | 360 | ) |
361 | } | 361 | } |
362 | |||
363 | #[test] | ||
364 | fn inherit_visibility() { | ||
365 | check( | ||
366 | r#" | ||
367 | pub(crate) enum En { | ||
368 | Var1(u8), | ||
369 | Var2 { | ||
370 | fld: u8, | ||
371 | }, | ||
372 | } | ||
373 | |||
374 | pub(crate) trait Tr { | ||
375 | fn f(); | ||
376 | fn method(&self) {} | ||
377 | } | ||
378 | "#, | ||
379 | expect![[r#" | ||
380 | pub(crate) enum En { | ||
381 | Var1( | ||
382 | pub(crate) 0: u8, | ||
383 | ), | ||
384 | Var2 { | ||
385 | pub(crate) fld: u8, | ||
386 | }, | ||
387 | } | ||
388 | |||
389 | pub(crate) trait Tr<Self> { | ||
390 | pub(crate) fn f() -> (); | ||
391 | |||
392 | // flags = 0x3 | ||
393 | pub(crate) fn method( | ||
394 | _: &Self, | ||
395 | ) -> (); | ||
396 | } | ||
397 | "#]], | ||
398 | ) | ||
399 | } | ||
diff --git a/crates/hir_def/src/keys.rs b/crates/hir_def/src/keys.rs index 89b3ed868..688cd9fcf 100644 --- a/crates/hir_def/src/keys.rs +++ b/crates/hir_def/src/keys.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use std::marker::PhantomData; | 3 | use std::marker::PhantomData; |
4 | 4 | ||
5 | use hir_expand::{InFile, MacroDefId}; | 5 | use hir_expand::{InFile, MacroCallId, MacroDefId}; |
6 | use rustc_hash::FxHashMap; | 6 | use rustc_hash::FxHashMap; |
7 | use syntax::{ast, AstNode, AstPtr}; | 7 | use syntax::{ast, AstNode, AstPtr}; |
8 | 8 | ||
@@ -32,6 +32,7 @@ pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new(); | |||
32 | pub const CONST_PARAM: Key<ast::ConstParam, ConstParamId> = Key::new(); | 32 | pub const CONST_PARAM: Key<ast::ConstParam, ConstParamId> = Key::new(); |
33 | 33 | ||
34 | pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new(); | 34 | pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new(); |
35 | pub const ATTR_MACRO: Key<ast::Item, MacroCallId> = Key::new(); | ||
35 | 36 | ||
36 | /// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are | 37 | /// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are |
37 | /// equal if they point to exactly the same object. | 38 | /// equal if they point to exactly the same object. |
diff --git a/crates/hir_def/src/lang_item.rs b/crates/hir_def/src/lang_item.rs index 9e90f745c..3a45cbfa1 100644 --- a/crates/hir_def/src/lang_item.rs +++ b/crates/hir_def/src/lang_item.rs | |||
@@ -141,6 +141,7 @@ impl LangItems { | |||
141 | ) where | 141 | ) where |
142 | T: Into<AttrDefId> + Copy, | 142 | T: Into<AttrDefId> + Copy, |
143 | { | 143 | { |
144 | let _p = profile::span("collect_lang_item"); | ||
144 | if let Some(lang_item_name) = lang_attr(db, item) { | 145 | if let Some(lang_item_name) = lang_attr(db, item) { |
145 | self.items.entry(lang_item_name).or_insert_with(|| constructor(item)); | 146 | self.items.entry(lang_item_name).or_insert_with(|| constructor(item)); |
146 | } | 147 | } |
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index 9aa95720a..bb174aec8 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs | |||
@@ -55,6 +55,7 @@ use std::{ | |||
55 | sync::Arc, | 55 | sync::Arc, |
56 | }; | 56 | }; |
57 | 57 | ||
58 | use attr::Attr; | ||
58 | use base_db::{impl_intern_key, salsa, CrateId}; | 59 | use base_db::{impl_intern_key, salsa, CrateId}; |
59 | use hir_expand::{ | 60 | use hir_expand::{ |
60 | ast_id_map::FileAstId, | 61 | ast_id_map::FileAstId, |
@@ -111,6 +112,10 @@ impl ModuleId { | |||
111 | self.def_map(db).containing_module(self.local_id) | 112 | self.def_map(db).containing_module(self.local_id) |
112 | } | 113 | } |
113 | 114 | ||
115 | pub fn containing_block(&self) -> Option<BlockId> { | ||
116 | self.block | ||
117 | } | ||
118 | |||
114 | /// Returns `true` if this module represents a block expression. | 119 | /// Returns `true` if this module represents a block expression. |
115 | /// | 120 | /// |
116 | /// Returns `false` if this module is a submodule *inside* a block expression | 121 | /// Returns `false` if this module is a submodule *inside* a block expression |
@@ -580,6 +585,18 @@ impl HasModule for GenericDefId { | |||
580 | } | 585 | } |
581 | } | 586 | } |
582 | 587 | ||
588 | impl HasModule for TypeAliasId { | ||
589 | fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { | ||
590 | self.lookup(db).module(db) | ||
591 | } | ||
592 | } | ||
593 | |||
594 | impl HasModule for TraitId { | ||
595 | fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { | ||
596 | self.lookup(db).container | ||
597 | } | ||
598 | } | ||
599 | |||
583 | impl HasModule for StaticLoc { | 600 | impl HasModule for StaticLoc { |
584 | fn module(&self, _db: &dyn db::DefDatabase) -> ModuleId { | 601 | fn module(&self, _db: &dyn db::DefDatabase) -> ModuleId { |
585 | self.container | 602 | self.container |
@@ -730,13 +747,11 @@ fn macro_call_as_call_id( | |||
730 | ) | 747 | ) |
731 | .map(MacroCallId::from) | 748 | .map(MacroCallId::from) |
732 | } else { | 749 | } else { |
733 | Ok(def | 750 | Ok(def.as_lazy_macro( |
734 | .as_lazy_macro( | 751 | db.upcast(), |
735 | db.upcast(), | 752 | krate, |
736 | krate, | 753 | MacroCallKind::FnLike { ast_id: call.ast_id, fragment }, |
737 | MacroCallKind::FnLike { ast_id: call.ast_id, fragment }, | 754 | )) |
738 | ) | ||
739 | .into()) | ||
740 | }; | 755 | }; |
741 | Ok(res) | 756 | Ok(res) |
742 | } | 757 | } |
@@ -755,16 +770,51 @@ fn derive_macro_as_call_id( | |||
755 | .segments() | 770 | .segments() |
756 | .last() | 771 | .last() |
757 | .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?; | 772 | .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?; |
758 | let res = def | 773 | let res = def.as_lazy_macro( |
759 | .as_lazy_macro( | 774 | db.upcast(), |
760 | db.upcast(), | 775 | krate, |
761 | krate, | 776 | MacroCallKind::Derive { |
762 | MacroCallKind::Derive { | 777 | ast_id: item_attr.ast_id, |
763 | ast_id: item_attr.ast_id, | 778 | derive_name: last_segment.to_string(), |
764 | derive_name: last_segment.to_string(), | 779 | derive_attr_index: derive_attr.ast_index, |
765 | derive_attr_index: derive_attr.ast_index, | 780 | }, |
766 | }, | 781 | ); |
767 | ) | 782 | Ok(res) |
768 | .into(); | 783 | } |
784 | |||
785 | fn attr_macro_as_call_id( | ||
786 | item_attr: &AstIdWithPath<ast::Item>, | ||
787 | macro_attr: &Attr, | ||
788 | db: &dyn db::DefDatabase, | ||
789 | krate: CrateId, | ||
790 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | ||
791 | ) -> Result<MacroCallId, UnresolvedMacro> { | ||
792 | let def: MacroDefId = resolver(item_attr.path.clone()) | ||
793 | .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?; | ||
794 | let last_segment = item_attr | ||
795 | .path | ||
796 | .segments() | ||
797 | .last() | ||
798 | .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?; | ||
799 | let mut arg = match ¯o_attr.input { | ||
800 | Some(input) => match &**input { | ||
801 | attr::AttrInput::Literal(_) => tt::Subtree::default(), | ||
802 | attr::AttrInput::TokenTree(tt) => tt.clone(), | ||
803 | }, | ||
804 | None => tt::Subtree::default(), | ||
805 | }; | ||
806 | // The parentheses are always disposed here. | ||
807 | arg.delimiter = None; | ||
808 | |||
809 | let res = def.as_lazy_macro( | ||
810 | db.upcast(), | ||
811 | krate, | ||
812 | MacroCallKind::Attr { | ||
813 | ast_id: item_attr.ast_id, | ||
814 | attr_name: last_segment.to_string(), | ||
815 | attr_args: arg, | ||
816 | invoc_attr_index: macro_attr.id.ast_index, | ||
817 | }, | ||
818 | ); | ||
769 | Ok(res) | 819 | Ok(res) |
770 | } | 820 | } |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 6b41921ae..6fab58f15 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -9,6 +9,7 @@ use base_db::{CrateId, Edition, FileId, ProcMacroId}; | |||
9 | use cfg::{CfgExpr, CfgOptions}; | 9 | use cfg::{CfgExpr, CfgOptions}; |
10 | use hir_expand::{ | 10 | use hir_expand::{ |
11 | ast_id_map::FileAstId, | 11 | ast_id_map::FileAstId, |
12 | builtin_attr::find_builtin_attr, | ||
12 | builtin_derive::find_builtin_derive, | 13 | builtin_derive::find_builtin_derive, |
13 | builtin_macro::find_builtin_macro, | 14 | builtin_macro::find_builtin_macro, |
14 | name::{name, AsName, Name}, | 15 | name::{name, AsName, Name}, |
@@ -23,7 +24,7 @@ use syntax::ast; | |||
23 | 24 | ||
24 | use crate::{ | 25 | use crate::{ |
25 | attr::{Attr, AttrId, AttrInput, Attrs}, | 26 | attr::{Attr, AttrId, AttrInput, Attrs}, |
26 | builtin_attr, | 27 | attr_macro_as_call_id, builtin_attr, |
27 | db::DefDatabase, | 28 | db::DefDatabase, |
28 | derive_macro_as_call_id, | 29 | derive_macro_as_call_id, |
29 | intern::Interned, | 30 | intern::Interned, |
@@ -223,7 +224,7 @@ struct MacroDirective { | |||
223 | enum MacroDirectiveKind { | 224 | enum MacroDirectiveKind { |
224 | FnLike { ast_id: AstIdWithPath<ast::MacroCall>, fragment: FragmentKind }, | 225 | FnLike { ast_id: AstIdWithPath<ast::MacroCall>, fragment: FragmentKind }, |
225 | Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId }, | 226 | Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId }, |
226 | Attr { ast_id: AstIdWithPath<ast::Item>, attr: AttrId, mod_item: ModItem }, | 227 | Attr { ast_id: AstIdWithPath<ast::Item>, attr: Attr, mod_item: ModItem }, |
227 | } | 228 | } |
228 | 229 | ||
229 | struct DefData<'a> { | 230 | struct DefData<'a> { |
@@ -419,7 +420,7 @@ impl DefCollector<'_> { | |||
419 | let mut unresolved_macros = std::mem::replace(&mut self.unresolved_macros, Vec::new()); | 420 | let mut unresolved_macros = std::mem::replace(&mut self.unresolved_macros, Vec::new()); |
420 | let pos = unresolved_macros.iter().position(|directive| { | 421 | let pos = unresolved_macros.iter().position(|directive| { |
421 | if let MacroDirectiveKind::Attr { ast_id, mod_item, attr } = &directive.kind { | 422 | if let MacroDirectiveKind::Attr { ast_id, mod_item, attr } = &directive.kind { |
422 | self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), *attr); | 423 | self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), attr.id); |
423 | 424 | ||
424 | let file_id = ast_id.ast_id.file_id; | 425 | let file_id = ast_id.ast_id.file_id; |
425 | let item_tree = self.db.file_item_tree(file_id); | 426 | let item_tree = self.db.file_item_tree(file_id); |
@@ -499,7 +500,7 @@ impl DefCollector<'_> { | |||
499 | let (per_ns, _) = self.def_map.resolve_path( | 500 | let (per_ns, _) = self.def_map.resolve_path( |
500 | self.db, | 501 | self.db, |
501 | self.def_map.root, | 502 | self.def_map.root, |
502 | &path, | 503 | path, |
503 | BuiltinShadowMode::Other, | 504 | BuiltinShadowMode::Other, |
504 | ); | 505 | ); |
505 | 506 | ||
@@ -721,7 +722,7 @@ impl DefCollector<'_> { | |||
721 | if import.is_extern_crate { | 722 | if import.is_extern_crate { |
722 | let res = self.def_map.resolve_name_in_extern_prelude( | 723 | let res = self.def_map.resolve_name_in_extern_prelude( |
723 | self.db, | 724 | self.db, |
724 | &import | 725 | import |
725 | .path | 726 | .path |
726 | .as_ident() | 727 | .as_ident() |
727 | .expect("extern crate should have been desugared to one-element path"), | 728 | .expect("extern crate should have been desugared to one-element path"), |
@@ -1050,7 +1051,7 @@ impl DefCollector<'_> { | |||
1050 | let file_id = ast_id.ast_id.file_id; | 1051 | let file_id = ast_id.ast_id.file_id; |
1051 | let item_tree = self.db.file_item_tree(file_id); | 1052 | let item_tree = self.db.file_item_tree(file_id); |
1052 | let mod_dir = self.mod_dirs[&directive.module_id].clone(); | 1053 | let mod_dir = self.mod_dirs[&directive.module_id].clone(); |
1053 | self.skip_attrs.insert(InFile::new(file_id, *mod_item), *attr); | 1054 | self.skip_attrs.insert(InFile::new(file_id, *mod_item), attr.id); |
1054 | ModCollector { | 1055 | ModCollector { |
1055 | def_collector: &mut *self, | 1056 | def_collector: &mut *self, |
1056 | macro_depth: directive.depth, | 1057 | macro_depth: directive.depth, |
@@ -1062,13 +1063,67 @@ impl DefCollector<'_> { | |||
1062 | .collect(&[*mod_item]); | 1063 | .collect(&[*mod_item]); |
1063 | 1064 | ||
1064 | // Remove the original directive since we resolved it. | 1065 | // Remove the original directive since we resolved it. |
1066 | res = ReachedFixedPoint::No; | ||
1065 | return false; | 1067 | return false; |
1066 | } | 1068 | } |
1067 | } | 1069 | } |
1068 | } | 1070 | } |
1069 | 1071 | ||
1072 | if !self.db.enable_proc_attr_macros() { | ||
1073 | return true; | ||
1074 | } | ||
1075 | |||
1070 | // Not resolved to a derive helper, so try to resolve as a macro. | 1076 | // Not resolved to a derive helper, so try to resolve as a macro. |
1071 | // FIXME: not yet :) | 1077 | match attr_macro_as_call_id( |
1078 | ast_id, | ||
1079 | attr, | ||
1080 | self.db, | ||
1081 | self.def_map.krate, | ||
1082 | &resolver, | ||
1083 | ) { | ||
1084 | Ok(call_id) => { | ||
1085 | let loc: MacroCallLoc = self.db.lookup_intern_macro(call_id); | ||
1086 | if let MacroDefKind::ProcMacro(exp, ..) = &loc.def.kind { | ||
1087 | if exp.is_dummy() { | ||
1088 | // Proc macros that cannot be expanded are treated as not | ||
1089 | // resolved, in order to fall back later. | ||
1090 | self.def_map.diagnostics.push( | ||
1091 | DefDiagnostic::unresolved_proc_macro( | ||
1092 | directive.module_id, | ||
1093 | loc.kind, | ||
1094 | ), | ||
1095 | ); | ||
1096 | |||
1097 | let file_id = ast_id.ast_id.file_id; | ||
1098 | let item_tree = self.db.file_item_tree(file_id); | ||
1099 | let mod_dir = self.mod_dirs[&directive.module_id].clone(); | ||
1100 | self.skip_attrs | ||
1101 | .insert(InFile::new(file_id, *mod_item), attr.id); | ||
1102 | ModCollector { | ||
1103 | def_collector: &mut *self, | ||
1104 | macro_depth: directive.depth, | ||
1105 | module_id: directive.module_id, | ||
1106 | file_id, | ||
1107 | item_tree: &item_tree, | ||
1108 | mod_dir, | ||
1109 | } | ||
1110 | .collect(&[*mod_item]); | ||
1111 | |||
1112 | // Remove the macro directive. | ||
1113 | return false; | ||
1114 | } | ||
1115 | } | ||
1116 | |||
1117 | self.def_map.modules[directive.module_id] | ||
1118 | .scope | ||
1119 | .add_attr_macro_invoc(ast_id.ast_id, call_id); | ||
1120 | |||
1121 | resolved.push((directive.module_id, call_id, directive.depth)); | ||
1122 | res = ReachedFixedPoint::No; | ||
1123 | return false; | ||
1124 | } | ||
1125 | Err(UnresolvedMacro { .. }) => (), | ||
1126 | } | ||
1072 | } | 1127 | } |
1073 | } | 1128 | } |
1074 | 1129 | ||
@@ -1296,7 +1351,7 @@ impl ModCollector<'_, '_> { | |||
1296 | let imports = Import::from_use( | 1351 | let imports = Import::from_use( |
1297 | self.def_collector.db, | 1352 | self.def_collector.db, |
1298 | krate, | 1353 | krate, |
1299 | &self.item_tree, | 1354 | self.item_tree, |
1300 | ItemTreeId::new(self.file_id, import_id), | 1355 | ItemTreeId::new(self.file_id, import_id), |
1301 | ); | 1356 | ); |
1302 | self.def_collector.unresolved_imports.extend(imports.into_iter().map( | 1357 | self.def_collector.unresolved_imports.extend(imports.into_iter().map( |
@@ -1313,7 +1368,7 @@ impl ModCollector<'_, '_> { | |||
1313 | import: Import::from_extern_crate( | 1368 | import: Import::from_extern_crate( |
1314 | self.def_collector.db, | 1369 | self.def_collector.db, |
1315 | krate, | 1370 | krate, |
1316 | &self.item_tree, | 1371 | self.item_tree, |
1317 | ItemTreeId::new(self.file_id, import_id), | 1372 | ItemTreeId::new(self.file_id, import_id), |
1318 | ), | 1373 | ), |
1319 | status: PartialResolvedImport::Unresolved, | 1374 | status: PartialResolvedImport::Unresolved, |
@@ -1628,7 +1683,7 @@ impl ModCollector<'_, '_> { | |||
1628 | self.def_collector.unresolved_macros.push(MacroDirective { | 1683 | self.def_collector.unresolved_macros.push(MacroDirective { |
1629 | module_id: self.module_id, | 1684 | module_id: self.module_id, |
1630 | depth: self.macro_depth + 1, | 1685 | depth: self.macro_depth + 1, |
1631 | kind: MacroDirectiveKind::Attr { ast_id, attr: attr.id, mod_item }, | 1686 | kind: MacroDirectiveKind::Attr { ast_id, attr: attr.clone(), mod_item }, |
1632 | }); | 1687 | }); |
1633 | 1688 | ||
1634 | return Err(()); | 1689 | return Err(()); |
@@ -1782,7 +1837,8 @@ impl ModCollector<'_, '_> { | |||
1782 | let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into()); | 1837 | let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into()); |
1783 | if attrs.by_key("rustc_builtin_macro").exists() { | 1838 | if attrs.by_key("rustc_builtin_macro").exists() { |
1784 | let macro_id = find_builtin_macro(&mac.name, krate, ast_id) | 1839 | let macro_id = find_builtin_macro(&mac.name, krate, ast_id) |
1785 | .or_else(|| find_builtin_derive(&mac.name, krate, ast_id)); | 1840 | .or_else(|| find_builtin_derive(&mac.name, krate, ast_id)) |
1841 | .or_else(|| find_builtin_attr(&mac.name, krate, ast_id)); | ||
1786 | 1842 | ||
1787 | match macro_id { | 1843 | match macro_id { |
1788 | Some(macro_id) => { | 1844 | Some(macro_id) => { |
@@ -1833,7 +1889,7 @@ impl ModCollector<'_, '_> { | |||
1833 | self.def_collector.def_map.with_ancestor_maps( | 1889 | self.def_collector.def_map.with_ancestor_maps( |
1834 | self.def_collector.db, | 1890 | self.def_collector.db, |
1835 | self.module_id, | 1891 | self.module_id, |
1836 | &mut |map, module| map[module].scope.get_legacy_macro(&name), | 1892 | &mut |map, module| map[module].scope.get_legacy_macro(name), |
1837 | ) | 1893 | ) |
1838 | }) | 1894 | }) |
1839 | }, | 1895 | }, |
@@ -1937,7 +1993,7 @@ mod tests { | |||
1937 | } | 1993 | } |
1938 | 1994 | ||
1939 | fn do_resolve(code: &str) -> DefMap { | 1995 | fn do_resolve(code: &str) -> DefMap { |
1940 | let (db, _file_id) = TestDB::with_single_file(&code); | 1996 | let (db, _file_id) = TestDB::with_single_file(code); |
1941 | let krate = db.test_crate(); | 1997 | let krate = db.test_crate(); |
1942 | 1998 | ||
1943 | let edition = db.crate_graph()[krate].edition; | 1999 | let edition = db.crate_graph()[krate].edition; |
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs index c984148c3..629bc7952 100644 --- a/crates/hir_def/src/nameres/path_resolution.rs +++ b/crates/hir_def/src/nameres/path_resolution.rs | |||
@@ -93,7 +93,7 @@ impl DefMap { | |||
93 | let mut vis = match visibility { | 93 | let mut vis = match visibility { |
94 | RawVisibility::Module(path) => { | 94 | RawVisibility::Module(path) => { |
95 | let (result, remaining) = | 95 | let (result, remaining) = |
96 | self.resolve_path(db, original_module, &path, BuiltinShadowMode::Module); | 96 | self.resolve_path(db, original_module, path, BuiltinShadowMode::Module); |
97 | if remaining.is_some() { | 97 | if remaining.is_some() { |
98 | return None; | 98 | return None; |
99 | } | 99 | } |
@@ -205,7 +205,7 @@ impl DefMap { | |||
205 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), | 205 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), |
206 | }; | 206 | }; |
207 | log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); | 207 | log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); |
208 | self.resolve_name_in_crate_root_or_extern_prelude(db, &segment) | 208 | self.resolve_name_in_crate_root_or_extern_prelude(db, segment) |
209 | } | 209 | } |
210 | PathKind::Plain => { | 210 | PathKind::Plain => { |
211 | let (_, segment) = match segments.next() { | 211 | let (_, segment) = match segments.next() { |
@@ -222,7 +222,7 @@ impl DefMap { | |||
222 | if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module }; | 222 | if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module }; |
223 | 223 | ||
224 | log::debug!("resolving {:?} in module", segment); | 224 | log::debug!("resolving {:?} in module", segment); |
225 | self.resolve_name_in_module(db, original_module, &segment, prefer_module) | 225 | self.resolve_name_in_module(db, original_module, segment, prefer_module) |
226 | } | 226 | } |
227 | PathKind::Super(lvl) => { | 227 | PathKind::Super(lvl) => { |
228 | let mut module = original_module; | 228 | let mut module = original_module; |
@@ -269,7 +269,7 @@ impl DefMap { | |||
269 | Some((_, segment)) => segment, | 269 | Some((_, segment)) => segment, |
270 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), | 270 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), |
271 | }; | 271 | }; |
272 | if let Some(def) = self.extern_prelude.get(&segment) { | 272 | if let Some(def) = self.extern_prelude.get(segment) { |
273 | log::debug!("absolute path {:?} resolved to crate {:?}", path, def); | 273 | log::debug!("absolute path {:?} resolved to crate {:?}", path, def); |
274 | PerNs::types(*def, Visibility::Public) | 274 | PerNs::types(*def, Visibility::Public) |
275 | } else { | 275 | } else { |
@@ -319,13 +319,13 @@ impl DefMap { | |||
319 | }; | 319 | }; |
320 | 320 | ||
321 | // Since it is a qualified path here, it should not contains legacy macros | 321 | // Since it is a qualified path here, it should not contains legacy macros |
322 | module_data.scope.get(&segment) | 322 | module_data.scope.get(segment) |
323 | } | 323 | } |
324 | ModuleDefId::AdtId(AdtId::EnumId(e)) => { | 324 | ModuleDefId::AdtId(AdtId::EnumId(e)) => { |
325 | // enum variant | 325 | // enum variant |
326 | cov_mark::hit!(can_import_enum_variant); | 326 | cov_mark::hit!(can_import_enum_variant); |
327 | let enum_data = db.enum_data(e); | 327 | let enum_data = db.enum_data(e); |
328 | match enum_data.variant(&segment) { | 328 | match enum_data.variant(segment) { |
329 | Some(local_id) => { | 329 | Some(local_id) => { |
330 | let variant = EnumVariantId { parent: e, local_id }; | 330 | let variant = EnumVariantId { parent: e, local_id }; |
331 | match &*enum_data.variants[local_id].variant_data { | 331 | match &*enum_data.variants[local_id].variant_data { |
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs index 58c01354a..cf43f2a96 100644 --- a/crates/hir_def/src/nameres/tests.rs +++ b/crates/hir_def/src/nameres/tests.rs | |||
@@ -2,7 +2,6 @@ mod globs; | |||
2 | mod incremental; | 2 | mod incremental; |
3 | mod macros; | 3 | mod macros; |
4 | mod mod_resolution; | 4 | mod mod_resolution; |
5 | mod diagnostics; | ||
6 | mod primitives; | 5 | mod primitives; |
7 | 6 | ||
8 | use std::sync::Arc; | 7 | use std::sync::Arc; |
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs deleted file mode 100644 index ec6670952..000000000 --- a/crates/hir_def/src/nameres/tests/diagnostics.rs +++ /dev/null | |||
@@ -1,229 +0,0 @@ | |||
1 | use base_db::fixture::WithFixture; | ||
2 | |||
3 | use crate::test_db::TestDB; | ||
4 | |||
5 | fn check_diagnostics(ra_fixture: &str) { | ||
6 | let db: TestDB = TestDB::with_files(ra_fixture); | ||
7 | db.check_diagnostics(); | ||
8 | } | ||
9 | |||
10 | fn check_no_diagnostics(ra_fixture: &str) { | ||
11 | let db: TestDB = TestDB::with_files(ra_fixture); | ||
12 | db.check_no_diagnostics(); | ||
13 | } | ||
14 | |||
15 | #[test] | ||
16 | fn unresolved_import() { | ||
17 | check_diagnostics( | ||
18 | r" | ||
19 | use does_exist; | ||
20 | use does_not_exist; | ||
21 | //^^^^^^^^^^^^^^^^^^^ UnresolvedImport | ||
22 | |||
23 | mod does_exist {} | ||
24 | ", | ||
25 | ); | ||
26 | } | ||
27 | |||
28 | #[test] | ||
29 | fn unresolved_extern_crate() { | ||
30 | check_diagnostics( | ||
31 | r" | ||
32 | //- /main.rs crate:main deps:core | ||
33 | extern crate core; | ||
34 | extern crate doesnotexist; | ||
35 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedExternCrate | ||
36 | //- /lib.rs crate:core | ||
37 | ", | ||
38 | ); | ||
39 | } | ||
40 | |||
41 | #[test] | ||
42 | fn extern_crate_self_as() { | ||
43 | cov_mark::check!(extern_crate_self_as); | ||
44 | check_diagnostics( | ||
45 | r" | ||
46 | //- /lib.rs | ||
47 | extern crate doesnotexist; | ||
48 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedExternCrate | ||
49 | // Should not error. | ||
50 | extern crate self as foo; | ||
51 | struct Foo; | ||
52 | use foo::Foo as Bar; | ||
53 | ", | ||
54 | ); | ||
55 | } | ||
56 | |||
57 | #[test] | ||
58 | fn dedup_unresolved_import_from_unresolved_crate() { | ||
59 | check_diagnostics( | ||
60 | r" | ||
61 | //- /main.rs crate:main | ||
62 | mod a { | ||
63 | extern crate doesnotexist; | ||
64 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedExternCrate | ||
65 | |||
66 | // Should not error, since we already errored for the missing crate. | ||
67 | use doesnotexist::{self, bla, *}; | ||
68 | |||
69 | use crate::doesnotexist; | ||
70 | //^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedImport | ||
71 | } | ||
72 | |||
73 | mod m { | ||
74 | use super::doesnotexist; | ||
75 | //^^^^^^^^^^^^^^^^^^^^^^^^ UnresolvedImport | ||
76 | } | ||
77 | ", | ||
78 | ); | ||
79 | } | ||
80 | |||
81 | #[test] | ||
82 | fn unresolved_module() { | ||
83 | check_diagnostics( | ||
84 | r" | ||
85 | //- /lib.rs | ||
86 | mod foo; | ||
87 | mod bar; | ||
88 | //^^^^^^^^ UnresolvedModule | ||
89 | mod baz {} | ||
90 | //- /foo.rs | ||
91 | ", | ||
92 | ); | ||
93 | } | ||
94 | |||
95 | #[test] | ||
96 | fn inactive_item() { | ||
97 | // Additional tests in `cfg` crate. This only tests disabled cfgs. | ||
98 | |||
99 | check_diagnostics( | ||
100 | r#" | ||
101 | //- /lib.rs | ||
102 | #[cfg(no)] pub fn f() {} | ||
103 | //^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode | ||
104 | |||
105 | #[cfg(no)] #[cfg(no2)] mod m; | ||
106 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode | ||
107 | |||
108 | #[cfg(all(not(a), b))] enum E {} | ||
109 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode | ||
110 | |||
111 | #[cfg(feature = "std")] use std; | ||
112 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode | ||
113 | "#, | ||
114 | ); | ||
115 | } | ||
116 | |||
117 | /// Tests that `cfg` attributes behind `cfg_attr` is handled properly. | ||
118 | #[test] | ||
119 | fn inactive_via_cfg_attr() { | ||
120 | cov_mark::check!(cfg_attr_active); | ||
121 | check_diagnostics( | ||
122 | r#" | ||
123 | //- /lib.rs | ||
124 | #[cfg_attr(not(never), cfg(no))] fn f() {} | ||
125 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode | ||
126 | |||
127 | #[cfg_attr(not(never), cfg(not(no)))] fn f() {} | ||
128 | |||
129 | #[cfg_attr(never, cfg(no))] fn g() {} | ||
130 | |||
131 | #[cfg_attr(not(never), inline, cfg(no))] fn h() {} | ||
132 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnconfiguredCode | ||
133 | "#, | ||
134 | ); | ||
135 | } | ||
136 | |||
137 | #[test] | ||
138 | fn unresolved_legacy_scope_macro() { | ||
139 | check_diagnostics( | ||
140 | r#" | ||
141 | //- /lib.rs | ||
142 | macro_rules! m { () => {} } | ||
143 | |||
144 | m!(); | ||
145 | m2!(); | ||
146 | //^^^^^^ UnresolvedMacroCall | ||
147 | "#, | ||
148 | ); | ||
149 | } | ||
150 | |||
151 | #[test] | ||
152 | fn unresolved_module_scope_macro() { | ||
153 | check_diagnostics( | ||
154 | r#" | ||
155 | //- /lib.rs | ||
156 | mod mac { | ||
157 | #[macro_export] | ||
158 | macro_rules! m { () => {} } | ||
159 | } | ||
160 | |||
161 | self::m!(); | ||
162 | self::m2!(); | ||
163 | //^^^^^^^^^^^^ UnresolvedMacroCall | ||
164 | "#, | ||
165 | ); | ||
166 | } | ||
167 | |||
168 | #[test] | ||
169 | fn builtin_macro_fails_expansion() { | ||
170 | check_diagnostics( | ||
171 | r#" | ||
172 | //- /lib.rs | ||
173 | #[rustc_builtin_macro] | ||
174 | macro_rules! include { () => {} } | ||
175 | |||
176 | include!("doesntexist"); | ||
177 | //^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `doesntexist` | ||
178 | "#, | ||
179 | ); | ||
180 | } | ||
181 | |||
182 | #[test] | ||
183 | fn include_macro_should_allow_empty_content() { | ||
184 | check_no_diagnostics( | ||
185 | r#" | ||
186 | //- /lib.rs | ||
187 | #[rustc_builtin_macro] | ||
188 | macro_rules! include { () => {} } | ||
189 | |||
190 | include!("bar.rs"); | ||
191 | //- /bar.rs | ||
192 | // empty | ||
193 | "#, | ||
194 | ); | ||
195 | } | ||
196 | |||
197 | #[test] | ||
198 | fn good_out_dir_diagnostic() { | ||
199 | check_diagnostics( | ||
200 | r#" | ||
201 | #[rustc_builtin_macro] | ||
202 | macro_rules! include { () => {} } | ||
203 | #[rustc_builtin_macro] | ||
204 | macro_rules! env { () => {} } | ||
205 | #[rustc_builtin_macro] | ||
206 | macro_rules! concat { () => {} } | ||
207 | |||
208 | include!(concat!(env!("OUT_DIR"), "/out.rs")); | ||
209 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix | ||
210 | "#, | ||
211 | ); | ||
212 | } | ||
213 | |||
214 | #[test] | ||
215 | fn register_attr_and_tool() { | ||
216 | cov_mark::check!(register_attr); | ||
217 | cov_mark::check!(register_tool); | ||
218 | check_no_diagnostics( | ||
219 | r#" | ||
220 | #![register_tool(tool)] | ||
221 | #![register_attr(attr)] | ||
222 | |||
223 | #[tool::path] | ||
224 | #[attr] | ||
225 | struct S; | ||
226 | "#, | ||
227 | ); | ||
228 | // NB: we don't currently emit diagnostics here | ||
229 | } | ||
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs index f6220aa92..27345d07c 100644 --- a/crates/hir_def/src/path/lower.rs +++ b/crates/hir_def/src/path/lower.rs | |||
@@ -208,13 +208,13 @@ fn lower_generic_args_from_fn_path( | |||
208 | let params = params?; | 208 | let params = params?; |
209 | let mut param_types = Vec::new(); | 209 | let mut param_types = Vec::new(); |
210 | for param in params.params() { | 210 | for param in params.params() { |
211 | let type_ref = TypeRef::from_ast_opt(&ctx, param.ty()); | 211 | let type_ref = TypeRef::from_ast_opt(ctx, param.ty()); |
212 | param_types.push(type_ref); | 212 | param_types.push(type_ref); |
213 | } | 213 | } |
214 | let arg = GenericArg::Type(TypeRef::Tuple(param_types)); | 214 | let arg = GenericArg::Type(TypeRef::Tuple(param_types)); |
215 | args.push(arg); | 215 | args.push(arg); |
216 | if let Some(ret_type) = ret_type { | 216 | if let Some(ret_type) = ret_type { |
217 | let type_ref = TypeRef::from_ast_opt(&ctx, ret_type.ty()); | 217 | let type_ref = TypeRef::from_ast_opt(ctx, ret_type.ty()); |
218 | bindings.push(AssociatedTypeBinding { | 218 | bindings.push(AssociatedTypeBinding { |
219 | name: name![Output], | 219 | name: name![Output], |
220 | type_ref: Some(type_ref), | 220 | type_ref: Some(type_ref), |
diff --git a/crates/hir_def/src/per_ns.rs b/crates/hir_def/src/per_ns.rs index a594afce6..a9f13cb82 100644 --- a/crates/hir_def/src/per_ns.rs +++ b/crates/hir_def/src/per_ns.rs | |||
@@ -62,6 +62,7 @@ impl PerNs { | |||
62 | } | 62 | } |
63 | 63 | ||
64 | pub fn filter_visibility(self, mut f: impl FnMut(Visibility) -> bool) -> PerNs { | 64 | pub fn filter_visibility(self, mut f: impl FnMut(Visibility) -> bool) -> PerNs { |
65 | let _p = profile::span("PerNs::filter_visibility"); | ||
65 | PerNs { | 66 | PerNs { |
66 | types: self.types.filter(|(_, v)| f(*v)), | 67 | types: self.types.filter(|(_, v)| f(*v)), |
67 | values: self.values.filter(|(_, v)| f(*v)), | 68 | values: self.values.filter(|(_, v)| f(*v)), |
@@ -86,6 +87,7 @@ impl PerNs { | |||
86 | } | 87 | } |
87 | 88 | ||
88 | pub fn iter_items(self) -> impl Iterator<Item = ItemInNs> { | 89 | pub fn iter_items(self) -> impl Iterator<Item = ItemInNs> { |
90 | let _p = profile::span("PerNs::iter_items"); | ||
89 | self.types | 91 | self.types |
90 | .map(|it| ItemInNs::Types(it.0)) | 92 | .map(|it| ItemInNs::Types(it.0)) |
91 | .into_iter() | 93 | .into_iter() |
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs index fb8a6f260..d4681fa3e 100644 --- a/crates/hir_def/src/resolver.rs +++ b/crates/hir_def/src/resolver.rs | |||
@@ -133,7 +133,7 @@ impl Resolver { | |||
133 | Some(it) => it, | 133 | Some(it) => it, |
134 | None => return PerNs::none(), | 134 | None => return PerNs::none(), |
135 | }; | 135 | }; |
136 | let (module_res, segment_index) = item_map.resolve_path(db, module, &path, shadow); | 136 | let (module_res, segment_index) = item_map.resolve_path(db, module, path, shadow); |
137 | if segment_index.is_some() { | 137 | if segment_index.is_some() { |
138 | return PerNs::none(); | 138 | return PerNs::none(); |
139 | } | 139 | } |
@@ -150,7 +150,7 @@ impl Resolver { | |||
150 | path: &ModPath, | 150 | path: &ModPath, |
151 | ) -> Option<TraitId> { | 151 | ) -> Option<TraitId> { |
152 | let (item_map, module) = self.module_scope()?; | 152 | let (item_map, module) = self.module_scope()?; |
153 | let (module_res, ..) = item_map.resolve_path(db, module, &path, BuiltinShadowMode::Module); | 153 | let (module_res, ..) = item_map.resolve_path(db, module, path, BuiltinShadowMode::Module); |
154 | match module_res.take_types()? { | 154 | match module_res.take_types()? { |
155 | ModuleDefId::TraitId(it) => Some(it), | 155 | ModuleDefId::TraitId(it) => Some(it), |
156 | _ => None, | 156 | _ => None, |
@@ -325,7 +325,7 @@ impl Resolver { | |||
325 | path: &ModPath, | 325 | path: &ModPath, |
326 | ) -> Option<MacroDefId> { | 326 | ) -> Option<MacroDefId> { |
327 | let (item_map, module) = self.module_scope()?; | 327 | let (item_map, module) = self.module_scope()?; |
328 | item_map.resolve_path(db, module, &path, BuiltinShadowMode::Other).0.take_macros() | 328 | item_map.resolve_path(db, module, path, BuiltinShadowMode::Other).0.take_macros() |
329 | } | 329 | } |
330 | 330 | ||
331 | pub fn process_all_names(&self, db: &dyn DefDatabase, f: &mut dyn FnMut(Name, ScopeDef)) { | 331 | pub fn process_all_names(&self, db: &dyn DefDatabase, f: &mut dyn FnMut(Name, ScopeDef)) { |
@@ -561,7 +561,7 @@ impl ModuleItemMap { | |||
561 | path: &ModPath, | 561 | path: &ModPath, |
562 | ) -> Option<ResolveValueResult> { | 562 | ) -> Option<ResolveValueResult> { |
563 | let (module_def, idx) = | 563 | let (module_def, idx) = |
564 | self.def_map.resolve_path_locally(db, self.module_id, &path, BuiltinShadowMode::Other); | 564 | self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other); |
565 | match idx { | 565 | match idx { |
566 | None => { | 566 | None => { |
567 | let value = to_value_ns(module_def)?; | 567 | let value = to_value_ns(module_def)?; |
@@ -591,7 +591,7 @@ impl ModuleItemMap { | |||
591 | path: &ModPath, | 591 | path: &ModPath, |
592 | ) -> Option<(TypeNs, Option<usize>)> { | 592 | ) -> Option<(TypeNs, Option<usize>)> { |
593 | let (module_def, idx) = | 593 | let (module_def, idx) = |
594 | self.def_map.resolve_path_locally(db, self.module_id, &path, BuiltinShadowMode::Other); | 594 | self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other); |
595 | let res = to_type_ns(module_def)?; | 595 | let res = to_type_ns(module_def)?; |
596 | Some((res, idx)) | 596 | Some((res, idx)) |
597 | } | 597 | } |
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs index e840fe5e8..2635b556e 100644 --- a/crates/hir_def/src/test_db.rs +++ b/crates/hir_def/src/test_db.rs | |||
@@ -6,19 +6,16 @@ use std::{ | |||
6 | }; | 6 | }; |
7 | 7 | ||
8 | use base_db::{ | 8 | use base_db::{ |
9 | salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, FileRange, Upcast, | 9 | salsa, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, |
10 | SourceDatabase, Upcast, | ||
10 | }; | 11 | }; |
11 | use base_db::{AnchoredPath, SourceDatabase}; | ||
12 | use hir_expand::{db::AstDatabase, InFile}; | 12 | use hir_expand::{db::AstDatabase, InFile}; |
13 | use rustc_hash::FxHashMap; | ||
14 | use rustc_hash::FxHashSet; | 13 | use rustc_hash::FxHashSet; |
15 | use syntax::{algo, ast, AstNode, SyntaxNode, SyntaxNodePtr, TextRange, TextSize}; | 14 | use syntax::{algo, ast, AstNode}; |
16 | use test_utils::extract_annotations; | ||
17 | 15 | ||
18 | use crate::{ | 16 | use crate::{ |
19 | body::BodyDiagnostic, | ||
20 | db::DefDatabase, | 17 | db::DefDatabase, |
21 | nameres::{diagnostics::DefDiagnosticKind, DefMap, ModuleSource}, | 18 | nameres::{DefMap, ModuleSource}, |
22 | src::HasSource, | 19 | src::HasSource, |
23 | LocalModuleId, Lookup, ModuleDefId, ModuleId, | 20 | LocalModuleId, Lookup, ModuleDefId, ModuleId, |
24 | }; | 21 | }; |
@@ -30,12 +27,19 @@ use crate::{ | |||
30 | crate::db::InternDatabaseStorage, | 27 | crate::db::InternDatabaseStorage, |
31 | crate::db::DefDatabaseStorage | 28 | crate::db::DefDatabaseStorage |
32 | )] | 29 | )] |
33 | #[derive(Default)] | ||
34 | pub(crate) struct TestDB { | 30 | pub(crate) struct TestDB { |
35 | storage: salsa::Storage<TestDB>, | 31 | storage: salsa::Storage<TestDB>, |
36 | events: Mutex<Option<Vec<salsa::Event>>>, | 32 | events: Mutex<Option<Vec<salsa::Event>>>, |
37 | } | 33 | } |
38 | 34 | ||
35 | impl Default for TestDB { | ||
36 | fn default() -> Self { | ||
37 | let mut this = Self { storage: Default::default(), events: Default::default() }; | ||
38 | this.set_enable_proc_attr_macros(true); | ||
39 | this | ||
40 | } | ||
41 | } | ||
42 | |||
39 | impl Upcast<dyn AstDatabase> for TestDB { | 43 | impl Upcast<dyn AstDatabase> for TestDB { |
40 | fn upcast(&self) -> &(dyn AstDatabase + 'static) { | 44 | fn upcast(&self) -> &(dyn AstDatabase + 'static) { |
41 | &*self | 45 | &*self |
@@ -238,145 +242,4 @@ impl TestDB { | |||
238 | }) | 242 | }) |
239 | .collect() | 243 | .collect() |
240 | } | 244 | } |
241 | |||
242 | pub(crate) fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> { | ||
243 | let mut files = Vec::new(); | ||
244 | let crate_graph = self.crate_graph(); | ||
245 | for krate in crate_graph.iter() { | ||
246 | let crate_def_map = self.crate_def_map(krate); | ||
247 | for (module_id, _) in crate_def_map.modules() { | ||
248 | let file_id = crate_def_map[module_id].origin.file_id(); | ||
249 | files.extend(file_id) | ||
250 | } | ||
251 | } | ||
252 | assert!(!files.is_empty()); | ||
253 | files | ||
254 | .into_iter() | ||
255 | .filter_map(|file_id| { | ||
256 | let text = self.file_text(file_id); | ||
257 | let annotations = extract_annotations(&text); | ||
258 | if annotations.is_empty() { | ||
259 | return None; | ||
260 | } | ||
261 | Some((file_id, annotations)) | ||
262 | }) | ||
263 | .collect() | ||
264 | } | ||
265 | |||
266 | pub(crate) fn diagnostics(&self, cb: &mut dyn FnMut(FileRange, String)) { | ||
267 | let crate_graph = self.crate_graph(); | ||
268 | for krate in crate_graph.iter() { | ||
269 | let crate_def_map = self.crate_def_map(krate); | ||
270 | |||
271 | for diag in crate_def_map.diagnostics() { | ||
272 | let (node, message): (InFile<SyntaxNode>, &str) = match &diag.kind { | ||
273 | DefDiagnosticKind::UnresolvedModule { ast, .. } => { | ||
274 | let node = ast.to_node(self.upcast()); | ||
275 | (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedModule") | ||
276 | } | ||
277 | DefDiagnosticKind::UnresolvedExternCrate { ast, .. } => { | ||
278 | let node = ast.to_node(self.upcast()); | ||
279 | (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedExternCrate") | ||
280 | } | ||
281 | DefDiagnosticKind::UnresolvedImport { id, .. } => { | ||
282 | let item_tree = id.item_tree(self.upcast()); | ||
283 | let import = &item_tree[id.value]; | ||
284 | let node = InFile::new(id.file_id(), import.ast_id).to_node(self.upcast()); | ||
285 | (InFile::new(id.file_id(), node.syntax().clone()), "UnresolvedImport") | ||
286 | } | ||
287 | DefDiagnosticKind::UnconfiguredCode { ast, .. } => { | ||
288 | let node = ast.to_node(self.upcast()); | ||
289 | (InFile::new(ast.file_id, node.syntax().clone()), "UnconfiguredCode") | ||
290 | } | ||
291 | DefDiagnosticKind::UnresolvedProcMacro { ast, .. } => { | ||
292 | (ast.to_node(self.upcast()), "UnresolvedProcMacro") | ||
293 | } | ||
294 | DefDiagnosticKind::UnresolvedMacroCall { ast, .. } => { | ||
295 | let node = ast.to_node(self.upcast()); | ||
296 | (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedMacroCall") | ||
297 | } | ||
298 | DefDiagnosticKind::MacroError { ast, message } => { | ||
299 | (ast.to_node(self.upcast()), message.as_str()) | ||
300 | } | ||
301 | DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => { | ||
302 | let node = ast.to_node(self.upcast()); | ||
303 | ( | ||
304 | InFile::new(ast.file_id, node.syntax().clone()), | ||
305 | "UnimplementedBuiltinMacro", | ||
306 | ) | ||
307 | } | ||
308 | }; | ||
309 | |||
310 | let frange = node.as_ref().original_file_range(self); | ||
311 | cb(frange, message.to_string()) | ||
312 | } | ||
313 | |||
314 | for (_module_id, module) in crate_def_map.modules() { | ||
315 | for decl in module.scope.declarations() { | ||
316 | if let ModuleDefId::FunctionId(it) = decl { | ||
317 | let source_map = self.body_with_source_map(it.into()).1; | ||
318 | for diag in source_map.diagnostics() { | ||
319 | let (ptr, message): (InFile<SyntaxNodePtr>, &str) = match diag { | ||
320 | BodyDiagnostic::InactiveCode { node, .. } => { | ||
321 | (node.clone().map(|it| it.into()), "InactiveCode") | ||
322 | } | ||
323 | BodyDiagnostic::MacroError { node, message } => { | ||
324 | (node.clone().map(|it| it.into()), message.as_str()) | ||
325 | } | ||
326 | BodyDiagnostic::UnresolvedProcMacro { node } => { | ||
327 | (node.clone().map(|it| it.into()), "UnresolvedProcMacro") | ||
328 | } | ||
329 | BodyDiagnostic::UnresolvedMacroCall { node, .. } => { | ||
330 | (node.clone().map(|it| it.into()), "UnresolvedMacroCall") | ||
331 | } | ||
332 | }; | ||
333 | |||
334 | let root = self.parse_or_expand(ptr.file_id).unwrap(); | ||
335 | let node = ptr.map(|ptr| ptr.to_node(&root)); | ||
336 | let frange = node.as_ref().original_file_range(self); | ||
337 | cb(frange, message.to_string()) | ||
338 | } | ||
339 | } | ||
340 | } | ||
341 | } | ||
342 | } | ||
343 | } | ||
344 | |||
345 | pub(crate) fn check_diagnostics(&self) { | ||
346 | let db: &TestDB = self; | ||
347 | let annotations = db.extract_annotations(); | ||
348 | assert!(!annotations.is_empty()); | ||
349 | |||
350 | let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); | ||
351 | db.diagnostics(&mut |frange, message| { | ||
352 | actual.entry(frange.file_id).or_default().push((frange.range, message)); | ||
353 | }); | ||
354 | |||
355 | for (file_id, diags) in actual.iter_mut() { | ||
356 | diags.sort_by_key(|it| it.0.start()); | ||
357 | let text = db.file_text(*file_id); | ||
358 | // For multiline spans, place them on line start | ||
359 | for (range, content) in diags { | ||
360 | if text[*range].contains('\n') { | ||
361 | *range = TextRange::new(range.start(), range.start() + TextSize::from(1)); | ||
362 | *content = format!("... {}", content); | ||
363 | } | ||
364 | } | ||
365 | } | ||
366 | |||
367 | assert_eq!(annotations, actual); | ||
368 | } | ||
369 | |||
370 | pub(crate) fn check_no_diagnostics(&self) { | ||
371 | let db: &TestDB = self; | ||
372 | let annotations = db.extract_annotations(); | ||
373 | assert!(annotations.is_empty()); | ||
374 | |||
375 | let mut has_diagnostics = false; | ||
376 | db.diagnostics(&mut |_, _| { | ||
377 | has_diagnostics = true; | ||
378 | }); | ||
379 | |||
380 | assert!(!has_diagnostics); | ||
381 | } | ||
382 | } | 245 | } |
diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs index cbde6b940..ffe499973 100644 --- a/crates/hir_def/src/type_ref.rs +++ b/crates/hir_def/src/type_ref.rs | |||
@@ -128,7 +128,7 @@ impl TypeRef { | |||
128 | /// Converts an `ast::TypeRef` to a `hir::TypeRef`. | 128 | /// Converts an `ast::TypeRef` to a `hir::TypeRef`. |
129 | pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self { | 129 | pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self { |
130 | match node { | 130 | match node { |
131 | ast::Type::ParenType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()), | 131 | ast::Type::ParenType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()), |
132 | ast::Type::TupleType(inner) => { | 132 | ast::Type::TupleType(inner) => { |
133 | TypeRef::Tuple(inner.fields().map(|it| TypeRef::from_ast(ctx, it)).collect()) | 133 | TypeRef::Tuple(inner.fields().map(|it| TypeRef::from_ast(ctx, it)).collect()) |
134 | } | 134 | } |
@@ -142,7 +142,7 @@ impl TypeRef { | |||
142 | .unwrap_or(TypeRef::Error) | 142 | .unwrap_or(TypeRef::Error) |
143 | } | 143 | } |
144 | ast::Type::PtrType(inner) => { | 144 | ast::Type::PtrType(inner) => { |
145 | let inner_ty = TypeRef::from_ast_opt(&ctx, inner.ty()); | 145 | let inner_ty = TypeRef::from_ast_opt(ctx, inner.ty()); |
146 | let mutability = Mutability::from_mutable(inner.mut_token().is_some()); | 146 | let mutability = Mutability::from_mutable(inner.mut_token().is_some()); |
147 | TypeRef::RawPtr(Box::new(inner_ty), mutability) | 147 | TypeRef::RawPtr(Box::new(inner_ty), mutability) |
148 | } | 148 | } |
@@ -156,13 +156,13 @@ impl TypeRef { | |||
156 | .map(ConstScalar::usize_from_literal_expr) | 156 | .map(ConstScalar::usize_from_literal_expr) |
157 | .unwrap_or(ConstScalar::Unknown); | 157 | .unwrap_or(ConstScalar::Unknown); |
158 | 158 | ||
159 | TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())), len) | 159 | TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len) |
160 | } | 160 | } |
161 | ast::Type::SliceType(inner) => { | 161 | ast::Type::SliceType(inner) => { |
162 | TypeRef::Slice(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty()))) | 162 | TypeRef::Slice(Box::new(TypeRef::from_ast_opt(ctx, inner.ty()))) |
163 | } | 163 | } |
164 | ast::Type::RefType(inner) => { | 164 | ast::Type::RefType(inner) => { |
165 | let inner_ty = TypeRef::from_ast_opt(&ctx, inner.ty()); | 165 | let inner_ty = TypeRef::from_ast_opt(ctx, inner.ty()); |
166 | let lifetime = inner.lifetime().map(|lt| LifetimeRef::new(<)); | 166 | let lifetime = inner.lifetime().map(|lt| LifetimeRef::new(<)); |
167 | let mutability = Mutability::from_mutable(inner.mut_token().is_some()); | 167 | let mutability = Mutability::from_mutable(inner.mut_token().is_some()); |
168 | TypeRef::Reference(Box::new(inner_ty), lifetime, mutability) | 168 | TypeRef::Reference(Box::new(inner_ty), lifetime, mutability) |
@@ -180,7 +180,7 @@ impl TypeRef { | |||
180 | is_varargs = param.dotdotdot_token().is_some(); | 180 | is_varargs = param.dotdotdot_token().is_some(); |
181 | } | 181 | } |
182 | 182 | ||
183 | pl.params().map(|p| p.ty()).map(|it| TypeRef::from_ast_opt(&ctx, it)).collect() | 183 | pl.params().map(|p| p.ty()).map(|it| TypeRef::from_ast_opt(ctx, it)).collect() |
184 | } else { | 184 | } else { |
185 | Vec::new() | 185 | Vec::new() |
186 | }; | 186 | }; |
@@ -188,7 +188,7 @@ impl TypeRef { | |||
188 | TypeRef::Fn(params, is_varargs) | 188 | TypeRef::Fn(params, is_varargs) |
189 | } | 189 | } |
190 | // for types are close enough for our purposes to the inner type for now... | 190 | // for types are close enough for our purposes to the inner type for now... |
191 | ast::Type::ForType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()), | 191 | ast::Type::ForType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()), |
192 | ast::Type::ImplTraitType(inner) => { | 192 | ast::Type::ImplTraitType(inner) => { |
193 | TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list())) | 193 | TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list())) |
194 | } | 194 | } |
@@ -229,7 +229,7 @@ impl TypeRef { | |||
229 | TypeRef::RawPtr(type_ref, _) | 229 | TypeRef::RawPtr(type_ref, _) |
230 | | TypeRef::Reference(type_ref, ..) | 230 | | TypeRef::Reference(type_ref, ..) |
231 | | TypeRef::Array(type_ref, _) | 231 | | TypeRef::Array(type_ref, _) |
232 | | TypeRef::Slice(type_ref) => go(&type_ref, f), | 232 | | TypeRef::Slice(type_ref) => go(type_ref, f), |
233 | TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => { | 233 | TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => { |
234 | for bound in bounds { | 234 | for bound in bounds { |
235 | match bound.as_ref() { | 235 | match bound.as_ref() { |
diff --git a/crates/hir_expand/src/builtin_attr.rs b/crates/hir_expand/src/builtin_attr.rs new file mode 100644 index 000000000..c8432005e --- /dev/null +++ b/crates/hir_expand/src/builtin_attr.rs | |||
@@ -0,0 +1,67 @@ | |||
1 | //! Builtin derives. | ||
2 | |||
3 | use syntax::ast; | ||
4 | |||
5 | use crate::{db::AstDatabase, name, AstId, CrateId, MacroCallId, MacroDefId, MacroDefKind}; | ||
6 | |||
7 | macro_rules! register_builtin { | ||
8 | ( $(($name:ident, $variant:ident) => $expand:ident),* ) => { | ||
9 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
10 | pub enum BuiltinAttrExpander { | ||
11 | $($variant),* | ||
12 | } | ||
13 | |||
14 | impl BuiltinAttrExpander { | ||
15 | pub fn expand( | ||
16 | &self, | ||
17 | db: &dyn AstDatabase, | ||
18 | id: MacroCallId, | ||
19 | tt: &tt::Subtree, | ||
20 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
21 | let expander = match *self { | ||
22 | $( BuiltinAttrExpander::$variant => $expand, )* | ||
23 | }; | ||
24 | expander(db, id, tt) | ||
25 | } | ||
26 | |||
27 | fn find_by_name(name: &name::Name) -> Option<Self> { | ||
28 | match name { | ||
29 | $( id if id == &name::name![$name] => Some(BuiltinAttrExpander::$variant), )* | ||
30 | _ => None, | ||
31 | } | ||
32 | } | ||
33 | } | ||
34 | |||
35 | }; | ||
36 | } | ||
37 | |||
38 | register_builtin! { | ||
39 | (bench, Bench) => dummy_attr_expand, | ||
40 | (cfg_accessible, CfgAccessible) => dummy_attr_expand, | ||
41 | (cfg_eval, CfgEval) => dummy_attr_expand, | ||
42 | (derive, Derive) => dummy_attr_expand, | ||
43 | (global_allocator, GlobalAllocator) => dummy_attr_expand, | ||
44 | (test, Test) => dummy_attr_expand, | ||
45 | (test_case, TestCase) => dummy_attr_expand | ||
46 | } | ||
47 | |||
48 | pub fn find_builtin_attr( | ||
49 | ident: &name::Name, | ||
50 | krate: CrateId, | ||
51 | ast_id: AstId<ast::Macro>, | ||
52 | ) -> Option<MacroDefId> { | ||
53 | let expander = BuiltinAttrExpander::find_by_name(ident)?; | ||
54 | Some(MacroDefId { | ||
55 | krate, | ||
56 | kind: MacroDefKind::BuiltInAttr(expander, ast_id), | ||
57 | local_inner: false, | ||
58 | }) | ||
59 | } | ||
60 | |||
61 | fn dummy_attr_expand( | ||
62 | _db: &dyn AstDatabase, | ||
63 | _id: MacroCallId, | ||
64 | tt: &tt::Subtree, | ||
65 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
66 | Ok(tt.clone()) | ||
67 | } | ||
diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs index fe9497b50..4610f6f91 100644 --- a/crates/hir_expand/src/builtin_derive.rs +++ b/crates/hir_expand/src/builtin_derive.rs | |||
@@ -325,7 +325,7 @@ $0 | |||
325 | }, | 325 | }, |
326 | }; | 326 | }; |
327 | 327 | ||
328 | let id: MacroCallId = db.intern_macro(loc).into(); | 328 | let id: MacroCallId = db.intern_macro(loc); |
329 | let parsed = db.parse_or_expand(id.as_file()).unwrap(); | 329 | let parsed = db.parse_or_expand(id.as_file()).unwrap(); |
330 | 330 | ||
331 | // FIXME text() for syntax nodes parsed from token tree looks weird | 331 | // FIXME text() for syntax nodes parsed from token tree looks weird |
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index 0b310ba2f..f24d1d919 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs | |||
@@ -354,7 +354,7 @@ fn concat_expand( | |||
354 | // concat works with string and char literals, so remove any quotes. | 354 | // concat works with string and char literals, so remove any quotes. |
355 | // It also works with integer, float and boolean literals, so just use the rest | 355 | // It also works with integer, float and boolean literals, so just use the rest |
356 | // as-is. | 356 | // as-is. |
357 | let component = unquote_str(&it).unwrap_or_else(|| it.text.to_string()); | 357 | let component = unquote_str(it).unwrap_or_else(|| it.text.to_string()); |
358 | text.push_str(&component); | 358 | text.push_str(&component); |
359 | } | 359 | } |
360 | // handle boolean literals | 360 | // handle boolean literals |
@@ -417,7 +417,7 @@ fn parse_string(tt: &tt::Subtree) -> Result<String, mbe::ExpandError> { | |||
417 | tt.token_trees | 417 | tt.token_trees |
418 | .get(0) | 418 | .get(0) |
419 | .and_then(|tt| match tt { | 419 | .and_then(|tt| match tt { |
420 | tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(&it), | 420 | tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(it), |
421 | _ => None, | 421 | _ => None, |
422 | }) | 422 | }) |
423 | .ok_or_else(|| mbe::ExpandError::ConversionError) | 423 | .ok_or_else(|| mbe::ExpandError::ConversionError) |
@@ -430,7 +430,7 @@ fn include_expand( | |||
430 | ) -> ExpandResult<Option<ExpandedEager>> { | 430 | ) -> ExpandResult<Option<ExpandedEager>> { |
431 | let res = (|| { | 431 | let res = (|| { |
432 | let path = parse_string(tt)?; | 432 | let path = parse_string(tt)?; |
433 | let file_id = relative_file(db, arg_id.into(), &path, false)?; | 433 | let file_id = relative_file(db, arg_id, &path, false)?; |
434 | 434 | ||
435 | let subtree = parse_to_token_tree(&db.file_text(file_id)) | 435 | let subtree = parse_to_token_tree(&db.file_text(file_id)) |
436 | .ok_or_else(|| mbe::ExpandError::ConversionError)? | 436 | .ok_or_else(|| mbe::ExpandError::ConversionError)? |
@@ -480,7 +480,7 @@ fn include_str_expand( | |||
480 | // it's unusual to `include_str!` a Rust file), but we can return an empty string. | 480 | // it's unusual to `include_str!` a Rust file), but we can return an empty string. |
481 | // Ideally, we'd be able to offer a precise expansion if the user asks for macro | 481 | // Ideally, we'd be able to offer a precise expansion if the user asks for macro |
482 | // expansion. | 482 | // expansion. |
483 | let file_id = match relative_file(db, arg_id.into(), &path, true) { | 483 | let file_id = match relative_file(db, arg_id, &path, true) { |
484 | Ok(file_id) => file_id, | 484 | Ok(file_id) => file_id, |
485 | Err(_) => { | 485 | Err(_) => { |
486 | return ExpandResult::ok(Some(ExpandedEager::new(quote!("")))); | 486 | return ExpandResult::ok(Some(ExpandedEager::new(quote!("")))); |
@@ -561,7 +561,7 @@ mod tests { | |||
561 | use syntax::ast::NameOwner; | 561 | use syntax::ast::NameOwner; |
562 | 562 | ||
563 | fn expand_builtin_macro(ra_fixture: &str) -> String { | 563 | fn expand_builtin_macro(ra_fixture: &str) -> String { |
564 | let (db, file_id) = TestDB::with_single_file(&ra_fixture); | 564 | let (db, file_id) = TestDB::with_single_file(ra_fixture); |
565 | let parsed = db.parse(file_id); | 565 | let parsed = db.parse(file_id); |
566 | let mut macro_rules: Vec<_> = | 566 | let mut macro_rules: Vec<_> = |
567 | parsed.syntax_node().descendants().filter_map(ast::MacroRules::cast).collect(); | 567 | parsed.syntax_node().descendants().filter_map(ast::MacroRules::cast).collect(); |
@@ -598,7 +598,7 @@ mod tests { | |||
598 | }, | 598 | }, |
599 | }; | 599 | }; |
600 | 600 | ||
601 | let id: MacroCallId = db.intern_macro(loc).into(); | 601 | let id: MacroCallId = db.intern_macro(loc); |
602 | id.as_file() | 602 | id.as_file() |
603 | } | 603 | } |
604 | Either::Right(expander) => { | 604 | Either::Right(expander) => { |
@@ -635,7 +635,7 @@ mod tests { | |||
635 | kind: MacroCallKind::FnLike { ast_id: call_id, fragment }, | 635 | kind: MacroCallKind::FnLike { ast_id: call_id, fragment }, |
636 | }; | 636 | }; |
637 | 637 | ||
638 | let id: MacroCallId = db.intern_macro(loc).into(); | 638 | let id: MacroCallId = db.intern_macro(loc); |
639 | id.as_file() | 639 | id.as_file() |
640 | } | 640 | } |
641 | }; | 641 | }; |
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index e8f4af309..66f44202b 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -12,9 +12,9 @@ use syntax::{ | |||
12 | }; | 12 | }; |
13 | 13 | ||
14 | use crate::{ | 14 | use crate::{ |
15 | ast_id_map::AstIdMap, hygiene::HygieneFrame, input::process_macro_input, BuiltinDeriveExpander, | 15 | ast_id_map::AstIdMap, hygiene::HygieneFrame, input::process_macro_input, BuiltinAttrExpander, |
16 | BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, MacroCallId, MacroCallLoc, MacroDefId, | 16 | BuiltinDeriveExpander, BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, MacroCallId, |
17 | MacroDefKind, MacroFile, ProcMacroExpander, | 17 | MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander, |
18 | }; | 18 | }; |
19 | 19 | ||
20 | /// Total limit on the number of tokens produced by any macro invocation. | 20 | /// Total limit on the number of tokens produced by any macro invocation. |
@@ -31,6 +31,8 @@ pub enum TokenExpander { | |||
31 | MacroDef { mac: mbe::MacroDef, def_site_token_map: mbe::TokenMap }, | 31 | MacroDef { mac: mbe::MacroDef, def_site_token_map: mbe::TokenMap }, |
32 | /// Stuff like `line!` and `file!`. | 32 | /// Stuff like `line!` and `file!`. |
33 | Builtin(BuiltinFnLikeExpander), | 33 | Builtin(BuiltinFnLikeExpander), |
34 | /// `global_allocator` and such. | ||
35 | BuiltinAttr(BuiltinAttrExpander), | ||
34 | /// `derive(Copy)` and such. | 36 | /// `derive(Copy)` and such. |
35 | BuiltinDerive(BuiltinDeriveExpander), | 37 | BuiltinDerive(BuiltinDeriveExpander), |
36 | /// The thing we love the most here in rust-analyzer -- procedural macros. | 38 | /// The thing we love the most here in rust-analyzer -- procedural macros. |
@@ -49,12 +51,13 @@ impl TokenExpander { | |||
49 | TokenExpander::MacroDef { mac, .. } => mac.expand(tt), | 51 | TokenExpander::MacroDef { mac, .. } => mac.expand(tt), |
50 | TokenExpander::Builtin(it) => it.expand(db, id, tt), | 52 | TokenExpander::Builtin(it) => it.expand(db, id, tt), |
51 | // FIXME switch these to ExpandResult as well | 53 | // FIXME switch these to ExpandResult as well |
54 | TokenExpander::BuiltinAttr(it) => it.expand(db, id, tt).into(), | ||
52 | TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), | 55 | TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), |
53 | TokenExpander::ProcMacro(_) => { | 56 | TokenExpander::ProcMacro(_) => { |
54 | // We store the result in salsa db to prevent non-deterministic behavior in | 57 | // We store the result in salsa db to prevent non-deterministic behavior in |
55 | // some proc-macro implementation | 58 | // some proc-macro implementation |
56 | // See #4315 for details | 59 | // See #4315 for details |
57 | db.expand_proc_macro(id.into()).into() | 60 | db.expand_proc_macro(id).into() |
58 | } | 61 | } |
59 | } | 62 | } |
60 | } | 63 | } |
@@ -64,6 +67,7 @@ impl TokenExpander { | |||
64 | TokenExpander::MacroRules { mac, .. } => mac.map_id_down(id), | 67 | TokenExpander::MacroRules { mac, .. } => mac.map_id_down(id), |
65 | TokenExpander::MacroDef { mac, .. } => mac.map_id_down(id), | 68 | TokenExpander::MacroDef { mac, .. } => mac.map_id_down(id), |
66 | TokenExpander::Builtin(..) | 69 | TokenExpander::Builtin(..) |
70 | | TokenExpander::BuiltinAttr(..) | ||
67 | | TokenExpander::BuiltinDerive(..) | 71 | | TokenExpander::BuiltinDerive(..) |
68 | | TokenExpander::ProcMacro(..) => id, | 72 | | TokenExpander::ProcMacro(..) => id, |
69 | } | 73 | } |
@@ -74,6 +78,7 @@ impl TokenExpander { | |||
74 | TokenExpander::MacroRules { mac, .. } => mac.map_id_up(id), | 78 | TokenExpander::MacroRules { mac, .. } => mac.map_id_up(id), |
75 | TokenExpander::MacroDef { mac, .. } => mac.map_id_up(id), | 79 | TokenExpander::MacroDef { mac, .. } => mac.map_id_up(id), |
76 | TokenExpander::Builtin(..) | 80 | TokenExpander::Builtin(..) |
81 | | TokenExpander::BuiltinAttr(..) | ||
77 | | TokenExpander::BuiltinDerive(..) | 82 | | TokenExpander::BuiltinDerive(..) |
78 | | TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call), | 83 | | TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call), |
79 | } | 84 | } |
@@ -85,7 +90,7 @@ impl TokenExpander { | |||
85 | pub trait AstDatabase: SourceDatabase { | 90 | pub trait AstDatabase: SourceDatabase { |
86 | fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>; | 91 | fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>; |
87 | 92 | ||
88 | /// Main public API -- parsis a hir file, not caring whether it's a real | 93 | /// Main public API -- parses a hir file, not caring whether it's a real |
89 | /// file or a macro expansion. | 94 | /// file or a macro expansion. |
90 | #[salsa::transparent] | 95 | #[salsa::transparent] |
91 | fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>; | 96 | fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>; |
@@ -236,7 +241,7 @@ fn parse_macro_expansion( | |||
236 | } | 241 | } |
237 | }; | 242 | }; |
238 | if is_self_replicating(&node, &call_node.value) { | 243 | if is_self_replicating(&node, &call_node.value) { |
239 | return ExpandResult::only_err(err); | 244 | ExpandResult::only_err(err) |
240 | } else { | 245 | } else { |
241 | ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) } | 246 | ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) } |
242 | } | 247 | } |
@@ -299,6 +304,9 @@ fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<TokenExpander>> | |||
299 | } | 304 | } |
300 | }, | 305 | }, |
301 | MacroDefKind::BuiltIn(expander, _) => Some(Arc::new(TokenExpander::Builtin(expander))), | 306 | MacroDefKind::BuiltIn(expander, _) => Some(Arc::new(TokenExpander::Builtin(expander))), |
307 | MacroDefKind::BuiltInAttr(expander, _) => { | ||
308 | Some(Arc::new(TokenExpander::BuiltinAttr(expander))) | ||
309 | } | ||
302 | MacroDefKind::BuiltInDerive(expander, _) => { | 310 | MacroDefKind::BuiltInDerive(expander, _) => { |
303 | Some(Arc::new(TokenExpander::BuiltinDerive(expander))) | 311 | Some(Arc::new(TokenExpander::BuiltinDerive(expander))) |
304 | } | 312 | } |
@@ -377,7 +385,12 @@ fn expand_proc_macro( | |||
377 | _ => unreachable!(), | 385 | _ => unreachable!(), |
378 | }; | 386 | }; |
379 | 387 | ||
380 | expander.expand(db, loc.krate, ¯o_arg.0) | 388 | let attr_arg = match &loc.kind { |
389 | MacroCallKind::Attr { attr_args, .. } => Some(attr_args), | ||
390 | _ => None, | ||
391 | }; | ||
392 | |||
393 | expander.expand(db, loc.krate, ¯o_arg.0, attr_arg) | ||
381 | } | 394 | } |
382 | 395 | ||
383 | fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool { | 396 | fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool { |
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs index 14af628a1..584ddcf9f 100644 --- a/crates/hir_expand/src/eager.rs +++ b/crates/hir_expand/src/eager.rs | |||
@@ -17,7 +17,7 @@ | |||
17 | //! > and we need to live with it because it's available on stable and widely relied upon. | 17 | //! > and we need to live with it because it's available on stable and widely relied upon. |
18 | //! | 18 | //! |
19 | //! | 19 | //! |
20 | //! See the full discussion : https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros | 20 | //! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros> |
21 | 21 | ||
22 | use crate::{ | 22 | use crate::{ |
23 | ast::{self, AstNode}, | 23 | ast::{self, AstNode}, |
@@ -128,7 +128,7 @@ pub fn expand_eager_macro( | |||
128 | }), | 128 | }), |
129 | kind: MacroCallKind::FnLike { ast_id: call_id, fragment: FragmentKind::Expr }, | 129 | kind: MacroCallKind::FnLike { ast_id: call_id, fragment: FragmentKind::Expr }, |
130 | }); | 130 | }); |
131 | let arg_file_id: MacroCallId = arg_id.into(); | 131 | let arg_file_id: MacroCallId = arg_id; |
132 | 132 | ||
133 | let parsed_args = | 133 | let parsed_args = |
134 | diagnostic_sink.result(mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr))?.0; | 134 | diagnostic_sink.result(mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr))?.0; |
@@ -177,13 +177,11 @@ fn lazy_expand( | |||
177 | let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value); | 177 | let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value); |
178 | 178 | ||
179 | let fragment = crate::to_fragment_kind(¯o_call.value); | 179 | let fragment = crate::to_fragment_kind(¯o_call.value); |
180 | let id: MacroCallId = def | 180 | let id: MacroCallId = def.as_lazy_macro( |
181 | .as_lazy_macro( | 181 | db, |
182 | db, | 182 | krate, |
183 | krate, | 183 | MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), fragment }, |
184 | MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), fragment }, | 184 | ); |
185 | ) | ||
186 | .into(); | ||
187 | 185 | ||
188 | let err = db.macro_expand_error(id); | 186 | let err = db.macro_expand_error(id); |
189 | let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)); | 187 | let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)); |
@@ -216,14 +214,14 @@ fn eager_macro_recur( | |||
216 | def, | 214 | def, |
217 | macro_resolver, | 215 | macro_resolver, |
218 | diagnostic_sink, | 216 | diagnostic_sink, |
219 | )? | 217 | )?; |
220 | .into(); | ||
221 | db.parse_or_expand(id.as_file()) | 218 | db.parse_or_expand(id.as_file()) |
222 | .expect("successful macro expansion should be parseable") | 219 | .expect("successful macro expansion should be parseable") |
223 | .clone_for_update() | 220 | .clone_for_update() |
224 | } | 221 | } |
225 | MacroDefKind::Declarative(_) | 222 | MacroDefKind::Declarative(_) |
226 | | MacroDefKind::BuiltIn(..) | 223 | | MacroDefKind::BuiltIn(..) |
224 | | MacroDefKind::BuiltInAttr(..) | ||
227 | | MacroDefKind::BuiltInDerive(..) | 225 | | MacroDefKind::BuiltInDerive(..) |
228 | | MacroDefKind::ProcMacro(..) => { | 226 | | MacroDefKind::ProcMacro(..) => { |
229 | let res = lazy_expand(db, &def, curr.with_value(child.clone()), krate); | 227 | let res = lazy_expand(db, &def, curr.with_value(child.clone()), krate); |
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs index d98913907..05c6c3fb1 100644 --- a/crates/hir_expand/src/hygiene.rs +++ b/crates/hir_expand/src/hygiene.rs | |||
@@ -192,6 +192,7 @@ impl HygieneFrame { | |||
192 | (info, Some(loc.def.krate), loc.def.local_inner) | 192 | (info, Some(loc.def.krate), loc.def.local_inner) |
193 | } | 193 | } |
194 | MacroDefKind::BuiltIn(..) => (info, Some(loc.def.krate), false), | 194 | MacroDefKind::BuiltIn(..) => (info, Some(loc.def.krate), false), |
195 | MacroDefKind::BuiltInAttr(..) => (info, None, false), | ||
195 | MacroDefKind::BuiltInDerive(..) => (info, None, false), | 196 | MacroDefKind::BuiltInDerive(..) => (info, None, false), |
196 | MacroDefKind::BuiltInEager(..) => (info, None, false), | 197 | MacroDefKind::BuiltInEager(..) => (info, None, false), |
197 | MacroDefKind::ProcMacro(..) => (info, None, false), | 198 | MacroDefKind::ProcMacro(..) => (info, None, false), |
diff --git a/crates/hir_expand/src/input.rs b/crates/hir_expand/src/input.rs index fe4790e7b..bc3ecc593 100644 --- a/crates/hir_expand/src/input.rs +++ b/crates/hir_expand/src/input.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Macro input conditioning. | 1 | //! Macro input conditioning. |
2 | 2 | ||
3 | use syntax::{ | 3 | use syntax::{ |
4 | ast::{self, AttrsOwner}, | 4 | ast::{self, make, AttrsOwner}, |
5 | AstNode, SyntaxNode, | 5 | AstNode, SyntaxNode, |
6 | }; | 6 | }; |
7 | 7 | ||
@@ -28,6 +28,14 @@ pub(crate) fn process_macro_input( | |||
28 | 28 | ||
29 | remove_derives_up_to(item, derive_attr_index as usize).syntax().clone() | 29 | remove_derives_up_to(item, derive_attr_index as usize).syntax().clone() |
30 | } | 30 | } |
31 | MacroCallKind::Attr { invoc_attr_index, .. } => { | ||
32 | let item = match ast::Item::cast(node.clone()) { | ||
33 | Some(item) => item, | ||
34 | None => return node, | ||
35 | }; | ||
36 | |||
37 | remove_attr_invoc(item, invoc_attr_index as usize).syntax().clone() | ||
38 | } | ||
31 | } | 39 | } |
32 | } | 40 | } |
33 | 41 | ||
@@ -46,6 +54,19 @@ fn remove_derives_up_to(item: ast::Item, attr_index: usize) -> ast::Item { | |||
46 | item | 54 | item |
47 | } | 55 | } |
48 | 56 | ||
57 | /// Removes the attribute invoking an attribute macro from `item`. | ||
58 | fn remove_attr_invoc(item: ast::Item, attr_index: usize) -> ast::Item { | ||
59 | let item = item.clone_for_update(); | ||
60 | let attr = item | ||
61 | .attrs() | ||
62 | .nth(attr_index) | ||
63 | .unwrap_or_else(|| panic!("cannot find attribute #{}", attr_index)); | ||
64 | let syntax_index = attr.syntax().index(); | ||
65 | let ws = make::tokens::whitespace(&" ".repeat(u32::from(attr.syntax().text().len()) as usize)); | ||
66 | item.syntax().splice_children(syntax_index..syntax_index + 1, vec![ws.into()]); | ||
67 | item | ||
68 | } | ||
69 | |||
49 | #[cfg(test)] | 70 | #[cfg(test)] |
50 | mod tests { | 71 | mod tests { |
51 | use base_db::fixture::WithFixture; | 72 | use base_db::fixture::WithFixture; |
@@ -57,7 +78,7 @@ mod tests { | |||
57 | use super::*; | 78 | use super::*; |
58 | 79 | ||
59 | fn test_remove_derives_up_to(attr: usize, ra_fixture: &str, expect: Expect) { | 80 | fn test_remove_derives_up_to(attr: usize, ra_fixture: &str, expect: Expect) { |
60 | let (db, file_id) = TestDB::with_single_file(&ra_fixture); | 81 | let (db, file_id) = TestDB::with_single_file(ra_fixture); |
61 | let parsed = db.parse(file_id); | 82 | let parsed = db.parse(file_id); |
62 | 83 | ||
63 | let mut items: Vec<_> = | 84 | let mut items: Vec<_> = |
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index 90d8ae240..33107aa24 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs | |||
@@ -8,6 +8,7 @@ pub mod db; | |||
8 | pub mod ast_id_map; | 8 | pub mod ast_id_map; |
9 | pub mod name; | 9 | pub mod name; |
10 | pub mod hygiene; | 10 | pub mod hygiene; |
11 | pub mod builtin_attr; | ||
11 | pub mod builtin_derive; | 12 | pub mod builtin_derive; |
12 | pub mod builtin_macro; | 13 | pub mod builtin_macro; |
13 | pub mod proc_macro; | 14 | pub mod proc_macro; |
@@ -32,6 +33,7 @@ use syntax::{ | |||
32 | }; | 33 | }; |
33 | 34 | ||
34 | use crate::ast_id_map::FileAstId; | 35 | use crate::ast_id_map::FileAstId; |
36 | use crate::builtin_attr::BuiltinAttrExpander; | ||
35 | use crate::builtin_derive::BuiltinDeriveExpander; | 37 | use crate::builtin_derive::BuiltinDeriveExpander; |
36 | use crate::builtin_macro::{BuiltinFnLikeExpander, EagerExpander}; | 38 | use crate::builtin_macro::{BuiltinFnLikeExpander, EagerExpander}; |
37 | use crate::proc_macro::ProcMacroExpander; | 39 | use crate::proc_macro::ProcMacroExpander; |
@@ -51,7 +53,7 @@ mod test_db; | |||
51 | /// this is a recursive definition! However, the size_of of `HirFileId` is | 53 | /// this is a recursive definition! However, the size_of of `HirFileId` is |
52 | /// finite (because everything bottoms out at the real `FileId`) and small | 54 | /// finite (because everything bottoms out at the real `FileId`) and small |
53 | /// (`MacroCallId` uses the location interning. You can check details here: | 55 | /// (`MacroCallId` uses the location interning. You can check details here: |
54 | /// https://en.wikipedia.org/wiki/String_interning). | 56 | /// <https://en.wikipedia.org/wiki/String_interning>). |
55 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 57 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
56 | pub struct HirFileId(HirFileIdRepr); | 58 | pub struct HirFileId(HirFileIdRepr); |
57 | 59 | ||
@@ -206,6 +208,7 @@ impl MacroDefId { | |||
206 | let id = match &self.kind { | 208 | let id = match &self.kind { |
207 | MacroDefKind::Declarative(id) => id, | 209 | MacroDefKind::Declarative(id) => id, |
208 | MacroDefKind::BuiltIn(_, id) => id, | 210 | MacroDefKind::BuiltIn(_, id) => id, |
211 | MacroDefKind::BuiltInAttr(_, id) => id, | ||
209 | MacroDefKind::BuiltInDerive(_, id) => id, | 212 | MacroDefKind::BuiltInDerive(_, id) => id, |
210 | MacroDefKind::BuiltInEager(_, id) => id, | 213 | MacroDefKind::BuiltInEager(_, id) => id, |
211 | MacroDefKind::ProcMacro(.., id) => return Either::Right(*id), | 214 | MacroDefKind::ProcMacro(.., id) => return Either::Right(*id), |
@@ -223,6 +226,7 @@ pub enum MacroDefKind { | |||
223 | Declarative(AstId<ast::Macro>), | 226 | Declarative(AstId<ast::Macro>), |
224 | BuiltIn(BuiltinFnLikeExpander, AstId<ast::Macro>), | 227 | BuiltIn(BuiltinFnLikeExpander, AstId<ast::Macro>), |
225 | // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander | 228 | // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander |
229 | BuiltInAttr(BuiltinAttrExpander, AstId<ast::Macro>), | ||
226 | BuiltInDerive(BuiltinDeriveExpander, AstId<ast::Macro>), | 230 | BuiltInDerive(BuiltinDeriveExpander, AstId<ast::Macro>), |
227 | BuiltInEager(EagerExpander, AstId<ast::Macro>), | 231 | BuiltInEager(EagerExpander, AstId<ast::Macro>), |
228 | ProcMacro(ProcMacroExpander, ProcMacroKind, AstId<ast::Fn>), | 232 | ProcMacro(ProcMacroExpander, ProcMacroKind, AstId<ast::Fn>), |
@@ -258,14 +262,29 @@ pub enum MacroCallKind { | |||
258 | /// out-of-line modules, which may have attributes spread across 2 files! | 262 | /// out-of-line modules, which may have attributes spread across 2 files! |
259 | derive_attr_index: u32, | 263 | derive_attr_index: u32, |
260 | }, | 264 | }, |
265 | Attr { | ||
266 | ast_id: AstId<ast::Item>, | ||
267 | attr_name: String, | ||
268 | attr_args: tt::Subtree, | ||
269 | /// Syntactical index of the invoking `#[attribute]`. | ||
270 | /// | ||
271 | /// Outer attributes are counted first, then inner attributes. This does not support | ||
272 | /// out-of-line modules, which may have attributes spread across 2 files! | ||
273 | invoc_attr_index: u32, | ||
274 | }, | ||
261 | } | 275 | } |
262 | 276 | ||
277 | // FIXME: attribute indices do not account for `cfg_attr`, which means that we'll strip the whole | ||
278 | // `cfg_attr` instead of just one of the attributes it expands to | ||
279 | |||
263 | impl MacroCallKind { | 280 | impl MacroCallKind { |
264 | /// Returns the file containing the macro invocation. | 281 | /// Returns the file containing the macro invocation. |
265 | fn file_id(&self) -> HirFileId { | 282 | fn file_id(&self) -> HirFileId { |
266 | match self { | 283 | match self { |
267 | MacroCallKind::FnLike { ast_id, .. } => ast_id.file_id, | 284 | MacroCallKind::FnLike { ast_id, .. } => ast_id.file_id, |
268 | MacroCallKind::Derive { ast_id, .. } => ast_id.file_id, | 285 | MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => { |
286 | ast_id.file_id | ||
287 | } | ||
269 | } | 288 | } |
270 | } | 289 | } |
271 | 290 | ||
@@ -274,7 +293,7 @@ impl MacroCallKind { | |||
274 | MacroCallKind::FnLike { ast_id, .. } => { | 293 | MacroCallKind::FnLike { ast_id, .. } => { |
275 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) | 294 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) |
276 | } | 295 | } |
277 | MacroCallKind::Derive { ast_id, .. } => { | 296 | MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => { |
278 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) | 297 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) |
279 | } | 298 | } |
280 | } | 299 | } |
@@ -285,7 +304,9 @@ impl MacroCallKind { | |||
285 | MacroCallKind::FnLike { ast_id, .. } => { | 304 | MacroCallKind::FnLike { ast_id, .. } => { |
286 | Some(ast_id.to_node(db).token_tree()?.syntax().clone()) | 305 | Some(ast_id.to_node(db).token_tree()?.syntax().clone()) |
287 | } | 306 | } |
288 | MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()), | 307 | MacroCallKind::Derive { ast_id, .. } | MacroCallKind::Attr { ast_id, .. } => { |
308 | Some(ast_id.to_node(db).syntax().clone()) | ||
309 | } | ||
289 | } | 310 | } |
290 | } | 311 | } |
291 | 312 | ||
@@ -293,6 +314,7 @@ impl MacroCallKind { | |||
293 | match self { | 314 | match self { |
294 | MacroCallKind::FnLike { fragment, .. } => *fragment, | 315 | MacroCallKind::FnLike { fragment, .. } => *fragment, |
295 | MacroCallKind::Derive { .. } => FragmentKind::Items, | 316 | MacroCallKind::Derive { .. } => FragmentKind::Items, |
317 | MacroCallKind::Attr { .. } => FragmentKind::Items, // is this always correct? | ||
296 | } | 318 | } |
297 | } | 319 | } |
298 | } | 320 | } |
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs index 00b8adc1e..376fe130f 100644 --- a/crates/hir_expand/src/name.rs +++ b/crates/hir_expand/src/name.rs | |||
@@ -160,7 +160,6 @@ pub mod known { | |||
160 | str, | 160 | str, |
161 | // Special names | 161 | // Special names |
162 | macro_rules, | 162 | macro_rules, |
163 | derive, | ||
164 | doc, | 163 | doc, |
165 | cfg, | 164 | cfg, |
166 | cfg_attr, | 165 | cfg_attr, |
@@ -240,6 +239,14 @@ pub mod known { | |||
240 | PartialOrd, | 239 | PartialOrd, |
241 | Eq, | 240 | Eq, |
242 | PartialEq, | 241 | PartialEq, |
242 | // Builtin attributes | ||
243 | bench, | ||
244 | cfg_accessible, | ||
245 | cfg_eval, | ||
246 | derive, | ||
247 | global_allocator, | ||
248 | test, | ||
249 | test_case, | ||
243 | // Safe intrinsics | 250 | // Safe intrinsics |
244 | abort, | 251 | abort, |
245 | size_of, | 252 | size_of, |
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs index d5643393a..025e10239 100644 --- a/crates/hir_expand/src/proc_macro.rs +++ b/crates/hir_expand/src/proc_macro.rs | |||
@@ -28,11 +28,16 @@ impl ProcMacroExpander { | |||
28 | Self { krate, proc_macro_id: None } | 28 | Self { krate, proc_macro_id: None } |
29 | } | 29 | } |
30 | 30 | ||
31 | pub fn is_dummy(&self) -> bool { | ||
32 | self.proc_macro_id.is_none() | ||
33 | } | ||
34 | |||
31 | pub fn expand( | 35 | pub fn expand( |
32 | self, | 36 | self, |
33 | db: &dyn AstDatabase, | 37 | db: &dyn AstDatabase, |
34 | calling_crate: CrateId, | 38 | calling_crate: CrateId, |
35 | tt: &tt::Subtree, | 39 | tt: &tt::Subtree, |
40 | attr_arg: Option<&tt::Subtree>, | ||
36 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 41 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
37 | match self.proc_macro_id { | 42 | match self.proc_macro_id { |
38 | Some(id) => { | 43 | Some(id) => { |
@@ -40,13 +45,12 @@ impl ProcMacroExpander { | |||
40 | let proc_macro = krate_graph[self.krate] | 45 | let proc_macro = krate_graph[self.krate] |
41 | .proc_macro | 46 | .proc_macro |
42 | .get(id.0 as usize) | 47 | .get(id.0 as usize) |
43 | .clone() | ||
44 | .ok_or_else(|| err!("No derive macro found."))?; | 48 | .ok_or_else(|| err!("No derive macro found."))?; |
45 | 49 | ||
46 | // Proc macros have access to the environment variables of the invoking crate. | 50 | // Proc macros have access to the environment variables of the invoking crate. |
47 | let env = &krate_graph[calling_crate].env; | 51 | let env = &krate_graph[calling_crate].env; |
48 | 52 | ||
49 | proc_macro.expander.expand(&tt, None, &env).map_err(mbe::ExpandError::from) | 53 | proc_macro.expander.expand(tt, attr_arg, env).map_err(mbe::ExpandError::from) |
50 | } | 54 | } |
51 | None => Err(mbe::ExpandError::UnresolvedProcMacro), | 55 | None => Err(mbe::ExpandError::UnresolvedProcMacro), |
52 | } | 56 | } |
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml index 4b714c6d8..a1894e8d8 100644 --- a/crates/hir_ty/Cargo.toml +++ b/crates/hir_ty/Cargo.toml | |||
@@ -10,7 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = { version = "1.1", features = ["thread-local"] } | 13 | cov-mark = "2.0.0-pre.1" |
14 | itertools = "0.10.0" | 14 | itertools = "0.10.0" |
15 | arrayvec = "0.7" | 15 | arrayvec = "0.7" |
16 | smallvec = "1.2.0" | 16 | smallvec = "1.2.0" |
diff --git a/crates/hir_ty/src/builder.rs b/crates/hir_ty/src/builder.rs index 893e727c2..bb9d84246 100644 --- a/crates/hir_ty/src/builder.rs +++ b/crates/hir_ty/src/builder.rs | |||
@@ -202,7 +202,7 @@ impl<T: HasInterner<Interner = Interner> + Fold<Interner>> TyBuilder<Binders<T>> | |||
202 | 202 | ||
203 | impl TyBuilder<Binders<Ty>> { | 203 | impl TyBuilder<Binders<Ty>> { |
204 | pub fn def_ty(db: &dyn HirDatabase, def: TyDefId) -> TyBuilder<Binders<Ty>> { | 204 | pub fn def_ty(db: &dyn HirDatabase, def: TyDefId) -> TyBuilder<Binders<Ty>> { |
205 | TyBuilder::subst_binders(db.ty(def.into())) | 205 | TyBuilder::subst_binders(db.ty(def)) |
206 | } | 206 | } |
207 | 207 | ||
208 | pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder<Binders<Ty>> { | 208 | pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder<Binders<Ty>> { |
diff --git a/crates/hir_ty/src/chalk_db.rs b/crates/hir_ty/src/chalk_db.rs index 4e042bf42..a4c09c742 100644 --- a/crates/hir_ty/src/chalk_db.rs +++ b/crates/hir_ty/src/chalk_db.rs | |||
@@ -10,16 +10,16 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait}; | |||
10 | use base_db::CrateId; | 10 | use base_db::CrateId; |
11 | use hir_def::{ | 11 | use hir_def::{ |
12 | lang_item::{lang_attr, LangItemTarget}, | 12 | lang_item::{lang_attr, LangItemTarget}, |
13 | AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId, | 13 | AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, ModuleId, TypeAliasId, |
14 | }; | 14 | }; |
15 | use hir_expand::name::name; | 15 | use hir_expand::name::name; |
16 | 16 | ||
17 | use crate::{ | 17 | use crate::{ |
18 | db::HirDatabase, | 18 | db::HirDatabase, |
19 | display::HirDisplay, | 19 | display::HirDisplay, |
20 | from_assoc_type_id, from_chalk_trait_id, make_only_type_binders, | 20 | from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, make_only_type_binders, |
21 | mapping::{from_chalk, ToChalk, TypeAliasAsValue}, | 21 | mapping::{from_chalk, ToChalk, TypeAliasAsValue}, |
22 | method_resolution::{TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS}, | 22 | method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS}, |
23 | to_assoc_type_id, to_chalk_trait_id, | 23 | to_assoc_type_id, to_chalk_trait_id, |
24 | traits::ChalkContext, | 24 | traits::ChalkContext, |
25 | utils::generics, | 25 | utils::generics, |
@@ -105,12 +105,30 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
105 | _ => self_ty_fp.as_ref().map(std::slice::from_ref).unwrap_or(&[]), | 105 | _ => self_ty_fp.as_ref().map(std::slice::from_ref).unwrap_or(&[]), |
106 | }; | 106 | }; |
107 | 107 | ||
108 | fn local_impls(db: &dyn HirDatabase, module: ModuleId) -> Option<Arc<TraitImpls>> { | ||
109 | db.trait_impls_in_block(module.containing_block()?) | ||
110 | } | ||
111 | |||
108 | // Note: Since we're using impls_for_trait, only impls where the trait | 112 | // Note: Since we're using impls_for_trait, only impls where the trait |
109 | // can be resolved should ever reach Chalk. Symbol’s value as variable is void: impl_datum relies on that | 113 | // can be resolved should ever reach Chalk. impl_datum relies on that |
110 | // and will panic if the trait can't be resolved. | 114 | // and will panic if the trait can't be resolved. |
111 | let in_deps = self.db.trait_impls_in_deps(self.krate); | 115 | let in_deps = self.db.trait_impls_in_deps(self.krate); |
112 | let in_self = self.db.trait_impls_in_crate(self.krate); | 116 | let in_self = self.db.trait_impls_in_crate(self.krate); |
113 | let impl_maps = [in_deps, in_self]; | 117 | let trait_module = trait_.module(self.db.upcast()); |
118 | let type_module = match self_ty_fp { | ||
119 | Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(self.db.upcast())), | ||
120 | Some(TyFingerprint::ForeignType(type_id)) => { | ||
121 | Some(from_foreign_def_id(type_id).module(self.db.upcast())) | ||
122 | } | ||
123 | Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(self.db.upcast())), | ||
124 | _ => None, | ||
125 | }; | ||
126 | let impl_maps = [ | ||
127 | Some(in_deps), | ||
128 | Some(in_self), | ||
129 | local_impls(self.db, trait_module), | ||
130 | type_module.and_then(|m| local_impls(self.db, m)), | ||
131 | ]; | ||
114 | 132 | ||
115 | let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db); | 133 | let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db); |
116 | 134 | ||
@@ -118,14 +136,16 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
118 | debug!("Unrestricted search for {:?} impls...", trait_); | 136 | debug!("Unrestricted search for {:?} impls...", trait_); |
119 | impl_maps | 137 | impl_maps |
120 | .iter() | 138 | .iter() |
121 | .flat_map(|crate_impl_defs| crate_impl_defs.for_trait(trait_).map(id_to_chalk)) | 139 | .filter_map(|o| o.as_ref()) |
140 | .flat_map(|impls| impls.for_trait(trait_).map(id_to_chalk)) | ||
122 | .collect() | 141 | .collect() |
123 | } else { | 142 | } else { |
124 | impl_maps | 143 | impl_maps |
125 | .iter() | 144 | .iter() |
126 | .flat_map(|crate_impl_defs| { | 145 | .filter_map(|o| o.as_ref()) |
146 | .flat_map(|impls| { | ||
127 | fps.iter().flat_map(move |fp| { | 147 | fps.iter().flat_map(move |fp| { |
128 | crate_impl_defs.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk) | 148 | impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk) |
129 | }) | 149 | }) |
130 | }) | 150 | }) |
131 | .collect() | 151 | .collect() |
@@ -430,8 +450,7 @@ pub(crate) fn trait_datum_query( | |||
430 | fundamental: false, | 450 | fundamental: false, |
431 | }; | 451 | }; |
432 | let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars); | 452 | let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars); |
433 | let associated_ty_ids = | 453 | let associated_ty_ids = trait_data.associated_types().map(to_assoc_type_id).collect(); |
434 | trait_data.associated_types().map(|type_alias| to_assoc_type_id(type_alias)).collect(); | ||
435 | let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses }; | 454 | let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses }; |
436 | let well_known = | 455 | let well_known = |
437 | lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name)); | 456 | lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name)); |
diff --git a/crates/hir_ty/src/consteval.rs b/crates/hir_ty/src/consteval.rs index e3ceb3d62..6f0bf8f8c 100644 --- a/crates/hir_ty/src/consteval.rs +++ b/crates/hir_ty/src/consteval.rs | |||
@@ -49,7 +49,7 @@ pub fn usize_const(value: Option<u64>) -> Const { | |||
49 | ConstData { | 49 | ConstData { |
50 | ty: TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(&Interner), | 50 | ty: TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(&Interner), |
51 | value: ConstValue::Concrete(chalk_ir::ConcreteConst { | 51 | value: ConstValue::Concrete(chalk_ir::ConcreteConst { |
52 | interned: value.map(|value| ConstScalar::Usize(value)).unwrap_or(ConstScalar::Unknown), | 52 | interned: value.map(ConstScalar::Usize).unwrap_or(ConstScalar::Unknown), |
53 | }), | 53 | }), |
54 | } | 54 | } |
55 | .intern(&Interner) | 55 | .intern(&Interner) |
diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs index be5b9110e..b9003c413 100644 --- a/crates/hir_ty/src/db.rs +++ b/crates/hir_ty/src/db.rs | |||
@@ -5,8 +5,8 @@ use std::sync::Arc; | |||
5 | 5 | ||
6 | use base_db::{impl_intern_key, salsa, CrateId, Upcast}; | 6 | use base_db::{impl_intern_key, salsa, CrateId, Upcast}; |
7 | use hir_def::{ | 7 | use hir_def::{ |
8 | db::DefDatabase, expr::ExprId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId, ImplId, | 8 | db::DefDatabase, expr::ExprId, BlockId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId, |
9 | LifetimeParamId, LocalFieldId, TypeParamId, VariantId, | 9 | ImplId, LifetimeParamId, LocalFieldId, TypeParamId, VariantId, |
10 | }; | 10 | }; |
11 | use la_arena::ArenaMap; | 11 | use la_arena::ArenaMap; |
12 | 12 | ||
@@ -79,6 +79,9 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { | |||
79 | #[salsa::invoke(TraitImpls::trait_impls_in_crate_query)] | 79 | #[salsa::invoke(TraitImpls::trait_impls_in_crate_query)] |
80 | fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>; | 80 | fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>; |
81 | 81 | ||
82 | #[salsa::invoke(TraitImpls::trait_impls_in_block_query)] | ||
83 | fn trait_impls_in_block(&self, krate: BlockId) -> Option<Arc<TraitImpls>>; | ||
84 | |||
82 | #[salsa::invoke(TraitImpls::trait_impls_in_deps_query)] | 85 | #[salsa::invoke(TraitImpls::trait_impls_in_deps_query)] |
83 | fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<TraitImpls>; | 86 | fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<TraitImpls>; |
84 | 87 | ||
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs index 283894704..6339c9687 100644 --- a/crates/hir_ty/src/diagnostics.rs +++ b/crates/hir_ty/src/diagnostics.rs | |||
@@ -4,325 +4,31 @@ mod match_check; | |||
4 | mod unsafe_check; | 4 | mod unsafe_check; |
5 | mod decl_check; | 5 | mod decl_check; |
6 | 6 | ||
7 | use std::{any::Any, fmt}; | 7 | use std::fmt; |
8 | 8 | ||
9 | use base_db::CrateId; | 9 | use base_db::CrateId; |
10 | use hir_def::{DefWithBodyId, ModuleDefId}; | 10 | use hir_def::ModuleDefId; |
11 | use hir_expand::{name::Name, HirFileId, InFile}; | 11 | use hir_expand::HirFileId; |
12 | use stdx::format_to; | 12 | use syntax::{ast, AstPtr}; |
13 | use syntax::{ast, AstPtr, SyntaxNodePtr}; | ||
14 | 13 | ||
15 | use crate::{ | 14 | use crate::db::HirDatabase; |
16 | db::HirDatabase, | ||
17 | diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink}, | ||
18 | }; | ||
19 | 15 | ||
20 | pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields}; | 16 | pub use crate::diagnostics::{ |
17 | expr::{ | ||
18 | record_literal_missing_fields, record_pattern_missing_fields, BodyValidationDiagnostic, | ||
19 | }, | ||
20 | unsafe_check::missing_unsafe, | ||
21 | }; | ||
21 | 22 | ||
22 | pub fn validate_module_item( | 23 | pub fn validate_module_item( |
23 | db: &dyn HirDatabase, | 24 | db: &dyn HirDatabase, |
24 | krate: CrateId, | 25 | krate: CrateId, |
25 | owner: ModuleDefId, | 26 | owner: ModuleDefId, |
26 | sink: &mut DiagnosticSink<'_>, | 27 | ) -> Vec<IncorrectCase> { |
27 | ) { | ||
28 | let _p = profile::span("validate_module_item"); | 28 | let _p = profile::span("validate_module_item"); |
29 | let mut validator = decl_check::DeclValidator::new(db, krate, sink); | 29 | let mut validator = decl_check::DeclValidator::new(db, krate); |
30 | validator.validate_item(owner); | 30 | validator.validate_item(owner); |
31 | } | 31 | validator.sink |
32 | |||
33 | pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) { | ||
34 | let _p = profile::span("validate_body"); | ||
35 | let infer = db.infer(owner); | ||
36 | infer.add_diagnostics(db, owner, sink); | ||
37 | let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink); | ||
38 | validator.validate_body(db); | ||
39 | let mut validator = unsafe_check::UnsafeValidator::new(owner, infer, sink); | ||
40 | validator.validate_body(db); | ||
41 | } | ||
42 | |||
43 | // Diagnostic: no-such-field | ||
44 | // | ||
45 | // This diagnostic is triggered if created structure does not have field provided in record. | ||
46 | #[derive(Debug)] | ||
47 | pub struct NoSuchField { | ||
48 | pub file: HirFileId, | ||
49 | pub field: AstPtr<ast::RecordExprField>, | ||
50 | } | ||
51 | |||
52 | impl Diagnostic for NoSuchField { | ||
53 | fn code(&self) -> DiagnosticCode { | ||
54 | DiagnosticCode("no-such-field") | ||
55 | } | ||
56 | |||
57 | fn message(&self) -> String { | ||
58 | "no such field".to_string() | ||
59 | } | ||
60 | |||
61 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
62 | InFile::new(self.file, self.field.clone().into()) | ||
63 | } | ||
64 | |||
65 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
66 | self | ||
67 | } | ||
68 | } | ||
69 | |||
70 | // Diagnostic: missing-structure-fields | ||
71 | // | ||
72 | // This diagnostic is triggered if record lacks some fields that exist in the corresponding structure. | ||
73 | // | ||
74 | // Example: | ||
75 | // | ||
76 | // ```rust | ||
77 | // struct A { a: u8, b: u8 } | ||
78 | // | ||
79 | // let a = A { a: 10 }; | ||
80 | // ``` | ||
81 | #[derive(Debug)] | ||
82 | pub struct MissingFields { | ||
83 | pub file: HirFileId, | ||
84 | pub field_list_parent: AstPtr<ast::RecordExpr>, | ||
85 | pub field_list_parent_path: Option<AstPtr<ast::Path>>, | ||
86 | pub missed_fields: Vec<Name>, | ||
87 | } | ||
88 | |||
89 | impl Diagnostic for MissingFields { | ||
90 | fn code(&self) -> DiagnosticCode { | ||
91 | DiagnosticCode("missing-structure-fields") | ||
92 | } | ||
93 | fn message(&self) -> String { | ||
94 | let mut buf = String::from("Missing structure fields:\n"); | ||
95 | for field in &self.missed_fields { | ||
96 | format_to!(buf, "- {}\n", field); | ||
97 | } | ||
98 | buf | ||
99 | } | ||
100 | |||
101 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
102 | InFile { | ||
103 | file_id: self.file, | ||
104 | value: self | ||
105 | .field_list_parent_path | ||
106 | .clone() | ||
107 | .map(SyntaxNodePtr::from) | ||
108 | .unwrap_or_else(|| self.field_list_parent.clone().into()), | ||
109 | } | ||
110 | } | ||
111 | |||
112 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
113 | self | ||
114 | } | ||
115 | } | ||
116 | |||
117 | // Diagnostic: missing-pat-fields | ||
118 | // | ||
119 | // This diagnostic is triggered if pattern lacks some fields that exist in the corresponding structure. | ||
120 | // | ||
121 | // Example: | ||
122 | // | ||
123 | // ```rust | ||
124 | // struct A { a: u8, b: u8 } | ||
125 | // | ||
126 | // let a = A { a: 10, b: 20 }; | ||
127 | // | ||
128 | // if let A { a } = a { | ||
129 | // // ... | ||
130 | // } | ||
131 | // ``` | ||
132 | #[derive(Debug)] | ||
133 | pub struct MissingPatFields { | ||
134 | pub file: HirFileId, | ||
135 | pub field_list_parent: AstPtr<ast::RecordPat>, | ||
136 | pub field_list_parent_path: Option<AstPtr<ast::Path>>, | ||
137 | pub missed_fields: Vec<Name>, | ||
138 | } | ||
139 | |||
140 | impl Diagnostic for MissingPatFields { | ||
141 | fn code(&self) -> DiagnosticCode { | ||
142 | DiagnosticCode("missing-pat-fields") | ||
143 | } | ||
144 | fn message(&self) -> String { | ||
145 | let mut buf = String::from("Missing structure fields:\n"); | ||
146 | for field in &self.missed_fields { | ||
147 | format_to!(buf, "- {}\n", field); | ||
148 | } | ||
149 | buf | ||
150 | } | ||
151 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
152 | InFile { | ||
153 | file_id: self.file, | ||
154 | value: self | ||
155 | .field_list_parent_path | ||
156 | .clone() | ||
157 | .map(SyntaxNodePtr::from) | ||
158 | .unwrap_or_else(|| self.field_list_parent.clone().into()), | ||
159 | } | ||
160 | } | ||
161 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
162 | self | ||
163 | } | ||
164 | } | ||
165 | |||
166 | // Diagnostic: missing-match-arm | ||
167 | // | ||
168 | // This diagnostic is triggered if `match` block is missing one or more match arms. | ||
169 | #[derive(Debug)] | ||
170 | pub struct MissingMatchArms { | ||
171 | pub file: HirFileId, | ||
172 | pub match_expr: AstPtr<ast::Expr>, | ||
173 | pub arms: AstPtr<ast::MatchArmList>, | ||
174 | } | ||
175 | |||
176 | impl Diagnostic for MissingMatchArms { | ||
177 | fn code(&self) -> DiagnosticCode { | ||
178 | DiagnosticCode("missing-match-arm") | ||
179 | } | ||
180 | fn message(&self) -> String { | ||
181 | String::from("Missing match arm") | ||
182 | } | ||
183 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
184 | InFile { file_id: self.file, value: self.match_expr.clone().into() } | ||
185 | } | ||
186 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
187 | self | ||
188 | } | ||
189 | } | ||
190 | |||
191 | // Diagnostic: missing-ok-or-some-in-tail-expr | ||
192 | // | ||
193 | // This diagnostic is triggered if a block that should return `Result` returns a value not wrapped in `Ok`, | ||
194 | // or if a block that should return `Option` returns a value not wrapped in `Some`. | ||
195 | // | ||
196 | // Example: | ||
197 | // | ||
198 | // ```rust | ||
199 | // fn foo() -> Result<u8, ()> { | ||
200 | // 10 | ||
201 | // } | ||
202 | // ``` | ||
203 | #[derive(Debug)] | ||
204 | pub struct MissingOkOrSomeInTailExpr { | ||
205 | pub file: HirFileId, | ||
206 | pub expr: AstPtr<ast::Expr>, | ||
207 | // `Some` or `Ok` depending on whether the return type is Result or Option | ||
208 | pub required: String, | ||
209 | } | ||
210 | |||
211 | impl Diagnostic for MissingOkOrSomeInTailExpr { | ||
212 | fn code(&self) -> DiagnosticCode { | ||
213 | DiagnosticCode("missing-ok-or-some-in-tail-expr") | ||
214 | } | ||
215 | fn message(&self) -> String { | ||
216 | format!("wrap return expression in {}", self.required) | ||
217 | } | ||
218 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
219 | InFile { file_id: self.file, value: self.expr.clone().into() } | ||
220 | } | ||
221 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
222 | self | ||
223 | } | ||
224 | } | ||
225 | |||
226 | #[derive(Debug)] | ||
227 | pub struct RemoveThisSemicolon { | ||
228 | pub file: HirFileId, | ||
229 | pub expr: AstPtr<ast::Expr>, | ||
230 | } | ||
231 | |||
232 | impl Diagnostic for RemoveThisSemicolon { | ||
233 | fn code(&self) -> DiagnosticCode { | ||
234 | DiagnosticCode("remove-this-semicolon") | ||
235 | } | ||
236 | |||
237 | fn message(&self) -> String { | ||
238 | "Remove this semicolon".to_string() | ||
239 | } | ||
240 | |||
241 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
242 | InFile { file_id: self.file, value: self.expr.clone().into() } | ||
243 | } | ||
244 | |||
245 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
246 | self | ||
247 | } | ||
248 | } | ||
249 | |||
250 | // Diagnostic: break-outside-of-loop | ||
251 | // | ||
252 | // This diagnostic is triggered if the `break` keyword is used outside of a loop. | ||
253 | #[derive(Debug)] | ||
254 | pub struct BreakOutsideOfLoop { | ||
255 | pub file: HirFileId, | ||
256 | pub expr: AstPtr<ast::Expr>, | ||
257 | } | ||
258 | |||
259 | impl Diagnostic for BreakOutsideOfLoop { | ||
260 | fn code(&self) -> DiagnosticCode { | ||
261 | DiagnosticCode("break-outside-of-loop") | ||
262 | } | ||
263 | fn message(&self) -> String { | ||
264 | "break outside of loop".to_string() | ||
265 | } | ||
266 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
267 | InFile { file_id: self.file, value: self.expr.clone().into() } | ||
268 | } | ||
269 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
270 | self | ||
271 | } | ||
272 | } | ||
273 | |||
274 | // Diagnostic: missing-unsafe | ||
275 | // | ||
276 | // This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block. | ||
277 | #[derive(Debug)] | ||
278 | pub struct MissingUnsafe { | ||
279 | pub file: HirFileId, | ||
280 | pub expr: AstPtr<ast::Expr>, | ||
281 | } | ||
282 | |||
283 | impl Diagnostic for MissingUnsafe { | ||
284 | fn code(&self) -> DiagnosticCode { | ||
285 | DiagnosticCode("missing-unsafe") | ||
286 | } | ||
287 | fn message(&self) -> String { | ||
288 | format!("This operation is unsafe and requires an unsafe function or block") | ||
289 | } | ||
290 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
291 | InFile { file_id: self.file, value: self.expr.clone().into() } | ||
292 | } | ||
293 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
294 | self | ||
295 | } | ||
296 | } | ||
297 | |||
298 | // Diagnostic: mismatched-arg-count | ||
299 | // | ||
300 | // This diagnostic is triggered if a function is invoked with an incorrect amount of arguments. | ||
301 | #[derive(Debug)] | ||
302 | pub struct MismatchedArgCount { | ||
303 | pub file: HirFileId, | ||
304 | pub call_expr: AstPtr<ast::Expr>, | ||
305 | pub expected: usize, | ||
306 | pub found: usize, | ||
307 | } | ||
308 | |||
309 | impl Diagnostic for MismatchedArgCount { | ||
310 | fn code(&self) -> DiagnosticCode { | ||
311 | DiagnosticCode("mismatched-arg-count") | ||
312 | } | ||
313 | fn message(&self) -> String { | ||
314 | let s = if self.expected == 1 { "" } else { "s" }; | ||
315 | format!("Expected {} argument{}, found {}", self.expected, s, self.found) | ||
316 | } | ||
317 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
318 | InFile { file_id: self.file, value: self.call_expr.clone().into() } | ||
319 | } | ||
320 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
321 | self | ||
322 | } | ||
323 | fn is_experimental(&self) -> bool { | ||
324 | true | ||
325 | } | ||
326 | } | 32 | } |
327 | 33 | ||
328 | #[derive(Debug)] | 34 | #[derive(Debug)] |
@@ -378,9 +84,6 @@ impl fmt::Display for IdentType { | |||
378 | } | 84 | } |
379 | } | 85 | } |
380 | 86 | ||
381 | // Diagnostic: incorrect-ident-case | ||
382 | // | ||
383 | // This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. | ||
384 | #[derive(Debug)] | 87 | #[derive(Debug)] |
385 | pub struct IncorrectCase { | 88 | pub struct IncorrectCase { |
386 | pub file: HirFileId, | 89 | pub file: HirFileId, |
@@ -390,450 +93,3 @@ pub struct IncorrectCase { | |||
390 | pub ident_text: String, | 93 | pub ident_text: String, |
391 | pub suggested_text: String, | 94 | pub suggested_text: String, |
392 | } | 95 | } |
393 | |||
394 | impl Diagnostic for IncorrectCase { | ||
395 | fn code(&self) -> DiagnosticCode { | ||
396 | DiagnosticCode("incorrect-ident-case") | ||
397 | } | ||
398 | |||
399 | fn message(&self) -> String { | ||
400 | format!( | ||
401 | "{} `{}` should have {} name, e.g. `{}`", | ||
402 | self.ident_type, | ||
403 | self.ident_text, | ||
404 | self.expected_case.to_string(), | ||
405 | self.suggested_text | ||
406 | ) | ||
407 | } | ||
408 | |||
409 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
410 | InFile::new(self.file, self.ident.clone().into()) | ||
411 | } | ||
412 | |||
413 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
414 | self | ||
415 | } | ||
416 | |||
417 | fn is_experimental(&self) -> bool { | ||
418 | true | ||
419 | } | ||
420 | } | ||
421 | |||
422 | // Diagnostic: replace-filter-map-next-with-find-map | ||
423 | // | ||
424 | // This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`. | ||
425 | #[derive(Debug)] | ||
426 | pub struct ReplaceFilterMapNextWithFindMap { | ||
427 | pub file: HirFileId, | ||
428 | /// This expression is the whole method chain up to and including `.filter_map(..).next()`. | ||
429 | pub next_expr: AstPtr<ast::Expr>, | ||
430 | } | ||
431 | |||
432 | impl Diagnostic for ReplaceFilterMapNextWithFindMap { | ||
433 | fn code(&self) -> DiagnosticCode { | ||
434 | DiagnosticCode("replace-filter-map-next-with-find-map") | ||
435 | } | ||
436 | fn message(&self) -> String { | ||
437 | "replace filter_map(..).next() with find_map(..)".to_string() | ||
438 | } | ||
439 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
440 | InFile { file_id: self.file, value: self.next_expr.clone().into() } | ||
441 | } | ||
442 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
443 | self | ||
444 | } | ||
445 | } | ||
446 | |||
447 | #[cfg(test)] | ||
448 | mod tests { | ||
449 | use base_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; | ||
450 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId}; | ||
451 | use hir_expand::db::AstDatabase; | ||
452 | use rustc_hash::FxHashMap; | ||
453 | use syntax::{TextRange, TextSize}; | ||
454 | |||
455 | use crate::{ | ||
456 | diagnostics::{validate_body, validate_module_item}, | ||
457 | diagnostics_sink::{Diagnostic, DiagnosticSinkBuilder}, | ||
458 | test_db::TestDB, | ||
459 | }; | ||
460 | |||
461 | impl TestDB { | ||
462 | fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) { | ||
463 | let crate_graph = self.crate_graph(); | ||
464 | for krate in crate_graph.iter() { | ||
465 | let crate_def_map = self.crate_def_map(krate); | ||
466 | |||
467 | let mut fns = Vec::new(); | ||
468 | for (module_id, _) in crate_def_map.modules() { | ||
469 | for decl in crate_def_map[module_id].scope.declarations() { | ||
470 | let mut sink = DiagnosticSinkBuilder::new().build(&mut cb); | ||
471 | validate_module_item(self, krate, decl, &mut sink); | ||
472 | |||
473 | if let ModuleDefId::FunctionId(f) = decl { | ||
474 | fns.push(f) | ||
475 | } | ||
476 | } | ||
477 | |||
478 | for impl_id in crate_def_map[module_id].scope.impls() { | ||
479 | let impl_data = self.impl_data(impl_id); | ||
480 | for item in impl_data.items.iter() { | ||
481 | if let AssocItemId::FunctionId(f) = item { | ||
482 | let mut sink = DiagnosticSinkBuilder::new().build(&mut cb); | ||
483 | validate_module_item( | ||
484 | self, | ||
485 | krate, | ||
486 | ModuleDefId::FunctionId(*f), | ||
487 | &mut sink, | ||
488 | ); | ||
489 | fns.push(*f) | ||
490 | } | ||
491 | } | ||
492 | } | ||
493 | } | ||
494 | |||
495 | for f in fns { | ||
496 | let mut sink = DiagnosticSinkBuilder::new().build(&mut cb); | ||
497 | validate_body(self, f.into(), &mut sink); | ||
498 | } | ||
499 | } | ||
500 | } | ||
501 | } | ||
502 | |||
503 | pub(crate) fn check_diagnostics(ra_fixture: &str) { | ||
504 | let db = TestDB::with_files(ra_fixture); | ||
505 | let annotations = db.extract_annotations(); | ||
506 | |||
507 | let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); | ||
508 | db.diagnostics(|d| { | ||
509 | let src = d.display_source(); | ||
510 | let root = db.parse_or_expand(src.file_id).unwrap(); | ||
511 | // FIXME: macros... | ||
512 | let file_id = src.file_id.original_file(&db); | ||
513 | let range = src.value.to_node(&root).text_range(); | ||
514 | let message = d.message(); | ||
515 | actual.entry(file_id).or_default().push((range, message)); | ||
516 | }); | ||
517 | |||
518 | for (file_id, diags) in actual.iter_mut() { | ||
519 | diags.sort_by_key(|it| it.0.start()); | ||
520 | let text = db.file_text(*file_id); | ||
521 | // For multiline spans, place them on line start | ||
522 | for (range, content) in diags { | ||
523 | if text[*range].contains('\n') { | ||
524 | *range = TextRange::new(range.start(), range.start() + TextSize::from(1)); | ||
525 | *content = format!("... {}", content); | ||
526 | } | ||
527 | } | ||
528 | } | ||
529 | |||
530 | assert_eq!(annotations, actual); | ||
531 | } | ||
532 | |||
533 | #[test] | ||
534 | fn no_such_field_diagnostics() { | ||
535 | check_diagnostics( | ||
536 | r#" | ||
537 | struct S { foo: i32, bar: () } | ||
538 | impl S { | ||
539 | fn new() -> S { | ||
540 | S { | ||
541 | //^ Missing structure fields: | ||
542 | //| - bar | ||
543 | foo: 92, | ||
544 | baz: 62, | ||
545 | //^^^^^^^ no such field | ||
546 | } | ||
547 | } | ||
548 | } | ||
549 | "#, | ||
550 | ); | ||
551 | } | ||
552 | #[test] | ||
553 | fn no_such_field_with_feature_flag_diagnostics() { | ||
554 | check_diagnostics( | ||
555 | r#" | ||
556 | //- /lib.rs crate:foo cfg:feature=foo | ||
557 | struct MyStruct { | ||
558 | my_val: usize, | ||
559 | #[cfg(feature = "foo")] | ||
560 | bar: bool, | ||
561 | } | ||
562 | |||
563 | impl MyStruct { | ||
564 | #[cfg(feature = "foo")] | ||
565 | pub(crate) fn new(my_val: usize, bar: bool) -> Self { | ||
566 | Self { my_val, bar } | ||
567 | } | ||
568 | #[cfg(not(feature = "foo"))] | ||
569 | pub(crate) fn new(my_val: usize, _bar: bool) -> Self { | ||
570 | Self { my_val } | ||
571 | } | ||
572 | } | ||
573 | "#, | ||
574 | ); | ||
575 | } | ||
576 | |||
577 | #[test] | ||
578 | fn no_such_field_enum_with_feature_flag_diagnostics() { | ||
579 | check_diagnostics( | ||
580 | r#" | ||
581 | //- /lib.rs crate:foo cfg:feature=foo | ||
582 | enum Foo { | ||
583 | #[cfg(not(feature = "foo"))] | ||
584 | Buz, | ||
585 | #[cfg(feature = "foo")] | ||
586 | Bar, | ||
587 | Baz | ||
588 | } | ||
589 | |||
590 | fn test_fn(f: Foo) { | ||
591 | match f { | ||
592 | Foo::Bar => {}, | ||
593 | Foo::Baz => {}, | ||
594 | } | ||
595 | } | ||
596 | "#, | ||
597 | ); | ||
598 | } | ||
599 | |||
600 | #[test] | ||
601 | fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() { | ||
602 | check_diagnostics( | ||
603 | r#" | ||
604 | //- /lib.rs crate:foo cfg:feature=foo | ||
605 | struct S { | ||
606 | #[cfg(feature = "foo")] | ||
607 | foo: u32, | ||
608 | #[cfg(not(feature = "foo"))] | ||
609 | bar: u32, | ||
610 | } | ||
611 | |||
612 | impl S { | ||
613 | #[cfg(feature = "foo")] | ||
614 | fn new(foo: u32) -> Self { | ||
615 | Self { foo } | ||
616 | } | ||
617 | #[cfg(not(feature = "foo"))] | ||
618 | fn new(bar: u32) -> Self { | ||
619 | Self { bar } | ||
620 | } | ||
621 | fn new2(bar: u32) -> Self { | ||
622 | #[cfg(feature = "foo")] | ||
623 | { Self { foo: bar } } | ||
624 | #[cfg(not(feature = "foo"))] | ||
625 | { Self { bar } } | ||
626 | } | ||
627 | fn new2(val: u32) -> Self { | ||
628 | Self { | ||
629 | #[cfg(feature = "foo")] | ||
630 | foo: val, | ||
631 | #[cfg(not(feature = "foo"))] | ||
632 | bar: val, | ||
633 | } | ||
634 | } | ||
635 | } | ||
636 | "#, | ||
637 | ); | ||
638 | } | ||
639 | |||
640 | #[test] | ||
641 | fn no_such_field_with_type_macro() { | ||
642 | check_diagnostics( | ||
643 | r#" | ||
644 | macro_rules! Type { () => { u32 }; } | ||
645 | struct Foo { bar: Type![] } | ||
646 | |||
647 | impl Foo { | ||
648 | fn new() -> Self { | ||
649 | Foo { bar: 0 } | ||
650 | } | ||
651 | } | ||
652 | "#, | ||
653 | ); | ||
654 | } | ||
655 | |||
656 | #[test] | ||
657 | fn missing_record_pat_field_diagnostic() { | ||
658 | check_diagnostics( | ||
659 | r#" | ||
660 | struct S { foo: i32, bar: () } | ||
661 | fn baz(s: S) { | ||
662 | let S { foo: _ } = s; | ||
663 | //^ Missing structure fields: | ||
664 | //| - bar | ||
665 | } | ||
666 | "#, | ||
667 | ); | ||
668 | } | ||
669 | |||
670 | #[test] | ||
671 | fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() { | ||
672 | check_diagnostics( | ||
673 | r" | ||
674 | struct S { foo: i32, bar: () } | ||
675 | fn baz(s: S) -> i32 { | ||
676 | match s { | ||
677 | S { foo, .. } => foo, | ||
678 | } | ||
679 | } | ||
680 | ", | ||
681 | ) | ||
682 | } | ||
683 | |||
684 | #[test] | ||
685 | fn missing_record_pat_field_box() { | ||
686 | check_diagnostics( | ||
687 | r" | ||
688 | struct S { s: Box<u32> } | ||
689 | fn x(a: S) { | ||
690 | let S { box s } = a; | ||
691 | } | ||
692 | ", | ||
693 | ) | ||
694 | } | ||
695 | |||
696 | #[test] | ||
697 | fn missing_record_pat_field_ref() { | ||
698 | check_diagnostics( | ||
699 | r" | ||
700 | struct S { s: u32 } | ||
701 | fn x(a: S) { | ||
702 | let S { ref s } = a; | ||
703 | } | ||
704 | ", | ||
705 | ) | ||
706 | } | ||
707 | |||
708 | #[test] | ||
709 | fn import_extern_crate_clash_with_inner_item() { | ||
710 | // This is more of a resolver test, but doesn't really work with the hir_def testsuite. | ||
711 | |||
712 | check_diagnostics( | ||
713 | r#" | ||
714 | //- /lib.rs crate:lib deps:jwt | ||
715 | mod permissions; | ||
716 | |||
717 | use permissions::jwt; | ||
718 | |||
719 | fn f() { | ||
720 | fn inner() {} | ||
721 | jwt::Claims {}; // should resolve to the local one with 0 fields, and not get a diagnostic | ||
722 | } | ||
723 | |||
724 | //- /permissions.rs | ||
725 | pub mod jwt { | ||
726 | pub struct Claims {} | ||
727 | } | ||
728 | |||
729 | //- /jwt/lib.rs crate:jwt | ||
730 | pub struct Claims { | ||
731 | field: u8, | ||
732 | } | ||
733 | "#, | ||
734 | ); | ||
735 | } | ||
736 | |||
737 | #[test] | ||
738 | fn break_outside_of_loop() { | ||
739 | check_diagnostics( | ||
740 | r#" | ||
741 | fn foo() { break; } | ||
742 | //^^^^^ break outside of loop | ||
743 | "#, | ||
744 | ); | ||
745 | } | ||
746 | |||
747 | #[test] | ||
748 | fn missing_semicolon() { | ||
749 | check_diagnostics( | ||
750 | r#" | ||
751 | fn test() -> i32 { 123; } | ||
752 | //^^^ Remove this semicolon | ||
753 | "#, | ||
754 | ); | ||
755 | } | ||
756 | |||
757 | // Register the required standard library types to make the tests work | ||
758 | fn add_filter_map_with_find_next_boilerplate(body: &str) -> String { | ||
759 | let prefix = r#" | ||
760 | //- /main.rs crate:main deps:core | ||
761 | use core::iter::Iterator; | ||
762 | use core::option::Option::{self, Some, None}; | ||
763 | "#; | ||
764 | let suffix = r#" | ||
765 | //- /core/lib.rs crate:core | ||
766 | pub mod option { | ||
767 | pub enum Option<T> { Some(T), None } | ||
768 | } | ||
769 | pub mod iter { | ||
770 | pub trait Iterator { | ||
771 | type Item; | ||
772 | fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap } | ||
773 | fn next(&mut self) -> Option<Self::Item>; | ||
774 | } | ||
775 | pub struct FilterMap {} | ||
776 | impl Iterator for FilterMap { | ||
777 | type Item = i32; | ||
778 | fn next(&mut self) -> i32 { 7 } | ||
779 | } | ||
780 | } | ||
781 | "#; | ||
782 | format!("{}{}{}", prefix, body, suffix) | ||
783 | } | ||
784 | |||
785 | #[test] | ||
786 | fn replace_filter_map_next_with_find_map2() { | ||
787 | check_diagnostics(&add_filter_map_with_find_next_boilerplate( | ||
788 | r#" | ||
789 | fn foo() { | ||
790 | let m = [1, 2, 3].iter().filter_map(|x| if *x == 2 { Some (4) } else { None }).next(); | ||
791 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ replace filter_map(..).next() with find_map(..) | ||
792 | } | ||
793 | "#, | ||
794 | )); | ||
795 | } | ||
796 | |||
797 | #[test] | ||
798 | fn replace_filter_map_next_with_find_map_no_diagnostic_without_next() { | ||
799 | check_diagnostics(&add_filter_map_with_find_next_boilerplate( | ||
800 | r#" | ||
801 | fn foo() { | ||
802 | let m = [1, 2, 3] | ||
803 | .iter() | ||
804 | .filter_map(|x| if *x == 2 { Some (4) } else { None }) | ||
805 | .len(); | ||
806 | } | ||
807 | "#, | ||
808 | )); | ||
809 | } | ||
810 | |||
811 | #[test] | ||
812 | fn replace_filter_map_next_with_find_map_no_diagnostic_with_intervening_methods() { | ||
813 | check_diagnostics(&add_filter_map_with_find_next_boilerplate( | ||
814 | r#" | ||
815 | fn foo() { | ||
816 | let m = [1, 2, 3] | ||
817 | .iter() | ||
818 | .filter_map(|x| if *x == 2 { Some (4) } else { None }) | ||
819 | .map(|x| x + 2) | ||
820 | .len(); | ||
821 | } | ||
822 | "#, | ||
823 | )); | ||
824 | } | ||
825 | |||
826 | #[test] | ||
827 | fn replace_filter_map_next_with_find_map_no_diagnostic_if_not_in_chain() { | ||
828 | check_diagnostics(&add_filter_map_with_find_next_boilerplate( | ||
829 | r#" | ||
830 | fn foo() { | ||
831 | let m = [1, 2, 3] | ||
832 | .iter() | ||
833 | .filter_map(|x| if *x == 2 { Some (4) } else { None }); | ||
834 | let n = m.next(); | ||
835 | } | ||
836 | "#, | ||
837 | )); | ||
838 | } | ||
839 | } | ||
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs index cfb5d7320..f26150b77 100644 --- a/crates/hir_ty/src/diagnostics/decl_check.rs +++ b/crates/hir_ty/src/diagnostics/decl_check.rs | |||
@@ -29,7 +29,6 @@ use syntax::{ | |||
29 | use crate::{ | 29 | use crate::{ |
30 | db::HirDatabase, | 30 | db::HirDatabase, |
31 | diagnostics::{decl_check::case_conv::*, CaseType, IdentType, IncorrectCase}, | 31 | diagnostics::{decl_check::case_conv::*, CaseType, IdentType, IncorrectCase}, |
32 | diagnostics_sink::DiagnosticSink, | ||
33 | }; | 32 | }; |
34 | 33 | ||
35 | mod allow { | 34 | mod allow { |
@@ -40,10 +39,10 @@ mod allow { | |||
40 | pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types"; | 39 | pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types"; |
41 | } | 40 | } |
42 | 41 | ||
43 | pub(super) struct DeclValidator<'a, 'b> { | 42 | pub(super) struct DeclValidator<'a> { |
44 | db: &'a dyn HirDatabase, | 43 | db: &'a dyn HirDatabase, |
45 | krate: CrateId, | 44 | krate: CrateId, |
46 | sink: &'a mut DiagnosticSink<'b>, | 45 | pub(super) sink: Vec<IncorrectCase>, |
47 | } | 46 | } |
48 | 47 | ||
49 | #[derive(Debug)] | 48 | #[derive(Debug)] |
@@ -53,13 +52,9 @@ struct Replacement { | |||
53 | expected_case: CaseType, | 52 | expected_case: CaseType, |
54 | } | 53 | } |
55 | 54 | ||
56 | impl<'a, 'b> DeclValidator<'a, 'b> { | 55 | impl<'a> DeclValidator<'a> { |
57 | pub(super) fn new( | 56 | pub(super) fn new(db: &'a dyn HirDatabase, krate: CrateId) -> DeclValidator<'a> { |
58 | db: &'a dyn HirDatabase, | 57 | DeclValidator { db, krate, sink: Vec::new() } |
59 | krate: CrateId, | ||
60 | sink: &'a mut DiagnosticSink<'b>, | ||
61 | ) -> DeclValidator<'a, 'b> { | ||
62 | DeclValidator { db, krate, sink } | ||
63 | } | 58 | } |
64 | 59 | ||
65 | pub(super) fn validate_item(&mut self, item: ModuleDefId) { | 60 | pub(super) fn validate_item(&mut self, item: ModuleDefId) { |
@@ -131,7 +126,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
131 | for (_, block_def_map) in body.blocks(self.db.upcast()) { | 126 | for (_, block_def_map) in body.blocks(self.db.upcast()) { |
132 | for (_, module) in block_def_map.modules() { | 127 | for (_, module) in block_def_map.modules() { |
133 | for def_id in module.scope.declarations() { | 128 | for def_id in module.scope.declarations() { |
134 | let mut validator = DeclValidator::new(self.db, self.krate, self.sink); | 129 | let mut validator = DeclValidator::new(self.db, self.krate); |
135 | validator.validate_item(def_id); | 130 | validator.validate_item(def_id); |
136 | } | 131 | } |
137 | } | 132 | } |
@@ -623,343 +618,3 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
623 | self.sink.push(diagnostic); | 618 | self.sink.push(diagnostic); |
624 | } | 619 | } |
625 | } | 620 | } |
626 | |||
627 | #[cfg(test)] | ||
628 | mod tests { | ||
629 | use crate::diagnostics::tests::check_diagnostics; | ||
630 | |||
631 | #[test] | ||
632 | fn incorrect_function_name() { | ||
633 | check_diagnostics( | ||
634 | r#" | ||
635 | fn NonSnakeCaseName() {} | ||
636 | // ^^^^^^^^^^^^^^^^ Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` | ||
637 | "#, | ||
638 | ); | ||
639 | } | ||
640 | |||
641 | #[test] | ||
642 | fn incorrect_function_params() { | ||
643 | check_diagnostics( | ||
644 | r#" | ||
645 | fn foo(SomeParam: u8) {} | ||
646 | // ^^^^^^^^^ Parameter `SomeParam` should have snake_case name, e.g. `some_param` | ||
647 | |||
648 | fn foo2(ok_param: &str, CAPS_PARAM: u8) {} | ||
649 | // ^^^^^^^^^^ Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param` | ||
650 | "#, | ||
651 | ); | ||
652 | } | ||
653 | |||
654 | #[test] | ||
655 | fn incorrect_variable_names() { | ||
656 | check_diagnostics( | ||
657 | r#" | ||
658 | fn foo() { | ||
659 | let SOME_VALUE = 10; | ||
660 | // ^^^^^^^^^^ Variable `SOME_VALUE` should have snake_case name, e.g. `some_value` | ||
661 | let AnotherValue = 20; | ||
662 | // ^^^^^^^^^^^^ Variable `AnotherValue` should have snake_case name, e.g. `another_value` | ||
663 | } | ||
664 | "#, | ||
665 | ); | ||
666 | } | ||
667 | |||
668 | #[test] | ||
669 | fn incorrect_struct_names() { | ||
670 | check_diagnostics( | ||
671 | r#" | ||
672 | struct non_camel_case_name {} | ||
673 | // ^^^^^^^^^^^^^^^^^^^ Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName` | ||
674 | |||
675 | struct SCREAMING_CASE {} | ||
676 | // ^^^^^^^^^^^^^^ Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase` | ||
677 | "#, | ||
678 | ); | ||
679 | } | ||
680 | |||
681 | #[test] | ||
682 | fn no_diagnostic_for_camel_cased_acronyms_in_struct_name() { | ||
683 | check_diagnostics( | ||
684 | r#" | ||
685 | struct AABB {} | ||
686 | "#, | ||
687 | ); | ||
688 | } | ||
689 | |||
690 | #[test] | ||
691 | fn incorrect_struct_field() { | ||
692 | check_diagnostics( | ||
693 | r#" | ||
694 | struct SomeStruct { SomeField: u8 } | ||
695 | // ^^^^^^^^^ Field `SomeField` should have snake_case name, e.g. `some_field` | ||
696 | "#, | ||
697 | ); | ||
698 | } | ||
699 | |||
700 | #[test] | ||
701 | fn incorrect_enum_names() { | ||
702 | check_diagnostics( | ||
703 | r#" | ||
704 | enum some_enum { Val(u8) } | ||
705 | // ^^^^^^^^^ Enum `some_enum` should have CamelCase name, e.g. `SomeEnum` | ||
706 | |||
707 | enum SOME_ENUM | ||
708 | // ^^^^^^^^^ Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum` | ||
709 | "#, | ||
710 | ); | ||
711 | } | ||
712 | |||
713 | #[test] | ||
714 | fn no_diagnostic_for_camel_cased_acronyms_in_enum_name() { | ||
715 | check_diagnostics( | ||
716 | r#" | ||
717 | enum AABB {} | ||
718 | "#, | ||
719 | ); | ||
720 | } | ||
721 | |||
722 | #[test] | ||
723 | fn incorrect_enum_variant_name() { | ||
724 | check_diagnostics( | ||
725 | r#" | ||
726 | enum SomeEnum { SOME_VARIANT(u8) } | ||
727 | // ^^^^^^^^^^^^ Variant `SOME_VARIANT` should have CamelCase name, e.g. `SomeVariant` | ||
728 | "#, | ||
729 | ); | ||
730 | } | ||
731 | |||
732 | #[test] | ||
733 | fn incorrect_const_name() { | ||
734 | check_diagnostics( | ||
735 | r#" | ||
736 | const some_weird_const: u8 = 10; | ||
737 | // ^^^^^^^^^^^^^^^^ Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` | ||
738 | |||
739 | fn func() { | ||
740 | const someConstInFunc: &str = "hi there"; | ||
741 | // ^^^^^^^^^^^^^^^ Constant `someConstInFunc` should have UPPER_SNAKE_CASE name, e.g. `SOME_CONST_IN_FUNC` | ||
742 | |||
743 | } | ||
744 | "#, | ||
745 | ); | ||
746 | } | ||
747 | |||
748 | #[test] | ||
749 | fn incorrect_static_name() { | ||
750 | check_diagnostics( | ||
751 | r#" | ||
752 | static some_weird_const: u8 = 10; | ||
753 | // ^^^^^^^^^^^^^^^^ Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` | ||
754 | |||
755 | fn func() { | ||
756 | static someConstInFunc: &str = "hi there"; | ||
757 | // ^^^^^^^^^^^^^^^ Static variable `someConstInFunc` should have UPPER_SNAKE_CASE name, e.g. `SOME_CONST_IN_FUNC` | ||
758 | } | ||
759 | "#, | ||
760 | ); | ||
761 | } | ||
762 | |||
763 | #[test] | ||
764 | fn fn_inside_impl_struct() { | ||
765 | check_diagnostics( | ||
766 | r#" | ||
767 | struct someStruct; | ||
768 | // ^^^^^^^^^^ Structure `someStruct` should have CamelCase name, e.g. `SomeStruct` | ||
769 | |||
770 | impl someStruct { | ||
771 | fn SomeFunc(&self) { | ||
772 | // ^^^^^^^^ Function `SomeFunc` should have snake_case name, e.g. `some_func` | ||
773 | static someConstInFunc: &str = "hi there"; | ||
774 | // ^^^^^^^^^^^^^^^ Static variable `someConstInFunc` should have UPPER_SNAKE_CASE name, e.g. `SOME_CONST_IN_FUNC` | ||
775 | let WHY_VAR_IS_CAPS = 10; | ||
776 | // ^^^^^^^^^^^^^^^ Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps` | ||
777 | } | ||
778 | } | ||
779 | "#, | ||
780 | ); | ||
781 | } | ||
782 | |||
783 | #[test] | ||
784 | fn no_diagnostic_for_enum_varinats() { | ||
785 | check_diagnostics( | ||
786 | r#" | ||
787 | enum Option { Some, None } | ||
788 | |||
789 | fn main() { | ||
790 | match Option::None { | ||
791 | None => (), | ||
792 | Some => (), | ||
793 | } | ||
794 | } | ||
795 | "#, | ||
796 | ); | ||
797 | } | ||
798 | |||
799 | #[test] | ||
800 | fn non_let_bind() { | ||
801 | check_diagnostics( | ||
802 | r#" | ||
803 | enum Option { Some, None } | ||
804 | |||
805 | fn main() { | ||
806 | match Option::None { | ||
807 | SOME_VAR @ None => (), | ||
808 | // ^^^^^^^^ Variable `SOME_VAR` should have snake_case name, e.g. `some_var` | ||
809 | Some => (), | ||
810 | } | ||
811 | } | ||
812 | "#, | ||
813 | ); | ||
814 | } | ||
815 | |||
816 | #[test] | ||
817 | fn allow_attributes() { | ||
818 | check_diagnostics( | ||
819 | r#" | ||
820 | #[allow(non_snake_case)] | ||
821 | fn NonSnakeCaseName(SOME_VAR: u8) -> u8{ | ||
822 | // cov_flags generated output from elsewhere in this file | ||
823 | extern "C" { | ||
824 | #[no_mangle] | ||
825 | static lower_case: u8; | ||
826 | } | ||
827 | |||
828 | let OtherVar = SOME_VAR + 1; | ||
829 | OtherVar | ||
830 | } | ||
831 | |||
832 | #[allow(nonstandard_style)] | ||
833 | mod CheckNonstandardStyle { | ||
834 | fn HiImABadFnName() {} | ||
835 | } | ||
836 | |||
837 | #[allow(bad_style)] | ||
838 | mod CheckBadStyle { | ||
839 | fn HiImABadFnName() {} | ||
840 | } | ||
841 | |||
842 | mod F { | ||
843 | #![allow(non_snake_case)] | ||
844 | fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {} | ||
845 | } | ||
846 | |||
847 | #[allow(non_snake_case, non_camel_case_types)] | ||
848 | pub struct some_type { | ||
849 | SOME_FIELD: u8, | ||
850 | SomeField: u16, | ||
851 | } | ||
852 | |||
853 | #[allow(non_upper_case_globals)] | ||
854 | pub const some_const: u8 = 10; | ||
855 | |||
856 | #[allow(non_upper_case_globals)] | ||
857 | pub static SomeStatic: u8 = 10; | ||
858 | "#, | ||
859 | ); | ||
860 | } | ||
861 | |||
862 | #[test] | ||
863 | fn allow_attributes_crate_attr() { | ||
864 | check_diagnostics( | ||
865 | r#" | ||
866 | #![allow(non_snake_case)] | ||
867 | |||
868 | mod F { | ||
869 | fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {} | ||
870 | } | ||
871 | "#, | ||
872 | ); | ||
873 | } | ||
874 | |||
875 | #[test] | ||
876 | #[ignore] | ||
877 | fn bug_trait_inside_fn() { | ||
878 | // FIXME: | ||
879 | // This is broken, and in fact, should not even be looked at by this | ||
880 | // lint in the first place. There's weird stuff going on in the | ||
881 | // collection phase. | ||
882 | // It's currently being brought in by: | ||
883 | // * validate_func on `a` recursing into modules | ||
884 | // * then it finds the trait and then the function while iterating | ||
885 | // through modules | ||
886 | // * then validate_func is called on Dirty | ||
887 | // * ... which then proceeds to look at some unknown module taking no | ||
888 | // attrs from either the impl or the fn a, and then finally to the root | ||
889 | // module | ||
890 | // | ||
891 | // It should find the attribute on the trait, but it *doesn't even see | ||
892 | // the trait* as far as I can tell. | ||
893 | |||
894 | check_diagnostics( | ||
895 | r#" | ||
896 | trait T { fn a(); } | ||
897 | struct U {} | ||
898 | impl T for U { | ||
899 | fn a() { | ||
900 | // this comes out of bitflags, mostly | ||
901 | #[allow(non_snake_case)] | ||
902 | trait __BitFlags { | ||
903 | const HiImAlsoBad: u8 = 2; | ||
904 | #[inline] | ||
905 | fn Dirty(&self) -> bool { | ||
906 | false | ||
907 | } | ||
908 | } | ||
909 | |||
910 | } | ||
911 | } | ||
912 | "#, | ||
913 | ); | ||
914 | } | ||
915 | |||
916 | #[test] | ||
917 | #[ignore] | ||
918 | fn bug_traits_arent_checked() { | ||
919 | // FIXME: Traits and functions in traits aren't currently checked by | ||
920 | // r-a, even though rustc will complain about them. | ||
921 | check_diagnostics( | ||
922 | r#" | ||
923 | trait BAD_TRAIT { | ||
924 | // ^^^^^^^^^ Trait `BAD_TRAIT` should have CamelCase name, e.g. `BadTrait` | ||
925 | fn BAD_FUNCTION(); | ||
926 | // ^^^^^^^^^^^^ Function `BAD_FUNCTION` should have snake_case name, e.g. `bad_function` | ||
927 | fn BadFunction(); | ||
928 | // ^^^^^^^^^^^^ Function `BadFunction` should have snake_case name, e.g. `bad_function` | ||
929 | } | ||
930 | "#, | ||
931 | ); | ||
932 | } | ||
933 | |||
934 | #[test] | ||
935 | fn ignores_extern_items() { | ||
936 | cov_mark::check!(extern_func_incorrect_case_ignored); | ||
937 | cov_mark::check!(extern_static_incorrect_case_ignored); | ||
938 | check_diagnostics( | ||
939 | r#" | ||
940 | extern { | ||
941 | fn NonSnakeCaseName(SOME_VAR: u8) -> u8; | ||
942 | pub static SomeStatic: u8 = 10; | ||
943 | } | ||
944 | "#, | ||
945 | ); | ||
946 | } | ||
947 | |||
948 | #[test] | ||
949 | fn infinite_loop_inner_items() { | ||
950 | check_diagnostics( | ||
951 | r#" | ||
952 | fn qualify() { | ||
953 | mod foo { | ||
954 | use super::*; | ||
955 | } | ||
956 | } | ||
957 | "#, | ||
958 | ) | ||
959 | } | ||
960 | |||
961 | #[test] // Issue #8809. | ||
962 | fn parenthesized_parameter() { | ||
963 | check_diagnostics(r#"fn f((O): _) {}"#) | ||
964 | } | ||
965 | } | ||
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index 3efbce773..b809b96a0 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs | |||
@@ -8,20 +8,15 @@ use hir_def::{ | |||
8 | expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId, HasModule, | 8 | expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId, HasModule, |
9 | }; | 9 | }; |
10 | use hir_expand::name; | 10 | use hir_expand::name; |
11 | use itertools::Either; | ||
11 | use rustc_hash::FxHashSet; | 12 | use rustc_hash::FxHashSet; |
12 | use syntax::{ast, AstPtr}; | ||
13 | 13 | ||
14 | use crate::{ | 14 | use crate::{ |
15 | db::HirDatabase, | 15 | db::HirDatabase, |
16 | diagnostics::{ | 16 | diagnostics::match_check::{ |
17 | match_check::{ | 17 | self, |
18 | self, | 18 | usefulness::{compute_match_usefulness, expand_pattern, MatchCheckCtx, PatternArena}, |
19 | usefulness::{compute_match_usefulness, expand_pattern, MatchCheckCtx, PatternArena}, | ||
20 | }, | ||
21 | MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, | ||
22 | MissingPatFields, RemoveThisSemicolon, | ||
23 | }, | 19 | }, |
24 | diagnostics_sink::DiagnosticSink, | ||
25 | AdtId, InferenceResult, Interner, TyExt, TyKind, | 20 | AdtId, InferenceResult, Interner, TyExt, TyKind, |
26 | }; | 21 | }; |
27 | 22 | ||
@@ -31,38 +26,67 @@ pub(crate) use hir_def::{ | |||
31 | LocalFieldId, VariantId, | 26 | LocalFieldId, VariantId, |
32 | }; | 27 | }; |
33 | 28 | ||
34 | use super::ReplaceFilterMapNextWithFindMap; | 29 | pub enum BodyValidationDiagnostic { |
30 | RecordMissingFields { | ||
31 | record: Either<ExprId, PatId>, | ||
32 | variant: VariantId, | ||
33 | missed_fields: Vec<LocalFieldId>, | ||
34 | }, | ||
35 | ReplaceFilterMapNextWithFindMap { | ||
36 | method_call_expr: ExprId, | ||
37 | }, | ||
38 | MismatchedArgCount { | ||
39 | call_expr: ExprId, | ||
40 | expected: usize, | ||
41 | found: usize, | ||
42 | }, | ||
43 | RemoveThisSemicolon { | ||
44 | expr: ExprId, | ||
45 | }, | ||
46 | MissingOkOrSomeInTailExpr { | ||
47 | expr: ExprId, | ||
48 | required: String, | ||
49 | }, | ||
50 | MissingMatchArms { | ||
51 | match_expr: ExprId, | ||
52 | }, | ||
53 | } | ||
54 | |||
55 | impl BodyValidationDiagnostic { | ||
56 | pub fn collect(db: &dyn HirDatabase, owner: DefWithBodyId) -> Vec<BodyValidationDiagnostic> { | ||
57 | let _p = profile::span("BodyValidationDiagnostic::collect"); | ||
58 | let infer = db.infer(owner); | ||
59 | let mut validator = ExprValidator::new(owner, infer.clone()); | ||
60 | validator.validate_body(db); | ||
61 | validator.diagnostics | ||
62 | } | ||
63 | } | ||
35 | 64 | ||
36 | pub(super) struct ExprValidator<'a, 'b: 'a> { | 65 | struct ExprValidator { |
37 | owner: DefWithBodyId, | 66 | owner: DefWithBodyId, |
38 | infer: Arc<InferenceResult>, | 67 | infer: Arc<InferenceResult>, |
39 | sink: &'a mut DiagnosticSink<'b>, | 68 | pub(super) diagnostics: Vec<BodyValidationDiagnostic>, |
40 | } | 69 | } |
41 | 70 | ||
42 | impl<'a, 'b> ExprValidator<'a, 'b> { | 71 | impl ExprValidator { |
43 | pub(super) fn new( | 72 | fn new(owner: DefWithBodyId, infer: Arc<InferenceResult>) -> ExprValidator { |
44 | owner: DefWithBodyId, | 73 | ExprValidator { owner, infer, diagnostics: Vec::new() } |
45 | infer: Arc<InferenceResult>, | ||
46 | sink: &'a mut DiagnosticSink<'b>, | ||
47 | ) -> ExprValidator<'a, 'b> { | ||
48 | ExprValidator { owner, infer, sink } | ||
49 | } | 74 | } |
50 | 75 | ||
51 | pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { | 76 | fn validate_body(&mut self, db: &dyn HirDatabase) { |
52 | self.check_for_filter_map_next(db); | 77 | self.check_for_filter_map_next(db); |
53 | 78 | ||
54 | let body = db.body(self.owner); | 79 | let body = db.body(self.owner); |
55 | 80 | ||
56 | for (id, expr) in body.exprs.iter() { | 81 | for (id, expr) in body.exprs.iter() { |
57 | if let Some((variant_def, missed_fields, true)) = | 82 | if let Some((variant, missed_fields, true)) = |
58 | record_literal_missing_fields(db, &self.infer, id, expr) | 83 | record_literal_missing_fields(db, &self.infer, id, expr) |
59 | { | 84 | { |
60 | self.create_record_literal_missing_fields_diagnostic( | 85 | self.diagnostics.push(BodyValidationDiagnostic::RecordMissingFields { |
61 | id, | 86 | record: Either::Left(id), |
62 | db, | 87 | variant, |
63 | variant_def, | ||
64 | missed_fields, | 88 | missed_fields, |
65 | ); | 89 | }); |
66 | } | 90 | } |
67 | 91 | ||
68 | match expr { | 92 | match expr { |
@@ -76,15 +100,14 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
76 | } | 100 | } |
77 | } | 101 | } |
78 | for (id, pat) in body.pats.iter() { | 102 | for (id, pat) in body.pats.iter() { |
79 | if let Some((variant_def, missed_fields, true)) = | 103 | if let Some((variant, missed_fields, true)) = |
80 | record_pattern_missing_fields(db, &self.infer, id, pat) | 104 | record_pattern_missing_fields(db, &self.infer, id, pat) |
81 | { | 105 | { |
82 | self.create_record_pattern_missing_fields_diagnostic( | 106 | self.diagnostics.push(BodyValidationDiagnostic::RecordMissingFields { |
83 | id, | 107 | record: Either::Right(id), |
84 | db, | 108 | variant, |
85 | variant_def, | ||
86 | missed_fields, | 109 | missed_fields, |
87 | ); | 110 | }); |
88 | } | 111 | } |
89 | } | 112 | } |
90 | let body_expr = &body[body.body_expr]; | 113 | let body_expr = &body[body.body_expr]; |
@@ -92,71 +115,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
92 | if let Some(t) = tail { | 115 | if let Some(t) = tail { |
93 | self.validate_results_in_tail_expr(body.body_expr, *t, db); | 116 | self.validate_results_in_tail_expr(body.body_expr, *t, db); |
94 | } else if let Some(Statement::Expr { expr: id, .. }) = statements.last() { | 117 | } else if let Some(Statement::Expr { expr: id, .. }) = statements.last() { |
95 | self.validate_missing_tail_expr(body.body_expr, *id, db); | 118 | self.validate_missing_tail_expr(body.body_expr, *id); |
96 | } | ||
97 | } | ||
98 | } | ||
99 | |||
100 | fn create_record_literal_missing_fields_diagnostic( | ||
101 | &mut self, | ||
102 | id: ExprId, | ||
103 | db: &dyn HirDatabase, | ||
104 | variant_def: VariantId, | ||
105 | missed_fields: Vec<LocalFieldId>, | ||
106 | ) { | ||
107 | // XXX: only look at source_map if we do have missing fields | ||
108 | let (_, source_map) = db.body_with_source_map(self.owner); | ||
109 | |||
110 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | ||
111 | let root = source_ptr.file_syntax(db.upcast()); | ||
112 | if let ast::Expr::RecordExpr(record_expr) = &source_ptr.value.to_node(&root) { | ||
113 | if let Some(_) = record_expr.record_expr_field_list() { | ||
114 | let variant_data = variant_def.variant_data(db.upcast()); | ||
115 | let missed_fields = missed_fields | ||
116 | .into_iter() | ||
117 | .map(|idx| variant_data.fields()[idx].name.clone()) | ||
118 | .collect(); | ||
119 | self.sink.push(MissingFields { | ||
120 | file: source_ptr.file_id, | ||
121 | field_list_parent: AstPtr::new(&record_expr), | ||
122 | field_list_parent_path: record_expr.path().map(|path| AstPtr::new(&path)), | ||
123 | missed_fields, | ||
124 | }) | ||
125 | } | ||
126 | } | ||
127 | } | ||
128 | } | ||
129 | |||
130 | fn create_record_pattern_missing_fields_diagnostic( | ||
131 | &mut self, | ||
132 | id: PatId, | ||
133 | db: &dyn HirDatabase, | ||
134 | variant_def: VariantId, | ||
135 | missed_fields: Vec<LocalFieldId>, | ||
136 | ) { | ||
137 | // XXX: only look at source_map if we do have missing fields | ||
138 | let (_, source_map) = db.body_with_source_map(self.owner); | ||
139 | |||
140 | if let Ok(source_ptr) = source_map.pat_syntax(id) { | ||
141 | if let Some(expr) = source_ptr.value.as_ref().left() { | ||
142 | let root = source_ptr.file_syntax(db.upcast()); | ||
143 | if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) { | ||
144 | if let Some(_) = record_pat.record_pat_field_list() { | ||
145 | let variant_data = variant_def.variant_data(db.upcast()); | ||
146 | let missed_fields = missed_fields | ||
147 | .into_iter() | ||
148 | .map(|idx| variant_data.fields()[idx].name.clone()) | ||
149 | .collect(); | ||
150 | self.sink.push(MissingPatFields { | ||
151 | file: source_ptr.file_id, | ||
152 | field_list_parent: AstPtr::new(&record_pat), | ||
153 | field_list_parent_path: record_pat | ||
154 | .path() | ||
155 | .map(|path| AstPtr::new(&path)), | ||
156 | missed_fields, | ||
157 | }) | ||
158 | } | ||
159 | } | ||
160 | } | 119 | } |
161 | } | 120 | } |
162 | } | 121 | } |
@@ -199,13 +158,11 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
199 | if function_id == *next_function_id { | 158 | if function_id == *next_function_id { |
200 | if let Some(filter_map_id) = prev { | 159 | if let Some(filter_map_id) = prev { |
201 | if *receiver == filter_map_id { | 160 | if *receiver == filter_map_id { |
202 | let (_, source_map) = db.body_with_source_map(self.owner); | 161 | self.diagnostics.push( |
203 | if let Ok(next_source_ptr) = source_map.expr_syntax(id) { | 162 | BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap { |
204 | self.sink.push(ReplaceFilterMapNextWithFindMap { | 163 | method_call_expr: id, |
205 | file: next_source_ptr.file_id, | 164 | }, |
206 | next_expr: next_source_ptr.value, | 165 | ); |
207 | }); | ||
208 | } | ||
209 | } | 166 | } |
210 | } | 167 | } |
211 | } | 168 | } |
@@ -266,19 +223,15 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
266 | let mut arg_count = args.len(); | 223 | let mut arg_count = args.len(); |
267 | 224 | ||
268 | if arg_count != param_count { | 225 | if arg_count != param_count { |
269 | let (_, source_map) = db.body_with_source_map(self.owner); | 226 | if is_method_call { |
270 | if let Ok(source_ptr) = source_map.expr_syntax(call_id) { | 227 | param_count -= 1; |
271 | if is_method_call { | 228 | arg_count -= 1; |
272 | param_count -= 1; | ||
273 | arg_count -= 1; | ||
274 | } | ||
275 | self.sink.push(MismatchedArgCount { | ||
276 | file: source_ptr.file_id, | ||
277 | call_expr: source_ptr.value, | ||
278 | expected: param_count, | ||
279 | found: arg_count, | ||
280 | }); | ||
281 | } | 229 | } |
230 | self.diagnostics.push(BodyValidationDiagnostic::MismatchedArgCount { | ||
231 | call_expr: call_id, | ||
232 | expected: param_count, | ||
233 | found: arg_count, | ||
234 | }); | ||
282 | } | 235 | } |
283 | } | 236 | } |
284 | 237 | ||
@@ -346,8 +299,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
346 | // fit the match expression, we skip this diagnostic. Skipping the entire | 299 | // fit the match expression, we skip this diagnostic. Skipping the entire |
347 | // diagnostic rather than just not including this match arm is preferred | 300 | // diagnostic rather than just not including this match arm is preferred |
348 | // to avoid the chance of false positives. | 301 | // to avoid the chance of false positives. |
349 | #[cfg(test)] | 302 | cov_mark::hit!(validate_match_bailed_out); |
350 | match_check::tests::report_bail_out(db, self.owner, arm.pat, self.sink); | ||
351 | return; | 303 | return; |
352 | } | 304 | } |
353 | 305 | ||
@@ -357,17 +309,20 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
357 | infer: &infer, | 309 | infer: &infer, |
358 | db, | 310 | db, |
359 | pattern_arena: &pattern_arena, | 311 | pattern_arena: &pattern_arena, |
360 | eprint_panic_context: &|| { | 312 | panic_context: &|| { |
361 | use syntax::AstNode; | 313 | use syntax::AstNode; |
362 | if let Ok(scrutinee_sptr) = source_map.expr_syntax(match_expr) { | 314 | let match_expr_text = source_map |
363 | let root = scrutinee_sptr.file_syntax(db.upcast()); | 315 | .expr_syntax(match_expr) |
364 | if let Some(match_ast) = scrutinee_sptr.value.to_node(&root).syntax().parent() { | 316 | .ok() |
365 | eprintln!( | 317 | .and_then(|scrutinee_sptr| { |
366 | "Match checking is about to panic on this expression:\n{}", | 318 | let root = scrutinee_sptr.file_syntax(db.upcast()); |
367 | match_ast.to_string(), | 319 | scrutinee_sptr.value.to_node(&root).syntax().parent() |
368 | ); | 320 | }) |
369 | } | 321 | .map(|node| node.to_string()); |
370 | } | 322 | format!( |
323 | "expression:\n{}", | ||
324 | match_expr_text.as_deref().unwrap_or("<synthesized expr>") | ||
325 | ) | ||
371 | }, | 326 | }, |
372 | }; | 327 | }; |
373 | let report = compute_match_usefulness(&cx, &m_arms); | 328 | let report = compute_match_usefulness(&cx, &m_arms); |
@@ -379,20 +334,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
379 | // FIXME Report witnesses | 334 | // FIXME Report witnesses |
380 | // eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses); | 335 | // eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses); |
381 | if !witnesses.is_empty() { | 336 | if !witnesses.is_empty() { |
382 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | 337 | self.diagnostics.push(BodyValidationDiagnostic::MissingMatchArms { match_expr: id }); |
383 | let root = source_ptr.file_syntax(db.upcast()); | ||
384 | if let ast::Expr::MatchExpr(match_expr) = &source_ptr.value.to_node(&root) { | ||
385 | if let (Some(match_expr), Some(arms)) = | ||
386 | (match_expr.expr(), match_expr.match_arm_list()) | ||
387 | { | ||
388 | self.sink.push(MissingMatchArms { | ||
389 | file: source_ptr.file_id, | ||
390 | match_expr: AstPtr::new(&match_expr), | ||
391 | arms: AstPtr::new(&arms), | ||
392 | }) | ||
393 | } | ||
394 | } | ||
395 | } | ||
396 | } | 338 | } |
397 | } | 339 | } |
398 | 340 | ||
@@ -450,24 +392,12 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
450 | if params.len(&Interner) > 0 | 392 | if params.len(&Interner) > 0 |
451 | && params.at(&Interner, 0).ty(&Interner) == Some(&mismatch.actual) | 393 | && params.at(&Interner, 0).ty(&Interner) == Some(&mismatch.actual) |
452 | { | 394 | { |
453 | let (_, source_map) = db.body_with_source_map(self.owner); | 395 | self.diagnostics |
454 | 396 | .push(BodyValidationDiagnostic::MissingOkOrSomeInTailExpr { expr: id, required }); | |
455 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | ||
456 | self.sink.push(MissingOkOrSomeInTailExpr { | ||
457 | file: source_ptr.file_id, | ||
458 | expr: source_ptr.value, | ||
459 | required, | ||
460 | }); | ||
461 | } | ||
462 | } | 397 | } |
463 | } | 398 | } |
464 | 399 | ||
465 | fn validate_missing_tail_expr( | 400 | fn validate_missing_tail_expr(&mut self, body_id: ExprId, possible_tail_id: ExprId) { |
466 | &mut self, | ||
467 | body_id: ExprId, | ||
468 | possible_tail_id: ExprId, | ||
469 | db: &dyn HirDatabase, | ||
470 | ) { | ||
471 | let mismatch = match self.infer.type_mismatch_for_expr(body_id) { | 401 | let mismatch = match self.infer.type_mismatch_for_expr(body_id) { |
472 | Some(m) => m, | 402 | Some(m) => m, |
473 | None => return, | 403 | None => return, |
@@ -482,12 +412,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
482 | return; | 412 | return; |
483 | } | 413 | } |
484 | 414 | ||
485 | let (_, source_map) = db.body_with_source_map(self.owner); | 415 | self.diagnostics |
486 | 416 | .push(BodyValidationDiagnostic::RemoveThisSemicolon { expr: possible_tail_id }); | |
487 | if let Ok(source_ptr) = source_map.expr_syntax(possible_tail_id) { | ||
488 | self.sink | ||
489 | .push(RemoveThisSemicolon { file: source_ptr.file_id, expr: source_ptr.value }); | ||
490 | } | ||
491 | } | 417 | } |
492 | } | 418 | } |
493 | 419 | ||
@@ -565,258 +491,3 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul | |||
565 | walk(pat, body, infer, &mut has_type_mismatches); | 491 | walk(pat, body, infer, &mut has_type_mismatches); |
566 | !has_type_mismatches | 492 | !has_type_mismatches |
567 | } | 493 | } |
568 | |||
569 | #[cfg(test)] | ||
570 | mod tests { | ||
571 | use crate::diagnostics::tests::check_diagnostics; | ||
572 | |||
573 | #[test] | ||
574 | fn simple_free_fn_zero() { | ||
575 | check_diagnostics( | ||
576 | r#" | ||
577 | fn zero() {} | ||
578 | fn f() { zero(1); } | ||
579 | //^^^^^^^ Expected 0 arguments, found 1 | ||
580 | "#, | ||
581 | ); | ||
582 | |||
583 | check_diagnostics( | ||
584 | r#" | ||
585 | fn zero() {} | ||
586 | fn f() { zero(); } | ||
587 | "#, | ||
588 | ); | ||
589 | } | ||
590 | |||
591 | #[test] | ||
592 | fn simple_free_fn_one() { | ||
593 | check_diagnostics( | ||
594 | r#" | ||
595 | fn one(arg: u8) {} | ||
596 | fn f() { one(); } | ||
597 | //^^^^^ Expected 1 argument, found 0 | ||
598 | "#, | ||
599 | ); | ||
600 | |||
601 | check_diagnostics( | ||
602 | r#" | ||
603 | fn one(arg: u8) {} | ||
604 | fn f() { one(1); } | ||
605 | "#, | ||
606 | ); | ||
607 | } | ||
608 | |||
609 | #[test] | ||
610 | fn method_as_fn() { | ||
611 | check_diagnostics( | ||
612 | r#" | ||
613 | struct S; | ||
614 | impl S { fn method(&self) {} } | ||
615 | |||
616 | fn f() { | ||
617 | S::method(); | ||
618 | } //^^^^^^^^^^^ Expected 1 argument, found 0 | ||
619 | "#, | ||
620 | ); | ||
621 | |||
622 | check_diagnostics( | ||
623 | r#" | ||
624 | struct S; | ||
625 | impl S { fn method(&self) {} } | ||
626 | |||
627 | fn f() { | ||
628 | S::method(&S); | ||
629 | S.method(); | ||
630 | } | ||
631 | "#, | ||
632 | ); | ||
633 | } | ||
634 | |||
635 | #[test] | ||
636 | fn method_with_arg() { | ||
637 | check_diagnostics( | ||
638 | r#" | ||
639 | struct S; | ||
640 | impl S { fn method(&self, arg: u8) {} } | ||
641 | |||
642 | fn f() { | ||
643 | S.method(); | ||
644 | } //^^^^^^^^^^ Expected 1 argument, found 0 | ||
645 | "#, | ||
646 | ); | ||
647 | |||
648 | check_diagnostics( | ||
649 | r#" | ||
650 | struct S; | ||
651 | impl S { fn method(&self, arg: u8) {} } | ||
652 | |||
653 | fn f() { | ||
654 | S::method(&S, 0); | ||
655 | S.method(1); | ||
656 | } | ||
657 | "#, | ||
658 | ); | ||
659 | } | ||
660 | |||
661 | #[test] | ||
662 | fn method_unknown_receiver() { | ||
663 | // note: this is incorrect code, so there might be errors on this in the | ||
664 | // future, but we shouldn't emit an argument count diagnostic here | ||
665 | check_diagnostics( | ||
666 | r#" | ||
667 | trait Foo { fn method(&self, arg: usize) {} } | ||
668 | |||
669 | fn f() { | ||
670 | let x; | ||
671 | x.method(); | ||
672 | } | ||
673 | "#, | ||
674 | ); | ||
675 | } | ||
676 | |||
677 | #[test] | ||
678 | fn tuple_struct() { | ||
679 | check_diagnostics( | ||
680 | r#" | ||
681 | struct Tup(u8, u16); | ||
682 | fn f() { | ||
683 | Tup(0); | ||
684 | } //^^^^^^ Expected 2 arguments, found 1 | ||
685 | "#, | ||
686 | ) | ||
687 | } | ||
688 | |||
689 | #[test] | ||
690 | fn enum_variant() { | ||
691 | check_diagnostics( | ||
692 | r#" | ||
693 | enum En { Variant(u8, u16), } | ||
694 | fn f() { | ||
695 | En::Variant(0); | ||
696 | } //^^^^^^^^^^^^^^ Expected 2 arguments, found 1 | ||
697 | "#, | ||
698 | ) | ||
699 | } | ||
700 | |||
701 | #[test] | ||
702 | fn enum_variant_type_macro() { | ||
703 | check_diagnostics( | ||
704 | r#" | ||
705 | macro_rules! Type { | ||
706 | () => { u32 }; | ||
707 | } | ||
708 | enum Foo { | ||
709 | Bar(Type![]) | ||
710 | } | ||
711 | impl Foo { | ||
712 | fn new() { | ||
713 | Foo::Bar(0); | ||
714 | Foo::Bar(0, 1); | ||
715 | //^^^^^^^^^^^^^^ Expected 1 argument, found 2 | ||
716 | Foo::Bar(); | ||
717 | //^^^^^^^^^^ Expected 1 argument, found 0 | ||
718 | } | ||
719 | } | ||
720 | "#, | ||
721 | ); | ||
722 | } | ||
723 | |||
724 | #[test] | ||
725 | fn varargs() { | ||
726 | check_diagnostics( | ||
727 | r#" | ||
728 | extern "C" { | ||
729 | fn fixed(fixed: u8); | ||
730 | fn varargs(fixed: u8, ...); | ||
731 | fn varargs2(...); | ||
732 | } | ||
733 | |||
734 | fn f() { | ||
735 | unsafe { | ||
736 | fixed(0); | ||
737 | fixed(0, 1); | ||
738 | //^^^^^^^^^^^ Expected 1 argument, found 2 | ||
739 | varargs(0); | ||
740 | varargs(0, 1); | ||
741 | varargs2(); | ||
742 | varargs2(0); | ||
743 | varargs2(0, 1); | ||
744 | } | ||
745 | } | ||
746 | "#, | ||
747 | ) | ||
748 | } | ||
749 | |||
750 | #[test] | ||
751 | fn arg_count_lambda() { | ||
752 | check_diagnostics( | ||
753 | r#" | ||
754 | fn main() { | ||
755 | let f = |()| (); | ||
756 | f(); | ||
757 | //^^^ Expected 1 argument, found 0 | ||
758 | f(()); | ||
759 | f((), ()); | ||
760 | //^^^^^^^^^ Expected 1 argument, found 2 | ||
761 | } | ||
762 | "#, | ||
763 | ) | ||
764 | } | ||
765 | |||
766 | #[test] | ||
767 | fn cfgd_out_call_arguments() { | ||
768 | check_diagnostics( | ||
769 | r#" | ||
770 | struct C(#[cfg(FALSE)] ()); | ||
771 | impl C { | ||
772 | fn new() -> Self { | ||
773 | Self( | ||
774 | #[cfg(FALSE)] | ||
775 | (), | ||
776 | ) | ||
777 | } | ||
778 | |||
779 | fn method(&self) {} | ||
780 | } | ||
781 | |||
782 | fn main() { | ||
783 | C::new().method(#[cfg(FALSE)] 0); | ||
784 | } | ||
785 | "#, | ||
786 | ); | ||
787 | } | ||
788 | |||
789 | #[test] | ||
790 | fn cfgd_out_fn_params() { | ||
791 | check_diagnostics( | ||
792 | r#" | ||
793 | fn foo(#[cfg(NEVER)] x: ()) {} | ||
794 | |||
795 | struct S; | ||
796 | |||
797 | impl S { | ||
798 | fn method(#[cfg(NEVER)] self) {} | ||
799 | fn method2(#[cfg(NEVER)] self, arg: u8) {} | ||
800 | fn method3(self, #[cfg(NEVER)] arg: u8) {} | ||
801 | } | ||
802 | |||
803 | extern "C" { | ||
804 | fn fixed(fixed: u8, #[cfg(NEVER)] ...); | ||
805 | fn varargs(#[cfg(not(NEVER))] ...); | ||
806 | } | ||
807 | |||
808 | fn main() { | ||
809 | foo(); | ||
810 | S::method(); | ||
811 | S::method2(0); | ||
812 | S::method3(S); | ||
813 | S.method3(); | ||
814 | unsafe { | ||
815 | fixed(0); | ||
816 | varargs(1, 2, 3); | ||
817 | } | ||
818 | } | ||
819 | "#, | ||
820 | ) | ||
821 | } | ||
822 | } | ||
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs index a9a99f57a..a30e42699 100644 --- a/crates/hir_ty/src/diagnostics/match_check.rs +++ b/crates/hir_ty/src/diagnostics/match_check.rs | |||
@@ -100,10 +100,19 @@ impl<'a> PatCtxt<'a> { | |||
100 | } | 100 | } |
101 | 101 | ||
102 | pub(crate) fn lower_pattern(&mut self, pat: hir_def::expr::PatId) -> Pat { | 102 | pub(crate) fn lower_pattern(&mut self, pat: hir_def::expr::PatId) -> Pat { |
103 | // FIXME: implement pattern adjustments (implicit pattern dereference; "RFC 2005-match-ergonomics") | 103 | // XXX(iDawer): Collecting pattern adjustments feels imprecise to me. |
104 | // When lowering of & and box patterns are implemented this should be tested | ||
105 | // in a manner of `match_ergonomics_issue_9095` test. | ||
106 | // Pattern adjustment is part of RFC 2005-match-ergonomics. | ||
104 | // More info https://github.com/rust-lang/rust/issues/42640#issuecomment-313535089 | 107 | // More info https://github.com/rust-lang/rust/issues/42640#issuecomment-313535089 |
105 | let unadjusted_pat = self.lower_pattern_unadjusted(pat); | 108 | let unadjusted_pat = self.lower_pattern_unadjusted(pat); |
106 | unadjusted_pat | 109 | self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold( |
110 | unadjusted_pat, | ||
111 | |subpattern, ref_ty| Pat { | ||
112 | ty: ref_ty.clone(), | ||
113 | kind: Box::new(PatKind::Deref { subpattern }), | ||
114 | }, | ||
115 | ) | ||
107 | } | 116 | } |
108 | 117 | ||
109 | fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat { | 118 | fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat { |
@@ -355,945 +364,3 @@ impl PatternFoldable for PatKind { | |||
355 | } | 364 | } |
356 | } | 365 | } |
357 | } | 366 | } |
358 | |||
359 | #[cfg(test)] | ||
360 | pub(super) mod tests { | ||
361 | mod report { | ||
362 | use std::any::Any; | ||
363 | |||
364 | use hir_def::{expr::PatId, DefWithBodyId}; | ||
365 | use hir_expand::{HirFileId, InFile}; | ||
366 | use syntax::SyntaxNodePtr; | ||
367 | |||
368 | use crate::{ | ||
369 | db::HirDatabase, | ||
370 | diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink}, | ||
371 | }; | ||
372 | |||
373 | /// In tests, match check bails out loudly. | ||
374 | /// This helps to catch incorrect tests that pass due to false negatives. | ||
375 | pub(crate) fn report_bail_out( | ||
376 | db: &dyn HirDatabase, | ||
377 | def: DefWithBodyId, | ||
378 | pat: PatId, | ||
379 | sink: &mut DiagnosticSink, | ||
380 | ) { | ||
381 | let (_, source_map) = db.body_with_source_map(def); | ||
382 | if let Ok(source_ptr) = source_map.pat_syntax(pat) { | ||
383 | let pat_syntax_ptr = source_ptr.value.either(Into::into, Into::into); | ||
384 | sink.push(BailedOut { file: source_ptr.file_id, pat_syntax_ptr }); | ||
385 | } | ||
386 | } | ||
387 | |||
388 | #[derive(Debug)] | ||
389 | struct BailedOut { | ||
390 | file: HirFileId, | ||
391 | pat_syntax_ptr: SyntaxNodePtr, | ||
392 | } | ||
393 | |||
394 | impl Diagnostic for BailedOut { | ||
395 | fn code(&self) -> DiagnosticCode { | ||
396 | DiagnosticCode("internal:match-check-bailed-out") | ||
397 | } | ||
398 | fn message(&self) -> String { | ||
399 | format!("Internal: match check bailed out") | ||
400 | } | ||
401 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
402 | InFile { file_id: self.file, value: self.pat_syntax_ptr.clone() } | ||
403 | } | ||
404 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
405 | self | ||
406 | } | ||
407 | } | ||
408 | } | ||
409 | |||
410 | use crate::diagnostics::tests::check_diagnostics; | ||
411 | |||
412 | pub(crate) use self::report::report_bail_out; | ||
413 | |||
414 | #[test] | ||
415 | fn empty_tuple() { | ||
416 | check_diagnostics( | ||
417 | r#" | ||
418 | fn main() { | ||
419 | match () { } | ||
420 | //^^ Missing match arm | ||
421 | match (()) { } | ||
422 | //^^^^ Missing match arm | ||
423 | |||
424 | match () { _ => (), } | ||
425 | match () { () => (), } | ||
426 | match (()) { (()) => (), } | ||
427 | } | ||
428 | "#, | ||
429 | ); | ||
430 | } | ||
431 | |||
432 | #[test] | ||
433 | fn tuple_of_two_empty_tuple() { | ||
434 | check_diagnostics( | ||
435 | r#" | ||
436 | fn main() { | ||
437 | match ((), ()) { } | ||
438 | //^^^^^^^^ Missing match arm | ||
439 | |||
440 | match ((), ()) { ((), ()) => (), } | ||
441 | } | ||
442 | "#, | ||
443 | ); | ||
444 | } | ||
445 | |||
446 | #[test] | ||
447 | fn boolean() { | ||
448 | check_diagnostics( | ||
449 | r#" | ||
450 | fn test_main() { | ||
451 | match false { } | ||
452 | //^^^^^ Missing match arm | ||
453 | match false { true => (), } | ||
454 | //^^^^^ Missing match arm | ||
455 | match (false, true) {} | ||
456 | //^^^^^^^^^^^^^ Missing match arm | ||
457 | match (false, true) { (true, true) => (), } | ||
458 | //^^^^^^^^^^^^^ Missing match arm | ||
459 | match (false, true) { | ||
460 | //^^^^^^^^^^^^^ Missing match arm | ||
461 | (false, true) => (), | ||
462 | (false, false) => (), | ||
463 | (true, false) => (), | ||
464 | } | ||
465 | match (false, true) { (true, _x) => (), } | ||
466 | //^^^^^^^^^^^^^ Missing match arm | ||
467 | |||
468 | match false { true => (), false => (), } | ||
469 | match (false, true) { | ||
470 | (false, _) => (), | ||
471 | (true, false) => (), | ||
472 | (_, true) => (), | ||
473 | } | ||
474 | match (false, true) { | ||
475 | (true, true) => (), | ||
476 | (true, false) => (), | ||
477 | (false, true) => (), | ||
478 | (false, false) => (), | ||
479 | } | ||
480 | match (false, true) { | ||
481 | (true, _x) => (), | ||
482 | (false, true) => (), | ||
483 | (false, false) => (), | ||
484 | } | ||
485 | match (false, true, false) { | ||
486 | (false, ..) => (), | ||
487 | (true, ..) => (), | ||
488 | } | ||
489 | match (false, true, false) { | ||
490 | (.., false) => (), | ||
491 | (.., true) => (), | ||
492 | } | ||
493 | match (false, true, false) { (..) => (), } | ||
494 | } | ||
495 | "#, | ||
496 | ); | ||
497 | } | ||
498 | |||
499 | #[test] | ||
500 | fn tuple_of_tuple_and_bools() { | ||
501 | check_diagnostics( | ||
502 | r#" | ||
503 | fn main() { | ||
504 | match (false, ((), false)) {} | ||
505 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
506 | match (false, ((), false)) { (true, ((), true)) => (), } | ||
507 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
508 | match (false, ((), false)) { (true, _) => (), } | ||
509 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
510 | |||
511 | match (false, ((), false)) { | ||
512 | (true, ((), true)) => (), | ||
513 | (true, ((), false)) => (), | ||
514 | (false, ((), true)) => (), | ||
515 | (false, ((), false)) => (), | ||
516 | } | ||
517 | match (false, ((), false)) { | ||
518 | (true, ((), true)) => (), | ||
519 | (true, ((), false)) => (), | ||
520 | (false, _) => (), | ||
521 | } | ||
522 | } | ||
523 | "#, | ||
524 | ); | ||
525 | } | ||
526 | |||
527 | #[test] | ||
528 | fn enums() { | ||
529 | check_diagnostics( | ||
530 | r#" | ||
531 | enum Either { A, B, } | ||
532 | |||
533 | fn main() { | ||
534 | match Either::A { } | ||
535 | //^^^^^^^^^ Missing match arm | ||
536 | match Either::B { Either::A => (), } | ||
537 | //^^^^^^^^^ Missing match arm | ||
538 | |||
539 | match &Either::B { | ||
540 | //^^^^^^^^^^ Missing match arm | ||
541 | Either::A => (), | ||
542 | } | ||
543 | |||
544 | match Either::B { | ||
545 | Either::A => (), Either::B => (), | ||
546 | } | ||
547 | match &Either::B { | ||
548 | Either::A => (), Either::B => (), | ||
549 | } | ||
550 | } | ||
551 | "#, | ||
552 | ); | ||
553 | } | ||
554 | |||
555 | #[test] | ||
556 | fn enum_containing_bool() { | ||
557 | check_diagnostics( | ||
558 | r#" | ||
559 | enum Either { A(bool), B } | ||
560 | |||
561 | fn main() { | ||
562 | match Either::B { } | ||
563 | //^^^^^^^^^ Missing match arm | ||
564 | match Either::B { | ||
565 | //^^^^^^^^^ Missing match arm | ||
566 | Either::A(true) => (), Either::B => () | ||
567 | } | ||
568 | |||
569 | match Either::B { | ||
570 | Either::A(true) => (), | ||
571 | Either::A(false) => (), | ||
572 | Either::B => (), | ||
573 | } | ||
574 | match Either::B { | ||
575 | Either::B => (), | ||
576 | _ => (), | ||
577 | } | ||
578 | match Either::B { | ||
579 | Either::A(_) => (), | ||
580 | Either::B => (), | ||
581 | } | ||
582 | |||
583 | } | ||
584 | "#, | ||
585 | ); | ||
586 | } | ||
587 | |||
588 | #[test] | ||
589 | fn enum_different_sizes() { | ||
590 | check_diagnostics( | ||
591 | r#" | ||
592 | enum Either { A(bool), B(bool, bool) } | ||
593 | |||
594 | fn main() { | ||
595 | match Either::A(false) { | ||
596 | //^^^^^^^^^^^^^^^^ Missing match arm | ||
597 | Either::A(_) => (), | ||
598 | Either::B(false, _) => (), | ||
599 | } | ||
600 | |||
601 | match Either::A(false) { | ||
602 | Either::A(_) => (), | ||
603 | Either::B(true, _) => (), | ||
604 | Either::B(false, _) => (), | ||
605 | } | ||
606 | match Either::A(false) { | ||
607 | Either::A(true) | Either::A(false) => (), | ||
608 | Either::B(true, _) => (), | ||
609 | Either::B(false, _) => (), | ||
610 | } | ||
611 | } | ||
612 | "#, | ||
613 | ); | ||
614 | } | ||
615 | |||
616 | #[test] | ||
617 | fn tuple_of_enum_no_diagnostic() { | ||
618 | check_diagnostics( | ||
619 | r#" | ||
620 | enum Either { A(bool), B(bool, bool) } | ||
621 | enum Either2 { C, D } | ||
622 | |||
623 | fn main() { | ||
624 | match (Either::A(false), Either2::C) { | ||
625 | (Either::A(true), _) | (Either::A(false), _) => (), | ||
626 | (Either::B(true, _), Either2::C) => (), | ||
627 | (Either::B(false, _), Either2::C) => (), | ||
628 | (Either::B(_, _), Either2::D) => (), | ||
629 | } | ||
630 | } | ||
631 | "#, | ||
632 | ); | ||
633 | } | ||
634 | |||
635 | #[test] | ||
636 | fn or_pattern_no_diagnostic() { | ||
637 | check_diagnostics( | ||
638 | r#" | ||
639 | enum Either {A, B} | ||
640 | |||
641 | fn main() { | ||
642 | match (Either::A, Either::B) { | ||
643 | (Either::A | Either::B, _) => (), | ||
644 | } | ||
645 | }"#, | ||
646 | ) | ||
647 | } | ||
648 | |||
649 | #[test] | ||
650 | fn mismatched_types() { | ||
651 | // Match statements with arms that don't match the | ||
652 | // expression pattern do not fire this diagnostic. | ||
653 | check_diagnostics( | ||
654 | r#" | ||
655 | enum Either { A, B } | ||
656 | enum Either2 { C, D } | ||
657 | |||
658 | fn main() { | ||
659 | match Either::A { | ||
660 | Either2::C => (), | ||
661 | // ^^^^^^^^^^ Internal: match check bailed out | ||
662 | Either2::D => (), | ||
663 | } | ||
664 | match (true, false) { | ||
665 | (true, false, true) => (), | ||
666 | // ^^^^^^^^^^^^^^^^^^^ Internal: match check bailed out | ||
667 | (true) => (), | ||
668 | } | ||
669 | match (true, false) { (true,) => {} } | ||
670 | // ^^^^^^^ Internal: match check bailed out | ||
671 | match (0) { () => () } | ||
672 | // ^^ Internal: match check bailed out | ||
673 | match Unresolved::Bar { Unresolved::Baz => () } | ||
674 | } | ||
675 | "#, | ||
676 | ); | ||
677 | } | ||
678 | |||
679 | #[test] | ||
680 | fn mismatched_types_in_or_patterns() { | ||
681 | check_diagnostics( | ||
682 | r#" | ||
683 | fn main() { | ||
684 | match false { true | () => {} } | ||
685 | // ^^^^^^^^^ Internal: match check bailed out | ||
686 | match (false,) { (true | (),) => {} } | ||
687 | // ^^^^^^^^^^^^ Internal: match check bailed out | ||
688 | } | ||
689 | "#, | ||
690 | ); | ||
691 | } | ||
692 | |||
693 | #[test] | ||
694 | fn malformed_match_arm_tuple_enum_missing_pattern() { | ||
695 | // We are testing to be sure we don't panic here when the match | ||
696 | // arm `Either::B` is missing its pattern. | ||
697 | check_diagnostics( | ||
698 | r#" | ||
699 | enum Either { A, B(u32) } | ||
700 | |||
701 | fn main() { | ||
702 | match Either::A { | ||
703 | Either::A => (), | ||
704 | Either::B() => (), | ||
705 | } | ||
706 | } | ||
707 | "#, | ||
708 | ); | ||
709 | } | ||
710 | |||
711 | #[test] | ||
712 | fn malformed_match_arm_extra_fields() { | ||
713 | check_diagnostics( | ||
714 | r#" | ||
715 | enum A { B(isize, isize), C } | ||
716 | fn main() { | ||
717 | match A::B(1, 2) { | ||
718 | A::B(_, _, _) => (), | ||
719 | // ^^^^^^^^^^^^^ Internal: match check bailed out | ||
720 | } | ||
721 | match A::B(1, 2) { | ||
722 | A::C(_) => (), | ||
723 | // ^^^^^^^ Internal: match check bailed out | ||
724 | } | ||
725 | } | ||
726 | "#, | ||
727 | ); | ||
728 | } | ||
729 | |||
730 | #[test] | ||
731 | fn expr_diverges() { | ||
732 | check_diagnostics( | ||
733 | r#" | ||
734 | enum Either { A, B } | ||
735 | |||
736 | fn main() { | ||
737 | match loop {} { | ||
738 | Either::A => (), | ||
739 | // ^^^^^^^^^ Internal: match check bailed out | ||
740 | Either::B => (), | ||
741 | } | ||
742 | match loop {} { | ||
743 | Either::A => (), | ||
744 | // ^^^^^^^^^ Internal: match check bailed out | ||
745 | } | ||
746 | match loop { break Foo::A } { | ||
747 | //^^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
748 | Either::A => (), | ||
749 | } | ||
750 | match loop { break Foo::A } { | ||
751 | Either::A => (), | ||
752 | Either::B => (), | ||
753 | } | ||
754 | } | ||
755 | "#, | ||
756 | ); | ||
757 | } | ||
758 | |||
759 | #[test] | ||
760 | fn expr_partially_diverges() { | ||
761 | check_diagnostics( | ||
762 | r#" | ||
763 | enum Either<T> { A(T), B } | ||
764 | |||
765 | fn foo() -> Either<!> { Either::B } | ||
766 | fn main() -> u32 { | ||
767 | match foo() { | ||
768 | Either::A(val) => val, | ||
769 | Either::B => 0, | ||
770 | } | ||
771 | } | ||
772 | "#, | ||
773 | ); | ||
774 | } | ||
775 | |||
776 | #[test] | ||
777 | fn enum_record() { | ||
778 | check_diagnostics( | ||
779 | r#" | ||
780 | enum Either { A { foo: bool }, B } | ||
781 | |||
782 | fn main() { | ||
783 | let a = Either::A { foo: true }; | ||
784 | match a { } | ||
785 | //^ Missing match arm | ||
786 | match a { Either::A { foo: true } => () } | ||
787 | //^ Missing match arm | ||
788 | match a { | ||
789 | Either::A { } => (), | ||
790 | //^^^^^^^^^ Missing structure fields: | ||
791 | // | - foo | ||
792 | Either::B => (), | ||
793 | } | ||
794 | match a { | ||
795 | //^ Missing match arm | ||
796 | Either::A { } => (), | ||
797 | } //^^^^^^^^^ Missing structure fields: | ||
798 | // | - foo | ||
799 | |||
800 | match a { | ||
801 | Either::A { foo: true } => (), | ||
802 | Either::A { foo: false } => (), | ||
803 | Either::B => (), | ||
804 | } | ||
805 | match a { | ||
806 | Either::A { foo: _ } => (), | ||
807 | Either::B => (), | ||
808 | } | ||
809 | } | ||
810 | "#, | ||
811 | ); | ||
812 | } | ||
813 | |||
814 | #[test] | ||
815 | fn enum_record_fields_out_of_order() { | ||
816 | check_diagnostics( | ||
817 | r#" | ||
818 | enum Either { | ||
819 | A { foo: bool, bar: () }, | ||
820 | B, | ||
821 | } | ||
822 | |||
823 | fn main() { | ||
824 | let a = Either::A { foo: true, bar: () }; | ||
825 | match a { | ||
826 | //^ Missing match arm | ||
827 | Either::A { bar: (), foo: false } => (), | ||
828 | Either::A { foo: true, bar: () } => (), | ||
829 | } | ||
830 | |||
831 | match a { | ||
832 | Either::A { bar: (), foo: false } => (), | ||
833 | Either::A { foo: true, bar: () } => (), | ||
834 | Either::B => (), | ||
835 | } | ||
836 | } | ||
837 | "#, | ||
838 | ); | ||
839 | } | ||
840 | |||
841 | #[test] | ||
842 | fn enum_record_ellipsis() { | ||
843 | check_diagnostics( | ||
844 | r#" | ||
845 | enum Either { | ||
846 | A { foo: bool, bar: bool }, | ||
847 | B, | ||
848 | } | ||
849 | |||
850 | fn main() { | ||
851 | let a = Either::B; | ||
852 | match a { | ||
853 | //^ Missing match arm | ||
854 | Either::A { foo: true, .. } => (), | ||
855 | Either::B => (), | ||
856 | } | ||
857 | match a { | ||
858 | //^ Missing match arm | ||
859 | Either::A { .. } => (), | ||
860 | } | ||
861 | |||
862 | match a { | ||
863 | Either::A { foo: true, .. } => (), | ||
864 | Either::A { foo: false, .. } => (), | ||
865 | Either::B => (), | ||
866 | } | ||
867 | |||
868 | match a { | ||
869 | Either::A { .. } => (), | ||
870 | Either::B => (), | ||
871 | } | ||
872 | } | ||
873 | "#, | ||
874 | ); | ||
875 | } | ||
876 | |||
877 | #[test] | ||
878 | fn enum_tuple_partial_ellipsis() { | ||
879 | check_diagnostics( | ||
880 | r#" | ||
881 | enum Either { | ||
882 | A(bool, bool, bool, bool), | ||
883 | B, | ||
884 | } | ||
885 | |||
886 | fn main() { | ||
887 | match Either::B { | ||
888 | //^^^^^^^^^ Missing match arm | ||
889 | Either::A(true, .., true) => (), | ||
890 | Either::A(true, .., false) => (), | ||
891 | Either::A(false, .., false) => (), | ||
892 | Either::B => (), | ||
893 | } | ||
894 | match Either::B { | ||
895 | //^^^^^^^^^ Missing match arm | ||
896 | Either::A(true, .., true) => (), | ||
897 | Either::A(true, .., false) => (), | ||
898 | Either::A(.., true) => (), | ||
899 | Either::B => (), | ||
900 | } | ||
901 | |||
902 | match Either::B { | ||
903 | Either::A(true, .., true) => (), | ||
904 | Either::A(true, .., false) => (), | ||
905 | Either::A(false, .., true) => (), | ||
906 | Either::A(false, .., false) => (), | ||
907 | Either::B => (), | ||
908 | } | ||
909 | match Either::B { | ||
910 | Either::A(true, .., true) => (), | ||
911 | Either::A(true, .., false) => (), | ||
912 | Either::A(.., true) => (), | ||
913 | Either::A(.., false) => (), | ||
914 | Either::B => (), | ||
915 | } | ||
916 | } | ||
917 | "#, | ||
918 | ); | ||
919 | } | ||
920 | |||
921 | #[test] | ||
922 | fn never() { | ||
923 | check_diagnostics( | ||
924 | r#" | ||
925 | enum Never {} | ||
926 | |||
927 | fn enum_(never: Never) { | ||
928 | match never {} | ||
929 | } | ||
930 | fn enum_ref(never: &Never) { | ||
931 | match never {} | ||
932 | //^^^^^ Missing match arm | ||
933 | } | ||
934 | fn bang(never: !) { | ||
935 | match never {} | ||
936 | } | ||
937 | "#, | ||
938 | ); | ||
939 | } | ||
940 | |||
941 | #[test] | ||
942 | fn unknown_type() { | ||
943 | check_diagnostics( | ||
944 | r#" | ||
945 | enum Option<T> { Some(T), None } | ||
946 | |||
947 | fn main() { | ||
948 | // `Never` is deliberately not defined so that it's an uninferred type. | ||
949 | match Option::<Never>::None { | ||
950 | None => (), | ||
951 | Some(never) => match never {}, | ||
952 | // ^^^^^^^^^^^ Internal: match check bailed out | ||
953 | } | ||
954 | match Option::<Never>::None { | ||
955 | //^^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
956 | Option::Some(_never) => {}, | ||
957 | } | ||
958 | } | ||
959 | "#, | ||
960 | ); | ||
961 | } | ||
962 | |||
963 | #[test] | ||
964 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { | ||
965 | check_diagnostics( | ||
966 | r#" | ||
967 | fn main() { | ||
968 | match (false, true, false) { | ||
969 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
970 | (false, ..) => (), | ||
971 | } | ||
972 | }"#, | ||
973 | ); | ||
974 | } | ||
975 | |||
976 | #[test] | ||
977 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { | ||
978 | check_diagnostics( | ||
979 | r#" | ||
980 | fn main() { | ||
981 | match (false, true, false) { | ||
982 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
983 | (.., false) => (), | ||
984 | } | ||
985 | }"#, | ||
986 | ); | ||
987 | } | ||
988 | |||
989 | #[test] | ||
990 | fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() { | ||
991 | check_diagnostics( | ||
992 | r#" | ||
993 | fn main() { | ||
994 | match (false, true, false) { | ||
995 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
996 | (true, .., false) => (), | ||
997 | } | ||
998 | }"#, | ||
999 | ); | ||
1000 | } | ||
1001 | |||
1002 | #[test] | ||
1003 | fn record_struct() { | ||
1004 | check_diagnostics( | ||
1005 | r#"struct Foo { a: bool } | ||
1006 | fn main(f: Foo) { | ||
1007 | match f {} | ||
1008 | //^ Missing match arm | ||
1009 | match f { Foo { a: true } => () } | ||
1010 | //^ Missing match arm | ||
1011 | match &f { Foo { a: true } => () } | ||
1012 | //^^ Missing match arm | ||
1013 | match f { Foo { a: _ } => () } | ||
1014 | match f { | ||
1015 | Foo { a: true } => (), | ||
1016 | Foo { a: false } => (), | ||
1017 | } | ||
1018 | match &f { | ||
1019 | Foo { a: true } => (), | ||
1020 | Foo { a: false } => (), | ||
1021 | } | ||
1022 | } | ||
1023 | "#, | ||
1024 | ); | ||
1025 | } | ||
1026 | |||
1027 | #[test] | ||
1028 | fn tuple_struct() { | ||
1029 | check_diagnostics( | ||
1030 | r#"struct Foo(bool); | ||
1031 | fn main(f: Foo) { | ||
1032 | match f {} | ||
1033 | //^ Missing match arm | ||
1034 | match f { Foo(true) => () } | ||
1035 | //^ Missing match arm | ||
1036 | match f { | ||
1037 | Foo(true) => (), | ||
1038 | Foo(false) => (), | ||
1039 | } | ||
1040 | } | ||
1041 | "#, | ||
1042 | ); | ||
1043 | } | ||
1044 | |||
1045 | #[test] | ||
1046 | fn unit_struct() { | ||
1047 | check_diagnostics( | ||
1048 | r#"struct Foo; | ||
1049 | fn main(f: Foo) { | ||
1050 | match f {} | ||
1051 | //^ Missing match arm | ||
1052 | match f { Foo => () } | ||
1053 | } | ||
1054 | "#, | ||
1055 | ); | ||
1056 | } | ||
1057 | |||
1058 | #[test] | ||
1059 | fn record_struct_ellipsis() { | ||
1060 | check_diagnostics( | ||
1061 | r#"struct Foo { foo: bool, bar: bool } | ||
1062 | fn main(f: Foo) { | ||
1063 | match f { Foo { foo: true, .. } => () } | ||
1064 | //^ Missing match arm | ||
1065 | match f { | ||
1066 | //^ Missing match arm | ||
1067 | Foo { foo: true, .. } => (), | ||
1068 | Foo { bar: false, .. } => () | ||
1069 | } | ||
1070 | match f { Foo { .. } => () } | ||
1071 | match f { | ||
1072 | Foo { foo: true, .. } => (), | ||
1073 | Foo { foo: false, .. } => () | ||
1074 | } | ||
1075 | } | ||
1076 | "#, | ||
1077 | ); | ||
1078 | } | ||
1079 | |||
1080 | #[test] | ||
1081 | fn internal_or() { | ||
1082 | check_diagnostics( | ||
1083 | r#" | ||
1084 | fn main() { | ||
1085 | enum Either { A(bool), B } | ||
1086 | match Either::B { | ||
1087 | //^^^^^^^^^ Missing match arm | ||
1088 | Either::A(true | false) => (), | ||
1089 | } | ||
1090 | } | ||
1091 | "#, | ||
1092 | ); | ||
1093 | } | ||
1094 | |||
1095 | #[test] | ||
1096 | fn no_panic_at_unimplemented_subpattern_type() { | ||
1097 | check_diagnostics( | ||
1098 | r#" | ||
1099 | struct S { a: char} | ||
1100 | fn main(v: S) { | ||
1101 | match v { S{ a } => {} } | ||
1102 | match v { S{ a: _x } => {} } | ||
1103 | match v { S{ a: 'a' } => {} } | ||
1104 | //^^^^^^^^^^^ Internal: match check bailed out | ||
1105 | match v { S{..} => {} } | ||
1106 | match v { _ => {} } | ||
1107 | match v { } | ||
1108 | //^ Missing match arm | ||
1109 | } | ||
1110 | "#, | ||
1111 | ); | ||
1112 | } | ||
1113 | |||
1114 | #[test] | ||
1115 | fn binding() { | ||
1116 | check_diagnostics( | ||
1117 | r#" | ||
1118 | fn main() { | ||
1119 | match true { | ||
1120 | _x @ true => {} | ||
1121 | false => {} | ||
1122 | } | ||
1123 | match true { _x @ true => {} } | ||
1124 | //^^^^ Missing match arm | ||
1125 | } | ||
1126 | "#, | ||
1127 | ); | ||
1128 | } | ||
1129 | |||
1130 | #[test] | ||
1131 | fn binding_ref_has_correct_type() { | ||
1132 | // Asserts `PatKind::Binding(ref _x): bool`, not &bool. | ||
1133 | // If that's not true match checking will panic with "incompatible constructors" | ||
1134 | // FIXME: make facilities to test this directly like `tests::check_infer(..)` | ||
1135 | check_diagnostics( | ||
1136 | r#" | ||
1137 | enum Foo { A } | ||
1138 | fn main() { | ||
1139 | // FIXME: this should not bail out but current behavior is such as the old algorithm. | ||
1140 | // ExprValidator::validate_match(..) checks types of top level patterns incorrecly. | ||
1141 | match Foo::A { | ||
1142 | ref _x => {} | ||
1143 | // ^^^^^^ Internal: match check bailed out | ||
1144 | Foo::A => {} | ||
1145 | } | ||
1146 | match (true,) { | ||
1147 | (ref _x,) => {} | ||
1148 | (true,) => {} | ||
1149 | } | ||
1150 | } | ||
1151 | "#, | ||
1152 | ); | ||
1153 | } | ||
1154 | |||
1155 | #[test] | ||
1156 | fn enum_non_exhaustive() { | ||
1157 | check_diagnostics( | ||
1158 | r#" | ||
1159 | //- /lib.rs crate:lib | ||
1160 | #[non_exhaustive] | ||
1161 | pub enum E { A, B } | ||
1162 | fn _local() { | ||
1163 | match E::A { _ => {} } | ||
1164 | match E::A { | ||
1165 | E::A => {} | ||
1166 | E::B => {} | ||
1167 | } | ||
1168 | match E::A { | ||
1169 | E::A | E::B => {} | ||
1170 | } | ||
1171 | } | ||
1172 | |||
1173 | //- /main.rs crate:main deps:lib | ||
1174 | use lib::E; | ||
1175 | fn main() { | ||
1176 | match E::A { _ => {} } | ||
1177 | match E::A { | ||
1178 | //^^^^ Missing match arm | ||
1179 | E::A => {} | ||
1180 | E::B => {} | ||
1181 | } | ||
1182 | match E::A { | ||
1183 | //^^^^ Missing match arm | ||
1184 | E::A | E::B => {} | ||
1185 | } | ||
1186 | } | ||
1187 | "#, | ||
1188 | ); | ||
1189 | } | ||
1190 | |||
1191 | #[test] | ||
1192 | fn match_guard() { | ||
1193 | check_diagnostics( | ||
1194 | r#" | ||
1195 | fn main() { | ||
1196 | match true { | ||
1197 | true if false => {} | ||
1198 | true => {} | ||
1199 | false => {} | ||
1200 | } | ||
1201 | match true { | ||
1202 | //^^^^ Missing match arm | ||
1203 | true if false => {} | ||
1204 | false => {} | ||
1205 | } | ||
1206 | "#, | ||
1207 | ); | ||
1208 | } | ||
1209 | |||
1210 | #[test] | ||
1211 | fn pattern_type_is_of_substitution() { | ||
1212 | cov_mark::check!(match_check_wildcard_expanded_to_substitutions); | ||
1213 | check_diagnostics( | ||
1214 | r#" | ||
1215 | struct Foo<T>(T); | ||
1216 | struct Bar; | ||
1217 | fn main() { | ||
1218 | match Foo(Bar) { | ||
1219 | _ | Foo(Bar) => {} | ||
1220 | } | ||
1221 | } | ||
1222 | "#, | ||
1223 | ); | ||
1224 | } | ||
1225 | |||
1226 | #[test] | ||
1227 | fn record_struct_no_such_field() { | ||
1228 | check_diagnostics( | ||
1229 | r#" | ||
1230 | struct Foo { } | ||
1231 | fn main(f: Foo) { | ||
1232 | match f { Foo { bar } => () } | ||
1233 | // ^^^^^^^^^^^ Internal: match check bailed out | ||
1234 | } | ||
1235 | "#, | ||
1236 | ); | ||
1237 | } | ||
1238 | |||
1239 | mod false_negatives { | ||
1240 | //! The implementation of match checking here is a work in progress. As we roll this out, we | ||
1241 | //! prefer false negatives to false positives (ideally there would be no false positives). This | ||
1242 | //! test module should document known false negatives. Eventually we will have a complete | ||
1243 | //! implementation of match checking and this module will be empty. | ||
1244 | //! | ||
1245 | //! The reasons for documenting known false negatives: | ||
1246 | //! | ||
1247 | //! 1. It acts as a backlog of work that can be done to improve the behavior of the system. | ||
1248 | //! 2. It ensures the code doesn't panic when handling these cases. | ||
1249 | use super::*; | ||
1250 | |||
1251 | #[test] | ||
1252 | fn integers() { | ||
1253 | // We don't currently check integer exhaustiveness. | ||
1254 | check_diagnostics( | ||
1255 | r#" | ||
1256 | fn main() { | ||
1257 | match 5 { | ||
1258 | 10 => (), | ||
1259 | // ^^ Internal: match check bailed out | ||
1260 | 11..20 => (), | ||
1261 | } | ||
1262 | } | ||
1263 | "#, | ||
1264 | ); | ||
1265 | } | ||
1266 | |||
1267 | #[test] | ||
1268 | fn reference_patterns_at_top_level() { | ||
1269 | check_diagnostics( | ||
1270 | r#" | ||
1271 | fn main() { | ||
1272 | match &false { | ||
1273 | &true => {} | ||
1274 | // ^^^^^ Internal: match check bailed out | ||
1275 | } | ||
1276 | } | ||
1277 | "#, | ||
1278 | ); | ||
1279 | } | ||
1280 | |||
1281 | #[test] | ||
1282 | fn reference_patterns_in_fields() { | ||
1283 | check_diagnostics( | ||
1284 | r#" | ||
1285 | fn main() { | ||
1286 | match (&false,) { | ||
1287 | (true,) => {} | ||
1288 | // ^^^^^^^ Internal: match check bailed out | ||
1289 | } | ||
1290 | match (&false,) { | ||
1291 | (&true,) => {} | ||
1292 | // ^^^^^^^^ Internal: match check bailed out | ||
1293 | } | ||
1294 | } | ||
1295 | "#, | ||
1296 | ); | ||
1297 | } | ||
1298 | } | ||
1299 | } | ||
diff --git a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs index 1f4219b42..471cd4921 100644 --- a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs +++ b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs | |||
@@ -528,7 +528,7 @@ impl SplitWildcard { | |||
528 | smallvec![NonExhaustive] | 528 | smallvec![NonExhaustive] |
529 | } | 529 | } |
530 | TyKind::Never => SmallVec::new(), | 530 | TyKind::Never => SmallVec::new(), |
531 | _ if cx.is_uninhabited(&pcx.ty) => SmallVec::new(), | 531 | _ if cx.is_uninhabited(pcx.ty) => SmallVec::new(), |
532 | TyKind::Adt(..) | TyKind::Tuple(..) | TyKind::Ref(..) => smallvec![Single], | 532 | TyKind::Adt(..) | TyKind::Tuple(..) | TyKind::Ref(..) => smallvec![Single], |
533 | // This type is one for which we cannot list constructors, like `str` or `f64`. | 533 | // This type is one for which we cannot list constructors, like `str` or `f64`. |
534 | _ => smallvec![NonExhaustive], | 534 | _ => smallvec![NonExhaustive], |
diff --git a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs index 83b094a89..8451f9df5 100644 --- a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs +++ b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | //! Based on rust-lang/rust 1.52.0-nightly (25c15cdbe 2021-04-22) | 1 | //! Based on rust-lang/rust 1.52.0-nightly (25c15cdbe 2021-04-22) |
2 | //! https://github.com/rust-lang/rust/blob/25c15cdbe/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs | 2 | //! <https://github.com/rust-lang/rust/blob/25c15cdbe/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs> |
3 | //! | 3 | //! |
4 | //! ----- | 4 | //! ----- |
5 | //! | 5 | //! |
@@ -295,7 +295,7 @@ pub(crate) struct MatchCheckCtx<'a> { | |||
295 | pub(crate) db: &'a dyn HirDatabase, | 295 | pub(crate) db: &'a dyn HirDatabase, |
296 | /// Lowered patterns from arms plus generated by the check. | 296 | /// Lowered patterns from arms plus generated by the check. |
297 | pub(crate) pattern_arena: &'a RefCell<PatternArena>, | 297 | pub(crate) pattern_arena: &'a RefCell<PatternArena>, |
298 | pub(crate) eprint_panic_context: &'a dyn Fn(), | 298 | pub(crate) panic_context: &'a dyn Fn() -> String, |
299 | } | 299 | } |
300 | 300 | ||
301 | impl<'a> MatchCheckCtx<'a> { | 301 | impl<'a> MatchCheckCtx<'a> { |
@@ -331,8 +331,7 @@ impl<'a> MatchCheckCtx<'a> { | |||
331 | 331 | ||
332 | #[track_caller] | 332 | #[track_caller] |
333 | pub(super) fn bug(&self, info: &str) -> ! { | 333 | pub(super) fn bug(&self, info: &str) -> ! { |
334 | (self.eprint_panic_context)(); | 334 | panic!("bug: {}\n{}", info, (self.panic_context)()); |
335 | panic!("bug: {}", info); | ||
336 | } | 335 | } |
337 | } | 336 | } |
338 | 337 | ||
@@ -646,7 +645,7 @@ impl SubPatSet { | |||
646 | (Seq { subpats: s_set }, Seq { subpats: mut o_set }) => { | 645 | (Seq { subpats: s_set }, Seq { subpats: mut o_set }) => { |
647 | s_set.retain(|i, s_sub_set| { | 646 | s_set.retain(|i, s_sub_set| { |
648 | // Missing entries count as full. | 647 | // Missing entries count as full. |
649 | let o_sub_set = o_set.remove(&i).unwrap_or(Full); | 648 | let o_sub_set = o_set.remove(i).unwrap_or(Full); |
650 | s_sub_set.union(o_sub_set); | 649 | s_sub_set.union(o_sub_set); |
651 | // We drop full entries. | 650 | // We drop full entries. |
652 | !s_sub_set.is_full() | 651 | !s_sub_set.is_full() |
@@ -657,7 +656,7 @@ impl SubPatSet { | |||
657 | (Alt { subpats: s_set, .. }, Alt { subpats: mut o_set, .. }) => { | 656 | (Alt { subpats: s_set, .. }, Alt { subpats: mut o_set, .. }) => { |
658 | s_set.retain(|i, s_sub_set| { | 657 | s_set.retain(|i, s_sub_set| { |
659 | // Missing entries count as empty. | 658 | // Missing entries count as empty. |
660 | let o_sub_set = o_set.remove(&i).unwrap_or(Empty); | 659 | let o_sub_set = o_set.remove(i).unwrap_or(Empty); |
661 | s_sub_set.union(o_sub_set); | 660 | s_sub_set.union(o_sub_set); |
662 | // We drop empty entries. | 661 | // We drop empty entries. |
663 | !s_sub_set.is_empty() | 662 | !s_sub_set.is_empty() |
@@ -899,7 +898,7 @@ impl Usefulness { | |||
899 | } else { | 898 | } else { |
900 | witnesses | 899 | witnesses |
901 | .into_iter() | 900 | .into_iter() |
902 | .map(|witness| witness.apply_constructor(pcx, &ctor, ctor_wild_subpatterns)) | 901 | .map(|witness| witness.apply_constructor(pcx, ctor, ctor_wild_subpatterns)) |
903 | .collect() | 902 | .collect() |
904 | }; | 903 | }; |
905 | WithWitnesses(new_witnesses) | 904 | WithWitnesses(new_witnesses) |
diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs index c3c483425..777f347b8 100644 --- a/crates/hir_ty/src/diagnostics/unsafe_check.rs +++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs | |||
@@ -1,8 +1,6 @@ | |||
1 | //! Provides validations for unsafe code. Currently checks if unsafe functions are missing | 1 | //! Provides validations for unsafe code. Currently checks if unsafe functions are missing |
2 | //! unsafe blocks. | 2 | //! unsafe blocks. |
3 | 3 | ||
4 | use std::sync::Arc; | ||
5 | |||
6 | use hir_def::{ | 4 | use hir_def::{ |
7 | body::Body, | 5 | body::Body, |
8 | expr::{Expr, ExprId, UnaryOp}, | 6 | expr::{Expr, ExprId, UnaryOp}, |
@@ -10,60 +8,32 @@ use hir_def::{ | |||
10 | DefWithBodyId, | 8 | DefWithBodyId, |
11 | }; | 9 | }; |
12 | 10 | ||
13 | use crate::{ | 11 | use crate::{db::HirDatabase, InferenceResult, Interner, TyExt, TyKind}; |
14 | db::HirDatabase, diagnostics::MissingUnsafe, diagnostics_sink::DiagnosticSink, InferenceResult, | ||
15 | Interner, TyExt, TyKind, | ||
16 | }; | ||
17 | 12 | ||
18 | pub(super) struct UnsafeValidator<'a, 'b: 'a> { | 13 | pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> { |
19 | owner: DefWithBodyId, | 14 | let infer = db.infer(def); |
20 | infer: Arc<InferenceResult>, | ||
21 | sink: &'a mut DiagnosticSink<'b>, | ||
22 | } | ||
23 | 15 | ||
24 | impl<'a, 'b> UnsafeValidator<'a, 'b> { | 16 | let is_unsafe = match def { |
25 | pub(super) fn new( | 17 | DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(), |
26 | owner: DefWithBodyId, | 18 | DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false, |
27 | infer: Arc<InferenceResult>, | 19 | }; |
28 | sink: &'a mut DiagnosticSink<'b>, | 20 | if is_unsafe { |
29 | ) -> UnsafeValidator<'a, 'b> { | 21 | return Vec::new(); |
30 | UnsafeValidator { owner, infer, sink } | ||
31 | } | 22 | } |
32 | 23 | ||
33 | pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { | 24 | unsafe_expressions(db, &infer, def) |
34 | let def = self.owner; | 25 | .into_iter() |
35 | let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def); | 26 | .filter(|it| !it.inside_unsafe_block) |
36 | let is_unsafe = match self.owner { | 27 | .map(|it| it.expr) |
37 | DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(), | 28 | .collect() |
38 | DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false, | ||
39 | }; | ||
40 | if is_unsafe | ||
41 | || unsafe_expressions | ||
42 | .iter() | ||
43 | .filter(|unsafe_expr| !unsafe_expr.inside_unsafe_block) | ||
44 | .count() | ||
45 | == 0 | ||
46 | { | ||
47 | return; | ||
48 | } | ||
49 | |||
50 | let (_, body_source) = db.body_with_source_map(def); | ||
51 | for unsafe_expr in unsafe_expressions { | ||
52 | if !unsafe_expr.inside_unsafe_block { | ||
53 | if let Ok(in_file) = body_source.as_ref().expr_syntax(unsafe_expr.expr) { | ||
54 | self.sink.push(MissingUnsafe { file: in_file.file_id, expr: in_file.value }) | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | } | 29 | } |
60 | 30 | ||
61 | pub(crate) struct UnsafeExpr { | 31 | struct UnsafeExpr { |
62 | pub(crate) expr: ExprId, | 32 | pub(crate) expr: ExprId, |
63 | pub(crate) inside_unsafe_block: bool, | 33 | pub(crate) inside_unsafe_block: bool, |
64 | } | 34 | } |
65 | 35 | ||
66 | pub(crate) fn unsafe_expressions( | 36 | fn unsafe_expressions( |
67 | db: &dyn HirDatabase, | 37 | db: &dyn HirDatabase, |
68 | infer: &InferenceResult, | 38 | infer: &InferenceResult, |
69 | def: DefWithBodyId, | 39 | def: DefWithBodyId, |
@@ -126,92 +96,3 @@ fn walk_unsafe( | |||
126 | walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block); | 96 | walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block); |
127 | }); | 97 | }); |
128 | } | 98 | } |
129 | |||
130 | #[cfg(test)] | ||
131 | mod tests { | ||
132 | use crate::diagnostics::tests::check_diagnostics; | ||
133 | |||
134 | #[test] | ||
135 | fn missing_unsafe_diagnostic_with_raw_ptr() { | ||
136 | check_diagnostics( | ||
137 | r#" | ||
138 | fn main() { | ||
139 | let x = &5 as *const usize; | ||
140 | unsafe { let y = *x; } | ||
141 | let z = *x; | ||
142 | } //^^ This operation is unsafe and requires an unsafe function or block | ||
143 | "#, | ||
144 | ) | ||
145 | } | ||
146 | |||
147 | #[test] | ||
148 | fn missing_unsafe_diagnostic_with_unsafe_call() { | ||
149 | check_diagnostics( | ||
150 | r#" | ||
151 | struct HasUnsafe; | ||
152 | |||
153 | impl HasUnsafe { | ||
154 | unsafe fn unsafe_fn(&self) { | ||
155 | let x = &5 as *const usize; | ||
156 | let y = *x; | ||
157 | } | ||
158 | } | ||
159 | |||
160 | unsafe fn unsafe_fn() { | ||
161 | let x = &5 as *const usize; | ||
162 | let y = *x; | ||
163 | } | ||
164 | |||
165 | fn main() { | ||
166 | unsafe_fn(); | ||
167 | //^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block | ||
168 | HasUnsafe.unsafe_fn(); | ||
169 | //^^^^^^^^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block | ||
170 | unsafe { | ||
171 | unsafe_fn(); | ||
172 | HasUnsafe.unsafe_fn(); | ||
173 | } | ||
174 | } | ||
175 | "#, | ||
176 | ); | ||
177 | } | ||
178 | |||
179 | #[test] | ||
180 | fn missing_unsafe_diagnostic_with_static_mut() { | ||
181 | check_diagnostics( | ||
182 | r#" | ||
183 | struct Ty { | ||
184 | a: u8, | ||
185 | } | ||
186 | |||
187 | static mut STATIC_MUT: Ty = Ty { a: 0 }; | ||
188 | |||
189 | fn main() { | ||
190 | let x = STATIC_MUT.a; | ||
191 | //^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block | ||
192 | unsafe { | ||
193 | let x = STATIC_MUT.a; | ||
194 | } | ||
195 | } | ||
196 | "#, | ||
197 | ); | ||
198 | } | ||
199 | |||
200 | #[test] | ||
201 | fn no_missing_unsafe_diagnostic_with_safe_intrinsic() { | ||
202 | check_diagnostics( | ||
203 | r#" | ||
204 | extern "rust-intrinsic" { | ||
205 | pub fn bitreverse(x: u32) -> u32; // Safe intrinsic | ||
206 | pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic | ||
207 | } | ||
208 | |||
209 | fn main() { | ||
210 | let _ = bitreverse(12); | ||
211 | let _ = floorf32(12.0); | ||
212 | //^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block | ||
213 | } | ||
214 | "#, | ||
215 | ); | ||
216 | } | ||
217 | } | ||
diff --git a/crates/hir_ty/src/diagnostics_sink.rs b/crates/hir_ty/src/diagnostics_sink.rs deleted file mode 100644 index 084fa8b06..000000000 --- a/crates/hir_ty/src/diagnostics_sink.rs +++ /dev/null | |||
@@ -1,109 +0,0 @@ | |||
1 | //! Semantic errors and warnings. | ||
2 | //! | ||
3 | //! The `Diagnostic` trait defines a trait object which can represent any | ||
4 | //! diagnostic. | ||
5 | //! | ||
6 | //! `DiagnosticSink` struct is used as an emitter for diagnostic. When creating | ||
7 | //! a `DiagnosticSink`, you supply a callback which can react to a `dyn | ||
8 | //! Diagnostic` or to any concrete diagnostic (downcasting is used internally). | ||
9 | //! | ||
10 | //! Because diagnostics store file offsets, it's a bad idea to store them | ||
11 | //! directly in salsa. For this reason, every hir subsytem defines it's own | ||
12 | //! strongly-typed closed set of diagnostics which use hir ids internally, are | ||
13 | //! stored in salsa and do *not* implement the `Diagnostic` trait. Instead, a | ||
14 | //! subsystem provides a separate, non-query-based API which can walk all stored | ||
15 | //! values and transform them into instances of `Diagnostic`. | ||
16 | |||
17 | use std::{any::Any, fmt}; | ||
18 | |||
19 | use hir_expand::InFile; | ||
20 | use syntax::SyntaxNodePtr; | ||
21 | |||
22 | #[derive(Copy, Clone, Debug, PartialEq)] | ||
23 | pub struct DiagnosticCode(pub &'static str); | ||
24 | |||
25 | impl DiagnosticCode { | ||
26 | pub fn as_str(&self) -> &str { | ||
27 | self.0 | ||
28 | } | ||
29 | } | ||
30 | |||
31 | pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { | ||
32 | fn code(&self) -> DiagnosticCode; | ||
33 | fn message(&self) -> String; | ||
34 | /// Source element that triggered the diagnostics. | ||
35 | /// | ||
36 | /// Note that this should reflect "semantics", rather than specific span we | ||
37 | /// want to highlight. When rendering the diagnostics into an error message, | ||
38 | /// the IDE will fetch the `SyntaxNode` and will narrow the span | ||
39 | /// appropriately. | ||
40 | fn display_source(&self) -> InFile<SyntaxNodePtr>; | ||
41 | fn as_any(&self) -> &(dyn Any + Send + 'static); | ||
42 | fn is_experimental(&self) -> bool { | ||
43 | false | ||
44 | } | ||
45 | } | ||
46 | |||
47 | pub struct DiagnosticSink<'a> { | ||
48 | callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>, | ||
49 | filters: Vec<Box<dyn FnMut(&dyn Diagnostic) -> bool + 'a>>, | ||
50 | default_callback: Box<dyn FnMut(&dyn Diagnostic) + 'a>, | ||
51 | } | ||
52 | |||
53 | impl<'a> DiagnosticSink<'a> { | ||
54 | pub fn push(&mut self, d: impl Diagnostic) { | ||
55 | let d: &dyn Diagnostic = &d; | ||
56 | self._push(d); | ||
57 | } | ||
58 | |||
59 | fn _push(&mut self, d: &dyn Diagnostic) { | ||
60 | for filter in &mut self.filters { | ||
61 | if !filter(d) { | ||
62 | return; | ||
63 | } | ||
64 | } | ||
65 | for cb in &mut self.callbacks { | ||
66 | match cb(d) { | ||
67 | Ok(()) => return, | ||
68 | Err(()) => (), | ||
69 | } | ||
70 | } | ||
71 | (self.default_callback)(d) | ||
72 | } | ||
73 | } | ||
74 | |||
75 | pub struct DiagnosticSinkBuilder<'a> { | ||
76 | callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>, | ||
77 | filters: Vec<Box<dyn FnMut(&dyn Diagnostic) -> bool + 'a>>, | ||
78 | } | ||
79 | |||
80 | impl<'a> DiagnosticSinkBuilder<'a> { | ||
81 | pub fn new() -> Self { | ||
82 | Self { callbacks: Vec::new(), filters: Vec::new() } | ||
83 | } | ||
84 | |||
85 | pub fn filter<F: FnMut(&dyn Diagnostic) -> bool + 'a>(mut self, cb: F) -> Self { | ||
86 | self.filters.push(Box::new(cb)); | ||
87 | self | ||
88 | } | ||
89 | |||
90 | pub fn on<D: Diagnostic, F: FnMut(&D) + 'a>(mut self, mut cb: F) -> Self { | ||
91 | let cb = move |diag: &dyn Diagnostic| match diag.as_any().downcast_ref::<D>() { | ||
92 | Some(d) => { | ||
93 | cb(d); | ||
94 | Ok(()) | ||
95 | } | ||
96 | None => Err(()), | ||
97 | }; | ||
98 | self.callbacks.push(Box::new(cb)); | ||
99 | self | ||
100 | } | ||
101 | |||
102 | pub fn build<F: FnMut(&dyn Diagnostic) + 'a>(self, default_callback: F) -> DiagnosticSink<'a> { | ||
103 | DiagnosticSink { | ||
104 | callbacks: self.callbacks, | ||
105 | filters: self.filters, | ||
106 | default_callback: Box::new(default_callback), | ||
107 | } | ||
108 | } | ||
109 | } | ||
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 7a4268819..63f37c0ab 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs | |||
@@ -35,11 +35,9 @@ use stdx::impl_from; | |||
35 | use syntax::SmolStr; | 35 | use syntax::SmolStr; |
36 | 36 | ||
37 | use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; | 37 | use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; |
38 | use crate::diagnostics_sink::DiagnosticSink; | ||
39 | use crate::{ | 38 | use crate::{ |
40 | db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic, | 39 | db::HirDatabase, fold_tys, lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, |
41 | lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution, | 40 | Goal, Interner, Substitution, TyBuilder, TyExt, TyKind, |
42 | TyBuilder, TyExt, TyKind, | ||
43 | }; | 41 | }; |
44 | 42 | ||
45 | // This lint has a false positive here. See the link below for details. | 43 | // This lint has a false positive here. See the link below for details. |
@@ -80,7 +78,7 @@ enum ExprOrPatId { | |||
80 | impl_from!(ExprId, PatId for ExprOrPatId); | 78 | impl_from!(ExprId, PatId for ExprOrPatId); |
81 | 79 | ||
82 | /// Binding modes inferred for patterns. | 80 | /// Binding modes inferred for patterns. |
83 | /// https://doc.rust-lang.org/reference/patterns.html#binding-modes | 81 | /// <https://doc.rust-lang.org/reference/patterns.html#binding-modes> |
84 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] | 82 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
85 | enum BindingMode { | 83 | enum BindingMode { |
86 | Move, | 84 | Move, |
@@ -111,6 +109,12 @@ pub(crate) struct InferOk { | |||
111 | pub(crate) struct TypeError; | 109 | pub(crate) struct TypeError; |
112 | pub(crate) type InferResult = Result<InferOk, TypeError>; | 110 | pub(crate) type InferResult = Result<InferOk, TypeError>; |
113 | 111 | ||
112 | #[derive(Debug, PartialEq, Eq, Clone)] | ||
113 | pub enum InferenceDiagnostic { | ||
114 | NoSuchField { expr: ExprId }, | ||
115 | BreakOutsideOfLoop { expr: ExprId }, | ||
116 | } | ||
117 | |||
114 | /// A mismatch between an expected and an inferred type. | 118 | /// A mismatch between an expected and an inferred type. |
115 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | 119 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] |
116 | pub struct TypeMismatch { | 120 | pub struct TypeMismatch { |
@@ -140,7 +144,7 @@ pub struct InferenceResult { | |||
140 | variant_resolutions: FxHashMap<ExprOrPatId, VariantId>, | 144 | variant_resolutions: FxHashMap<ExprOrPatId, VariantId>, |
141 | /// For each associated item record what it resolves to | 145 | /// For each associated item record what it resolves to |
142 | assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>, | 146 | assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>, |
143 | diagnostics: Vec<InferenceDiagnostic>, | 147 | pub diagnostics: Vec<InferenceDiagnostic>, |
144 | pub type_of_expr: ArenaMap<ExprId, Ty>, | 148 | pub type_of_expr: ArenaMap<ExprId, Ty>, |
145 | /// For each pattern record the type it resolves to. | 149 | /// For each pattern record the type it resolves to. |
146 | /// | 150 | /// |
@@ -150,6 +154,8 @@ pub struct InferenceResult { | |||
150 | type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>, | 154 | type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>, |
151 | /// Interned Unknown to return references to. | 155 | /// Interned Unknown to return references to. |
152 | standard_types: InternedStandardTypes, | 156 | standard_types: InternedStandardTypes, |
157 | /// Stores the types which were implicitly dereferenced in pattern binding modes. | ||
158 | pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>, | ||
153 | } | 159 | } |
154 | 160 | ||
155 | impl InferenceResult { | 161 | impl InferenceResult { |
@@ -189,14 +195,6 @@ impl InferenceResult { | |||
189 | _ => None, | 195 | _ => None, |
190 | }) | 196 | }) |
191 | } | 197 | } |
192 | pub fn add_diagnostics( | ||
193 | &self, | ||
194 | db: &dyn HirDatabase, | ||
195 | owner: DefWithBodyId, | ||
196 | sink: &mut DiagnosticSink, | ||
197 | ) { | ||
198 | self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink)) | ||
199 | } | ||
200 | } | 198 | } |
201 | 199 | ||
202 | impl Index<ExprId> for InferenceResult { | 200 | impl Index<ExprId> for InferenceResult { |
@@ -763,6 +761,38 @@ impl Expectation { | |||
763 | Expectation::RValueLikeUnsized(_) | Expectation::None => None, | 761 | Expectation::RValueLikeUnsized(_) | Expectation::None => None, |
764 | } | 762 | } |
765 | } | 763 | } |
764 | |||
765 | /// Comment copied from rustc: | ||
766 | /// Disregard "castable to" expectations because they | ||
767 | /// can lead us astray. Consider for example `if cond | ||
768 | /// {22} else {c} as u8` -- if we propagate the | ||
769 | /// "castable to u8" constraint to 22, it will pick the | ||
770 | /// type 22u8, which is overly constrained (c might not | ||
771 | /// be a u8). In effect, the problem is that the | ||
772 | /// "castable to" expectation is not the tightest thing | ||
773 | /// we can say, so we want to drop it in this case. | ||
774 | /// The tightest thing we can say is "must unify with | ||
775 | /// else branch". Note that in the case of a "has type" | ||
776 | /// constraint, this limitation does not hold. | ||
777 | /// | ||
778 | /// If the expected type is just a type variable, then don't use | ||
779 | /// an expected type. Otherwise, we might write parts of the type | ||
780 | /// when checking the 'then' block which are incompatible with the | ||
781 | /// 'else' branch. | ||
782 | fn adjust_for_branches(&self, table: &mut unify::InferenceTable) -> Expectation { | ||
783 | match self { | ||
784 | Expectation::HasType(ety) => { | ||
785 | let ety = table.resolve_ty_shallow(ety); | ||
786 | if !ety.is_ty_var() { | ||
787 | Expectation::HasType(ety) | ||
788 | } else { | ||
789 | Expectation::None | ||
790 | } | ||
791 | } | ||
792 | Expectation::RValueLikeUnsized(ety) => Expectation::RValueLikeUnsized(ety.clone()), | ||
793 | _ => Expectation::None, | ||
794 | } | ||
795 | } | ||
766 | } | 796 | } |
767 | 797 | ||
768 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] | 798 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] |
@@ -802,43 +832,3 @@ impl std::ops::BitOrAssign for Diverges { | |||
802 | *self = *self | other; | 832 | *self = *self | other; |
803 | } | 833 | } |
804 | } | 834 | } |
805 | |||
806 | mod diagnostics { | ||
807 | use hir_def::{expr::ExprId, DefWithBodyId}; | ||
808 | |||
809 | use crate::{ | ||
810 | db::HirDatabase, | ||
811 | diagnostics::{BreakOutsideOfLoop, NoSuchField}, | ||
812 | diagnostics_sink::DiagnosticSink, | ||
813 | }; | ||
814 | |||
815 | #[derive(Debug, PartialEq, Eq, Clone)] | ||
816 | pub(super) enum InferenceDiagnostic { | ||
817 | NoSuchField { expr: ExprId }, | ||
818 | BreakOutsideOfLoop { expr: ExprId }, | ||
819 | } | ||
820 | |||
821 | impl InferenceDiagnostic { | ||
822 | pub(super) fn add_to( | ||
823 | &self, | ||
824 | db: &dyn HirDatabase, | ||
825 | owner: DefWithBodyId, | ||
826 | sink: &mut DiagnosticSink, | ||
827 | ) { | ||
828 | match self { | ||
829 | InferenceDiagnostic::NoSuchField { expr } => { | ||
830 | let (_, source_map) = db.body_with_source_map(owner); | ||
831 | let field = source_map.field_syntax(*expr); | ||
832 | sink.push(NoSuchField { file: field.file_id, field: field.value }) | ||
833 | } | ||
834 | InferenceDiagnostic::BreakOutsideOfLoop { expr } => { | ||
835 | let (_, source_map) = db.body_with_source_map(owner); | ||
836 | let ptr = source_map | ||
837 | .expr_syntax(*expr) | ||
838 | .expect("break outside of loop in synthetic syntax"); | ||
839 | sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value }) | ||
840 | } | ||
841 | } | ||
842 | } | ||
843 | } | ||
844 | } | ||
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 03b97e7db..4b7f31521 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs | |||
@@ -2,8 +2,8 @@ | |||
2 | //! happen in certain places, e.g. weakening `&mut` to `&` or deref coercions | 2 | //! happen in certain places, e.g. weakening `&mut` to `&` or deref coercions |
3 | //! like going from `&Vec<T>` to `&[T]`. | 3 | //! like going from `&Vec<T>` to `&[T]`. |
4 | //! | 4 | //! |
5 | //! See https://doc.rust-lang.org/nomicon/coercions.html and | 5 | //! See <https://doc.rust-lang.org/nomicon/coercions.html> and |
6 | //! librustc_typeck/check/coercion.rs. | 6 | //! `librustc_typeck/check/coercion.rs`. |
7 | 7 | ||
8 | use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; | 8 | use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; |
9 | use hir_def::{expr::ExprId, lang_item::LangItemTarget}; | 9 | use hir_def::{expr::ExprId, lang_item::LangItemTarget}; |
@@ -109,7 +109,7 @@ impl<'a> InferenceContext<'a> { | |||
109 | } | 109 | } |
110 | 110 | ||
111 | // Consider coercing the subtype to a DST | 111 | // Consider coercing the subtype to a DST |
112 | if let Ok(ret) = self.try_coerce_unsized(&from_ty, &to_ty) { | 112 | if let Ok(ret) = self.try_coerce_unsized(&from_ty, to_ty) { |
113 | return Ok(ret); | 113 | return Ok(ret); |
114 | } | 114 | } |
115 | 115 | ||
@@ -331,7 +331,7 @@ impl<'a> InferenceContext<'a> { | |||
331 | 331 | ||
332 | /// Coerce a type using `from_ty: CoerceUnsized<ty_ty>` | 332 | /// Coerce a type using `from_ty: CoerceUnsized<ty_ty>` |
333 | /// | 333 | /// |
334 | /// See: https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html | 334 | /// See: <https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html> |
335 | fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> InferResult { | 335 | fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> InferResult { |
336 | // These 'if' statements require some explanation. | 336 | // These 'if' statements require some explanation. |
337 | // The `CoerceUnsized` trait is special - it is only | 337 | // The `CoerceUnsized` trait is special - it is only |
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index f73bf43b2..5ea2e5934 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs | |||
@@ -54,7 +54,7 @@ impl<'a> InferenceContext<'a> { | |||
54 | /// Infer type of expression with possibly implicit coerce to the expected type. | 54 | /// Infer type of expression with possibly implicit coerce to the expected type. |
55 | /// Return the type after possible coercion. | 55 | /// Return the type after possible coercion. |
56 | pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty { | 56 | pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty { |
57 | let ty = self.infer_expr_inner(expr, &expected); | 57 | let ty = self.infer_expr_inner(expr, expected); |
58 | let ty = if let Some(target) = expected.only_has_type(&mut self.table) { | 58 | let ty = if let Some(target) = expected.only_has_type(&mut self.table) { |
59 | if !self.coerce(&ty, &target) { | 59 | if !self.coerce(&ty, &target) { |
60 | self.result | 60 | self.result |
@@ -135,11 +135,11 @@ impl<'a> InferenceContext<'a> { | |||
135 | let mut both_arms_diverge = Diverges::Always; | 135 | let mut both_arms_diverge = Diverges::Always; |
136 | 136 | ||
137 | let mut result_ty = self.table.new_type_var(); | 137 | let mut result_ty = self.table.new_type_var(); |
138 | let then_ty = self.infer_expr_inner(*then_branch, &expected); | 138 | let then_ty = self.infer_expr_inner(*then_branch, expected); |
139 | both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe); | 139 | both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe); |
140 | result_ty = self.coerce_merge_branch(Some(*then_branch), &result_ty, &then_ty); | 140 | result_ty = self.coerce_merge_branch(Some(*then_branch), &result_ty, &then_ty); |
141 | let else_ty = match else_branch { | 141 | let else_ty = match else_branch { |
142 | Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), | 142 | Some(else_branch) => self.infer_expr_inner(*else_branch, expected), |
143 | None => TyBuilder::unit(), | 143 | None => TyBuilder::unit(), |
144 | }; | 144 | }; |
145 | both_arms_diverge &= self.diverges; | 145 | both_arms_diverge &= self.diverges; |
@@ -327,20 +327,19 @@ impl<'a> InferenceContext<'a> { | |||
327 | self.normalize_associated_types_in(ret_ty) | 327 | self.normalize_associated_types_in(ret_ty) |
328 | } | 328 | } |
329 | Expr::MethodCall { receiver, args, method_name, generic_args } => self | 329 | Expr::MethodCall { receiver, args, method_name, generic_args } => self |
330 | .infer_method_call( | 330 | .infer_method_call(tgt_expr, *receiver, args, method_name, generic_args.as_deref()), |
331 | tgt_expr, | ||
332 | *receiver, | ||
333 | &args, | ||
334 | &method_name, | ||
335 | generic_args.as_deref(), | ||
336 | ), | ||
337 | Expr::Match { expr, arms } => { | 331 | Expr::Match { expr, arms } => { |
338 | let input_ty = self.infer_expr(*expr, &Expectation::none()); | 332 | let input_ty = self.infer_expr(*expr, &Expectation::none()); |
339 | 333 | ||
334 | let expected = expected.adjust_for_branches(&mut self.table); | ||
335 | |||
340 | let mut result_ty = if arms.is_empty() { | 336 | let mut result_ty = if arms.is_empty() { |
341 | TyKind::Never.intern(&Interner) | 337 | TyKind::Never.intern(&Interner) |
342 | } else { | 338 | } else { |
343 | self.table.new_type_var() | 339 | match &expected { |
340 | Expectation::HasType(ty) => ty.clone(), | ||
341 | _ => self.table.new_type_var(), | ||
342 | } | ||
344 | }; | 343 | }; |
345 | 344 | ||
346 | let matchee_diverges = self.diverges; | 345 | let matchee_diverges = self.diverges; |
@@ -988,7 +987,7 @@ impl<'a> InferenceContext<'a> { | |||
988 | } | 987 | } |
989 | 988 | ||
990 | fn register_obligations_for_call(&mut self, callable_ty: &Ty) { | 989 | fn register_obligations_for_call(&mut self, callable_ty: &Ty) { |
991 | let callable_ty = self.resolve_ty_shallow(&callable_ty); | 990 | let callable_ty = self.resolve_ty_shallow(callable_ty); |
992 | if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind(&Interner) { | 991 | if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind(&Interner) { |
993 | let def: CallableDefId = from_chalk(self.db, *fn_def); | 992 | let def: CallableDefId = from_chalk(self.db, *fn_def); |
994 | let generic_predicates = self.db.generic_predicates(def.into()); | 993 | let generic_predicates = self.db.generic_predicates(def.into()); |
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs index 83e0a7a9e..035f4ded6 100644 --- a/crates/hir_ty/src/infer/pat.rs +++ b/crates/hir_ty/src/infer/pat.rs | |||
@@ -101,7 +101,9 @@ impl<'a> InferenceContext<'a> { | |||
101 | let mut expected = self.resolve_ty_shallow(expected); | 101 | let mut expected = self.resolve_ty_shallow(expected); |
102 | 102 | ||
103 | if is_non_ref_pat(&body, pat) { | 103 | if is_non_ref_pat(&body, pat) { |
104 | let mut pat_adjustments = Vec::new(); | ||
104 | while let Some((inner, _lifetime, mutability)) = expected.as_reference() { | 105 | while let Some((inner, _lifetime, mutability)) = expected.as_reference() { |
106 | pat_adjustments.push(expected.clone()); | ||
105 | expected = self.resolve_ty_shallow(inner); | 107 | expected = self.resolve_ty_shallow(inner); |
106 | default_bm = match default_bm { | 108 | default_bm = match default_bm { |
107 | BindingMode::Move => BindingMode::Ref(mutability), | 109 | BindingMode::Move => BindingMode::Ref(mutability), |
@@ -109,6 +111,11 @@ impl<'a> InferenceContext<'a> { | |||
109 | BindingMode::Ref(Mutability::Mut) => BindingMode::Ref(mutability), | 111 | BindingMode::Ref(Mutability::Mut) => BindingMode::Ref(mutability), |
110 | } | 112 | } |
111 | } | 113 | } |
114 | |||
115 | if !pat_adjustments.is_empty() { | ||
116 | pat_adjustments.shrink_to_fit(); | ||
117 | self.result.pat_adjustments.insert(pat, pat_adjustments); | ||
118 | } | ||
112 | } else if let Pat::Ref { .. } = &body[pat] { | 119 | } else if let Pat::Ref { .. } = &body[pat] { |
113 | cov_mark::hit!(match_ergonomics_ref); | 120 | cov_mark::hit!(match_ergonomics_ref); |
114 | // When you encounter a `&pat` pattern, reset to Move. | 121 | // When you encounter a `&pat` pattern, reset to Move. |
@@ -185,7 +192,7 @@ impl<'a> InferenceContext<'a> { | |||
185 | Pat::Path(path) => { | 192 | Pat::Path(path) => { |
186 | // FIXME use correct resolver for the surrounding expression | 193 | // FIXME use correct resolver for the surrounding expression |
187 | let resolver = self.resolver.clone(); | 194 | let resolver = self.resolver.clone(); |
188 | self.infer_path(&resolver, &path, pat.into()).unwrap_or(self.err_ty()) | 195 | self.infer_path(&resolver, path, pat.into()).unwrap_or(self.err_ty()) |
189 | } | 196 | } |
190 | Pat::Bind { mode, name: _, subpat } => { | 197 | Pat::Bind { mode, name: _, subpat } => { |
191 | let mode = if mode == &BindingAnnotation::Unannotated { | 198 | let mode = if mode == &BindingAnnotation::Unannotated { |
@@ -268,7 +275,7 @@ impl<'a> InferenceContext<'a> { | |||
268 | if !self.unify(&ty, &expected) { | 275 | if !self.unify(&ty, &expected) { |
269 | self.result | 276 | self.result |
270 | .type_mismatches | 277 | .type_mismatches |
271 | .insert(pat.into(), TypeMismatch { expected: expected, actual: ty.clone() }); | 278 | .insert(pat.into(), TypeMismatch { expected, actual: ty.clone() }); |
272 | } | 279 | } |
273 | self.write_pat_ty(pat, ty.clone()); | 280 | self.write_pat_ty(pat, ty.clone()); |
274 | ty | 281 | ty |
@@ -290,6 +297,10 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool { | |||
290 | Expr::Literal(Literal::String(..)) => false, | 297 | Expr::Literal(Literal::String(..)) => false, |
291 | _ => true, | 298 | _ => true, |
292 | }, | 299 | }, |
300 | Pat::Bind { mode: BindingAnnotation::Mutable, subpat: Some(subpat), .. } | ||
301 | | Pat::Bind { mode: BindingAnnotation::Unannotated, subpat: Some(subpat), .. } => { | ||
302 | is_non_ref_pat(body, *subpat) | ||
303 | } | ||
293 | Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false, | 304 | Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false, |
294 | } | 305 | } |
295 | } | 306 | } |
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs index 14c99eafd..056cdb5d5 100644 --- a/crates/hir_ty/src/infer/path.rs +++ b/crates/hir_ty/src/infer/path.rs | |||
@@ -43,11 +43,11 @@ impl<'a> InferenceContext<'a> { | |||
43 | } | 43 | } |
44 | let ty = self.make_ty(type_ref); | 44 | let ty = self.make_ty(type_ref); |
45 | let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1); | 45 | let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1); |
46 | let ctx = crate::lower::TyLoweringContext::new(self.db, &resolver); | 46 | let ctx = crate::lower::TyLoweringContext::new(self.db, resolver); |
47 | let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty); | 47 | let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty); |
48 | self.resolve_ty_assoc_item( | 48 | self.resolve_ty_assoc_item( |
49 | ty, | 49 | ty, |
50 | &path.segments().last().expect("path had at least one segment").name, | 50 | path.segments().last().expect("path had at least one segment").name, |
51 | id, | 51 | id, |
52 | )? | 52 | )? |
53 | } else { | 53 | } else { |
@@ -154,7 +154,7 @@ impl<'a> InferenceContext<'a> { | |||
154 | let segment = | 154 | let segment = |
155 | remaining_segments.last().expect("there should be at least one segment here"); | 155 | remaining_segments.last().expect("there should be at least one segment here"); |
156 | 156 | ||
157 | self.resolve_ty_assoc_item(ty, &segment.name, id) | 157 | self.resolve_ty_assoc_item(ty, segment.name, id) |
158 | } | 158 | } |
159 | } | 159 | } |
160 | } | 160 | } |
diff --git a/crates/hir_ty/src/interner.rs b/crates/hir_ty/src/interner.rs index 29ffdd9b7..5fef878e8 100644 --- a/crates/hir_ty/src/interner.rs +++ b/crates/hir_ty/src/interner.rs | |||
@@ -331,7 +331,7 @@ impl chalk_ir::interner::Interner for Interner { | |||
331 | &self, | 331 | &self, |
332 | clauses: &'a Self::InternedProgramClauses, | 332 | clauses: &'a Self::InternedProgramClauses, |
333 | ) -> &'a [chalk_ir::ProgramClause<Self>] { | 333 | ) -> &'a [chalk_ir::ProgramClause<Self>] { |
334 | &clauses | 334 | clauses |
335 | } | 335 | } |
336 | 336 | ||
337 | fn intern_quantified_where_clauses<E>( | 337 | fn intern_quantified_where_clauses<E>( |
@@ -373,7 +373,7 @@ impl chalk_ir::interner::Interner for Interner { | |||
373 | &self, | 373 | &self, |
374 | canonical_var_kinds: &'a Self::InternedCanonicalVarKinds, | 374 | canonical_var_kinds: &'a Self::InternedCanonicalVarKinds, |
375 | ) -> &'a [chalk_ir::CanonicalVarKind<Self>] { | 375 | ) -> &'a [chalk_ir::CanonicalVarKind<Self>] { |
376 | &canonical_var_kinds | 376 | canonical_var_kinds |
377 | } | 377 | } |
378 | 378 | ||
379 | fn intern_constraints<E>( | 379 | fn intern_constraints<E>( |
@@ -413,7 +413,7 @@ impl chalk_ir::interner::Interner for Interner { | |||
413 | &self, | 413 | &self, |
414 | variances: &'a Self::InternedVariances, | 414 | variances: &'a Self::InternedVariances, |
415 | ) -> &'a [chalk_ir::Variance] { | 415 | ) -> &'a [chalk_ir::Variance] { |
416 | &variances | 416 | variances |
417 | } | 417 | } |
418 | } | 418 | } |
419 | 419 | ||
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 50e0d6333..128cae830 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs | |||
@@ -21,7 +21,6 @@ mod utils; | |||
21 | mod walk; | 21 | mod walk; |
22 | pub mod db; | 22 | pub mod db; |
23 | pub mod diagnostics; | 23 | pub mod diagnostics; |
24 | pub mod diagnostics_sink; | ||
25 | pub mod display; | 24 | pub mod display; |
26 | pub mod method_resolution; | 25 | pub mod method_resolution; |
27 | pub mod primitive; | 26 | pub mod primitive; |
@@ -50,7 +49,7 @@ use crate::{db::HirDatabase, utils::generics}; | |||
50 | pub use autoderef::autoderef; | 49 | pub use autoderef::autoderef; |
51 | pub use builder::TyBuilder; | 50 | pub use builder::TyBuilder; |
52 | pub use chalk_ext::*; | 51 | pub use chalk_ext::*; |
53 | pub use infer::{could_unify, InferenceResult}; | 52 | pub use infer::{could_unify, InferenceDiagnostic, InferenceResult}; |
54 | pub use interner::Interner; | 53 | pub use interner::Interner; |
55 | pub use lower::{ | 54 | pub use lower::{ |
56 | associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode, | 55 | associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode, |
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index c83933c73..817a65c20 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs | |||
@@ -238,7 +238,7 @@ impl<'a> TyLoweringContext<'a> { | |||
238 | // away instead of two. | 238 | // away instead of two. |
239 | let actual_opaque_type_data = self | 239 | let actual_opaque_type_data = self |
240 | .with_debruijn(DebruijnIndex::INNERMOST, |ctx| { | 240 | .with_debruijn(DebruijnIndex::INNERMOST, |ctx| { |
241 | ctx.lower_impl_trait(&bounds) | 241 | ctx.lower_impl_trait(bounds) |
242 | }); | 242 | }); |
243 | self.opaque_type_data.borrow_mut()[idx as usize] = actual_opaque_type_data; | 243 | self.opaque_type_data.borrow_mut()[idx as usize] = actual_opaque_type_data; |
244 | 244 | ||
@@ -421,7 +421,7 @@ impl<'a> TyLoweringContext<'a> { | |||
421 | let found = self | 421 | let found = self |
422 | .db | 422 | .db |
423 | .trait_data(trait_ref.hir_trait_id()) | 423 | .trait_data(trait_ref.hir_trait_id()) |
424 | .associated_type_by_name(&segment.name); | 424 | .associated_type_by_name(segment.name); |
425 | match found { | 425 | match found { |
426 | Some(associated_ty) => { | 426 | Some(associated_ty) => { |
427 | // FIXME handle type parameters on the segment | 427 | // FIXME handle type parameters on the segment |
@@ -505,7 +505,7 @@ impl<'a> TyLoweringContext<'a> { | |||
505 | pub(crate) fn lower_path(&self, path: &Path) -> (Ty, Option<TypeNs>) { | 505 | pub(crate) fn lower_path(&self, path: &Path) -> (Ty, Option<TypeNs>) { |
506 | // Resolve the path (in type namespace) | 506 | // Resolve the path (in type namespace) |
507 | if let Some(type_ref) = path.type_anchor() { | 507 | if let Some(type_ref) = path.type_anchor() { |
508 | let (ty, res) = self.lower_ty_ext(&type_ref); | 508 | let (ty, res) = self.lower_ty_ext(type_ref); |
509 | return self.lower_ty_relative_path(ty, res, path.segments()); | 509 | return self.lower_ty_relative_path(ty, res, path.segments()); |
510 | } | 510 | } |
511 | let (resolution, remaining_index) = | 511 | let (resolution, remaining_index) = |
@@ -784,7 +784,7 @@ impl<'a> TyLoweringContext<'a> { | |||
784 | let trait_ref = match bound { | 784 | let trait_ref = match bound { |
785 | TypeBound::Path(path) => { | 785 | TypeBound::Path(path) => { |
786 | bindings = self.lower_trait_ref_from_path(path, Some(self_ty)); | 786 | bindings = self.lower_trait_ref_from_path(path, Some(self_ty)); |
787 | bindings.clone().map(WhereClause::Implemented).map(|b| crate::wrap_empty_binders(b)) | 787 | bindings.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders) |
788 | } | 788 | } |
789 | TypeBound::Lifetime(_) => None, | 789 | TypeBound::Lifetime(_) => None, |
790 | TypeBound::Error => None, | 790 | TypeBound::Error => None, |
@@ -957,7 +957,7 @@ pub(crate) fn field_types_query( | |||
957 | /// like `T::Item`. | 957 | /// like `T::Item`. |
958 | /// | 958 | /// |
959 | /// See the analogous query in rustc and its comment: | 959 | /// See the analogous query in rustc and its comment: |
960 | /// https://github.com/rust-lang/rust/blob/9150f844e2624eb013ec78ca08c1d416e6644026/src/librustc_typeck/astconv.rs#L46 | 960 | /// <https://github.com/rust-lang/rust/blob/9150f844e2624eb013ec78ca08c1d416e6644026/src/librustc_typeck/astconv.rs#L46> |
961 | /// This is a query mostly to handle cycles somewhat gracefully; e.g. the | 961 | /// This is a query mostly to handle cycles somewhat gracefully; e.g. the |
962 | /// following bounds are disallowed: `T: Foo<U::Item>, U: Foo<T::Item>`, but | 962 | /// following bounds are disallowed: `T: Foo<U::Item>, U: Foo<T::Item>`, but |
963 | /// these are fine: `T: Foo<U::Item>, U: Foo<()>`. | 963 | /// these are fine: `T: Foo<U::Item>, U: Foo<()>`. |
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index af6b6cda7..3d233b1e2 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs | |||
@@ -5,10 +5,10 @@ | |||
5 | use std::{iter, sync::Arc}; | 5 | use std::{iter, sync::Arc}; |
6 | 6 | ||
7 | use arrayvec::ArrayVec; | 7 | use arrayvec::ArrayVec; |
8 | use base_db::CrateId; | 8 | use base_db::{CrateId, Edition}; |
9 | use chalk_ir::{cast::Cast, Mutability, UniverseIndex}; | 9 | use chalk_ir::{cast::Cast, Mutability, UniverseIndex}; |
10 | use hir_def::{ | 10 | use hir_def::{ |
11 | lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, FunctionId, | 11 | lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, BlockId, FunctionId, |
12 | GenericDefId, HasModule, ImplId, Lookup, ModuleId, TraitId, | 12 | GenericDefId, HasModule, ImplId, Lookup, ModuleId, TraitId, |
13 | }; | 13 | }; |
14 | use hir_expand::name::Name; | 14 | use hir_expand::name::Name; |
@@ -60,7 +60,7 @@ impl TyFingerprint { | |||
60 | TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt), | 60 | TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt), |
61 | TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability), | 61 | TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability), |
62 | TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id), | 62 | TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id), |
63 | TyKind::Dyn(_) => ty.dyn_trait().map(|trait_| TyFingerprint::Dyn(trait_))?, | 63 | TyKind::Dyn(_) => ty.dyn_trait().map(TyFingerprint::Dyn)?, |
64 | _ => return None, | 64 | _ => return None, |
65 | }; | 65 | }; |
66 | Some(fp) | 66 | Some(fp) |
@@ -77,7 +77,7 @@ impl TyFingerprint { | |||
77 | TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt), | 77 | TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt), |
78 | TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability), | 78 | TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability), |
79 | TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id), | 79 | TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id), |
80 | TyKind::Dyn(_) => ty.dyn_trait().map(|trait_| TyFingerprint::Dyn(trait_))?, | 80 | TyKind::Dyn(_) => ty.dyn_trait().map(TyFingerprint::Dyn)?, |
81 | TyKind::Ref(_, _, ty) => return TyFingerprint::for_trait_impl(ty), | 81 | TyKind::Ref(_, _, ty) => return TyFingerprint::for_trait_impl(ty), |
82 | TyKind::Tuple(_, subst) => { | 82 | TyKind::Tuple(_, subst) => { |
83 | let first_ty = subst.interned().get(0).map(|arg| arg.assert_ty_ref(&Interner)); | 83 | let first_ty = subst.interned().get(0).map(|arg| arg.assert_ty_ref(&Interner)); |
@@ -139,35 +139,47 @@ impl TraitImpls { | |||
139 | let mut impls = Self { map: FxHashMap::default() }; | 139 | let mut impls = Self { map: FxHashMap::default() }; |
140 | 140 | ||
141 | let crate_def_map = db.crate_def_map(krate); | 141 | let crate_def_map = db.crate_def_map(krate); |
142 | collect_def_map(db, &crate_def_map, &mut impls); | 142 | impls.collect_def_map(db, &crate_def_map); |
143 | 143 | ||
144 | return Arc::new(impls); | 144 | return Arc::new(impls); |
145 | } | ||
145 | 146 | ||
146 | fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap, impls: &mut TraitImpls) { | 147 | pub(crate) fn trait_impls_in_block_query( |
147 | for (_module_id, module_data) in def_map.modules() { | 148 | db: &dyn HirDatabase, |
148 | for impl_id in module_data.scope.impls() { | 149 | block: BlockId, |
149 | let target_trait = match db.impl_trait(impl_id) { | 150 | ) -> Option<Arc<Self>> { |
150 | Some(tr) => tr.skip_binders().hir_trait_id(), | 151 | let _p = profile::span("trait_impls_in_block_query"); |
151 | None => continue, | 152 | let mut impls = Self { map: FxHashMap::default() }; |
152 | }; | ||
153 | let self_ty = db.impl_self_ty(impl_id); | ||
154 | let self_ty_fp = TyFingerprint::for_trait_impl(self_ty.skip_binders()); | ||
155 | impls | ||
156 | .map | ||
157 | .entry(target_trait) | ||
158 | .or_default() | ||
159 | .entry(self_ty_fp) | ||
160 | .or_default() | ||
161 | .push(impl_id); | ||
162 | } | ||
163 | 153 | ||
164 | // To better support custom derives, collect impls in all unnamed const items. | 154 | let block_def_map = db.block_def_map(block)?; |
165 | // const _: () = { ... }; | 155 | impls.collect_def_map(db, &block_def_map); |
166 | for konst in module_data.scope.unnamed_consts() { | 156 | |
167 | let body = db.body(konst.into()); | 157 | return Some(Arc::new(impls)); |
168 | for (_, block_def_map) in body.blocks(db.upcast()) { | 158 | } |
169 | collect_def_map(db, &block_def_map, impls); | 159 | |
170 | } | 160 | fn collect_def_map(&mut self, db: &dyn HirDatabase, def_map: &DefMap) { |
161 | for (_module_id, module_data) in def_map.modules() { | ||
162 | for impl_id in module_data.scope.impls() { | ||
163 | let target_trait = match db.impl_trait(impl_id) { | ||
164 | Some(tr) => tr.skip_binders().hir_trait_id(), | ||
165 | None => continue, | ||
166 | }; | ||
167 | let self_ty = db.impl_self_ty(impl_id); | ||
168 | let self_ty_fp = TyFingerprint::for_trait_impl(self_ty.skip_binders()); | ||
169 | self.map | ||
170 | .entry(target_trait) | ||
171 | .or_default() | ||
172 | .entry(self_ty_fp) | ||
173 | .or_default() | ||
174 | .push(impl_id); | ||
175 | } | ||
176 | |||
177 | // To better support custom derives, collect impls in all unnamed const items. | ||
178 | // const _: () = { ... }; | ||
179 | for konst in module_data.scope.unnamed_consts() { | ||
180 | let body = db.body(konst.into()); | ||
181 | for (_, block_def_map) in body.blocks(db.upcast()) { | ||
182 | self.collect_def_map(db, &block_def_map); | ||
171 | } | 183 | } |
172 | } | 184 | } |
173 | } | 185 | } |
@@ -372,7 +384,7 @@ pub(crate) fn lookup_method( | |||
372 | db, | 384 | db, |
373 | env, | 385 | env, |
374 | krate, | 386 | krate, |
375 | &traits_in_scope, | 387 | traits_in_scope, |
376 | visible_from_module, | 388 | visible_from_module, |
377 | Some(name), | 389 | Some(name), |
378 | LookupMode::MethodCall, | 390 | LookupMode::MethodCall, |
@@ -484,7 +496,7 @@ fn iterate_method_candidates_impl( | |||
484 | LookupMode::Path => { | 496 | LookupMode::Path => { |
485 | // No autoderef for path lookups | 497 | // No autoderef for path lookups |
486 | iterate_method_candidates_for_self_ty( | 498 | iterate_method_candidates_for_self_ty( |
487 | &ty, | 499 | ty, |
488 | db, | 500 | db, |
489 | env, | 501 | env, |
490 | krate, | 502 | krate, |
@@ -513,7 +525,7 @@ fn iterate_method_candidates_with_autoref( | |||
513 | db, | 525 | db, |
514 | env.clone(), | 526 | env.clone(), |
515 | krate, | 527 | krate, |
516 | &traits_in_scope, | 528 | traits_in_scope, |
517 | visible_from_module, | 529 | visible_from_module, |
518 | name, | 530 | name, |
519 | &mut callback, | 531 | &mut callback, |
@@ -531,7 +543,7 @@ fn iterate_method_candidates_with_autoref( | |||
531 | db, | 543 | db, |
532 | env.clone(), | 544 | env.clone(), |
533 | krate, | 545 | krate, |
534 | &traits_in_scope, | 546 | traits_in_scope, |
535 | visible_from_module, | 547 | visible_from_module, |
536 | name, | 548 | name, |
537 | &mut callback, | 549 | &mut callback, |
@@ -549,7 +561,7 @@ fn iterate_method_candidates_with_autoref( | |||
549 | db, | 561 | db, |
550 | env, | 562 | env, |
551 | krate, | 563 | krate, |
552 | &traits_in_scope, | 564 | traits_in_scope, |
553 | visible_from_module, | 565 | visible_from_module, |
554 | name, | 566 | name, |
555 | &mut callback, | 567 | &mut callback, |
@@ -593,7 +605,7 @@ fn iterate_method_candidates_by_receiver( | |||
593 | db, | 605 | db, |
594 | env.clone(), | 606 | env.clone(), |
595 | krate, | 607 | krate, |
596 | &traits_in_scope, | 608 | traits_in_scope, |
597 | name, | 609 | name, |
598 | Some(receiver_ty), | 610 | Some(receiver_ty), |
599 | &mut callback, | 611 | &mut callback, |
@@ -639,6 +651,7 @@ fn iterate_trait_method_candidates( | |||
639 | receiver_ty: Option<&Canonical<Ty>>, | 651 | receiver_ty: Option<&Canonical<Ty>>, |
640 | callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool, | 652 | callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool, |
641 | ) -> bool { | 653 | ) -> bool { |
654 | let receiver_is_array = matches!(self_ty.value.kind(&Interner), chalk_ir::TyKind::Array(..)); | ||
642 | // if ty is `dyn Trait`, the trait doesn't need to be in scope | 655 | // if ty is `dyn Trait`, the trait doesn't need to be in scope |
643 | let inherent_trait = | 656 | let inherent_trait = |
644 | self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t)); | 657 | self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t)); |
@@ -655,6 +668,19 @@ fn iterate_trait_method_candidates( | |||
655 | 'traits: for t in traits { | 668 | 'traits: for t in traits { |
656 | let data = db.trait_data(t); | 669 | let data = db.trait_data(t); |
657 | 670 | ||
671 | // Traits annotated with `#[rustc_skip_array_during_method_dispatch]` are skipped during | ||
672 | // method resolution, if the receiver is an array, and we're compiling for editions before | ||
673 | // 2021. | ||
674 | // This is to make `[a].into_iter()` not break code with the new `IntoIterator` impl for | ||
675 | // arrays. | ||
676 | if data.skip_array_during_method_dispatch && receiver_is_array { | ||
677 | // FIXME: this should really be using the edition of the method name's span, in case it | ||
678 | // comes from a macro | ||
679 | if db.crate_graph()[krate].edition < Edition::Edition2021 { | ||
680 | continue; | ||
681 | } | ||
682 | } | ||
683 | |||
658 | // we'll be lazy about checking whether the type implements the | 684 | // we'll be lazy about checking whether the type implements the |
659 | // trait, but if we find out it doesn't, we'll skip the rest of the | 685 | // trait, but if we find out it doesn't, we'll skip the rest of the |
660 | // iteration | 686 | // iteration |
@@ -856,7 +882,7 @@ fn transform_receiver_ty( | |||
856 | .fill_with_unknown() | 882 | .fill_with_unknown() |
857 | .build(), | 883 | .build(), |
858 | AssocContainerId::ImplId(impl_id) => { | 884 | AssocContainerId::ImplId(impl_id) => { |
859 | let impl_substs = inherent_impl_substs(db, env, impl_id, &self_ty)?; | 885 | let impl_substs = inherent_impl_substs(db, env, impl_id, self_ty)?; |
860 | TyBuilder::subst_for_def(db, function_id) | 886 | TyBuilder::subst_for_def(db, function_id) |
861 | .use_parent_substs(&impl_substs) | 887 | .use_parent_substs(&impl_substs) |
862 | .fill_with_unknown() | 888 | .fill_with_unknown() |
diff --git a/crates/hir_ty/src/test_db.rs b/crates/hir_ty/src/test_db.rs index 381b98ba8..4640ea821 100644 --- a/crates/hir_ty/src/test_db.rs +++ b/crates/hir_ty/src/test_db.rs | |||
@@ -22,11 +22,19 @@ use test_utils::extract_annotations; | |||
22 | hir_def::db::DefDatabaseStorage, | 22 | hir_def::db::DefDatabaseStorage, |
23 | crate::db::HirDatabaseStorage | 23 | crate::db::HirDatabaseStorage |
24 | )] | 24 | )] |
25 | #[derive(Default)] | ||
26 | pub(crate) struct TestDB { | 25 | pub(crate) struct TestDB { |
27 | storage: salsa::Storage<TestDB>, | 26 | storage: salsa::Storage<TestDB>, |
28 | events: Mutex<Option<Vec<salsa::Event>>>, | 27 | events: Mutex<Option<Vec<salsa::Event>>>, |
29 | } | 28 | } |
29 | |||
30 | impl Default for TestDB { | ||
31 | fn default() -> Self { | ||
32 | let mut this = Self { storage: Default::default(), events: Default::default() }; | ||
33 | this.set_enable_proc_attr_macros(true); | ||
34 | this | ||
35 | } | ||
36 | } | ||
37 | |||
30 | impl fmt::Debug for TestDB { | 38 | impl fmt::Debug for TestDB { |
31 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 39 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
32 | f.debug_struct("TestDB").finish() | 40 | f.debug_struct("TestDB").finish() |
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs index 9d726b024..b873585c4 100644 --- a/crates/hir_ty/src/tests.rs +++ b/crates/hir_ty/src/tests.rs | |||
@@ -9,7 +9,7 @@ mod macros; | |||
9 | mod display_source_code; | 9 | mod display_source_code; |
10 | mod incremental; | 10 | mod incremental; |
11 | 11 | ||
12 | use std::{env, sync::Arc}; | 12 | use std::{collections::HashMap, env, sync::Arc}; |
13 | 13 | ||
14 | use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; | 14 | use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; |
15 | use expect_test::Expect; | 15 | use expect_test::Expect; |
@@ -83,9 +83,105 @@ fn check_types_impl(ra_fixture: &str, display_source: bool) { | |||
83 | checked_one = true; | 83 | checked_one = true; |
84 | } | 84 | } |
85 | } | 85 | } |
86 | |||
86 | assert!(checked_one, "no `//^` annotations found"); | 87 | assert!(checked_one, "no `//^` annotations found"); |
87 | } | 88 | } |
88 | 89 | ||
90 | fn check_no_mismatches(ra_fixture: &str) { | ||
91 | check_mismatches_impl(ra_fixture, true) | ||
92 | } | ||
93 | |||
94 | #[allow(unused)] | ||
95 | fn check_mismatches(ra_fixture: &str) { | ||
96 | check_mismatches_impl(ra_fixture, false) | ||
97 | } | ||
98 | |||
99 | fn check_mismatches_impl(ra_fixture: &str, allow_none: bool) { | ||
100 | let _tracing = setup_tracing(); | ||
101 | let (db, file_id) = TestDB::with_single_file(ra_fixture); | ||
102 | let module = db.module_for_file(file_id); | ||
103 | let def_map = module.def_map(&db); | ||
104 | |||
105 | let mut defs: Vec<DefWithBodyId> = Vec::new(); | ||
106 | visit_module(&db, &def_map, module.local_id, &mut |it| defs.push(it)); | ||
107 | defs.sort_by_key(|def| match def { | ||
108 | DefWithBodyId::FunctionId(it) => { | ||
109 | let loc = it.lookup(&db); | ||
110 | loc.source(&db).value.syntax().text_range().start() | ||
111 | } | ||
112 | DefWithBodyId::ConstId(it) => { | ||
113 | let loc = it.lookup(&db); | ||
114 | loc.source(&db).value.syntax().text_range().start() | ||
115 | } | ||
116 | DefWithBodyId::StaticId(it) => { | ||
117 | let loc = it.lookup(&db); | ||
118 | loc.source(&db).value.syntax().text_range().start() | ||
119 | } | ||
120 | }); | ||
121 | let mut mismatches = HashMap::new(); | ||
122 | let mut push_mismatch = |src_ptr: InFile<SyntaxNode>, mismatch: TypeMismatch| { | ||
123 | let range = src_ptr.value.text_range(); | ||
124 | if src_ptr.file_id.call_node(&db).is_some() { | ||
125 | panic!("type mismatch in macro expansion"); | ||
126 | } | ||
127 | let file_range = FileRange { file_id: src_ptr.file_id.original_file(&db), range }; | ||
128 | let actual = format!( | ||
129 | "expected {}, got {}", | ||
130 | mismatch.expected.display_test(&db), | ||
131 | mismatch.actual.display_test(&db) | ||
132 | ); | ||
133 | mismatches.insert(file_range, actual); | ||
134 | }; | ||
135 | for def in defs { | ||
136 | let (_body, body_source_map) = db.body_with_source_map(def); | ||
137 | let inference_result = db.infer(def); | ||
138 | for (pat, mismatch) in inference_result.pat_type_mismatches() { | ||
139 | let syntax_ptr = match body_source_map.pat_syntax(pat) { | ||
140 | Ok(sp) => { | ||
141 | let root = db.parse_or_expand(sp.file_id).unwrap(); | ||
142 | sp.map(|ptr| { | ||
143 | ptr.either( | ||
144 | |it| it.to_node(&root).syntax().clone(), | ||
145 | |it| it.to_node(&root).syntax().clone(), | ||
146 | ) | ||
147 | }) | ||
148 | } | ||
149 | Err(SyntheticSyntax) => continue, | ||
150 | }; | ||
151 | push_mismatch(syntax_ptr, mismatch.clone()); | ||
152 | } | ||
153 | for (expr, mismatch) in inference_result.expr_type_mismatches() { | ||
154 | let node = match body_source_map.expr_syntax(expr) { | ||
155 | Ok(sp) => { | ||
156 | let root = db.parse_or_expand(sp.file_id).unwrap(); | ||
157 | sp.map(|ptr| ptr.to_node(&root).syntax().clone()) | ||
158 | } | ||
159 | Err(SyntheticSyntax) => continue, | ||
160 | }; | ||
161 | push_mismatch(node, mismatch.clone()); | ||
162 | } | ||
163 | } | ||
164 | let mut checked_one = false; | ||
165 | for (file_id, annotations) in db.extract_annotations() { | ||
166 | for (range, expected) in annotations { | ||
167 | let file_range = FileRange { file_id, range }; | ||
168 | if let Some(mismatch) = mismatches.remove(&file_range) { | ||
169 | assert_eq!(mismatch, expected); | ||
170 | } else { | ||
171 | assert!(false, "Expected mismatch not encountered: {}\n", expected); | ||
172 | } | ||
173 | checked_one = true; | ||
174 | } | ||
175 | } | ||
176 | let mut buf = String::new(); | ||
177 | for (range, mismatch) in mismatches { | ||
178 | format_to!(buf, "{:?}: {}\n", range.range, mismatch,); | ||
179 | } | ||
180 | assert!(buf.is_empty(), "Unexpected type mismatches:\n{}", buf); | ||
181 | |||
182 | assert!(checked_one || allow_none, "no `//^` annotations found"); | ||
183 | } | ||
184 | |||
89 | fn type_at_range(db: &TestDB, pos: FileRange) -> Ty { | 185 | fn type_at_range(db: &TestDB, pos: FileRange) -> Ty { |
90 | let file = db.parse(pos.file_id).ok().unwrap(); | 186 | let file = db.parse(pos.file_id).ok().unwrap(); |
91 | let expr = algo::find_node_at_range::<ast::Expr>(file.syntax(), pos.range).unwrap(); | 187 | let expr = algo::find_node_at_range::<ast::Expr>(file.syntax(), pos.range).unwrap(); |
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs index 6dac7e103..71047703d 100644 --- a/crates/hir_ty/src/tests/coercion.rs +++ b/crates/hir_ty/src/tests/coercion.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use expect_test::expect; | 1 | use expect_test::expect; |
2 | 2 | ||
3 | use super::{check_infer, check_infer_with_mismatches, check_types}; | 3 | use super::{check_infer, check_infer_with_mismatches, check_no_mismatches, check_types}; |
4 | 4 | ||
5 | #[test] | 5 | #[test] |
6 | fn infer_block_expr_type_mismatch() { | 6 | fn infer_block_expr_type_mismatch() { |
@@ -963,7 +963,7 @@ fn test() -> i32 { | |||
963 | 963 | ||
964 | #[test] | 964 | #[test] |
965 | fn panic_macro() { | 965 | fn panic_macro() { |
966 | check_infer_with_mismatches( | 966 | check_no_mismatches( |
967 | r#" | 967 | r#" |
968 | mod panic { | 968 | mod panic { |
969 | #[macro_export] | 969 | #[macro_export] |
@@ -991,15 +991,34 @@ fn main() { | |||
991 | panic!() | 991 | panic!() |
992 | } | 992 | } |
993 | "#, | 993 | "#, |
994 | expect![[r#" | 994 | ); |
995 | 174..185 '{ loop {} }': ! | 995 | } |
996 | 176..183 'loop {}': ! | 996 | |
997 | 181..183 '{}': () | 997 | #[test] |
998 | !0..24 '$crate...:panic': fn panic() -> ! | 998 | fn coerce_unsize_expected_type() { |
999 | !0..26 '$crate...anic()': ! | 999 | check_no_mismatches( |
1000 | !0..26 '$crate...anic()': ! | 1000 | r#" |
1001 | !0..28 '$crate...015!()': ! | 1001 | #[lang = "sized"] |
1002 | 454..470 '{ ...c!() }': () | 1002 | pub trait Sized {} |
1003 | "#]], | 1003 | #[lang = "unsize"] |
1004 | pub trait Unsize<T> {} | ||
1005 | #[lang = "coerce_unsized"] | ||
1006 | pub trait CoerceUnsized<T> {} | ||
1007 | |||
1008 | impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {} | ||
1009 | |||
1010 | fn main() { | ||
1011 | let foo: &[u32] = &[1, 2]; | ||
1012 | let foo: &[u32] = match true { | ||
1013 | true => &[1, 2], | ||
1014 | false => &[1, 2, 3], | ||
1015 | }; | ||
1016 | let foo: &[u32] = if true { | ||
1017 | &[1, 2] | ||
1018 | } else { | ||
1019 | &[1, 2, 3] | ||
1020 | }; | ||
1021 | } | ||
1022 | "#, | ||
1004 | ); | 1023 | ); |
1005 | } | 1024 | } |
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs index 058eb9129..f26b2c8a7 100644 --- a/crates/hir_ty/src/tests/method_resolution.rs +++ b/crates/hir_ty/src/tests/method_resolution.rs | |||
@@ -1349,3 +1349,52 @@ fn f() { | |||
1349 | "#, | 1349 | "#, |
1350 | ); | 1350 | ); |
1351 | } | 1351 | } |
1352 | |||
1353 | #[test] | ||
1354 | fn skip_array_during_method_dispatch() { | ||
1355 | check_types( | ||
1356 | r#" | ||
1357 | //- /main2018.rs crate:main2018 deps:core | ||
1358 | use core::IntoIterator; | ||
1359 | |||
1360 | fn f() { | ||
1361 | let v = [4].into_iter(); | ||
1362 | v; | ||
1363 | //^ &i32 | ||
1364 | |||
1365 | let a = [0, 1].into_iter(); | ||
1366 | a; | ||
1367 | //^ &i32 | ||
1368 | } | ||
1369 | |||
1370 | //- /main2021.rs crate:main2021 deps:core edition:2021 | ||
1371 | use core::IntoIterator; | ||
1372 | |||
1373 | fn f() { | ||
1374 | let v = [4].into_iter(); | ||
1375 | v; | ||
1376 | //^ i32 | ||
1377 | |||
1378 | let a = [0, 1].into_iter(); | ||
1379 | a; | ||
1380 | //^ &i32 | ||
1381 | } | ||
1382 | |||
1383 | //- /core.rs crate:core | ||
1384 | #[rustc_skip_array_during_method_dispatch] | ||
1385 | pub trait IntoIterator { | ||
1386 | type Out; | ||
1387 | fn into_iter(self) -> Self::Out; | ||
1388 | } | ||
1389 | |||
1390 | impl<T> IntoIterator for [T; 1] { | ||
1391 | type Out = T; | ||
1392 | fn into_iter(self) -> Self::Out {} | ||
1393 | } | ||
1394 | impl<'a, T> IntoIterator for &'a [T] { | ||
1395 | type Out = &'a T; | ||
1396 | fn into_iter(self) -> Self::Out {} | ||
1397 | } | ||
1398 | "#, | ||
1399 | ); | ||
1400 | } | ||
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs index cd08b5c7a..aa513c56d 100644 --- a/crates/hir_ty/src/tests/patterns.rs +++ b/crates/hir_ty/src/tests/patterns.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use expect_test::expect; | 1 | use expect_test::expect; |
2 | 2 | ||
3 | use super::{check_infer, check_infer_with_mismatches, check_types}; | 3 | use super::{check_infer, check_infer_with_mismatches, check_mismatches, check_types}; |
4 | 4 | ||
5 | #[test] | 5 | #[test] |
6 | fn infer_pattern() { | 6 | fn infer_pattern() { |
@@ -20,6 +20,8 @@ fn infer_pattern() { | |||
20 | let h = val; | 20 | let h = val; |
21 | } | 21 | } |
22 | 22 | ||
23 | if let x @ true = &true {} | ||
24 | |||
23 | let lambda = |a: u64, b, c: i32| { a + b; c }; | 25 | let lambda = |a: u64, b, c: i32| { a + b; c }; |
24 | 26 | ||
25 | let ref ref_to_x = x; | 27 | let ref ref_to_x = x; |
@@ -30,7 +32,7 @@ fn infer_pattern() { | |||
30 | "#, | 32 | "#, |
31 | expect![[r#" | 33 | expect![[r#" |
32 | 8..9 'x': &i32 | 34 | 8..9 'x': &i32 |
33 | 17..368 '{ ...o_x; }': () | 35 | 17..400 '{ ...o_x; }': () |
34 | 27..28 'y': &i32 | 36 | 27..28 'y': &i32 |
35 | 31..32 'x': &i32 | 37 | 31..32 'x': &i32 |
36 | 42..44 '&z': &i32 | 38 | 42..44 '&z': &i32 |
@@ -59,24 +61,31 @@ fn infer_pattern() { | |||
59 | 176..204 '{ ... }': () | 61 | 176..204 '{ ... }': () |
60 | 190..191 'h': {unknown} | 62 | 190..191 'h': {unknown} |
61 | 194..197 'val': {unknown} | 63 | 194..197 'val': {unknown} |
62 | 214..220 'lambda': |u64, u64, i32| -> i32 | 64 | 210..236 'if let...rue {}': () |
63 | 223..255 '|a: u6...b; c }': |u64, u64, i32| -> i32 | 65 | 217..225 'x @ true': &bool |
64 | 224..225 'a': u64 | 66 | 221..225 'true': bool |
65 | 232..233 'b': u64 | 67 | 221..225 'true': bool |
66 | 235..236 'c': i32 | 68 | 228..233 '&true': &bool |
67 | 243..255 '{ a + b; c }': i32 | 69 | 229..233 'true': bool |
68 | 245..246 'a': u64 | 70 | 234..236 '{}': () |
69 | 245..250 'a + b': u64 | 71 | 246..252 'lambda': |u64, u64, i32| -> i32 |
70 | 249..250 'b': u64 | 72 | 255..287 '|a: u6...b; c }': |u64, u64, i32| -> i32 |
71 | 252..253 'c': i32 | 73 | 256..257 'a': u64 |
72 | 266..278 'ref ref_to_x': &&i32 | 74 | 264..265 'b': u64 |
73 | 281..282 'x': &i32 | 75 | 267..268 'c': i32 |
74 | 292..301 'mut mut_x': &i32 | 76 | 275..287 '{ a + b; c }': i32 |
75 | 304..305 'x': &i32 | 77 | 277..278 'a': u64 |
76 | 315..335 'ref mu...f_to_x': &mut &i32 | 78 | 277..282 'a + b': u64 |
77 | 338..339 'x': &i32 | 79 | 281..282 'b': u64 |
78 | 349..350 'k': &mut &i32 | 80 | 284..285 'c': i32 |
79 | 353..365 'mut_ref_to_x': &mut &i32 | 81 | 298..310 'ref ref_to_x': &&i32 |
82 | 313..314 'x': &i32 | ||
83 | 324..333 'mut mut_x': &i32 | ||
84 | 336..337 'x': &i32 | ||
85 | 347..367 'ref mu...f_to_x': &mut &i32 | ||
86 | 370..371 'x': &i32 | ||
87 | 381..382 'k': &mut &i32 | ||
88 | 385..397 'mut_ref_to_x': &mut &i32 | ||
80 | "#]], | 89 | "#]], |
81 | ); | 90 | ); |
82 | } | 91 | } |
@@ -509,47 +518,24 @@ fn infer_generics_in_patterns() { | |||
509 | 518 | ||
510 | #[test] | 519 | #[test] |
511 | fn infer_const_pattern() { | 520 | fn infer_const_pattern() { |
512 | check_infer_with_mismatches( | 521 | check_mismatches( |
513 | r#" | 522 | r#" |
514 | enum Option<T> { None } | 523 | enum Option<T> { None } |
515 | use Option::None; | 524 | use Option::None; |
516 | struct Foo; | 525 | struct Foo; |
517 | const Bar: usize = 1; | 526 | const Bar: usize = 1; |
518 | 527 | ||
519 | fn test() { | 528 | fn test() { |
520 | let a: Option<u32> = None; | 529 | let a: Option<u32> = None; |
521 | let b: Option<i64> = match a { | 530 | let b: Option<i64> = match a { |
522 | None => None, | 531 | None => None, |
523 | }; | 532 | }; |
524 | let _: () = match () { Foo => Foo }; // Expected mismatch | 533 | let _: () = match () { Foo => () }; |
525 | let _: () = match () { Bar => Bar }; // Expected mismatch | 534 | // ^^^ expected (), got Foo |
526 | } | 535 | let _: () = match () { Bar => () }; |
536 | // ^^^ expected (), got usize | ||
537 | } | ||
527 | "#, | 538 | "#, |
528 | expect![[r#" | ||
529 | 73..74 '1': usize | ||
530 | 87..309 '{ ...atch }': () | ||
531 | 97..98 'a': Option<u32> | ||
532 | 114..118 'None': Option<u32> | ||
533 | 128..129 'b': Option<i64> | ||
534 | 145..182 'match ... }': Option<i64> | ||
535 | 151..152 'a': Option<u32> | ||
536 | 163..167 'None': Option<u32> | ||
537 | 171..175 'None': Option<i64> | ||
538 | 192..193 '_': () | ||
539 | 200..223 'match ... Foo }': Foo | ||
540 | 206..208 '()': () | ||
541 | 211..214 'Foo': Foo | ||
542 | 218..221 'Foo': Foo | ||
543 | 254..255 '_': () | ||
544 | 262..285 'match ... Bar }': usize | ||
545 | 268..270 '()': () | ||
546 | 273..276 'Bar': usize | ||
547 | 280..283 'Bar': usize | ||
548 | 200..223: expected (), got Foo | ||
549 | 211..214: expected (), got Foo | ||
550 | 262..285: expected (), got usize | ||
551 | 273..276: expected (), got usize | ||
552 | "#]], | ||
553 | ); | 539 | ); |
554 | } | 540 | } |
555 | 541 | ||
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index 588f0d1d4..6bcede4c4 100644 --- a/crates/hir_ty/src/tests/traits.rs +++ b/crates/hir_ty/src/tests/traits.rs | |||
@@ -3740,3 +3740,70 @@ mod future { | |||
3740 | "#, | 3740 | "#, |
3741 | ); | 3741 | ); |
3742 | } | 3742 | } |
3743 | |||
3744 | #[test] | ||
3745 | fn local_impl_1() { | ||
3746 | check_types( | ||
3747 | r#" | ||
3748 | trait Trait<T> { | ||
3749 | fn foo(&self) -> T; | ||
3750 | } | ||
3751 | |||
3752 | fn test() { | ||
3753 | struct S; | ||
3754 | impl Trait<u32> for S { | ||
3755 | fn foo(&self) { 0 } | ||
3756 | } | ||
3757 | |||
3758 | S.foo(); | ||
3759 | // ^^^^^^^ u32 | ||
3760 | } | ||
3761 | "#, | ||
3762 | ); | ||
3763 | } | ||
3764 | |||
3765 | #[test] | ||
3766 | fn local_impl_2() { | ||
3767 | check_types( | ||
3768 | r#" | ||
3769 | struct S; | ||
3770 | |||
3771 | fn test() { | ||
3772 | trait Trait<T> { | ||
3773 | fn foo(&self) -> T; | ||
3774 | } | ||
3775 | impl Trait<u32> for S { | ||
3776 | fn foo(&self) { 0 } | ||
3777 | } | ||
3778 | |||
3779 | S.foo(); | ||
3780 | // ^^^^^^^ u32 | ||
3781 | } | ||
3782 | "#, | ||
3783 | ); | ||
3784 | } | ||
3785 | |||
3786 | #[test] | ||
3787 | fn local_impl_3() { | ||
3788 | check_types( | ||
3789 | r#" | ||
3790 | trait Trait<T> { | ||
3791 | fn foo(&self) -> T; | ||
3792 | } | ||
3793 | |||
3794 | fn test() { | ||
3795 | struct S1; | ||
3796 | { | ||
3797 | struct S2; | ||
3798 | |||
3799 | impl Trait<S1> for S2 { | ||
3800 | fn foo(&self) { S1 } | ||
3801 | } | ||
3802 | |||
3803 | S2.foo(); | ||
3804 | // ^^^^^^^^ S1 | ||
3805 | } | ||
3806 | } | ||
3807 | "#, | ||
3808 | ); | ||
3809 | } | ||
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml index 88f3d09d3..f12928225 100644 --- a/crates/ide/Cargo.toml +++ b/crates/ide/Cargo.toml | |||
@@ -10,7 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = { version = "1.1", features = ["thread-local"] } | 13 | cov-mark = "2.0.0-pre.1" |
14 | either = "1.5.3" | 14 | either = "1.5.3" |
15 | indexmap = "1.4.0" | 15 | indexmap = "1.4.0" |
16 | itertools = "0.10.0" | 16 | itertools = "0.10.0" |
@@ -39,4 +39,3 @@ hir = { path = "../hir", version = "0.0.0" } | |||
39 | [dev-dependencies] | 39 | [dev-dependencies] |
40 | test_utils = { path = "../test_utils" } | 40 | test_utils = { path = "../test_utils" } |
41 | expect-test = "1.1" | 41 | expect-test = "1.1" |
42 | cov-mark = "1.1.0" | ||
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index d5c954b8b..815a633e5 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -4,69 +4,94 @@ | |||
4 | //! macro-expanded files, but we need to present them to the users in terms of | 4 | //! macro-expanded files, but we need to present them to the users in terms of |
5 | //! original files. So we need to map the ranges. | 5 | //! original files. So we need to map the ranges. |
6 | 6 | ||
7 | mod fixes; | 7 | mod break_outside_of_loop; |
8 | mod field_shorthand; | 8 | mod inactive_code; |
9 | mod incorrect_case; | ||
10 | mod macro_error; | ||
11 | mod mismatched_arg_count; | ||
12 | mod missing_fields; | ||
13 | mod missing_match_arms; | ||
14 | mod missing_ok_or_some_in_tail_expr; | ||
15 | mod missing_unsafe; | ||
16 | mod no_such_field; | ||
17 | mod remove_this_semicolon; | ||
18 | mod replace_filter_map_next_with_find_map; | ||
19 | mod unimplemented_builtin_macro; | ||
9 | mod unlinked_file; | 20 | mod unlinked_file; |
21 | mod unresolved_extern_crate; | ||
22 | mod unresolved_import; | ||
23 | mod unresolved_macro_call; | ||
24 | mod unresolved_module; | ||
25 | mod unresolved_proc_macro; | ||
10 | 26 | ||
11 | use std::cell::RefCell; | 27 | mod field_shorthand; |
12 | 28 | ||
13 | use hir::{ | 29 | use hir::{diagnostics::AnyDiagnostic, Semantics}; |
14 | db::AstDatabase, | ||
15 | diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder}, | ||
16 | InFile, Semantics, | ||
17 | }; | ||
18 | use ide_assists::AssistResolveStrategy; | 30 | use ide_assists::AssistResolveStrategy; |
19 | use ide_db::{base_db::SourceDatabase, RootDatabase}; | 31 | use ide_db::{base_db::SourceDatabase, RootDatabase}; |
20 | use itertools::Itertools; | 32 | use itertools::Itertools; |
21 | use rustc_hash::FxHashSet; | 33 | use rustc_hash::FxHashSet; |
22 | use syntax::{ | 34 | use syntax::{ |
23 | ast::{self, AstNode}, | 35 | ast::{self, AstNode}, |
24 | SyntaxNode, SyntaxNodePtr, TextRange, TextSize, | 36 | SyntaxNode, TextRange, |
25 | }; | 37 | }; |
26 | use text_edit::TextEdit; | 38 | use text_edit::TextEdit; |
27 | use unlinked_file::UnlinkedFile; | 39 | use unlinked_file::UnlinkedFile; |
28 | 40 | ||
29 | use crate::{Assist, AssistId, AssistKind, FileId, Label, SourceChange}; | 41 | use crate::{Assist, AssistId, AssistKind, FileId, Label, SourceChange}; |
30 | 42 | ||
31 | use self::fixes::DiagnosticWithFixes; | 43 | #[derive(Copy, Clone, Debug, PartialEq)] |
44 | pub struct DiagnosticCode(pub &'static str); | ||
45 | |||
46 | impl DiagnosticCode { | ||
47 | pub fn as_str(&self) -> &str { | ||
48 | self.0 | ||
49 | } | ||
50 | } | ||
32 | 51 | ||
33 | #[derive(Debug)] | 52 | #[derive(Debug)] |
34 | pub struct Diagnostic { | 53 | pub struct Diagnostic { |
35 | // pub name: Option<String>, | 54 | pub code: DiagnosticCode, |
36 | pub message: String, | 55 | pub message: String, |
37 | pub range: TextRange, | 56 | pub range: TextRange, |
38 | pub severity: Severity, | 57 | pub severity: Severity, |
39 | pub fixes: Option<Vec<Assist>>, | ||
40 | pub unused: bool, | 58 | pub unused: bool, |
41 | pub code: Option<DiagnosticCode>, | 59 | pub experimental: bool, |
60 | pub fixes: Option<Vec<Assist>>, | ||
42 | } | 61 | } |
43 | 62 | ||
44 | impl Diagnostic { | 63 | impl Diagnostic { |
45 | fn error(range: TextRange, message: String) -> Self { | 64 | fn new(code: &'static str, message: impl Into<String>, range: TextRange) -> Diagnostic { |
46 | Self { message, range, severity: Severity::Error, fixes: None, unused: false, code: None } | 65 | let message = message.into(); |
47 | } | 66 | Diagnostic { |
48 | 67 | code: DiagnosticCode(code), | |
49 | fn hint(range: TextRange, message: String) -> Self { | ||
50 | Self { | ||
51 | message, | 68 | message, |
52 | range, | 69 | range, |
53 | severity: Severity::WeakWarning, | 70 | severity: Severity::Error, |
54 | fixes: None, | ||
55 | unused: false, | 71 | unused: false, |
56 | code: None, | 72 | experimental: false, |
73 | fixes: None, | ||
57 | } | 74 | } |
58 | } | 75 | } |
59 | 76 | ||
60 | fn with_fixes(self, fixes: Option<Vec<Assist>>) -> Self { | 77 | fn experimental(mut self) -> Diagnostic { |
61 | Self { fixes, ..self } | 78 | self.experimental = true; |
79 | self | ||
80 | } | ||
81 | |||
82 | fn severity(mut self, severity: Severity) -> Diagnostic { | ||
83 | self.severity = severity; | ||
84 | self | ||
62 | } | 85 | } |
63 | 86 | ||
64 | fn with_unused(self, unused: bool) -> Self { | 87 | fn with_fixes(mut self, fixes: Option<Vec<Assist>>) -> Diagnostic { |
65 | Self { unused, ..self } | 88 | self.fixes = fixes; |
89 | self | ||
66 | } | 90 | } |
67 | 91 | ||
68 | fn with_code(self, code: Option<DiagnosticCode>) -> Self { | 92 | fn with_unused(mut self, unused: bool) -> Diagnostic { |
69 | Self { code, ..self } | 93 | self.unused = unused; |
94 | self | ||
70 | } | 95 | } |
71 | } | 96 | } |
72 | 97 | ||
@@ -82,6 +107,12 @@ pub struct DiagnosticsConfig { | |||
82 | pub disabled: FxHashSet<String>, | 107 | pub disabled: FxHashSet<String>, |
83 | } | 108 | } |
84 | 109 | ||
110 | struct DiagnosticsContext<'a> { | ||
111 | config: &'a DiagnosticsConfig, | ||
112 | sema: Semantics<'a, RootDatabase>, | ||
113 | resolve: &'a AssistResolveStrategy, | ||
114 | } | ||
115 | |||
85 | pub(crate) fn diagnostics( | 116 | pub(crate) fn diagnostics( |
86 | db: &RootDatabase, | 117 | db: &RootDatabase, |
87 | config: &DiagnosticsConfig, | 118 | config: &DiagnosticsConfig, |
@@ -95,144 +126,64 @@ pub(crate) fn diagnostics( | |||
95 | 126 | ||
96 | // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily. | 127 | // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily. |
97 | res.extend( | 128 | res.extend( |
98 | parse | 129 | parse.errors().iter().take(128).map(|err| { |
99 | .errors() | 130 | Diagnostic::new("syntax-error", format!("Syntax Error: {}", err), err.range()) |
100 | .iter() | 131 | }), |
101 | .take(128) | ||
102 | .map(|err| Diagnostic::error(err.range(), format!("Syntax Error: {}", err))), | ||
103 | ); | 132 | ); |
104 | 133 | ||
105 | for node in parse.tree().syntax().descendants() { | 134 | for node in parse.tree().syntax().descendants() { |
106 | check_unnecessary_braces_in_use_statement(&mut res, file_id, &node); | 135 | check_unnecessary_braces_in_use_statement(&mut res, file_id, &node); |
107 | field_shorthand::check(&mut res, file_id, &node); | 136 | field_shorthand::check(&mut res, file_id, &node); |
108 | } | 137 | } |
109 | let res = RefCell::new(res); | ||
110 | let sink_builder = DiagnosticSinkBuilder::new() | ||
111 | .on::<hir::diagnostics::UnresolvedModule, _>(|d| { | ||
112 | res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve)); | ||
113 | }) | ||
114 | .on::<hir::diagnostics::MissingFields, _>(|d| { | ||
115 | res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve)); | ||
116 | }) | ||
117 | .on::<hir::diagnostics::MissingOkOrSomeInTailExpr, _>(|d| { | ||
118 | res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve)); | ||
119 | }) | ||
120 | .on::<hir::diagnostics::NoSuchField, _>(|d| { | ||
121 | res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve)); | ||
122 | }) | ||
123 | .on::<hir::diagnostics::RemoveThisSemicolon, _>(|d| { | ||
124 | res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve)); | ||
125 | }) | ||
126 | .on::<hir::diagnostics::IncorrectCase, _>(|d| { | ||
127 | res.borrow_mut().push(warning_with_fix(d, &sema, resolve)); | ||
128 | }) | ||
129 | .on::<hir::diagnostics::ReplaceFilterMapNextWithFindMap, _>(|d| { | ||
130 | res.borrow_mut().push(warning_with_fix(d, &sema, resolve)); | ||
131 | }) | ||
132 | .on::<hir::diagnostics::InactiveCode, _>(|d| { | ||
133 | // If there's inactive code somewhere in a macro, don't propagate to the call-site. | ||
134 | if d.display_source().file_id.expansion_info(db).is_some() { | ||
135 | return; | ||
136 | } | ||
137 | 138 | ||
138 | // Override severity and mark as unused. | 139 | let mut diags = Vec::new(); |
139 | res.borrow_mut().push( | 140 | let module = sema.to_module_def(file_id); |
140 | Diagnostic::hint( | 141 | if let Some(m) = module { |
141 | sema.diagnostics_display_range(d.display_source()).range, | 142 | m.diagnostics(db, &mut diags) |
142 | d.message(), | ||
143 | ) | ||
144 | .with_unused(true) | ||
145 | .with_code(Some(d.code())), | ||
146 | ); | ||
147 | }) | ||
148 | .on::<UnlinkedFile, _>(|d| { | ||
149 | // Limit diagnostic to the first few characters in the file. This matches how VS Code | ||
150 | // renders it with the full span, but on other editors, and is less invasive. | ||
151 | let range = sema.diagnostics_display_range(d.display_source()).range; | ||
152 | let range = range.intersect(TextRange::up_to(TextSize::of("..."))).unwrap_or(range); | ||
153 | |||
154 | // Override severity and mark as unused. | ||
155 | res.borrow_mut().push( | ||
156 | Diagnostic::hint(range, d.message()) | ||
157 | .with_fixes(d.fixes(&sema, resolve)) | ||
158 | .with_code(Some(d.code())), | ||
159 | ); | ||
160 | }) | ||
161 | .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| { | ||
162 | // Use more accurate position if available. | ||
163 | let display_range = d | ||
164 | .precise_location | ||
165 | .unwrap_or_else(|| sema.diagnostics_display_range(d.display_source()).range); | ||
166 | |||
167 | // FIXME: it would be nice to tell the user whether proc macros are currently disabled | ||
168 | res.borrow_mut() | ||
169 | .push(Diagnostic::hint(display_range, d.message()).with_code(Some(d.code()))); | ||
170 | }) | ||
171 | .on::<hir::diagnostics::UnresolvedMacroCall, _>(|d| { | ||
172 | let last_path_segment = sema.db.parse_or_expand(d.file).and_then(|root| { | ||
173 | d.node | ||
174 | .to_node(&root) | ||
175 | .path() | ||
176 | .and_then(|it| it.segment()) | ||
177 | .and_then(|it| it.name_ref()) | ||
178 | .map(|it| InFile::new(d.file, SyntaxNodePtr::new(it.syntax()))) | ||
179 | }); | ||
180 | let diagnostics = last_path_segment.unwrap_or_else(|| d.display_source()); | ||
181 | let display_range = sema.diagnostics_display_range(diagnostics).range; | ||
182 | res.borrow_mut() | ||
183 | .push(Diagnostic::error(display_range, d.message()).with_code(Some(d.code()))); | ||
184 | }) | ||
185 | .on::<hir::diagnostics::UnimplementedBuiltinMacro, _>(|d| { | ||
186 | let display_range = sema.diagnostics_display_range(d.display_source()).range; | ||
187 | res.borrow_mut() | ||
188 | .push(Diagnostic::hint(display_range, d.message()).with_code(Some(d.code()))); | ||
189 | }) | ||
190 | // Only collect experimental diagnostics when they're enabled. | ||
191 | .filter(|diag| !(diag.is_experimental() && config.disable_experimental)) | ||
192 | .filter(|diag| !config.disabled.contains(diag.code().as_str())); | ||
193 | |||
194 | // Finalize the `DiagnosticSink` building process. | ||
195 | let mut sink = sink_builder | ||
196 | // Diagnostics not handled above get no fix and default treatment. | ||
197 | .build(|d| { | ||
198 | res.borrow_mut().push( | ||
199 | Diagnostic::error( | ||
200 | sema.diagnostics_display_range(d.display_source()).range, | ||
201 | d.message(), | ||
202 | ) | ||
203 | .with_code(Some(d.code())), | ||
204 | ); | ||
205 | }); | ||
206 | |||
207 | match sema.to_module_def(file_id) { | ||
208 | Some(m) => m.diagnostics(db, &mut sink), | ||
209 | None => { | ||
210 | sink.push(UnlinkedFile { file_id, node: SyntaxNodePtr::new(&parse.tree().syntax()) }); | ||
211 | } | ||
212 | } | 143 | } |
213 | 144 | ||
214 | drop(sink); | 145 | let ctx = DiagnosticsContext { config, sema, resolve }; |
215 | res.into_inner() | 146 | if module.is_none() { |
216 | } | 147 | let d = UnlinkedFile { file: file_id }; |
148 | let d = unlinked_file::unlinked_file(&ctx, &d); | ||
149 | res.push(d) | ||
150 | } | ||
217 | 151 | ||
218 | fn diagnostic_with_fix<D: DiagnosticWithFixes>( | 152 | for diag in diags { |
219 | d: &D, | 153 | #[rustfmt::skip] |
220 | sema: &Semantics<RootDatabase>, | 154 | let d = match diag { |
221 | resolve: &AssistResolveStrategy, | 155 | AnyDiagnostic::BreakOutsideOfLoop(d) => break_outside_of_loop::break_outside_of_loop(&ctx, &d), |
222 | ) -> Diagnostic { | 156 | AnyDiagnostic::IncorrectCase(d) => incorrect_case::incorrect_case(&ctx, &d), |
223 | Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message()) | 157 | AnyDiagnostic::MacroError(d) => macro_error::macro_error(&ctx, &d), |
224 | .with_fixes(d.fixes(&sema, resolve)) | 158 | AnyDiagnostic::MismatchedArgCount(d) => mismatched_arg_count::mismatched_arg_count(&ctx, &d), |
225 | .with_code(Some(d.code())) | 159 | AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d), |
226 | } | 160 | AnyDiagnostic::MissingMatchArms(d) => missing_match_arms::missing_match_arms(&ctx, &d), |
161 | AnyDiagnostic::MissingOkOrSomeInTailExpr(d) => missing_ok_or_some_in_tail_expr::missing_ok_or_some_in_tail_expr(&ctx, &d), | ||
162 | AnyDiagnostic::MissingUnsafe(d) => missing_unsafe::missing_unsafe(&ctx, &d), | ||
163 | AnyDiagnostic::NoSuchField(d) => no_such_field::no_such_field(&ctx, &d), | ||
164 | AnyDiagnostic::RemoveThisSemicolon(d) => remove_this_semicolon::remove_this_semicolon(&ctx, &d), | ||
165 | AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d), | ||
166 | AnyDiagnostic::UnimplementedBuiltinMacro(d) => unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d), | ||
167 | AnyDiagnostic::UnresolvedExternCrate(d) => unresolved_extern_crate::unresolved_extern_crate(&ctx, &d), | ||
168 | AnyDiagnostic::UnresolvedImport(d) => unresolved_import::unresolved_import(&ctx, &d), | ||
169 | AnyDiagnostic::UnresolvedMacroCall(d) => unresolved_macro_call::unresolved_macro_call(&ctx, &d), | ||
170 | AnyDiagnostic::UnresolvedModule(d) => unresolved_module::unresolved_module(&ctx, &d), | ||
171 | AnyDiagnostic::UnresolvedProcMacro(d) => unresolved_proc_macro::unresolved_proc_macro(&ctx, &d), | ||
172 | |||
173 | AnyDiagnostic::InactiveCode(d) => match inactive_code::inactive_code(&ctx, &d) { | ||
174 | Some(it) => it, | ||
175 | None => continue, | ||
176 | } | ||
177 | }; | ||
178 | res.push(d) | ||
179 | } | ||
227 | 180 | ||
228 | fn warning_with_fix<D: DiagnosticWithFixes>( | 181 | res.retain(|d| { |
229 | d: &D, | 182 | !ctx.config.disabled.contains(d.code.as_str()) |
230 | sema: &Semantics<RootDatabase>, | 183 | && !(ctx.config.disable_experimental && d.experimental) |
231 | resolve: &AssistResolveStrategy, | 184 | }); |
232 | ) -> Diagnostic { | 185 | |
233 | Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message()) | 186 | res |
234 | .with_fixes(d.fixes(&sema, resolve)) | ||
235 | .with_code(Some(d.code())) | ||
236 | } | 187 | } |
237 | 188 | ||
238 | fn check_unnecessary_braces_in_use_statement( | 189 | fn check_unnecessary_braces_in_use_statement( |
@@ -260,13 +211,18 @@ fn check_unnecessary_braces_in_use_statement( | |||
260 | }); | 211 | }); |
261 | 212 | ||
262 | acc.push( | 213 | acc.push( |
263 | Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string()) | 214 | Diagnostic::new( |
264 | .with_fixes(Some(vec![fix( | 215 | "unnecessary-braces", |
265 | "remove_braces", | 216 | "Unnecessary braces in use statement".to_string(), |
266 | "Remove unnecessary braces", | 217 | use_range, |
267 | SourceChange::from_text_edit(file_id, edit), | 218 | ) |
268 | use_range, | 219 | .severity(Severity::WeakWarning) |
269 | )])), | 220 | .with_fixes(Some(vec![fix( |
221 | "remove_braces", | ||
222 | "Remove unnecessary braces", | ||
223 | SourceChange::from_text_edit(file_id, edit), | ||
224 | use_range, | ||
225 | )])), | ||
270 | ); | 226 | ); |
271 | } | 227 | } |
272 | 228 | ||
@@ -344,8 +300,8 @@ mod tests { | |||
344 | ) | 300 | ) |
345 | .unwrap() | 301 | .unwrap() |
346 | .pop() | 302 | .pop() |
347 | .unwrap(); | 303 | .expect("no diagnostics"); |
348 | let fix = &diagnostic.fixes.unwrap()[nth]; | 304 | let fix = &diagnostic.fixes.expect("diagnostic misses fixes")[nth]; |
349 | let actual = { | 305 | let actual = { |
350 | let source_change = fix.source_change.as_ref().unwrap(); | 306 | let source_change = fix.source_change.as_ref().unwrap(); |
351 | let file_id = *source_change.source_file_edits.keys().next().unwrap(); | 307 | let file_id = *source_change.source_file_edits.keys().next().unwrap(); |
@@ -365,8 +321,9 @@ mod tests { | |||
365 | file_position.offset | 321 | file_position.offset |
366 | ); | 322 | ); |
367 | } | 323 | } |
324 | |||
368 | /// Checks that there's a diagnostic *without* fix at `$0`. | 325 | /// Checks that there's a diagnostic *without* fix at `$0`. |
369 | fn check_no_fix(ra_fixture: &str) { | 326 | pub(crate) fn check_no_fix(ra_fixture: &str) { |
370 | let (analysis, file_position) = fixture::position(ra_fixture); | 327 | let (analysis, file_position) = fixture::position(ra_fixture); |
371 | let diagnostic = analysis | 328 | let diagnostic = analysis |
372 | .diagnostics( | 329 | .diagnostics( |
@@ -380,21 +337,6 @@ mod tests { | |||
380 | assert!(diagnostic.fixes.is_none(), "got a fix when none was expected: {:?}", diagnostic); | 337 | assert!(diagnostic.fixes.is_none(), "got a fix when none was expected: {:?}", diagnostic); |
381 | } | 338 | } |
382 | 339 | ||
383 | /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics | ||
384 | /// apply to the file containing the cursor. | ||
385 | pub(crate) fn check_no_diagnostics(ra_fixture: &str) { | ||
386 | let (analysis, files) = fixture::files(ra_fixture); | ||
387 | let diagnostics = files | ||
388 | .into_iter() | ||
389 | .flat_map(|file_id| { | ||
390 | analysis | ||
391 | .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id) | ||
392 | .unwrap() | ||
393 | }) | ||
394 | .collect::<Vec<_>>(); | ||
395 | assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics); | ||
396 | } | ||
397 | |||
398 | pub(crate) fn check_expect(ra_fixture: &str, expect: Expect) { | 340 | pub(crate) fn check_expect(ra_fixture: &str, expect: Expect) { |
399 | let (analysis, file_id) = fixture::file(ra_fixture); | 341 | let (analysis, file_id) = fixture::file(ra_fixture); |
400 | let diagnostics = analysis | 342 | let diagnostics = analysis |
@@ -403,90 +345,31 @@ mod tests { | |||
403 | expect.assert_debug_eq(&diagnostics) | 345 | expect.assert_debug_eq(&diagnostics) |
404 | } | 346 | } |
405 | 347 | ||
348 | #[track_caller] | ||
406 | pub(crate) fn check_diagnostics(ra_fixture: &str) { | 349 | pub(crate) fn check_diagnostics(ra_fixture: &str) { |
407 | let (analysis, file_id) = fixture::file(ra_fixture); | 350 | let mut config = DiagnosticsConfig::default(); |
408 | let diagnostics = analysis | 351 | config.disabled.insert("inactive-code".to_string()); |
409 | .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id) | 352 | check_diagnostics_with_config(config, ra_fixture) |
410 | .unwrap(); | ||
411 | |||
412 | let expected = extract_annotations(&*analysis.file_text(file_id).unwrap()); | ||
413 | let actual = diagnostics.into_iter().map(|d| (d.range, d.message)).collect::<Vec<_>>(); | ||
414 | assert_eq!(expected, actual); | ||
415 | } | ||
416 | |||
417 | #[test] | ||
418 | fn test_unresolved_macro_range() { | ||
419 | check_diagnostics( | ||
420 | r#" | ||
421 | foo::bar!(92); | ||
422 | //^^^ unresolved macro `foo::bar!` | ||
423 | "#, | ||
424 | ); | ||
425 | } | ||
426 | |||
427 | #[test] | ||
428 | fn unresolved_import_in_use_tree() { | ||
429 | // Only the relevant part of a nested `use` item should be highlighted. | ||
430 | check_diagnostics( | ||
431 | r#" | ||
432 | use does_exist::{Exists, DoesntExist}; | ||
433 | //^^^^^^^^^^^ unresolved import | ||
434 | |||
435 | use {does_not_exist::*, does_exist}; | ||
436 | //^^^^^^^^^^^^^^^^^ unresolved import | ||
437 | |||
438 | use does_not_exist::{ | ||
439 | a, | ||
440 | //^ unresolved import | ||
441 | b, | ||
442 | //^ unresolved import | ||
443 | c, | ||
444 | //^ unresolved import | ||
445 | }; | ||
446 | |||
447 | mod does_exist { | ||
448 | pub struct Exists; | ||
449 | } | ||
450 | "#, | ||
451 | ); | ||
452 | } | 353 | } |
453 | 354 | ||
454 | #[test] | 355 | #[track_caller] |
455 | fn range_mapping_out_of_macros() { | 356 | pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixture: &str) { |
456 | // FIXME: this is very wrong, but somewhat tricky to fix. | 357 | let (analysis, files) = fixture::files(ra_fixture); |
457 | check_fix( | 358 | for file_id in files { |
458 | r#" | 359 | let diagnostics = |
459 | fn some() {} | 360 | analysis.diagnostics(&config, AssistResolveStrategy::All, file_id).unwrap(); |
460 | fn items() {} | 361 | |
461 | fn here() {} | 362 | let expected = extract_annotations(&*analysis.file_text(file_id).unwrap()); |
462 | 363 | let mut actual = | |
463 | macro_rules! id { ($($tt:tt)*) => { $($tt)*}; } | 364 | diagnostics.into_iter().map(|d| (d.range, d.message)).collect::<Vec<_>>(); |
464 | 365 | actual.sort_by_key(|(range, _)| range.start()); | |
465 | fn main() { | 366 | assert_eq!(expected, actual); |
466 | let _x = id![Foo { a: $042 }]; | 367 | } |
467 | } | ||
468 | |||
469 | pub struct Foo { pub a: i32, pub b: i32 } | ||
470 | "#, | ||
471 | r#" | ||
472 | fn some(, b: () ) {} | ||
473 | fn items() {} | ||
474 | fn here() {} | ||
475 | |||
476 | macro_rules! id { ($($tt:tt)*) => { $($tt)*}; } | ||
477 | |||
478 | fn main() { | ||
479 | let _x = id![Foo { a: 42 }]; | ||
480 | } | ||
481 | |||
482 | pub struct Foo { pub a: i32, pub b: i32 } | ||
483 | "#, | ||
484 | ); | ||
485 | } | 368 | } |
486 | 369 | ||
487 | #[test] | 370 | #[test] |
488 | fn test_check_unnecessary_braces_in_use_statement() { | 371 | fn test_check_unnecessary_braces_in_use_statement() { |
489 | check_no_diagnostics( | 372 | check_diagnostics( |
490 | r#" | 373 | r#" |
491 | use a; | 374 | use a; |
492 | use a::{c, d::e}; | 375 | use a::{c, d::e}; |
@@ -499,7 +382,7 @@ mod a { | |||
499 | } | 382 | } |
500 | "#, | 383 | "#, |
501 | ); | 384 | ); |
502 | check_no_diagnostics( | 385 | check_diagnostics( |
503 | r#" | 386 | r#" |
504 | use a; | 387 | use a; |
505 | use a::{ | 388 | use a::{ |
@@ -585,138 +468,31 @@ mod a { | |||
585 | } | 468 | } |
586 | 469 | ||
587 | #[test] | 470 | #[test] |
588 | fn unlinked_file_prepend_first_item() { | 471 | fn import_extern_crate_clash_with_inner_item() { |
589 | cov_mark::check!(unlinked_file_prepend_before_first_item); | 472 | // This is more of a resolver test, but doesn't really work with the hir_def testsuite. |
590 | // Only tests the first one for `pub mod` since the rest are the same | ||
591 | check_fixes( | ||
592 | r#" | ||
593 | //- /main.rs | ||
594 | fn f() {} | ||
595 | //- /foo.rs | ||
596 | $0 | ||
597 | "#, | ||
598 | vec![ | ||
599 | r#" | ||
600 | mod foo; | ||
601 | |||
602 | fn f() {} | ||
603 | "#, | ||
604 | r#" | ||
605 | pub mod foo; | ||
606 | |||
607 | fn f() {} | ||
608 | "#, | ||
609 | ], | ||
610 | ); | ||
611 | } | ||
612 | |||
613 | #[test] | ||
614 | fn unlinked_file_append_mod() { | ||
615 | cov_mark::check!(unlinked_file_append_to_existing_mods); | ||
616 | check_fix( | ||
617 | r#" | ||
618 | //- /main.rs | ||
619 | //! Comment on top | ||
620 | |||
621 | mod preexisting; | ||
622 | |||
623 | mod preexisting2; | ||
624 | |||
625 | struct S; | ||
626 | |||
627 | mod preexisting_bottom;) | ||
628 | //- /foo.rs | ||
629 | $0 | ||
630 | "#, | ||
631 | r#" | ||
632 | //! Comment on top | ||
633 | 473 | ||
634 | mod preexisting; | 474 | check_diagnostics( |
635 | |||
636 | mod preexisting2; | ||
637 | mod foo; | ||
638 | |||
639 | struct S; | ||
640 | |||
641 | mod preexisting_bottom;) | ||
642 | "#, | ||
643 | ); | ||
644 | } | ||
645 | |||
646 | #[test] | ||
647 | fn unlinked_file_insert_in_empty_file() { | ||
648 | cov_mark::check!(unlinked_file_empty_file); | ||
649 | check_fix( | ||
650 | r#" | ||
651 | //- /main.rs | ||
652 | //- /foo.rs | ||
653 | $0 | ||
654 | "#, | ||
655 | r#" | ||
656 | mod foo; | ||
657 | "#, | ||
658 | ); | ||
659 | } | ||
660 | |||
661 | #[test] | ||
662 | fn unlinked_file_old_style_modrs() { | ||
663 | check_fix( | ||
664 | r#" | ||
665 | //- /main.rs | ||
666 | mod submod; | ||
667 | //- /submod/mod.rs | ||
668 | // in mod.rs | ||
669 | //- /submod/foo.rs | ||
670 | $0 | ||
671 | "#, | ||
672 | r#" | ||
673 | // in mod.rs | ||
674 | mod foo; | ||
675 | "#, | ||
676 | ); | ||
677 | } | ||
678 | |||
679 | #[test] | ||
680 | fn unlinked_file_new_style_mod() { | ||
681 | check_fix( | ||
682 | r#" | ||
683 | //- /main.rs | ||
684 | mod submod; | ||
685 | //- /submod.rs | ||
686 | //- /submod/foo.rs | ||
687 | $0 | ||
688 | "#, | ||
689 | r#" | 475 | r#" |
690 | mod foo; | 476 | //- /lib.rs crate:lib deps:jwt |
691 | "#, | 477 | mod permissions; |
692 | ); | ||
693 | } | ||
694 | 478 | ||
695 | #[test] | 479 | use permissions::jwt; |
696 | fn unlinked_file_with_cfg_off() { | ||
697 | cov_mark::check!(unlinked_file_skip_fix_when_mod_already_exists); | ||
698 | check_no_fix( | ||
699 | r#" | ||
700 | //- /main.rs | ||
701 | #[cfg(never)] | ||
702 | mod foo; | ||
703 | 480 | ||
704 | //- /foo.rs | 481 | fn f() { |
705 | $0 | 482 | fn inner() {} |
706 | "#, | 483 | jwt::Claims {}; // should resolve to the local one with 0 fields, and not get a diagnostic |
707 | ); | 484 | } |
708 | } | ||
709 | 485 | ||
710 | #[test] | 486 | //- /permissions.rs |
711 | fn unlinked_file_with_cfg_on() { | 487 | pub mod jwt { |
712 | check_no_diagnostics( | 488 | pub struct Claims {} |
713 | r#" | 489 | } |
714 | //- /main.rs | ||
715 | #[cfg(not(never))] | ||
716 | mod foo; | ||
717 | 490 | ||
718 | //- /foo.rs | 491 | //- /jwt/lib.rs crate:jwt |
719 | "#, | 492 | pub struct Claims { |
493 | field: u8, | ||
494 | } | ||
495 | "#, | ||
720 | ); | 496 | ); |
721 | } | 497 | } |
722 | } | 498 | } |
diff --git a/crates/ide/src/diagnostics/break_outside_of_loop.rs b/crates/ide/src/diagnostics/break_outside_of_loop.rs new file mode 100644 index 000000000..80e68f3cc --- /dev/null +++ b/crates/ide/src/diagnostics/break_outside_of_loop.rs | |||
@@ -0,0 +1,30 @@ | |||
1 | use crate::diagnostics::{Diagnostic, DiagnosticsContext}; | ||
2 | |||
3 | // Diagnostic: break-outside-of-loop | ||
4 | // | ||
5 | // This diagnostic is triggered if the `break` keyword is used outside of a loop. | ||
6 | pub(super) fn break_outside_of_loop( | ||
7 | ctx: &DiagnosticsContext<'_>, | ||
8 | d: &hir::BreakOutsideOfLoop, | ||
9 | ) -> Diagnostic { | ||
10 | Diagnostic::new( | ||
11 | "break-outside-of-loop", | ||
12 | "break outside of loop", | ||
13 | ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, | ||
14 | ) | ||
15 | } | ||
16 | |||
17 | #[cfg(test)] | ||
18 | mod tests { | ||
19 | use crate::diagnostics::tests::check_diagnostics; | ||
20 | |||
21 | #[test] | ||
22 | fn break_outside_of_loop() { | ||
23 | check_diagnostics( | ||
24 | r#" | ||
25 | fn foo() { break; } | ||
26 | //^^^^^ break outside of loop | ||
27 | "#, | ||
28 | ); | ||
29 | } | ||
30 | } | ||
diff --git a/crates/ide/src/diagnostics/field_shorthand.rs b/crates/ide/src/diagnostics/field_shorthand.rs index 01bd2dba6..c7f4dab8e 100644 --- a/crates/ide/src/diagnostics/field_shorthand.rs +++ b/crates/ide/src/diagnostics/field_shorthand.rs | |||
@@ -5,7 +5,7 @@ use ide_db::{base_db::FileId, source_change::SourceChange}; | |||
5 | use syntax::{ast, match_ast, AstNode, SyntaxNode}; | 5 | use syntax::{ast, match_ast, AstNode, SyntaxNode}; |
6 | use text_edit::TextEdit; | 6 | use text_edit::TextEdit; |
7 | 7 | ||
8 | use crate::{diagnostics::fix, Diagnostic}; | 8 | use crate::{diagnostics::fix, Diagnostic, Severity}; |
9 | 9 | ||
10 | pub(super) fn check(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) { | 10 | pub(super) fn check(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) { |
11 | match_ast! { | 11 | match_ast! { |
@@ -46,7 +46,8 @@ fn check_expr_field_shorthand( | |||
46 | 46 | ||
47 | let field_range = record_field.syntax().text_range(); | 47 | let field_range = record_field.syntax().text_range(); |
48 | acc.push( | 48 | acc.push( |
49 | Diagnostic::hint(field_range, "Shorthand struct initialization".to_string()) | 49 | Diagnostic::new("use-field-shorthand", "Shorthand struct initialization", field_range) |
50 | .severity(Severity::WeakWarning) | ||
50 | .with_fixes(Some(vec![fix( | 51 | .with_fixes(Some(vec![fix( |
51 | "use_expr_field_shorthand", | 52 | "use_expr_field_shorthand", |
52 | "Use struct shorthand initialization", | 53 | "Use struct shorthand initialization", |
@@ -85,30 +86,32 @@ fn check_pat_field_shorthand( | |||
85 | let edit = edit_builder.finish(); | 86 | let edit = edit_builder.finish(); |
86 | 87 | ||
87 | let field_range = record_pat_field.syntax().text_range(); | 88 | let field_range = record_pat_field.syntax().text_range(); |
88 | acc.push(Diagnostic::hint(field_range, "Shorthand struct pattern".to_string()).with_fixes( | 89 | acc.push( |
89 | Some(vec![fix( | 90 | Diagnostic::new("use-field-shorthand", "Shorthand struct pattern", field_range) |
90 | "use_pat_field_shorthand", | 91 | .severity(Severity::WeakWarning) |
91 | "Use struct field shorthand", | 92 | .with_fixes(Some(vec![fix( |
92 | SourceChange::from_text_edit(file_id, edit), | 93 | "use_pat_field_shorthand", |
93 | field_range, | 94 | "Use struct field shorthand", |
94 | )]), | 95 | SourceChange::from_text_edit(file_id, edit), |
95 | )); | 96 | field_range, |
97 | )])), | ||
98 | ); | ||
96 | } | 99 | } |
97 | } | 100 | } |
98 | 101 | ||
99 | #[cfg(test)] | 102 | #[cfg(test)] |
100 | mod tests { | 103 | mod tests { |
101 | use crate::diagnostics::tests::{check_fix, check_no_diagnostics}; | 104 | use crate::diagnostics::tests::{check_diagnostics, check_fix}; |
102 | 105 | ||
103 | #[test] | 106 | #[test] |
104 | fn test_check_expr_field_shorthand() { | 107 | fn test_check_expr_field_shorthand() { |
105 | check_no_diagnostics( | 108 | check_diagnostics( |
106 | r#" | 109 | r#" |
107 | struct A { a: &'static str } | 110 | struct A { a: &'static str } |
108 | fn main() { A { a: "hello" } } | 111 | fn main() { A { a: "hello" } } |
109 | "#, | 112 | "#, |
110 | ); | 113 | ); |
111 | check_no_diagnostics( | 114 | check_diagnostics( |
112 | r#" | 115 | r#" |
113 | struct A(usize); | 116 | struct A(usize); |
114 | fn main() { A { 0: 0 } } | 117 | fn main() { A { 0: 0 } } |
@@ -154,13 +157,13 @@ fn main() { | |||
154 | 157 | ||
155 | #[test] | 158 | #[test] |
156 | fn test_check_pat_field_shorthand() { | 159 | fn test_check_pat_field_shorthand() { |
157 | check_no_diagnostics( | 160 | check_diagnostics( |
158 | r#" | 161 | r#" |
159 | struct A { a: &'static str } | 162 | struct A { a: &'static str } |
160 | fn f(a: A) { let A { a: hello } = a; } | 163 | fn f(a: A) { let A { a: hello } = a; } |
161 | "#, | 164 | "#, |
162 | ); | 165 | ); |
163 | check_no_diagnostics( | 166 | check_diagnostics( |
164 | r#" | 167 | r#" |
165 | struct A(usize); | 168 | struct A(usize); |
166 | fn f(a: A) { let A { 0: 0 } = a; } | 169 | fn f(a: A) { let A { 0: 0 } = a; } |
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs deleted file mode 100644 index 258ac6974..000000000 --- a/crates/ide/src/diagnostics/fixes.rs +++ /dev/null | |||
@@ -1,31 +0,0 @@ | |||
1 | //! Provides a way to attach fixes to the diagnostics. | ||
2 | //! The same module also has all curret custom fixes for the diagnostics implemented. | ||
3 | mod change_case; | ||
4 | mod create_field; | ||
5 | mod fill_missing_fields; | ||
6 | mod remove_semicolon; | ||
7 | mod replace_with_find_map; | ||
8 | mod unresolved_module; | ||
9 | mod wrap_tail_expr; | ||
10 | |||
11 | use hir::{diagnostics::Diagnostic, Semantics}; | ||
12 | use ide_assists::AssistResolveStrategy; | ||
13 | use ide_db::RootDatabase; | ||
14 | |||
15 | use crate::Assist; | ||
16 | |||
17 | /// A [Diagnostic] that potentially has some fixes available. | ||
18 | /// | ||
19 | /// [Diagnostic]: hir::diagnostics::Diagnostic | ||
20 | pub(crate) trait DiagnosticWithFixes: Diagnostic { | ||
21 | /// `resolve` determines if the diagnostic should fill in the `edit` field | ||
22 | /// of the assist. | ||
23 | /// | ||
24 | /// If `resolve` is false, the edit will be computed later, on demand, and | ||
25 | /// can be omitted. | ||
26 | fn fixes( | ||
27 | &self, | ||
28 | sema: &Semantics<RootDatabase>, | ||
29 | _resolve: &AssistResolveStrategy, | ||
30 | ) -> Option<Vec<Assist>>; | ||
31 | } | ||
diff --git a/crates/ide/src/diagnostics/fixes/change_case.rs b/crates/ide/src/diagnostics/fixes/change_case.rs deleted file mode 100644 index 42be3375f..000000000 --- a/crates/ide/src/diagnostics/fixes/change_case.rs +++ /dev/null | |||
@@ -1,155 +0,0 @@ | |||
1 | use hir::{db::AstDatabase, diagnostics::IncorrectCase, InFile, Semantics}; | ||
2 | use ide_assists::{Assist, AssistResolveStrategy}; | ||
3 | use ide_db::{base_db::FilePosition, RootDatabase}; | ||
4 | use syntax::AstNode; | ||
5 | |||
6 | use crate::{ | ||
7 | diagnostics::{unresolved_fix, DiagnosticWithFixes}, | ||
8 | references::rename::rename_with_semantics, | ||
9 | }; | ||
10 | |||
11 | impl DiagnosticWithFixes for IncorrectCase { | ||
12 | fn fixes( | ||
13 | &self, | ||
14 | sema: &Semantics<RootDatabase>, | ||
15 | resolve: &AssistResolveStrategy, | ||
16 | ) -> Option<Vec<Assist>> { | ||
17 | let root = sema.db.parse_or_expand(self.file)?; | ||
18 | let name_node = self.ident.to_node(&root); | ||
19 | |||
20 | let name_node = InFile::new(self.file, name_node.syntax()); | ||
21 | let frange = name_node.original_file_range(sema.db); | ||
22 | let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; | ||
23 | |||
24 | let label = format!("Rename to {}", self.suggested_text); | ||
25 | let mut res = unresolved_fix("change_case", &label, frange.range); | ||
26 | if resolve.should_resolve(&res.id) { | ||
27 | let source_change = rename_with_semantics(sema, file_position, &self.suggested_text); | ||
28 | res.source_change = Some(source_change.ok().unwrap_or_default()); | ||
29 | } | ||
30 | |||
31 | Some(vec![res]) | ||
32 | } | ||
33 | } | ||
34 | |||
35 | #[cfg(test)] | ||
36 | mod change_case { | ||
37 | use crate::{ | ||
38 | diagnostics::tests::{check_fix, check_no_diagnostics}, | ||
39 | fixture, AssistResolveStrategy, DiagnosticsConfig, | ||
40 | }; | ||
41 | |||
42 | #[test] | ||
43 | fn test_rename_incorrect_case() { | ||
44 | check_fix( | ||
45 | r#" | ||
46 | pub struct test_struct$0 { one: i32 } | ||
47 | |||
48 | pub fn some_fn(val: test_struct) -> test_struct { | ||
49 | test_struct { one: val.one + 1 } | ||
50 | } | ||
51 | "#, | ||
52 | r#" | ||
53 | pub struct TestStruct { one: i32 } | ||
54 | |||
55 | pub fn some_fn(val: TestStruct) -> TestStruct { | ||
56 | TestStruct { one: val.one + 1 } | ||
57 | } | ||
58 | "#, | ||
59 | ); | ||
60 | |||
61 | check_fix( | ||
62 | r#" | ||
63 | pub fn some_fn(NonSnakeCase$0: u8) -> u8 { | ||
64 | NonSnakeCase | ||
65 | } | ||
66 | "#, | ||
67 | r#" | ||
68 | pub fn some_fn(non_snake_case: u8) -> u8 { | ||
69 | non_snake_case | ||
70 | } | ||
71 | "#, | ||
72 | ); | ||
73 | |||
74 | check_fix( | ||
75 | r#" | ||
76 | pub fn SomeFn$0(val: u8) -> u8 { | ||
77 | if val != 0 { SomeFn(val - 1) } else { val } | ||
78 | } | ||
79 | "#, | ||
80 | r#" | ||
81 | pub fn some_fn(val: u8) -> u8 { | ||
82 | if val != 0 { some_fn(val - 1) } else { val } | ||
83 | } | ||
84 | "#, | ||
85 | ); | ||
86 | |||
87 | check_fix( | ||
88 | r#" | ||
89 | fn some_fn() { | ||
90 | let whatAWeird_Formatting$0 = 10; | ||
91 | another_func(whatAWeird_Formatting); | ||
92 | } | ||
93 | "#, | ||
94 | r#" | ||
95 | fn some_fn() { | ||
96 | let what_a_weird_formatting = 10; | ||
97 | another_func(what_a_weird_formatting); | ||
98 | } | ||
99 | "#, | ||
100 | ); | ||
101 | } | ||
102 | |||
103 | #[test] | ||
104 | fn test_uppercase_const_no_diagnostics() { | ||
105 | check_no_diagnostics( | ||
106 | r#" | ||
107 | fn foo() { | ||
108 | const ANOTHER_ITEM$0: &str = "some_item"; | ||
109 | } | ||
110 | "#, | ||
111 | ); | ||
112 | } | ||
113 | |||
114 | #[test] | ||
115 | fn test_rename_incorrect_case_struct_method() { | ||
116 | check_fix( | ||
117 | r#" | ||
118 | pub struct TestStruct; | ||
119 | |||
120 | impl TestStruct { | ||
121 | pub fn SomeFn$0() -> TestStruct { | ||
122 | TestStruct | ||
123 | } | ||
124 | } | ||
125 | "#, | ||
126 | r#" | ||
127 | pub struct TestStruct; | ||
128 | |||
129 | impl TestStruct { | ||
130 | pub fn some_fn() -> TestStruct { | ||
131 | TestStruct | ||
132 | } | ||
133 | } | ||
134 | "#, | ||
135 | ); | ||
136 | } | ||
137 | |||
138 | #[test] | ||
139 | fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() { | ||
140 | let input = r#"fn FOO$0() {}"#; | ||
141 | let expected = r#"fn foo() {}"#; | ||
142 | |||
143 | let (analysis, file_position) = fixture::position(input); | ||
144 | let diagnostics = analysis | ||
145 | .diagnostics( | ||
146 | &DiagnosticsConfig::default(), | ||
147 | AssistResolveStrategy::All, | ||
148 | file_position.file_id, | ||
149 | ) | ||
150 | .unwrap(); | ||
151 | assert_eq!(diagnostics.len(), 1); | ||
152 | |||
153 | check_fix(input, expected); | ||
154 | } | ||
155 | } | ||
diff --git a/crates/ide/src/diagnostics/fixes/fill_missing_fields.rs b/crates/ide/src/diagnostics/fixes/fill_missing_fields.rs deleted file mode 100644 index b5dd64c08..000000000 --- a/crates/ide/src/diagnostics/fixes/fill_missing_fields.rs +++ /dev/null | |||
@@ -1,217 +0,0 @@ | |||
1 | use hir::{db::AstDatabase, diagnostics::MissingFields, Semantics}; | ||
2 | use ide_assists::AssistResolveStrategy; | ||
3 | use ide_db::{source_change::SourceChange, RootDatabase}; | ||
4 | use syntax::{algo, ast::make, AstNode}; | ||
5 | use text_edit::TextEdit; | ||
6 | |||
7 | use crate::{ | ||
8 | diagnostics::{fix, fixes::DiagnosticWithFixes}, | ||
9 | Assist, | ||
10 | }; | ||
11 | |||
12 | impl DiagnosticWithFixes for MissingFields { | ||
13 | fn fixes( | ||
14 | &self, | ||
15 | sema: &Semantics<RootDatabase>, | ||
16 | _resolve: &AssistResolveStrategy, | ||
17 | ) -> Option<Vec<Assist>> { | ||
18 | // Note that although we could add a diagnostics to | ||
19 | // fill the missing tuple field, e.g : | ||
20 | // `struct A(usize);` | ||
21 | // `let a = A { 0: () }` | ||
22 | // but it is uncommon usage and it should not be encouraged. | ||
23 | if self.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) { | ||
24 | return None; | ||
25 | } | ||
26 | |||
27 | let root = sema.db.parse_or_expand(self.file)?; | ||
28 | let field_list_parent = self.field_list_parent.to_node(&root); | ||
29 | let old_field_list = field_list_parent.record_expr_field_list()?; | ||
30 | let new_field_list = old_field_list.clone_for_update(); | ||
31 | for f in self.missed_fields.iter() { | ||
32 | let field = | ||
33 | make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit())) | ||
34 | .clone_for_update(); | ||
35 | new_field_list.add_field(field); | ||
36 | } | ||
37 | |||
38 | let edit = { | ||
39 | let mut builder = TextEdit::builder(); | ||
40 | algo::diff(&old_field_list.syntax(), &new_field_list.syntax()) | ||
41 | .into_text_edit(&mut builder); | ||
42 | builder.finish() | ||
43 | }; | ||
44 | Some(vec![fix( | ||
45 | "fill_missing_fields", | ||
46 | "Fill struct fields", | ||
47 | SourceChange::from_text_edit(self.file.original_file(sema.db), edit), | ||
48 | sema.original_range(&field_list_parent.syntax()).range, | ||
49 | )]) | ||
50 | } | ||
51 | } | ||
52 | |||
53 | #[cfg(test)] | ||
54 | mod tests { | ||
55 | use crate::diagnostics::tests::{check_fix, check_no_diagnostics}; | ||
56 | |||
57 | #[test] | ||
58 | fn test_fill_struct_fields_empty() { | ||
59 | check_fix( | ||
60 | r#" | ||
61 | struct TestStruct { one: i32, two: i64 } | ||
62 | |||
63 | fn test_fn() { | ||
64 | let s = TestStruct {$0}; | ||
65 | } | ||
66 | "#, | ||
67 | r#" | ||
68 | struct TestStruct { one: i32, two: i64 } | ||
69 | |||
70 | fn test_fn() { | ||
71 | let s = TestStruct { one: (), two: () }; | ||
72 | } | ||
73 | "#, | ||
74 | ); | ||
75 | } | ||
76 | |||
77 | #[test] | ||
78 | fn test_fill_struct_fields_self() { | ||
79 | check_fix( | ||
80 | r#" | ||
81 | struct TestStruct { one: i32 } | ||
82 | |||
83 | impl TestStruct { | ||
84 | fn test_fn() { let s = Self {$0}; } | ||
85 | } | ||
86 | "#, | ||
87 | r#" | ||
88 | struct TestStruct { one: i32 } | ||
89 | |||
90 | impl TestStruct { | ||
91 | fn test_fn() { let s = Self { one: () }; } | ||
92 | } | ||
93 | "#, | ||
94 | ); | ||
95 | } | ||
96 | |||
97 | #[test] | ||
98 | fn test_fill_struct_fields_enum() { | ||
99 | check_fix( | ||
100 | r#" | ||
101 | enum Expr { | ||
102 | Bin { lhs: Box<Expr>, rhs: Box<Expr> } | ||
103 | } | ||
104 | |||
105 | impl Expr { | ||
106 | fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { | ||
107 | Expr::Bin {$0 } | ||
108 | } | ||
109 | } | ||
110 | "#, | ||
111 | r#" | ||
112 | enum Expr { | ||
113 | Bin { lhs: Box<Expr>, rhs: Box<Expr> } | ||
114 | } | ||
115 | |||
116 | impl Expr { | ||
117 | fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { | ||
118 | Expr::Bin { lhs: (), rhs: () } | ||
119 | } | ||
120 | } | ||
121 | "#, | ||
122 | ); | ||
123 | } | ||
124 | |||
125 | #[test] | ||
126 | fn test_fill_struct_fields_partial() { | ||
127 | check_fix( | ||
128 | r#" | ||
129 | struct TestStruct { one: i32, two: i64 } | ||
130 | |||
131 | fn test_fn() { | ||
132 | let s = TestStruct{ two: 2$0 }; | ||
133 | } | ||
134 | "#, | ||
135 | r" | ||
136 | struct TestStruct { one: i32, two: i64 } | ||
137 | |||
138 | fn test_fn() { | ||
139 | let s = TestStruct{ two: 2, one: () }; | ||
140 | } | ||
141 | ", | ||
142 | ); | ||
143 | } | ||
144 | |||
145 | #[test] | ||
146 | fn test_fill_struct_fields_raw_ident() { | ||
147 | check_fix( | ||
148 | r#" | ||
149 | struct TestStruct { r#type: u8 } | ||
150 | |||
151 | fn test_fn() { | ||
152 | TestStruct { $0 }; | ||
153 | } | ||
154 | "#, | ||
155 | r" | ||
156 | struct TestStruct { r#type: u8 } | ||
157 | |||
158 | fn test_fn() { | ||
159 | TestStruct { r#type: () }; | ||
160 | } | ||
161 | ", | ||
162 | ); | ||
163 | } | ||
164 | |||
165 | #[test] | ||
166 | fn test_fill_struct_fields_no_diagnostic() { | ||
167 | check_no_diagnostics( | ||
168 | r#" | ||
169 | struct TestStruct { one: i32, two: i64 } | ||
170 | |||
171 | fn test_fn() { | ||
172 | let one = 1; | ||
173 | let s = TestStruct{ one, two: 2 }; | ||
174 | } | ||
175 | "#, | ||
176 | ); | ||
177 | } | ||
178 | |||
179 | #[test] | ||
180 | fn test_fill_struct_fields_no_diagnostic_on_spread() { | ||
181 | check_no_diagnostics( | ||
182 | r#" | ||
183 | struct TestStruct { one: i32, two: i64 } | ||
184 | |||
185 | fn test_fn() { | ||
186 | let one = 1; | ||
187 | let s = TestStruct{ ..a }; | ||
188 | } | ||
189 | "#, | ||
190 | ); | ||
191 | } | ||
192 | |||
193 | #[test] | ||
194 | fn test_fill_struct_fields_blank_line() { | ||
195 | check_fix( | ||
196 | r#" | ||
197 | struct S { a: (), b: () } | ||
198 | |||
199 | fn f() { | ||
200 | S { | ||
201 | $0 | ||
202 | }; | ||
203 | } | ||
204 | "#, | ||
205 | r#" | ||
206 | struct S { a: (), b: () } | ||
207 | |||
208 | fn f() { | ||
209 | S { | ||
210 | a: (), | ||
211 | b: (), | ||
212 | }; | ||
213 | } | ||
214 | "#, | ||
215 | ); | ||
216 | } | ||
217 | } | ||
diff --git a/crates/ide/src/diagnostics/fixes/remove_semicolon.rs b/crates/ide/src/diagnostics/fixes/remove_semicolon.rs deleted file mode 100644 index f1724d479..000000000 --- a/crates/ide/src/diagnostics/fixes/remove_semicolon.rs +++ /dev/null | |||
@@ -1,41 +0,0 @@ | |||
1 | use hir::{db::AstDatabase, diagnostics::RemoveThisSemicolon, Semantics}; | ||
2 | use ide_assists::{Assist, AssistResolveStrategy}; | ||
3 | use ide_db::{source_change::SourceChange, RootDatabase}; | ||
4 | use syntax::{ast, AstNode}; | ||
5 | use text_edit::TextEdit; | ||
6 | |||
7 | use crate::diagnostics::{fix, DiagnosticWithFixes}; | ||
8 | |||
9 | impl DiagnosticWithFixes for RemoveThisSemicolon { | ||
10 | fn fixes( | ||
11 | &self, | ||
12 | sema: &Semantics<RootDatabase>, | ||
13 | _resolve: &AssistResolveStrategy, | ||
14 | ) -> Option<Vec<Assist>> { | ||
15 | let root = sema.db.parse_or_expand(self.file)?; | ||
16 | |||
17 | let semicolon = self | ||
18 | .expr | ||
19 | .to_node(&root) | ||
20 | .syntax() | ||
21 | .parent() | ||
22 | .and_then(ast::ExprStmt::cast) | ||
23 | .and_then(|expr| expr.semicolon_token())? | ||
24 | .text_range(); | ||
25 | |||
26 | let edit = TextEdit::delete(semicolon); | ||
27 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); | ||
28 | |||
29 | Some(vec![fix("remove_semicolon", "Remove this semicolon", source_change, semicolon)]) | ||
30 | } | ||
31 | } | ||
32 | |||
33 | #[cfg(test)] | ||
34 | mod tests { | ||
35 | use crate::diagnostics::tests::check_fix; | ||
36 | |||
37 | #[test] | ||
38 | fn remove_semicolon() { | ||
39 | check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#); | ||
40 | } | ||
41 | } | ||
diff --git a/crates/ide/src/diagnostics/fixes/replace_with_find_map.rs b/crates/ide/src/diagnostics/fixes/replace_with_find_map.rs deleted file mode 100644 index 444bf563b..000000000 --- a/crates/ide/src/diagnostics/fixes/replace_with_find_map.rs +++ /dev/null | |||
@@ -1,84 +0,0 @@ | |||
1 | use hir::{db::AstDatabase, diagnostics::ReplaceFilterMapNextWithFindMap, Semantics}; | ||
2 | use ide_assists::{Assist, AssistResolveStrategy}; | ||
3 | use ide_db::{source_change::SourceChange, RootDatabase}; | ||
4 | use syntax::{ | ||
5 | ast::{self, ArgListOwner}, | ||
6 | AstNode, TextRange, | ||
7 | }; | ||
8 | use text_edit::TextEdit; | ||
9 | |||
10 | use crate::diagnostics::{fix, DiagnosticWithFixes}; | ||
11 | |||
12 | impl DiagnosticWithFixes for ReplaceFilterMapNextWithFindMap { | ||
13 | fn fixes( | ||
14 | &self, | ||
15 | sema: &Semantics<RootDatabase>, | ||
16 | _resolve: &AssistResolveStrategy, | ||
17 | ) -> Option<Vec<Assist>> { | ||
18 | let root = sema.db.parse_or_expand(self.file)?; | ||
19 | let next_expr = self.next_expr.to_node(&root); | ||
20 | let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?; | ||
21 | |||
22 | let filter_map_call = ast::MethodCallExpr::cast(next_call.receiver()?.syntax().clone())?; | ||
23 | let filter_map_name_range = filter_map_call.name_ref()?.ident_token()?.text_range(); | ||
24 | let filter_map_args = filter_map_call.arg_list()?; | ||
25 | |||
26 | let range_to_replace = | ||
27 | TextRange::new(filter_map_name_range.start(), next_expr.syntax().text_range().end()); | ||
28 | let replacement = format!("find_map{}", filter_map_args.syntax().text()); | ||
29 | let trigger_range = next_expr.syntax().text_range(); | ||
30 | |||
31 | let edit = TextEdit::replace(range_to_replace, replacement); | ||
32 | |||
33 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); | ||
34 | |||
35 | Some(vec![fix( | ||
36 | "replace_with_find_map", | ||
37 | "Replace filter_map(..).next() with find_map()", | ||
38 | source_change, | ||
39 | trigger_range, | ||
40 | )]) | ||
41 | } | ||
42 | } | ||
43 | |||
44 | #[cfg(test)] | ||
45 | mod tests { | ||
46 | use crate::diagnostics::tests::check_fix; | ||
47 | |||
48 | #[test] | ||
49 | fn replace_with_wind_map() { | ||
50 | check_fix( | ||
51 | r#" | ||
52 | //- /main.rs crate:main deps:core | ||
53 | use core::iter::Iterator; | ||
54 | use core::option::Option::{self, Some, None}; | ||
55 | fn foo() { | ||
56 | let m = [1, 2, 3].iter().$0filter_map(|x| if *x == 2 { Some (4) } else { None }).next(); | ||
57 | } | ||
58 | //- /core/lib.rs crate:core | ||
59 | pub mod option { | ||
60 | pub enum Option<T> { Some(T), None } | ||
61 | } | ||
62 | pub mod iter { | ||
63 | pub trait Iterator { | ||
64 | type Item; | ||
65 | fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap } | ||
66 | fn next(&mut self) -> Option<Self::Item>; | ||
67 | } | ||
68 | pub struct FilterMap {} | ||
69 | impl Iterator for FilterMap { | ||
70 | type Item = i32; | ||
71 | fn next(&mut self) -> i32 { 7 } | ||
72 | } | ||
73 | } | ||
74 | "#, | ||
75 | r#" | ||
76 | use core::iter::Iterator; | ||
77 | use core::option::Option::{self, Some, None}; | ||
78 | fn foo() { | ||
79 | let m = [1, 2, 3].iter().find_map(|x| if *x == 2 { Some (4) } else { None }); | ||
80 | } | ||
81 | "#, | ||
82 | ) | ||
83 | } | ||
84 | } | ||
diff --git a/crates/ide/src/diagnostics/inactive_code.rs b/crates/ide/src/diagnostics/inactive_code.rs new file mode 100644 index 000000000..d9d3e88c1 --- /dev/null +++ b/crates/ide/src/diagnostics/inactive_code.rs | |||
@@ -0,0 +1,119 @@ | |||
1 | use cfg::DnfExpr; | ||
2 | use stdx::format_to; | ||
3 | |||
4 | use 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. | ||
12 | pub(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)] | ||
39 | mod tests { | ||
40 | use crate::{diagnostics::tests::check_diagnostics_with_config, DiagnosticsConfig}; | ||
41 | |||
42 | pub(crate) fn check(ra_fixture: &str) { | ||
43 | let config = DiagnosticsConfig::default(); | ||
44 | check_diagnostics_with_config(config, ra_fixture) | ||
45 | } | ||
46 | |||
47 | #[test] | ||
48 | fn cfg_diagnostics() { | ||
49 | check( | ||
50 | r#" | ||
51 | fn f() { | ||
52 | // The three g̶e̶n̶d̶e̶r̶s̶ statements: | ||
53 | |||
54 | #[cfg(a)] fn f() {} // Item statement | ||
55 | //^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | ||
56 | #[cfg(a)] {} // Expression statement | ||
57 | //^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | ||
58 | #[cfg(a)] let x = 0; // let statement | ||
59 | //^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | ||
60 | |||
61 | abc(#[cfg(a)] 0); | ||
62 | //^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | ||
63 | let x = Struct { | ||
64 | #[cfg(a)] f: 0, | ||
65 | //^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | ||
66 | }; | ||
67 | match () { | ||
68 | () => (), | ||
69 | #[cfg(a)] () => (), | ||
70 | //^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | ||
71 | } | ||
72 | |||
73 | #[cfg(a)] 0 // Trailing expression of block | ||
74 | //^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | ||
75 | } | ||
76 | "#, | ||
77 | ); | ||
78 | } | ||
79 | |||
80 | #[test] | ||
81 | fn inactive_item() { | ||
82 | // Additional tests in `cfg` crate. This only tests disabled cfgs. | ||
83 | |||
84 | check( | ||
85 | r#" | ||
86 | #[cfg(no)] pub fn f() {} | ||
87 | //^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled | ||
88 | |||
89 | #[cfg(no)] #[cfg(no2)] mod m; | ||
90 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no and no2 are disabled | ||
91 | |||
92 | #[cfg(all(not(a), b))] enum E {} | ||
93 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: b is disabled | ||
94 | |||
95 | #[cfg(feature = "std")] use std; | ||
96 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: feature = "std" is disabled | ||
97 | "#, | ||
98 | ); | ||
99 | } | ||
100 | |||
101 | /// Tests that `cfg` attributes behind `cfg_attr` is handled properly. | ||
102 | #[test] | ||
103 | fn inactive_via_cfg_attr() { | ||
104 | cov_mark::check!(cfg_attr_active); | ||
105 | check( | ||
106 | r#" | ||
107 | #[cfg_attr(not(never), cfg(no))] fn f() {} | ||
108 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled | ||
109 | |||
110 | #[cfg_attr(not(never), cfg(not(no)))] fn f() {} | ||
111 | |||
112 | #[cfg_attr(never, cfg(no))] fn g() {} | ||
113 | |||
114 | #[cfg_attr(not(never), inline, cfg(no))] fn h() {} | ||
115 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled | ||
116 | "#, | ||
117 | ); | ||
118 | } | ||
119 | } | ||
diff --git a/crates/ide/src/diagnostics/incorrect_case.rs b/crates/ide/src/diagnostics/incorrect_case.rs new file mode 100644 index 000000000..832394400 --- /dev/null +++ b/crates/ide/src/diagnostics/incorrect_case.rs | |||
@@ -0,0 +1,488 @@ | |||
1 | use hir::{db::AstDatabase, InFile}; | ||
2 | use ide_assists::Assist; | ||
3 | use ide_db::base_db::FilePosition; | ||
4 | use syntax::AstNode; | ||
5 | |||
6 | use crate::{ | ||
7 | diagnostics::{unresolved_fix, Diagnostic, DiagnosticsContext}, | ||
8 | references::rename::rename_with_semantics, | ||
9 | Severity, | ||
10 | }; | ||
11 | |||
12 | // Diagnostic: incorrect-ident-case | ||
13 | // | ||
14 | // This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. | ||
15 | pub(super) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic { | ||
16 | Diagnostic::new( | ||
17 | "incorrect-ident-case", | ||
18 | format!( | ||
19 | "{} `{}` should have {} name, e.g. `{}`", | ||
20 | d.ident_type, d.ident_text, d.expected_case, d.suggested_text | ||
21 | ), | ||
22 | ctx.sema.diagnostics_display_range(InFile::new(d.file, d.ident.clone().into())).range, | ||
23 | ) | ||
24 | .severity(Severity::WeakWarning) | ||
25 | .with_fixes(fixes(ctx, d)) | ||
26 | } | ||
27 | |||
28 | fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option<Vec<Assist>> { | ||
29 | let root = ctx.sema.db.parse_or_expand(d.file)?; | ||
30 | let name_node = d.ident.to_node(&root); | ||
31 | |||
32 | let name_node = InFile::new(d.file, name_node.syntax()); | ||
33 | let frange = name_node.original_file_range(ctx.sema.db); | ||
34 | let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; | ||
35 | |||
36 | let label = format!("Rename to {}", d.suggested_text); | ||
37 | let mut res = unresolved_fix("change_case", &label, frange.range); | ||
38 | if ctx.resolve.should_resolve(&res.id) { | ||
39 | let source_change = rename_with_semantics(&ctx.sema, file_position, &d.suggested_text); | ||
40 | res.source_change = Some(source_change.ok().unwrap_or_default()); | ||
41 | } | ||
42 | |||
43 | Some(vec![res]) | ||
44 | } | ||
45 | |||
46 | #[cfg(test)] | ||
47 | mod change_case { | ||
48 | use crate::{ | ||
49 | diagnostics::tests::{check_diagnostics, check_fix}, | ||
50 | fixture, AssistResolveStrategy, DiagnosticsConfig, | ||
51 | }; | ||
52 | |||
53 | #[test] | ||
54 | fn test_rename_incorrect_case() { | ||
55 | check_fix( | ||
56 | r#" | ||
57 | pub struct test_struct$0 { one: i32 } | ||
58 | |||
59 | pub fn some_fn(val: test_struct) -> test_struct { | ||
60 | test_struct { one: val.one + 1 } | ||
61 | } | ||
62 | "#, | ||
63 | r#" | ||
64 | pub struct TestStruct { one: i32 } | ||
65 | |||
66 | pub fn some_fn(val: TestStruct) -> TestStruct { | ||
67 | TestStruct { one: val.one + 1 } | ||
68 | } | ||
69 | "#, | ||
70 | ); | ||
71 | |||
72 | check_fix( | ||
73 | r#" | ||
74 | pub fn some_fn(NonSnakeCase$0: u8) -> u8 { | ||
75 | NonSnakeCase | ||
76 | } | ||
77 | "#, | ||
78 | r#" | ||
79 | pub fn some_fn(non_snake_case: u8) -> u8 { | ||
80 | non_snake_case | ||
81 | } | ||
82 | "#, | ||
83 | ); | ||
84 | |||
85 | check_fix( | ||
86 | r#" | ||
87 | pub fn SomeFn$0(val: u8) -> u8 { | ||
88 | if val != 0 { SomeFn(val - 1) } else { val } | ||
89 | } | ||
90 | "#, | ||
91 | r#" | ||
92 | pub fn some_fn(val: u8) -> u8 { | ||
93 | if val != 0 { some_fn(val - 1) } else { val } | ||
94 | } | ||
95 | "#, | ||
96 | ); | ||
97 | |||
98 | check_fix( | ||
99 | r#" | ||
100 | fn some_fn() { | ||
101 | let whatAWeird_Formatting$0 = 10; | ||
102 | another_func(whatAWeird_Formatting); | ||
103 | } | ||
104 | "#, | ||
105 | r#" | ||
106 | fn some_fn() { | ||
107 | let what_a_weird_formatting = 10; | ||
108 | another_func(what_a_weird_formatting); | ||
109 | } | ||
110 | "#, | ||
111 | ); | ||
112 | } | ||
113 | |||
114 | #[test] | ||
115 | fn test_uppercase_const_no_diagnostics() { | ||
116 | check_diagnostics( | ||
117 | r#" | ||
118 | fn foo() { | ||
119 | const ANOTHER_ITEM$0: &str = "some_item"; | ||
120 | } | ||
121 | "#, | ||
122 | ); | ||
123 | } | ||
124 | |||
125 | #[test] | ||
126 | fn test_rename_incorrect_case_struct_method() { | ||
127 | check_fix( | ||
128 | r#" | ||
129 | pub struct TestStruct; | ||
130 | |||
131 | impl TestStruct { | ||
132 | pub fn SomeFn$0() -> TestStruct { | ||
133 | TestStruct | ||
134 | } | ||
135 | } | ||
136 | "#, | ||
137 | r#" | ||
138 | pub struct TestStruct; | ||
139 | |||
140 | impl TestStruct { | ||
141 | pub fn some_fn() -> TestStruct { | ||
142 | TestStruct | ||
143 | } | ||
144 | } | ||
145 | "#, | ||
146 | ); | ||
147 | } | ||
148 | |||
149 | #[test] | ||
150 | fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() { | ||
151 | let input = r#"fn FOO$0() {}"#; | ||
152 | let expected = r#"fn foo() {}"#; | ||
153 | |||
154 | let (analysis, file_position) = fixture::position(input); | ||
155 | let diagnostics = analysis | ||
156 | .diagnostics( | ||
157 | &DiagnosticsConfig::default(), | ||
158 | AssistResolveStrategy::All, | ||
159 | file_position.file_id, | ||
160 | ) | ||
161 | .unwrap(); | ||
162 | assert_eq!(diagnostics.len(), 1); | ||
163 | |||
164 | check_fix(input, expected); | ||
165 | } | ||
166 | |||
167 | #[test] | ||
168 | fn incorrect_function_name() { | ||
169 | check_diagnostics( | ||
170 | r#" | ||
171 | fn NonSnakeCaseName() {} | ||
172 | // ^^^^^^^^^^^^^^^^ Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` | ||
173 | "#, | ||
174 | ); | ||
175 | } | ||
176 | |||
177 | #[test] | ||
178 | fn incorrect_function_params() { | ||
179 | check_diagnostics( | ||
180 | r#" | ||
181 | fn foo(SomeParam: u8) {} | ||
182 | // ^^^^^^^^^ Parameter `SomeParam` should have snake_case name, e.g. `some_param` | ||
183 | |||
184 | fn foo2(ok_param: &str, CAPS_PARAM: u8) {} | ||
185 | // ^^^^^^^^^^ Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param` | ||
186 | "#, | ||
187 | ); | ||
188 | } | ||
189 | |||
190 | #[test] | ||
191 | fn incorrect_variable_names() { | ||
192 | check_diagnostics( | ||
193 | r#" | ||
194 | fn foo() { | ||
195 | let SOME_VALUE = 10; | ||
196 | // ^^^^^^^^^^ Variable `SOME_VALUE` should have snake_case name, e.g. `some_value` | ||
197 | let AnotherValue = 20; | ||
198 | // ^^^^^^^^^^^^ Variable `AnotherValue` should have snake_case name, e.g. `another_value` | ||
199 | } | ||
200 | "#, | ||
201 | ); | ||
202 | } | ||
203 | |||
204 | #[test] | ||
205 | fn incorrect_struct_names() { | ||
206 | check_diagnostics( | ||
207 | r#" | ||
208 | struct non_camel_case_name {} | ||
209 | // ^^^^^^^^^^^^^^^^^^^ Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName` | ||
210 | |||
211 | struct SCREAMING_CASE {} | ||
212 | // ^^^^^^^^^^^^^^ Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase` | ||
213 | "#, | ||
214 | ); | ||
215 | } | ||
216 | |||
217 | #[test] | ||
218 | fn no_diagnostic_for_camel_cased_acronyms_in_struct_name() { | ||
219 | check_diagnostics( | ||
220 | r#" | ||
221 | struct AABB {} | ||
222 | "#, | ||
223 | ); | ||
224 | } | ||
225 | |||
226 | #[test] | ||
227 | fn incorrect_struct_field() { | ||
228 | check_diagnostics( | ||
229 | r#" | ||
230 | struct SomeStruct { SomeField: u8 } | ||
231 | // ^^^^^^^^^ Field `SomeField` should have snake_case name, e.g. `some_field` | ||
232 | "#, | ||
233 | ); | ||
234 | } | ||
235 | |||
236 | #[test] | ||
237 | fn incorrect_enum_names() { | ||
238 | check_diagnostics( | ||
239 | r#" | ||
240 | enum some_enum { Val(u8) } | ||
241 | // ^^^^^^^^^ Enum `some_enum` should have CamelCase name, e.g. `SomeEnum` | ||
242 | |||
243 | enum SOME_ENUM {} | ||
244 | // ^^^^^^^^^ Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum` | ||
245 | "#, | ||
246 | ); | ||
247 | } | ||
248 | |||
249 | #[test] | ||
250 | fn no_diagnostic_for_camel_cased_acronyms_in_enum_name() { | ||
251 | check_diagnostics( | ||
252 | r#" | ||
253 | enum AABB {} | ||
254 | "#, | ||
255 | ); | ||
256 | } | ||
257 | |||
258 | #[test] | ||
259 | fn incorrect_enum_variant_name() { | ||
260 | check_diagnostics( | ||
261 | r#" | ||
262 | enum SomeEnum { SOME_VARIANT(u8) } | ||
263 | // ^^^^^^^^^^^^ Variant `SOME_VARIANT` should have CamelCase name, e.g. `SomeVariant` | ||
264 | "#, | ||
265 | ); | ||
266 | } | ||
267 | |||
268 | #[test] | ||
269 | fn incorrect_const_name() { | ||
270 | check_diagnostics( | ||
271 | r#" | ||
272 | const some_weird_const: u8 = 10; | ||
273 | // ^^^^^^^^^^^^^^^^ Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` | ||
274 | "#, | ||
275 | ); | ||
276 | } | ||
277 | |||
278 | #[test] | ||
279 | fn incorrect_static_name() { | ||
280 | check_diagnostics( | ||
281 | r#" | ||
282 | static some_weird_const: u8 = 10; | ||
283 | // ^^^^^^^^^^^^^^^^ Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` | ||
284 | "#, | ||
285 | ); | ||
286 | } | ||
287 | |||
288 | #[test] | ||
289 | fn fn_inside_impl_struct() { | ||
290 | check_diagnostics( | ||
291 | r#" | ||
292 | struct someStruct; | ||
293 | // ^^^^^^^^^^ Structure `someStruct` should have CamelCase name, e.g. `SomeStruct` | ||
294 | |||
295 | impl someStruct { | ||
296 | fn SomeFunc(&self) { | ||
297 | // ^^^^^^^^ Function `SomeFunc` should have snake_case name, e.g. `some_func` | ||
298 | let WHY_VAR_IS_CAPS = 10; | ||
299 | // ^^^^^^^^^^^^^^^ Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps` | ||
300 | } | ||
301 | } | ||
302 | "#, | ||
303 | ); | ||
304 | } | ||
305 | |||
306 | #[test] | ||
307 | fn no_diagnostic_for_enum_varinats() { | ||
308 | check_diagnostics( | ||
309 | r#" | ||
310 | enum Option { Some, None } | ||
311 | |||
312 | fn main() { | ||
313 | match Option::None { | ||
314 | None => (), | ||
315 | Some => (), | ||
316 | } | ||
317 | } | ||
318 | "#, | ||
319 | ); | ||
320 | } | ||
321 | |||
322 | #[test] | ||
323 | fn non_let_bind() { | ||
324 | check_diagnostics( | ||
325 | r#" | ||
326 | enum Option { Some, None } | ||
327 | |||
328 | fn main() { | ||
329 | match Option::None { | ||
330 | SOME_VAR @ None => (), | ||
331 | // ^^^^^^^^ Variable `SOME_VAR` should have snake_case name, e.g. `some_var` | ||
332 | Some => (), | ||
333 | } | ||
334 | } | ||
335 | "#, | ||
336 | ); | ||
337 | } | ||
338 | |||
339 | #[test] | ||
340 | fn allow_attributes_crate_attr() { | ||
341 | check_diagnostics( | ||
342 | r#" | ||
343 | #![allow(non_snake_case)] | ||
344 | |||
345 | mod F { | ||
346 | fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {} | ||
347 | } | ||
348 | "#, | ||
349 | ); | ||
350 | } | ||
351 | |||
352 | #[test] | ||
353 | #[ignore] | ||
354 | fn bug_trait_inside_fn() { | ||
355 | // FIXME: | ||
356 | // This is broken, and in fact, should not even be looked at by this | ||
357 | // lint in the first place. There's weird stuff going on in the | ||
358 | // collection phase. | ||
359 | // It's currently being brought in by: | ||
360 | // * validate_func on `a` recursing into modules | ||
361 | // * then it finds the trait and then the function while iterating | ||
362 | // through modules | ||
363 | // * then validate_func is called on Dirty | ||
364 | // * ... which then proceeds to look at some unknown module taking no | ||
365 | // attrs from either the impl or the fn a, and then finally to the root | ||
366 | // module | ||
367 | // | ||
368 | // It should find the attribute on the trait, but it *doesn't even see | ||
369 | // the trait* as far as I can tell. | ||
370 | |||
371 | check_diagnostics( | ||
372 | r#" | ||
373 | trait T { fn a(); } | ||
374 | struct U {} | ||
375 | impl T for U { | ||
376 | fn a() { | ||
377 | // this comes out of bitflags, mostly | ||
378 | #[allow(non_snake_case)] | ||
379 | trait __BitFlags { | ||
380 | const HiImAlsoBad: u8 = 2; | ||
381 | #[inline] | ||
382 | fn Dirty(&self) -> bool { | ||
383 | false | ||
384 | } | ||
385 | } | ||
386 | |||
387 | } | ||
388 | } | ||
389 | "#, | ||
390 | ); | ||
391 | } | ||
392 | |||
393 | #[test] | ||
394 | fn infinite_loop_inner_items() { | ||
395 | check_diagnostics( | ||
396 | r#" | ||
397 | fn qualify() { | ||
398 | mod foo { | ||
399 | use super::*; | ||
400 | } | ||
401 | } | ||
402 | "#, | ||
403 | ) | ||
404 | } | ||
405 | |||
406 | #[test] // Issue #8809. | ||
407 | fn parenthesized_parameter() { | ||
408 | check_diagnostics(r#"fn f((O): _) {}"#) | ||
409 | } | ||
410 | |||
411 | #[test] | ||
412 | fn ignores_extern_items() { | ||
413 | cov_mark::check!(extern_func_incorrect_case_ignored); | ||
414 | cov_mark::check!(extern_static_incorrect_case_ignored); | ||
415 | check_diagnostics( | ||
416 | r#" | ||
417 | extern { | ||
418 | fn NonSnakeCaseName(SOME_VAR: u8) -> u8; | ||
419 | pub static SomeStatic: u8 = 10; | ||
420 | } | ||
421 | "#, | ||
422 | ); | ||
423 | } | ||
424 | |||
425 | #[test] | ||
426 | #[ignore] | ||
427 | fn bug_traits_arent_checked() { | ||
428 | // FIXME: Traits and functions in traits aren't currently checked by | ||
429 | // r-a, even though rustc will complain about them. | ||
430 | check_diagnostics( | ||
431 | r#" | ||
432 | trait BAD_TRAIT { | ||
433 | // ^^^^^^^^^ Trait `BAD_TRAIT` should have CamelCase name, e.g. `BadTrait` | ||
434 | fn BAD_FUNCTION(); | ||
435 | // ^^^^^^^^^^^^ Function `BAD_FUNCTION` should have snake_case name, e.g. `bad_function` | ||
436 | fn BadFunction(); | ||
437 | // ^^^^^^^^^^^^ Function `BadFunction` should have snake_case name, e.g. `bad_function` | ||
438 | } | ||
439 | "#, | ||
440 | ); | ||
441 | } | ||
442 | |||
443 | #[test] | ||
444 | fn allow_attributes() { | ||
445 | check_diagnostics( | ||
446 | r#" | ||
447 | #[allow(non_snake_case)] | ||
448 | fn NonSnakeCaseName(SOME_VAR: u8) -> u8{ | ||
449 | // cov_flags generated output from elsewhere in this file | ||
450 | extern "C" { | ||
451 | #[no_mangle] | ||
452 | static lower_case: u8; | ||
453 | } | ||
454 | |||
455 | let OtherVar = SOME_VAR + 1; | ||
456 | OtherVar | ||
457 | } | ||
458 | |||
459 | #[allow(nonstandard_style)] | ||
460 | mod CheckNonstandardStyle { | ||
461 | fn HiImABadFnName() {} | ||
462 | } | ||
463 | |||
464 | #[allow(bad_style)] | ||
465 | mod CheckBadStyle { | ||
466 | fn HiImABadFnName() {} | ||
467 | } | ||
468 | |||
469 | mod F { | ||
470 | #![allow(non_snake_case)] | ||
471 | fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {} | ||
472 | } | ||
473 | |||
474 | #[allow(non_snake_case, non_camel_case_types)] | ||
475 | pub struct some_type { | ||
476 | SOME_FIELD: u8, | ||
477 | SomeField: u16, | ||
478 | } | ||
479 | |||
480 | #[allow(non_upper_case_globals)] | ||
481 | pub const some_const: u8 = 10; | ||
482 | |||
483 | #[allow(non_upper_case_globals)] | ||
484 | pub static SomeStatic: u8 = 10; | ||
485 | "#, | ||
486 | ); | ||
487 | } | ||
488 | } | ||
diff --git a/crates/ide/src/diagnostics/macro_error.rs b/crates/ide/src/diagnostics/macro_error.rs new file mode 100644 index 000000000..5f97f190d --- /dev/null +++ b/crates/ide/src/diagnostics/macro_error.rs | |||
@@ -0,0 +1,173 @@ | |||
1 | use crate::diagnostics::{Diagnostic, DiagnosticsContext}; | ||
2 | |||
3 | // Diagnostic: macro-error | ||
4 | // | ||
5 | // This diagnostic is shown for macro expansion errors. | ||
6 | pub(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)] | ||
16 | mod tests { | ||
17 | use crate::{ | ||
18 | diagnostics::tests::{check_diagnostics, check_diagnostics_with_config}, | ||
19 | DiagnosticsConfig, | ||
20 | }; | ||
21 | |||
22 | #[test] | ||
23 | fn builtin_macro_fails_expansion() { | ||
24 | check_diagnostics( | ||
25 | r#" | ||
26 | #[rustc_builtin_macro] | ||
27 | macro_rules! include { () => {} } | ||
28 | |||
29 | include!("doesntexist"); | ||
30 | //^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `doesntexist` | ||
31 | "#, | ||
32 | ); | ||
33 | } | ||
34 | |||
35 | #[test] | ||
36 | fn include_macro_should_allow_empty_content() { | ||
37 | let mut config = DiagnosticsConfig::default(); | ||
38 | |||
39 | // FIXME: This is a false-positive, the file is actually linked in via | ||
40 | // `include!` macro | ||
41 | config.disabled.insert("unlinked-file".to_string()); | ||
42 | |||
43 | check_diagnostics_with_config( | ||
44 | config, | ||
45 | r#" | ||
46 | //- /lib.rs | ||
47 | #[rustc_builtin_macro] | ||
48 | macro_rules! include { () => {} } | ||
49 | |||
50 | include!("foo/bar.rs"); | ||
51 | //- /foo/bar.rs | ||
52 | // empty | ||
53 | "#, | ||
54 | ); | ||
55 | } | ||
56 | |||
57 | #[test] | ||
58 | fn good_out_dir_diagnostic() { | ||
59 | check_diagnostics( | ||
60 | r#" | ||
61 | #[rustc_builtin_macro] | ||
62 | macro_rules! include { () => {} } | ||
63 | #[rustc_builtin_macro] | ||
64 | macro_rules! env { () => {} } | ||
65 | #[rustc_builtin_macro] | ||
66 | macro_rules! concat { () => {} } | ||
67 | |||
68 | include!(concat!(env!("OUT_DIR"), "/out.rs")); | ||
69 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix | ||
70 | "#, | ||
71 | ); | ||
72 | } | ||
73 | |||
74 | #[test] | ||
75 | fn register_attr_and_tool() { | ||
76 | cov_mark::check!(register_attr); | ||
77 | cov_mark::check!(register_tool); | ||
78 | check_diagnostics( | ||
79 | r#" | ||
80 | #![register_tool(tool)] | ||
81 | #![register_attr(attr)] | ||
82 | |||
83 | #[tool::path] | ||
84 | #[attr] | ||
85 | struct S; | ||
86 | "#, | ||
87 | ); | ||
88 | // NB: we don't currently emit diagnostics here | ||
89 | } | ||
90 | |||
91 | #[test] | ||
92 | fn macro_diag_builtin() { | ||
93 | check_diagnostics( | ||
94 | r#" | ||
95 | #[rustc_builtin_macro] | ||
96 | macro_rules! env {} | ||
97 | |||
98 | #[rustc_builtin_macro] | ||
99 | macro_rules! include {} | ||
100 | |||
101 | #[rustc_builtin_macro] | ||
102 | macro_rules! compile_error {} | ||
103 | |||
104 | #[rustc_builtin_macro] | ||
105 | macro_rules! format_args { () => {} } | ||
106 | |||
107 | fn main() { | ||
108 | // Test a handful of built-in (eager) macros: | ||
109 | |||
110 | include!(invalid); | ||
111 | //^^^^^^^^^^^^^^^^^ could not convert tokens | ||
112 | include!("does not exist"); | ||
113 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ failed to load file `does not exist` | ||
114 | |||
115 | env!(invalid); | ||
116 | //^^^^^^^^^^^^^ could not convert tokens | ||
117 | |||
118 | env!("OUT_DIR"); | ||
119 | //^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix | ||
120 | |||
121 | compile_error!("compile_error works"); | ||
122 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ compile_error works | ||
123 | |||
124 | // Lazy: | ||
125 | |||
126 | format_args!(); | ||
127 | //^^^^^^^^^^^^^^ no rule matches input tokens | ||
128 | } | ||
129 | "#, | ||
130 | ); | ||
131 | } | ||
132 | |||
133 | #[test] | ||
134 | fn macro_rules_diag() { | ||
135 | check_diagnostics( | ||
136 | r#" | ||
137 | macro_rules! m { | ||
138 | () => {}; | ||
139 | } | ||
140 | fn f() { | ||
141 | m!(); | ||
142 | |||
143 | m!(hi); | ||
144 | //^^^^^^ leftover tokens | ||
145 | } | ||
146 | "#, | ||
147 | ); | ||
148 | } | ||
149 | #[test] | ||
150 | fn dollar_crate_in_builtin_macro() { | ||
151 | check_diagnostics( | ||
152 | r#" | ||
153 | #[macro_export] | ||
154 | #[rustc_builtin_macro] | ||
155 | macro_rules! format_args {} | ||
156 | |||
157 | #[macro_export] | ||
158 | macro_rules! arg { () => {} } | ||
159 | |||
160 | #[macro_export] | ||
161 | macro_rules! outer { | ||
162 | () => { | ||
163 | $crate::format_args!( "", $crate::arg!(1) ) | ||
164 | }; | ||
165 | } | ||
166 | |||
167 | fn f() { | ||
168 | outer!(); | ||
169 | } //^^^^^^^^ leftover tokens | ||
170 | "#, | ||
171 | ) | ||
172 | } | ||
173 | } | ||
diff --git a/crates/ide/src/diagnostics/mismatched_arg_count.rs b/crates/ide/src/diagnostics/mismatched_arg_count.rs new file mode 100644 index 000000000..08e1cfa5f --- /dev/null +++ b/crates/ide/src/diagnostics/mismatched_arg_count.rs | |||
@@ -0,0 +1,272 @@ | |||
1 | use crate::diagnostics::{Diagnostic, DiagnosticsContext}; | ||
2 | |||
3 | // Diagnostic: mismatched-arg-count | ||
4 | // | ||
5 | // This diagnostic is triggered if a function is invoked with an incorrect amount of arguments. | ||
6 | pub(super) fn mismatched_arg_count( | ||
7 | ctx: &DiagnosticsContext<'_>, | ||
8 | d: &hir::MismatchedArgCount, | ||
9 | ) -> Diagnostic { | ||
10 | let s = if d.expected == 1 { "" } else { "s" }; | ||
11 | let message = format!("expected {} argument{}, found {}", d.expected, s, d.found); | ||
12 | Diagnostic::new( | ||
13 | "mismatched-arg-count", | ||
14 | message, | ||
15 | ctx.sema.diagnostics_display_range(d.call_expr.clone().map(|it| it.into())).range, | ||
16 | ) | ||
17 | } | ||
18 | |||
19 | #[cfg(test)] | ||
20 | mod tests { | ||
21 | use crate::diagnostics::tests::check_diagnostics; | ||
22 | |||
23 | #[test] | ||
24 | fn simple_free_fn_zero() { | ||
25 | check_diagnostics( | ||
26 | r#" | ||
27 | fn zero() {} | ||
28 | fn f() { zero(1); } | ||
29 | //^^^^^^^ expected 0 arguments, found 1 | ||
30 | "#, | ||
31 | ); | ||
32 | |||
33 | check_diagnostics( | ||
34 | r#" | ||
35 | fn zero() {} | ||
36 | fn f() { zero(); } | ||
37 | "#, | ||
38 | ); | ||
39 | } | ||
40 | |||
41 | #[test] | ||
42 | fn simple_free_fn_one() { | ||
43 | check_diagnostics( | ||
44 | r#" | ||
45 | fn one(arg: u8) {} | ||
46 | fn f() { one(); } | ||
47 | //^^^^^ expected 1 argument, found 0 | ||
48 | "#, | ||
49 | ); | ||
50 | |||
51 | check_diagnostics( | ||
52 | r#" | ||
53 | fn one(arg: u8) {} | ||
54 | fn f() { one(1); } | ||
55 | "#, | ||
56 | ); | ||
57 | } | ||
58 | |||
59 | #[test] | ||
60 | fn method_as_fn() { | ||
61 | check_diagnostics( | ||
62 | r#" | ||
63 | struct S; | ||
64 | impl S { fn method(&self) {} } | ||
65 | |||
66 | fn f() { | ||
67 | S::method(); | ||
68 | } //^^^^^^^^^^^ expected 1 argument, found 0 | ||
69 | "#, | ||
70 | ); | ||
71 | |||
72 | check_diagnostics( | ||
73 | r#" | ||
74 | struct S; | ||
75 | impl S { fn method(&self) {} } | ||
76 | |||
77 | fn f() { | ||
78 | S::method(&S); | ||
79 | S.method(); | ||
80 | } | ||
81 | "#, | ||
82 | ); | ||
83 | } | ||
84 | |||
85 | #[test] | ||
86 | fn method_with_arg() { | ||
87 | check_diagnostics( | ||
88 | r#" | ||
89 | struct S; | ||
90 | impl S { fn method(&self, arg: u8) {} } | ||
91 | |||
92 | fn f() { | ||
93 | S.method(); | ||
94 | } //^^^^^^^^^^ expected 1 argument, found 0 | ||
95 | "#, | ||
96 | ); | ||
97 | |||
98 | check_diagnostics( | ||
99 | r#" | ||
100 | struct S; | ||
101 | impl S { fn method(&self, arg: u8) {} } | ||
102 | |||
103 | fn f() { | ||
104 | S::method(&S, 0); | ||
105 | S.method(1); | ||
106 | } | ||
107 | "#, | ||
108 | ); | ||
109 | } | ||
110 | |||
111 | #[test] | ||
112 | fn method_unknown_receiver() { | ||
113 | // note: this is incorrect code, so there might be errors on this in the | ||
114 | // future, but we shouldn't emit an argument count diagnostic here | ||
115 | check_diagnostics( | ||
116 | r#" | ||
117 | trait Foo { fn method(&self, arg: usize) {} } | ||
118 | |||
119 | fn f() { | ||
120 | let x; | ||
121 | x.method(); | ||
122 | } | ||
123 | "#, | ||
124 | ); | ||
125 | } | ||
126 | |||
127 | #[test] | ||
128 | fn tuple_struct() { | ||
129 | check_diagnostics( | ||
130 | r#" | ||
131 | struct Tup(u8, u16); | ||
132 | fn f() { | ||
133 | Tup(0); | ||
134 | } //^^^^^^ expected 2 arguments, found 1 | ||
135 | "#, | ||
136 | ) | ||
137 | } | ||
138 | |||
139 | #[test] | ||
140 | fn enum_variant() { | ||
141 | check_diagnostics( | ||
142 | r#" | ||
143 | enum En { Variant(u8, u16), } | ||
144 | fn f() { | ||
145 | En::Variant(0); | ||
146 | } //^^^^^^^^^^^^^^ expected 2 arguments, found 1 | ||
147 | "#, | ||
148 | ) | ||
149 | } | ||
150 | |||
151 | #[test] | ||
152 | fn enum_variant_type_macro() { | ||
153 | check_diagnostics( | ||
154 | r#" | ||
155 | macro_rules! Type { | ||
156 | () => { u32 }; | ||
157 | } | ||
158 | enum Foo { | ||
159 | Bar(Type![]) | ||
160 | } | ||
161 | impl Foo { | ||
162 | fn new() { | ||
163 | Foo::Bar(0); | ||
164 | Foo::Bar(0, 1); | ||
165 | //^^^^^^^^^^^^^^ expected 1 argument, found 2 | ||
166 | Foo::Bar(); | ||
167 | //^^^^^^^^^^ expected 1 argument, found 0 | ||
168 | } | ||
169 | } | ||
170 | "#, | ||
171 | ); | ||
172 | } | ||
173 | |||
174 | #[test] | ||
175 | fn varargs() { | ||
176 | check_diagnostics( | ||
177 | r#" | ||
178 | extern "C" { | ||
179 | fn fixed(fixed: u8); | ||
180 | fn varargs(fixed: u8, ...); | ||
181 | fn varargs2(...); | ||
182 | } | ||
183 | |||
184 | fn f() { | ||
185 | unsafe { | ||
186 | fixed(0); | ||
187 | fixed(0, 1); | ||
188 | //^^^^^^^^^^^ expected 1 argument, found 2 | ||
189 | varargs(0); | ||
190 | varargs(0, 1); | ||
191 | varargs2(); | ||
192 | varargs2(0); | ||
193 | varargs2(0, 1); | ||
194 | } | ||
195 | } | ||
196 | "#, | ||
197 | ) | ||
198 | } | ||
199 | |||
200 | #[test] | ||
201 | fn arg_count_lambda() { | ||
202 | check_diagnostics( | ||
203 | r#" | ||
204 | fn main() { | ||
205 | let f = |()| (); | ||
206 | f(); | ||
207 | //^^^ expected 1 argument, found 0 | ||
208 | f(()); | ||
209 | f((), ()); | ||
210 | //^^^^^^^^^ expected 1 argument, found 2 | ||
211 | } | ||
212 | "#, | ||
213 | ) | ||
214 | } | ||
215 | |||
216 | #[test] | ||
217 | fn cfgd_out_call_arguments() { | ||
218 | check_diagnostics( | ||
219 | r#" | ||
220 | struct C(#[cfg(FALSE)] ()); | ||
221 | impl C { | ||
222 | fn new() -> Self { | ||
223 | Self( | ||
224 | #[cfg(FALSE)] | ||
225 | (), | ||
226 | ) | ||
227 | } | ||
228 | |||
229 | fn method(&self) {} | ||
230 | } | ||
231 | |||
232 | fn main() { | ||
233 | C::new().method(#[cfg(FALSE)] 0); | ||
234 | } | ||
235 | "#, | ||
236 | ); | ||
237 | } | ||
238 | |||
239 | #[test] | ||
240 | fn cfgd_out_fn_params() { | ||
241 | check_diagnostics( | ||
242 | r#" | ||
243 | fn foo(#[cfg(NEVER)] x: ()) {} | ||
244 | |||
245 | struct S; | ||
246 | |||
247 | impl S { | ||
248 | fn method(#[cfg(NEVER)] self) {} | ||
249 | fn method2(#[cfg(NEVER)] self, arg: u8) {} | ||
250 | fn method3(self, #[cfg(NEVER)] arg: u8) {} | ||
251 | } | ||
252 | |||
253 | extern "C" { | ||
254 | fn fixed(fixed: u8, #[cfg(NEVER)] ...); | ||
255 | fn varargs(#[cfg(not(NEVER))] ...); | ||
256 | } | ||
257 | |||
258 | fn main() { | ||
259 | foo(); | ||
260 | S::method(); | ||
261 | S::method2(0); | ||
262 | S::method3(S); | ||
263 | S.method3(); | ||
264 | unsafe { | ||
265 | fixed(0); | ||
266 | varargs(1, 2, 3); | ||
267 | } | ||
268 | } | ||
269 | "#, | ||
270 | ) | ||
271 | } | ||
272 | } | ||
diff --git a/crates/ide/src/diagnostics/missing_fields.rs b/crates/ide/src/diagnostics/missing_fields.rs new file mode 100644 index 000000000..d01f05041 --- /dev/null +++ b/crates/ide/src/diagnostics/missing_fields.rs | |||
@@ -0,0 +1,327 @@ | |||
1 | use either::Either; | ||
2 | use hir::{db::AstDatabase, InFile}; | ||
3 | use ide_assists::Assist; | ||
4 | use ide_db::source_change::SourceChange; | ||
5 | use stdx::format_to; | ||
6 | use syntax::{algo, ast::make, AstNode, SyntaxNodePtr}; | ||
7 | use text_edit::TextEdit; | ||
8 | |||
9 | use crate::diagnostics::{fix, Diagnostic, DiagnosticsContext}; | ||
10 | |||
11 | // Diagnostic: missing-fields | ||
12 | // | ||
13 | // This diagnostic is triggered if record lacks some fields that exist in the corresponding structure. | ||
14 | // | ||
15 | // Example: | ||
16 | // | ||
17 | // ```rust | ||
18 | // struct A { a: u8, b: u8 } | ||
19 | // | ||
20 | // let a = A { a: 10 }; | ||
21 | // ``` | ||
22 | pub(super) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Diagnostic { | ||
23 | let mut message = String::from("Missing structure fields:\n"); | ||
24 | for field in &d.missed_fields { | ||
25 | format_to!(message, "- {}\n", field); | ||
26 | } | ||
27 | |||
28 | let ptr = InFile::new( | ||
29 | d.file, | ||
30 | d.field_list_parent_path | ||
31 | .clone() | ||
32 | .map(SyntaxNodePtr::from) | ||
33 | .unwrap_or_else(|| d.field_list_parent.clone().either(|it| it.into(), |it| it.into())), | ||
34 | ); | ||
35 | |||
36 | Diagnostic::new("missing-fields", message, ctx.sema.diagnostics_display_range(ptr).range) | ||
37 | .with_fixes(fixes(ctx, d)) | ||
38 | } | ||
39 | |||
40 | fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Assist>> { | ||
41 | // Note that although we could add a diagnostics to | ||
42 | // fill the missing tuple field, e.g : | ||
43 | // `struct A(usize);` | ||
44 | // `let a = A { 0: () }` | ||
45 | // but it is uncommon usage and it should not be encouraged. | ||
46 | if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) { | ||
47 | return None; | ||
48 | } | ||
49 | |||
50 | let root = ctx.sema.db.parse_or_expand(d.file)?; | ||
51 | let field_list_parent = match &d.field_list_parent { | ||
52 | Either::Left(record_expr) => record_expr.to_node(&root), | ||
53 | // FIXE: patterns should be fixable as well. | ||
54 | Either::Right(_) => return None, | ||
55 | }; | ||
56 | let old_field_list = field_list_parent.record_expr_field_list()?; | ||
57 | let new_field_list = old_field_list.clone_for_update(); | ||
58 | for f in d.missed_fields.iter() { | ||
59 | let field = | ||
60 | make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit())) | ||
61 | .clone_for_update(); | ||
62 | new_field_list.add_field(field); | ||
63 | } | ||
64 | |||
65 | let edit = { | ||
66 | let mut builder = TextEdit::builder(); | ||
67 | algo::diff(old_field_list.syntax(), new_field_list.syntax()).into_text_edit(&mut builder); | ||
68 | builder.finish() | ||
69 | }; | ||
70 | Some(vec![fix( | ||
71 | "fill_missing_fields", | ||
72 | "Fill struct fields", | ||
73 | SourceChange::from_text_edit(d.file.original_file(ctx.sema.db), edit), | ||
74 | ctx.sema.original_range(field_list_parent.syntax()).range, | ||
75 | )]) | ||
76 | } | ||
77 | |||
78 | #[cfg(test)] | ||
79 | mod tests { | ||
80 | use crate::diagnostics::tests::{check_diagnostics, check_fix}; | ||
81 | |||
82 | #[test] | ||
83 | fn missing_record_pat_field_diagnostic() { | ||
84 | check_diagnostics( | ||
85 | r#" | ||
86 | struct S { foo: i32, bar: () } | ||
87 | fn baz(s: S) { | ||
88 | let S { foo: _ } = s; | ||
89 | //^ Missing structure fields: | ||
90 | //| - bar | ||
91 | } | ||
92 | "#, | ||
93 | ); | ||
94 | } | ||
95 | |||
96 | #[test] | ||
97 | fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() { | ||
98 | check_diagnostics( | ||
99 | r" | ||
100 | struct S { foo: i32, bar: () } | ||
101 | fn baz(s: S) -> i32 { | ||
102 | match s { | ||
103 | S { foo, .. } => foo, | ||
104 | } | ||
105 | } | ||
106 | ", | ||
107 | ) | ||
108 | } | ||
109 | |||
110 | #[test] | ||
111 | fn missing_record_pat_field_box() { | ||
112 | check_diagnostics( | ||
113 | r" | ||
114 | struct S { s: Box<u32> } | ||
115 | fn x(a: S) { | ||
116 | let S { box s } = a; | ||
117 | } | ||
118 | ", | ||
119 | ) | ||
120 | } | ||
121 | |||
122 | #[test] | ||
123 | fn missing_record_pat_field_ref() { | ||
124 | check_diagnostics( | ||
125 | r" | ||
126 | struct S { s: u32 } | ||
127 | fn x(a: S) { | ||
128 | let S { ref s } = a; | ||
129 | } | ||
130 | ", | ||
131 | ) | ||
132 | } | ||
133 | |||
134 | #[test] | ||
135 | fn range_mapping_out_of_macros() { | ||
136 | // FIXME: this is very wrong, but somewhat tricky to fix. | ||
137 | check_fix( | ||
138 | r#" | ||
139 | fn some() {} | ||
140 | fn items() {} | ||
141 | fn here() {} | ||
142 | |||
143 | macro_rules! id { ($($tt:tt)*) => { $($tt)*}; } | ||
144 | |||
145 | fn main() { | ||
146 | let _x = id![Foo { a: $042 }]; | ||
147 | } | ||
148 | |||
149 | pub struct Foo { pub a: i32, pub b: i32 } | ||
150 | "#, | ||
151 | r#" | ||
152 | fn some(, b: () ) {} | ||
153 | fn items() {} | ||
154 | fn here() {} | ||
155 | |||
156 | macro_rules! id { ($($tt:tt)*) => { $($tt)*}; } | ||
157 | |||
158 | fn main() { | ||
159 | let _x = id![Foo { a: 42 }]; | ||
160 | } | ||
161 | |||
162 | pub struct Foo { pub a: i32, pub b: i32 } | ||
163 | "#, | ||
164 | ); | ||
165 | } | ||
166 | |||
167 | #[test] | ||
168 | fn test_fill_struct_fields_empty() { | ||
169 | check_fix( | ||
170 | r#" | ||
171 | struct TestStruct { one: i32, two: i64 } | ||
172 | |||
173 | fn test_fn() { | ||
174 | let s = TestStruct {$0}; | ||
175 | } | ||
176 | "#, | ||
177 | r#" | ||
178 | struct TestStruct { one: i32, two: i64 } | ||
179 | |||
180 | fn test_fn() { | ||
181 | let s = TestStruct { one: (), two: () }; | ||
182 | } | ||
183 | "#, | ||
184 | ); | ||
185 | } | ||
186 | |||
187 | #[test] | ||
188 | fn test_fill_struct_fields_self() { | ||
189 | check_fix( | ||
190 | r#" | ||
191 | struct TestStruct { one: i32 } | ||
192 | |||
193 | impl TestStruct { | ||
194 | fn test_fn() { let s = Self {$0}; } | ||
195 | } | ||
196 | "#, | ||
197 | r#" | ||
198 | struct TestStruct { one: i32 } | ||
199 | |||
200 | impl TestStruct { | ||
201 | fn test_fn() { let s = Self { one: () }; } | ||
202 | } | ||
203 | "#, | ||
204 | ); | ||
205 | } | ||
206 | |||
207 | #[test] | ||
208 | fn test_fill_struct_fields_enum() { | ||
209 | check_fix( | ||
210 | r#" | ||
211 | enum Expr { | ||
212 | Bin { lhs: Box<Expr>, rhs: Box<Expr> } | ||
213 | } | ||
214 | |||
215 | impl Expr { | ||
216 | fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { | ||
217 | Expr::Bin {$0 } | ||
218 | } | ||
219 | } | ||
220 | "#, | ||
221 | r#" | ||
222 | enum Expr { | ||
223 | Bin { lhs: Box<Expr>, rhs: Box<Expr> } | ||
224 | } | ||
225 | |||
226 | impl Expr { | ||
227 | fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { | ||
228 | Expr::Bin { lhs: (), rhs: () } | ||
229 | } | ||
230 | } | ||
231 | "#, | ||
232 | ); | ||
233 | } | ||
234 | |||
235 | #[test] | ||
236 | fn test_fill_struct_fields_partial() { | ||
237 | check_fix( | ||
238 | r#" | ||
239 | struct TestStruct { one: i32, two: i64 } | ||
240 | |||
241 | fn test_fn() { | ||
242 | let s = TestStruct{ two: 2$0 }; | ||
243 | } | ||
244 | "#, | ||
245 | r" | ||
246 | struct TestStruct { one: i32, two: i64 } | ||
247 | |||
248 | fn test_fn() { | ||
249 | let s = TestStruct{ two: 2, one: () }; | ||
250 | } | ||
251 | ", | ||
252 | ); | ||
253 | } | ||
254 | |||
255 | #[test] | ||
256 | fn test_fill_struct_fields_raw_ident() { | ||
257 | check_fix( | ||
258 | r#" | ||
259 | struct TestStruct { r#type: u8 } | ||
260 | |||
261 | fn test_fn() { | ||
262 | TestStruct { $0 }; | ||
263 | } | ||
264 | "#, | ||
265 | r" | ||
266 | struct TestStruct { r#type: u8 } | ||
267 | |||
268 | fn test_fn() { | ||
269 | TestStruct { r#type: () }; | ||
270 | } | ||
271 | ", | ||
272 | ); | ||
273 | } | ||
274 | |||
275 | #[test] | ||
276 | fn test_fill_struct_fields_no_diagnostic() { | ||
277 | check_diagnostics( | ||
278 | r#" | ||
279 | struct TestStruct { one: i32, two: i64 } | ||
280 | |||
281 | fn test_fn() { | ||
282 | let one = 1; | ||
283 | let s = TestStruct{ one, two: 2 }; | ||
284 | } | ||
285 | "#, | ||
286 | ); | ||
287 | } | ||
288 | |||
289 | #[test] | ||
290 | fn test_fill_struct_fields_no_diagnostic_on_spread() { | ||
291 | check_diagnostics( | ||
292 | r#" | ||
293 | struct TestStruct { one: i32, two: i64 } | ||
294 | |||
295 | fn test_fn() { | ||
296 | let one = 1; | ||
297 | let s = TestStruct{ ..a }; | ||
298 | } | ||
299 | "#, | ||
300 | ); | ||
301 | } | ||
302 | |||
303 | #[test] | ||
304 | fn test_fill_struct_fields_blank_line() { | ||
305 | check_fix( | ||
306 | r#" | ||
307 | struct S { a: (), b: () } | ||
308 | |||
309 | fn f() { | ||
310 | S { | ||
311 | $0 | ||
312 | }; | ||
313 | } | ||
314 | "#, | ||
315 | r#" | ||
316 | struct S { a: (), b: () } | ||
317 | |||
318 | fn f() { | ||
319 | S { | ||
320 | a: (), | ||
321 | b: (), | ||
322 | }; | ||
323 | } | ||
324 | "#, | ||
325 | ); | ||
326 | } | ||
327 | } | ||
diff --git a/crates/ide/src/diagnostics/missing_match_arms.rs b/crates/ide/src/diagnostics/missing_match_arms.rs new file mode 100644 index 000000000..b636489b3 --- /dev/null +++ b/crates/ide/src/diagnostics/missing_match_arms.rs | |||
@@ -0,0 +1,929 @@ | |||
1 | use hir::InFile; | ||
2 | |||
3 | use crate::diagnostics::{Diagnostic, DiagnosticsContext}; | ||
4 | |||
5 | // Diagnostic: missing-match-arm | ||
6 | // | ||
7 | // This diagnostic is triggered if `match` block is missing one or more match arms. | ||
8 | pub(super) fn missing_match_arms( | ||
9 | ctx: &DiagnosticsContext<'_>, | ||
10 | d: &hir::MissingMatchArms, | ||
11 | ) -> Diagnostic { | ||
12 | Diagnostic::new( | ||
13 | "missing-match-arm", | ||
14 | "missing match arm", | ||
15 | ctx.sema.diagnostics_display_range(InFile::new(d.file, d.match_expr.clone().into())).range, | ||
16 | ) | ||
17 | } | ||
18 | |||
19 | #[cfg(test)] | ||
20 | pub(super) mod tests { | ||
21 | use crate::diagnostics::tests::check_diagnostics; | ||
22 | |||
23 | fn check_diagnostics_no_bails(ra_fixture: &str) { | ||
24 | cov_mark::check_count!(validate_match_bailed_out, 0); | ||
25 | crate::diagnostics::tests::check_diagnostics(ra_fixture) | ||
26 | } | ||
27 | |||
28 | #[test] | ||
29 | fn empty_tuple() { | ||
30 | check_diagnostics_no_bails( | ||
31 | r#" | ||
32 | fn main() { | ||
33 | match () { } | ||
34 | //^^ missing match arm | ||
35 | match (()) { } | ||
36 | //^^^^ missing match arm | ||
37 | |||
38 | match () { _ => (), } | ||
39 | match () { () => (), } | ||
40 | match (()) { (()) => (), } | ||
41 | } | ||
42 | "#, | ||
43 | ); | ||
44 | } | ||
45 | |||
46 | #[test] | ||
47 | fn tuple_of_two_empty_tuple() { | ||
48 | check_diagnostics_no_bails( | ||
49 | r#" | ||
50 | fn main() { | ||
51 | match ((), ()) { } | ||
52 | //^^^^^^^^ missing match arm | ||
53 | |||
54 | match ((), ()) { ((), ()) => (), } | ||
55 | } | ||
56 | "#, | ||
57 | ); | ||
58 | } | ||
59 | |||
60 | #[test] | ||
61 | fn boolean() { | ||
62 | check_diagnostics_no_bails( | ||
63 | r#" | ||
64 | fn test_main() { | ||
65 | match false { } | ||
66 | //^^^^^ missing match arm | ||
67 | match false { true => (), } | ||
68 | //^^^^^ missing match arm | ||
69 | match (false, true) {} | ||
70 | //^^^^^^^^^^^^^ missing match arm | ||
71 | match (false, true) { (true, true) => (), } | ||
72 | //^^^^^^^^^^^^^ missing match arm | ||
73 | match (false, true) { | ||
74 | //^^^^^^^^^^^^^ missing match arm | ||
75 | (false, true) => (), | ||
76 | (false, false) => (), | ||
77 | (true, false) => (), | ||
78 | } | ||
79 | match (false, true) { (true, _x) => (), } | ||
80 | //^^^^^^^^^^^^^ missing match arm | ||
81 | |||
82 | match false { true => (), false => (), } | ||
83 | match (false, true) { | ||
84 | (false, _) => (), | ||
85 | (true, false) => (), | ||
86 | (_, true) => (), | ||
87 | } | ||
88 | match (false, true) { | ||
89 | (true, true) => (), | ||
90 | (true, false) => (), | ||
91 | (false, true) => (), | ||
92 | (false, false) => (), | ||
93 | } | ||
94 | match (false, true) { | ||
95 | (true, _x) => (), | ||
96 | (false, true) => (), | ||
97 | (false, false) => (), | ||
98 | } | ||
99 | match (false, true, false) { | ||
100 | (false, ..) => (), | ||
101 | (true, ..) => (), | ||
102 | } | ||
103 | match (false, true, false) { | ||
104 | (.., false) => (), | ||
105 | (.., true) => (), | ||
106 | } | ||
107 | match (false, true, false) { (..) => (), } | ||
108 | } | ||
109 | "#, | ||
110 | ); | ||
111 | } | ||
112 | |||
113 | #[test] | ||
114 | fn tuple_of_tuple_and_bools() { | ||
115 | check_diagnostics_no_bails( | ||
116 | r#" | ||
117 | fn main() { | ||
118 | match (false, ((), false)) {} | ||
119 | //^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
120 | match (false, ((), false)) { (true, ((), true)) => (), } | ||
121 | //^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
122 | match (false, ((), false)) { (true, _) => (), } | ||
123 | //^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
124 | |||
125 | match (false, ((), false)) { | ||
126 | (true, ((), true)) => (), | ||
127 | (true, ((), false)) => (), | ||
128 | (false, ((), true)) => (), | ||
129 | (false, ((), false)) => (), | ||
130 | } | ||
131 | match (false, ((), false)) { | ||
132 | (true, ((), true)) => (), | ||
133 | (true, ((), false)) => (), | ||
134 | (false, _) => (), | ||
135 | } | ||
136 | } | ||
137 | "#, | ||
138 | ); | ||
139 | } | ||
140 | |||
141 | #[test] | ||
142 | fn enums() { | ||
143 | check_diagnostics_no_bails( | ||
144 | r#" | ||
145 | enum Either { A, B, } | ||
146 | |||
147 | fn main() { | ||
148 | match Either::A { } | ||
149 | //^^^^^^^^^ missing match arm | ||
150 | match Either::B { Either::A => (), } | ||
151 | //^^^^^^^^^ missing match arm | ||
152 | |||
153 | match &Either::B { | ||
154 | //^^^^^^^^^^ missing match arm | ||
155 | Either::A => (), | ||
156 | } | ||
157 | |||
158 | match Either::B { | ||
159 | Either::A => (), Either::B => (), | ||
160 | } | ||
161 | match &Either::B { | ||
162 | Either::A => (), Either::B => (), | ||
163 | } | ||
164 | } | ||
165 | "#, | ||
166 | ); | ||
167 | } | ||
168 | |||
169 | #[test] | ||
170 | fn enum_containing_bool() { | ||
171 | check_diagnostics_no_bails( | ||
172 | r#" | ||
173 | enum Either { A(bool), B } | ||
174 | |||
175 | fn main() { | ||
176 | match Either::B { } | ||
177 | //^^^^^^^^^ missing match arm | ||
178 | match Either::B { | ||
179 | //^^^^^^^^^ missing match arm | ||
180 | Either::A(true) => (), Either::B => () | ||
181 | } | ||
182 | |||
183 | match Either::B { | ||
184 | Either::A(true) => (), | ||
185 | Either::A(false) => (), | ||
186 | Either::B => (), | ||
187 | } | ||
188 | match Either::B { | ||
189 | Either::B => (), | ||
190 | _ => (), | ||
191 | } | ||
192 | match Either::B { | ||
193 | Either::A(_) => (), | ||
194 | Either::B => (), | ||
195 | } | ||
196 | |||
197 | } | ||
198 | "#, | ||
199 | ); | ||
200 | } | ||
201 | |||
202 | #[test] | ||
203 | fn enum_different_sizes() { | ||
204 | check_diagnostics_no_bails( | ||
205 | r#" | ||
206 | enum Either { A(bool), B(bool, bool) } | ||
207 | |||
208 | fn main() { | ||
209 | match Either::A(false) { | ||
210 | //^^^^^^^^^^^^^^^^ missing match arm | ||
211 | Either::A(_) => (), | ||
212 | Either::B(false, _) => (), | ||
213 | } | ||
214 | |||
215 | match Either::A(false) { | ||
216 | Either::A(_) => (), | ||
217 | Either::B(true, _) => (), | ||
218 | Either::B(false, _) => (), | ||
219 | } | ||
220 | match Either::A(false) { | ||
221 | Either::A(true) | Either::A(false) => (), | ||
222 | Either::B(true, _) => (), | ||
223 | Either::B(false, _) => (), | ||
224 | } | ||
225 | } | ||
226 | "#, | ||
227 | ); | ||
228 | } | ||
229 | |||
230 | #[test] | ||
231 | fn tuple_of_enum_no_diagnostic() { | ||
232 | check_diagnostics_no_bails( | ||
233 | r#" | ||
234 | enum Either { A(bool), B(bool, bool) } | ||
235 | enum Either2 { C, D } | ||
236 | |||
237 | fn main() { | ||
238 | match (Either::A(false), Either2::C) { | ||
239 | (Either::A(true), _) | (Either::A(false), _) => (), | ||
240 | (Either::B(true, _), Either2::C) => (), | ||
241 | (Either::B(false, _), Either2::C) => (), | ||
242 | (Either::B(_, _), Either2::D) => (), | ||
243 | } | ||
244 | } | ||
245 | "#, | ||
246 | ); | ||
247 | } | ||
248 | |||
249 | #[test] | ||
250 | fn or_pattern_no_diagnostic() { | ||
251 | check_diagnostics_no_bails( | ||
252 | r#" | ||
253 | enum Either {A, B} | ||
254 | |||
255 | fn main() { | ||
256 | match (Either::A, Either::B) { | ||
257 | (Either::A | Either::B, _) => (), | ||
258 | } | ||
259 | }"#, | ||
260 | ) | ||
261 | } | ||
262 | |||
263 | #[test] | ||
264 | fn mismatched_types() { | ||
265 | cov_mark::check_count!(validate_match_bailed_out, 4); | ||
266 | // Match statements with arms that don't match the | ||
267 | // expression pattern do not fire this diagnostic. | ||
268 | check_diagnostics( | ||
269 | r#" | ||
270 | enum Either { A, B } | ||
271 | enum Either2 { C, D } | ||
272 | |||
273 | fn main() { | ||
274 | match Either::A { | ||
275 | Either2::C => (), | ||
276 | Either2::D => (), | ||
277 | } | ||
278 | match (true, false) { | ||
279 | (true, false, true) => (), | ||
280 | (true) => (), | ||
281 | } | ||
282 | match (true, false) { (true,) => {} } | ||
283 | match (0) { () => () } | ||
284 | match Unresolved::Bar { Unresolved::Baz => () } | ||
285 | } | ||
286 | "#, | ||
287 | ); | ||
288 | } | ||
289 | |||
290 | #[test] | ||
291 | fn mismatched_types_in_or_patterns() { | ||
292 | cov_mark::check_count!(validate_match_bailed_out, 2); | ||
293 | check_diagnostics( | ||
294 | r#" | ||
295 | fn main() { | ||
296 | match false { true | () => {} } | ||
297 | match (false,) { (true | (),) => {} } | ||
298 | } | ||
299 | "#, | ||
300 | ); | ||
301 | } | ||
302 | |||
303 | #[test] | ||
304 | fn malformed_match_arm_tuple_enum_missing_pattern() { | ||
305 | // We are testing to be sure we don't panic here when the match | ||
306 | // arm `Either::B` is missing its pattern. | ||
307 | check_diagnostics_no_bails( | ||
308 | r#" | ||
309 | enum Either { A, B(u32) } | ||
310 | |||
311 | fn main() { | ||
312 | match Either::A { | ||
313 | Either::A => (), | ||
314 | Either::B() => (), | ||
315 | } | ||
316 | } | ||
317 | "#, | ||
318 | ); | ||
319 | } | ||
320 | |||
321 | #[test] | ||
322 | fn malformed_match_arm_extra_fields() { | ||
323 | cov_mark::check_count!(validate_match_bailed_out, 2); | ||
324 | check_diagnostics( | ||
325 | r#" | ||
326 | enum A { B(isize, isize), C } | ||
327 | fn main() { | ||
328 | match A::B(1, 2) { | ||
329 | A::B(_, _, _) => (), | ||
330 | } | ||
331 | match A::B(1, 2) { | ||
332 | A::C(_) => (), | ||
333 | } | ||
334 | } | ||
335 | "#, | ||
336 | ); | ||
337 | } | ||
338 | |||
339 | #[test] | ||
340 | fn expr_diverges() { | ||
341 | cov_mark::check_count!(validate_match_bailed_out, 2); | ||
342 | check_diagnostics( | ||
343 | r#" | ||
344 | enum Either { A, B } | ||
345 | |||
346 | fn main() { | ||
347 | match loop {} { | ||
348 | Either::A => (), | ||
349 | Either::B => (), | ||
350 | } | ||
351 | match loop {} { | ||
352 | Either::A => (), | ||
353 | } | ||
354 | match loop { break Foo::A } { | ||
355 | //^^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
356 | Either::A => (), | ||
357 | } | ||
358 | match loop { break Foo::A } { | ||
359 | Either::A => (), | ||
360 | Either::B => (), | ||
361 | } | ||
362 | } | ||
363 | "#, | ||
364 | ); | ||
365 | } | ||
366 | |||
367 | #[test] | ||
368 | fn expr_partially_diverges() { | ||
369 | check_diagnostics_no_bails( | ||
370 | r#" | ||
371 | enum Either<T> { A(T), B } | ||
372 | |||
373 | fn foo() -> Either<!> { Either::B } | ||
374 | fn main() -> u32 { | ||
375 | match foo() { | ||
376 | Either::A(val) => val, | ||
377 | Either::B => 0, | ||
378 | } | ||
379 | } | ||
380 | "#, | ||
381 | ); | ||
382 | } | ||
383 | |||
384 | #[test] | ||
385 | fn enum_record() { | ||
386 | check_diagnostics_no_bails( | ||
387 | r#" | ||
388 | enum Either { A { foo: bool }, B } | ||
389 | |||
390 | fn main() { | ||
391 | let a = Either::A { foo: true }; | ||
392 | match a { } | ||
393 | //^ missing match arm | ||
394 | match a { Either::A { foo: true } => () } | ||
395 | //^ missing match arm | ||
396 | match a { | ||
397 | Either::A { } => (), | ||
398 | //^^^^^^^^^ Missing structure fields: | ||
399 | // | - foo | ||
400 | Either::B => (), | ||
401 | } | ||
402 | match a { | ||
403 | //^ missing match arm | ||
404 | Either::A { } => (), | ||
405 | } //^^^^^^^^^ Missing structure fields: | ||
406 | // | - foo | ||
407 | |||
408 | match a { | ||
409 | Either::A { foo: true } => (), | ||
410 | Either::A { foo: false } => (), | ||
411 | Either::B => (), | ||
412 | } | ||
413 | match a { | ||
414 | Either::A { foo: _ } => (), | ||
415 | Either::B => (), | ||
416 | } | ||
417 | } | ||
418 | "#, | ||
419 | ); | ||
420 | } | ||
421 | |||
422 | #[test] | ||
423 | fn enum_record_fields_out_of_order() { | ||
424 | check_diagnostics_no_bails( | ||
425 | r#" | ||
426 | enum Either { | ||
427 | A { foo: bool, bar: () }, | ||
428 | B, | ||
429 | } | ||
430 | |||
431 | fn main() { | ||
432 | let a = Either::A { foo: true, bar: () }; | ||
433 | match a { | ||
434 | //^ missing match arm | ||
435 | Either::A { bar: (), foo: false } => (), | ||
436 | Either::A { foo: true, bar: () } => (), | ||
437 | } | ||
438 | |||
439 | match a { | ||
440 | Either::A { bar: (), foo: false } => (), | ||
441 | Either::A { foo: true, bar: () } => (), | ||
442 | Either::B => (), | ||
443 | } | ||
444 | } | ||
445 | "#, | ||
446 | ); | ||
447 | } | ||
448 | |||
449 | #[test] | ||
450 | fn enum_record_ellipsis() { | ||
451 | check_diagnostics_no_bails( | ||
452 | r#" | ||
453 | enum Either { | ||
454 | A { foo: bool, bar: bool }, | ||
455 | B, | ||
456 | } | ||
457 | |||
458 | fn main() { | ||
459 | let a = Either::B; | ||
460 | match a { | ||
461 | //^ missing match arm | ||
462 | Either::A { foo: true, .. } => (), | ||
463 | Either::B => (), | ||
464 | } | ||
465 | match a { | ||
466 | //^ missing match arm | ||
467 | Either::A { .. } => (), | ||
468 | } | ||
469 | |||
470 | match a { | ||
471 | Either::A { foo: true, .. } => (), | ||
472 | Either::A { foo: false, .. } => (), | ||
473 | Either::B => (), | ||
474 | } | ||
475 | |||
476 | match a { | ||
477 | Either::A { .. } => (), | ||
478 | Either::B => (), | ||
479 | } | ||
480 | } | ||
481 | "#, | ||
482 | ); | ||
483 | } | ||
484 | |||
485 | #[test] | ||
486 | fn enum_tuple_partial_ellipsis() { | ||
487 | check_diagnostics_no_bails( | ||
488 | r#" | ||
489 | enum Either { | ||
490 | A(bool, bool, bool, bool), | ||
491 | B, | ||
492 | } | ||
493 | |||
494 | fn main() { | ||
495 | match Either::B { | ||
496 | //^^^^^^^^^ missing match arm | ||
497 | Either::A(true, .., true) => (), | ||
498 | Either::A(true, .., false) => (), | ||
499 | Either::A(false, .., false) => (), | ||
500 | Either::B => (), | ||
501 | } | ||
502 | match Either::B { | ||
503 | //^^^^^^^^^ missing match arm | ||
504 | Either::A(true, .., true) => (), | ||
505 | Either::A(true, .., false) => (), | ||
506 | Either::A(.., true) => (), | ||
507 | Either::B => (), | ||
508 | } | ||
509 | |||
510 | match Either::B { | ||
511 | Either::A(true, .., true) => (), | ||
512 | Either::A(true, .., false) => (), | ||
513 | Either::A(false, .., true) => (), | ||
514 | Either::A(false, .., false) => (), | ||
515 | Either::B => (), | ||
516 | } | ||
517 | match Either::B { | ||
518 | Either::A(true, .., true) => (), | ||
519 | Either::A(true, .., false) => (), | ||
520 | Either::A(.., true) => (), | ||
521 | Either::A(.., false) => (), | ||
522 | Either::B => (), | ||
523 | } | ||
524 | } | ||
525 | "#, | ||
526 | ); | ||
527 | } | ||
528 | |||
529 | #[test] | ||
530 | fn never() { | ||
531 | check_diagnostics_no_bails( | ||
532 | r#" | ||
533 | enum Never {} | ||
534 | |||
535 | fn enum_(never: Never) { | ||
536 | match never {} | ||
537 | } | ||
538 | fn enum_ref(never: &Never) { | ||
539 | match never {} | ||
540 | //^^^^^ missing match arm | ||
541 | } | ||
542 | fn bang(never: !) { | ||
543 | match never {} | ||
544 | } | ||
545 | "#, | ||
546 | ); | ||
547 | } | ||
548 | |||
549 | #[test] | ||
550 | fn unknown_type() { | ||
551 | cov_mark::check_count!(validate_match_bailed_out, 1); | ||
552 | |||
553 | check_diagnostics( | ||
554 | r#" | ||
555 | enum Option<T> { Some(T), None } | ||
556 | |||
557 | fn main() { | ||
558 | // `Never` is deliberately not defined so that it's an uninferred type. | ||
559 | match Option::<Never>::None { | ||
560 | None => (), | ||
561 | Some(never) => match never {}, | ||
562 | } | ||
563 | match Option::<Never>::None { | ||
564 | //^^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
565 | Option::Some(_never) => {}, | ||
566 | } | ||
567 | } | ||
568 | "#, | ||
569 | ); | ||
570 | } | ||
571 | |||
572 | #[test] | ||
573 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { | ||
574 | check_diagnostics_no_bails( | ||
575 | r#" | ||
576 | fn main() { | ||
577 | match (false, true, false) { | ||
578 | //^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
579 | (false, ..) => (), | ||
580 | } | ||
581 | }"#, | ||
582 | ); | ||
583 | } | ||
584 | |||
585 | #[test] | ||
586 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { | ||
587 | check_diagnostics_no_bails( | ||
588 | r#" | ||
589 | fn main() { | ||
590 | match (false, true, false) { | ||
591 | //^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
592 | (.., false) => (), | ||
593 | } | ||
594 | }"#, | ||
595 | ); | ||
596 | } | ||
597 | |||
598 | #[test] | ||
599 | fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() { | ||
600 | check_diagnostics_no_bails( | ||
601 | r#" | ||
602 | fn main() { | ||
603 | match (false, true, false) { | ||
604 | //^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
605 | (true, .., false) => (), | ||
606 | } | ||
607 | }"#, | ||
608 | ); | ||
609 | } | ||
610 | |||
611 | #[test] | ||
612 | fn record_struct() { | ||
613 | check_diagnostics_no_bails( | ||
614 | r#"struct Foo { a: bool } | ||
615 | fn main(f: Foo) { | ||
616 | match f {} | ||
617 | //^ missing match arm | ||
618 | match f { Foo { a: true } => () } | ||
619 | //^ missing match arm | ||
620 | match &f { Foo { a: true } => () } | ||
621 | //^^ missing match arm | ||
622 | match f { Foo { a: _ } => () } | ||
623 | match f { | ||
624 | Foo { a: true } => (), | ||
625 | Foo { a: false } => (), | ||
626 | } | ||
627 | match &f { | ||
628 | Foo { a: true } => (), | ||
629 | Foo { a: false } => (), | ||
630 | } | ||
631 | } | ||
632 | "#, | ||
633 | ); | ||
634 | } | ||
635 | |||
636 | #[test] | ||
637 | fn tuple_struct() { | ||
638 | check_diagnostics_no_bails( | ||
639 | r#"struct Foo(bool); | ||
640 | fn main(f: Foo) { | ||
641 | match f {} | ||
642 | //^ missing match arm | ||
643 | match f { Foo(true) => () } | ||
644 | //^ missing match arm | ||
645 | match f { | ||
646 | Foo(true) => (), | ||
647 | Foo(false) => (), | ||
648 | } | ||
649 | } | ||
650 | "#, | ||
651 | ); | ||
652 | } | ||
653 | |||
654 | #[test] | ||
655 | fn unit_struct() { | ||
656 | check_diagnostics_no_bails( | ||
657 | r#"struct Foo; | ||
658 | fn main(f: Foo) { | ||
659 | match f {} | ||
660 | //^ missing match arm | ||
661 | match f { Foo => () } | ||
662 | } | ||
663 | "#, | ||
664 | ); | ||
665 | } | ||
666 | |||
667 | #[test] | ||
668 | fn record_struct_ellipsis() { | ||
669 | check_diagnostics_no_bails( | ||
670 | r#"struct Foo { foo: bool, bar: bool } | ||
671 | fn main(f: Foo) { | ||
672 | match f { Foo { foo: true, .. } => () } | ||
673 | //^ missing match arm | ||
674 | match f { | ||
675 | //^ missing match arm | ||
676 | Foo { foo: true, .. } => (), | ||
677 | Foo { bar: false, .. } => () | ||
678 | } | ||
679 | match f { Foo { .. } => () } | ||
680 | match f { | ||
681 | Foo { foo: true, .. } => (), | ||
682 | Foo { foo: false, .. } => () | ||
683 | } | ||
684 | } | ||
685 | "#, | ||
686 | ); | ||
687 | } | ||
688 | |||
689 | #[test] | ||
690 | fn internal_or() { | ||
691 | check_diagnostics_no_bails( | ||
692 | r#" | ||
693 | fn main() { | ||
694 | enum Either { A(bool), B } | ||
695 | match Either::B { | ||
696 | //^^^^^^^^^ missing match arm | ||
697 | Either::A(true | false) => (), | ||
698 | } | ||
699 | } | ||
700 | "#, | ||
701 | ); | ||
702 | } | ||
703 | |||
704 | #[test] | ||
705 | fn no_panic_at_unimplemented_subpattern_type() { | ||
706 | cov_mark::check_count!(validate_match_bailed_out, 1); | ||
707 | |||
708 | check_diagnostics( | ||
709 | r#" | ||
710 | struct S { a: char} | ||
711 | fn main(v: S) { | ||
712 | match v { S{ a } => {} } | ||
713 | match v { S{ a: _x } => {} } | ||
714 | match v { S{ a: 'a' } => {} } | ||
715 | match v { S{..} => {} } | ||
716 | match v { _ => {} } | ||
717 | match v { } | ||
718 | //^ missing match arm | ||
719 | } | ||
720 | "#, | ||
721 | ); | ||
722 | } | ||
723 | |||
724 | #[test] | ||
725 | fn binding() { | ||
726 | check_diagnostics_no_bails( | ||
727 | r#" | ||
728 | fn main() { | ||
729 | match true { | ||
730 | _x @ true => {} | ||
731 | false => {} | ||
732 | } | ||
733 | match true { _x @ true => {} } | ||
734 | //^^^^ missing match arm | ||
735 | } | ||
736 | "#, | ||
737 | ); | ||
738 | } | ||
739 | |||
740 | #[test] | ||
741 | fn binding_ref_has_correct_type() { | ||
742 | cov_mark::check_count!(validate_match_bailed_out, 1); | ||
743 | |||
744 | // Asserts `PatKind::Binding(ref _x): bool`, not &bool. | ||
745 | // If that's not true match checking will panic with "incompatible constructors" | ||
746 | // FIXME: make facilities to test this directly like `tests::check_infer(..)` | ||
747 | check_diagnostics( | ||
748 | r#" | ||
749 | enum Foo { A } | ||
750 | fn main() { | ||
751 | // FIXME: this should not bail out but current behavior is such as the old algorithm. | ||
752 | // ExprValidator::validate_match(..) checks types of top level patterns incorrecly. | ||
753 | match Foo::A { | ||
754 | ref _x => {} | ||
755 | Foo::A => {} | ||
756 | } | ||
757 | match (true,) { | ||
758 | (ref _x,) => {} | ||
759 | (true,) => {} | ||
760 | } | ||
761 | } | ||
762 | "#, | ||
763 | ); | ||
764 | } | ||
765 | |||
766 | #[test] | ||
767 | fn enum_non_exhaustive() { | ||
768 | check_diagnostics_no_bails( | ||
769 | r#" | ||
770 | //- /lib.rs crate:lib | ||
771 | #[non_exhaustive] | ||
772 | pub enum E { A, B } | ||
773 | fn _local() { | ||
774 | match E::A { _ => {} } | ||
775 | match E::A { | ||
776 | E::A => {} | ||
777 | E::B => {} | ||
778 | } | ||
779 | match E::A { | ||
780 | E::A | E::B => {} | ||
781 | } | ||
782 | } | ||
783 | |||
784 | //- /main.rs crate:main deps:lib | ||
785 | use lib::E; | ||
786 | fn main() { | ||
787 | match E::A { _ => {} } | ||
788 | match E::A { | ||
789 | //^^^^ missing match arm | ||
790 | E::A => {} | ||
791 | E::B => {} | ||
792 | } | ||
793 | match E::A { | ||
794 | //^^^^ missing match arm | ||
795 | E::A | E::B => {} | ||
796 | } | ||
797 | } | ||
798 | "#, | ||
799 | ); | ||
800 | } | ||
801 | |||
802 | #[test] | ||
803 | fn match_guard() { | ||
804 | check_diagnostics_no_bails( | ||
805 | r#" | ||
806 | fn main() { | ||
807 | match true { | ||
808 | true if false => {} | ||
809 | true => {} | ||
810 | false => {} | ||
811 | } | ||
812 | match true { | ||
813 | //^^^^ missing match arm | ||
814 | true if false => {} | ||
815 | false => {} | ||
816 | } | ||
817 | } | ||
818 | "#, | ||
819 | ); | ||
820 | } | ||
821 | |||
822 | #[test] | ||
823 | fn pattern_type_is_of_substitution() { | ||
824 | cov_mark::check!(match_check_wildcard_expanded_to_substitutions); | ||
825 | check_diagnostics_no_bails( | ||
826 | r#" | ||
827 | struct Foo<T>(T); | ||
828 | struct Bar; | ||
829 | fn main() { | ||
830 | match Foo(Bar) { | ||
831 | _ | Foo(Bar) => {} | ||
832 | } | ||
833 | } | ||
834 | "#, | ||
835 | ); | ||
836 | } | ||
837 | |||
838 | #[test] | ||
839 | fn record_struct_no_such_field() { | ||
840 | cov_mark::check_count!(validate_match_bailed_out, 1); | ||
841 | |||
842 | check_diagnostics( | ||
843 | r#" | ||
844 | struct Foo { } | ||
845 | fn main(f: Foo) { | ||
846 | match f { Foo { bar } => () } | ||
847 | } | ||
848 | "#, | ||
849 | ); | ||
850 | } | ||
851 | |||
852 | #[test] | ||
853 | fn match_ergonomics_issue_9095() { | ||
854 | check_diagnostics_no_bails( | ||
855 | r#" | ||
856 | enum Foo<T> { A(T) } | ||
857 | fn main() { | ||
858 | match &Foo::A(true) { | ||
859 | _ => {} | ||
860 | Foo::A(_) => {} | ||
861 | } | ||
862 | } | ||
863 | "#, | ||
864 | ); | ||
865 | } | ||
866 | |||
867 | mod false_negatives { | ||
868 | //! The implementation of match checking here is a work in progress. As we roll this out, we | ||
869 | //! prefer false negatives to false positives (ideally there would be no false positives). This | ||
870 | //! test module should document known false negatives. Eventually we will have a complete | ||
871 | //! implementation of match checking and this module will be empty. | ||
872 | //! | ||
873 | //! The reasons for documenting known false negatives: | ||
874 | //! | ||
875 | //! 1. It acts as a backlog of work that can be done to improve the behavior of the system. | ||
876 | //! 2. It ensures the code doesn't panic when handling these cases. | ||
877 | use super::*; | ||
878 | |||
879 | #[test] | ||
880 | fn integers() { | ||
881 | cov_mark::check_count!(validate_match_bailed_out, 1); | ||
882 | |||
883 | // We don't currently check integer exhaustiveness. | ||
884 | check_diagnostics( | ||
885 | r#" | ||
886 | fn main() { | ||
887 | match 5 { | ||
888 | 10 => (), | ||
889 | 11..20 => (), | ||
890 | } | ||
891 | } | ||
892 | "#, | ||
893 | ); | ||
894 | } | ||
895 | |||
896 | #[test] | ||
897 | fn reference_patterns_at_top_level() { | ||
898 | cov_mark::check_count!(validate_match_bailed_out, 1); | ||
899 | |||
900 | check_diagnostics( | ||
901 | r#" | ||
902 | fn main() { | ||
903 | match &false { | ||
904 | &true => {} | ||
905 | } | ||
906 | } | ||
907 | "#, | ||
908 | ); | ||
909 | } | ||
910 | |||
911 | #[test] | ||
912 | fn reference_patterns_in_fields() { | ||
913 | cov_mark::check_count!(validate_match_bailed_out, 2); | ||
914 | |||
915 | check_diagnostics( | ||
916 | r#" | ||
917 | fn main() { | ||
918 | match (&false,) { | ||
919 | (true,) => {} | ||
920 | } | ||
921 | match (&false,) { | ||
922 | (&true,) => {} | ||
923 | } | ||
924 | } | ||
925 | "#, | ||
926 | ); | ||
927 | } | ||
928 | } | ||
929 | } | ||
diff --git a/crates/ide/src/diagnostics/fixes/wrap_tail_expr.rs b/crates/ide/src/diagnostics/missing_ok_or_some_in_tail_expr.rs index 715a403b9..06005d156 100644 --- a/crates/ide/src/diagnostics/fixes/wrap_tail_expr.rs +++ b/crates/ide/src/diagnostics/missing_ok_or_some_in_tail_expr.rs | |||
@@ -1,31 +1,50 @@ | |||
1 | use hir::{db::AstDatabase, diagnostics::MissingOkOrSomeInTailExpr, Semantics}; | 1 | use hir::db::AstDatabase; |
2 | use ide_assists::{Assist, AssistResolveStrategy}; | 2 | use ide_assists::Assist; |
3 | use ide_db::{source_change::SourceChange, RootDatabase}; | 3 | use ide_db::source_change::SourceChange; |
4 | use syntax::AstNode; | 4 | use syntax::AstNode; |
5 | use text_edit::TextEdit; | 5 | use text_edit::TextEdit; |
6 | 6 | ||
7 | use crate::diagnostics::{fix, DiagnosticWithFixes}; | 7 | use crate::diagnostics::{fix, Diagnostic, DiagnosticsContext}; |
8 | 8 | ||
9 | impl DiagnosticWithFixes for MissingOkOrSomeInTailExpr { | 9 | // Diagnostic: missing-ok-or-some-in-tail-expr |
10 | fn fixes( | 10 | // |
11 | &self, | 11 | // This diagnostic is triggered if a block that should return `Result` returns a value not wrapped in `Ok`, |
12 | sema: &Semantics<RootDatabase>, | 12 | // or if a block that should return `Option` returns a value not wrapped in `Some`. |
13 | _resolve: &AssistResolveStrategy, | 13 | // |
14 | ) -> Option<Vec<Assist>> { | 14 | // Example: |
15 | let root = sema.db.parse_or_expand(self.file)?; | 15 | // |
16 | let tail_expr = self.expr.to_node(&root); | 16 | // ```rust |
17 | let tail_expr_range = tail_expr.syntax().text_range(); | 17 | // fn foo() -> Result<u8, ()> { |
18 | let replacement = format!("{}({})", self.required, tail_expr.syntax()); | 18 | // 10 |
19 | let edit = TextEdit::replace(tail_expr_range, replacement); | 19 | // } |
20 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); | 20 | // ``` |
21 | let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" }; | 21 | pub(super) fn missing_ok_or_some_in_tail_expr( |
22 | Some(vec![fix("wrap_tail_expr", name, source_change, tail_expr_range)]) | 22 | ctx: &DiagnosticsContext<'_>, |
23 | } | 23 | d: &hir::MissingOkOrSomeInTailExpr, |
24 | ) -> Diagnostic { | ||
25 | Diagnostic::new( | ||
26 | "missing-ok-or-some-in-tail-expr", | ||
27 | format!("wrap return expression in {}", d.required), | ||
28 | ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, | ||
29 | ) | ||
30 | .with_fixes(fixes(ctx, d)) | ||
31 | } | ||
32 | |||
33 | fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingOkOrSomeInTailExpr) -> Option<Vec<Assist>> { | ||
34 | let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?; | ||
35 | let tail_expr = d.expr.value.to_node(&root); | ||
36 | let tail_expr_range = tail_expr.syntax().text_range(); | ||
37 | let replacement = format!("{}({})", d.required, tail_expr.syntax()); | ||
38 | let edit = TextEdit::replace(tail_expr_range, replacement); | ||
39 | let source_change = | ||
40 | SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit); | ||
41 | let name = if d.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" }; | ||
42 | Some(vec![fix("wrap_tail_expr", name, source_change, tail_expr_range)]) | ||
24 | } | 43 | } |
25 | 44 | ||
26 | #[cfg(test)] | 45 | #[cfg(test)] |
27 | mod tests { | 46 | mod tests { |
28 | use crate::diagnostics::tests::{check_fix, check_no_diagnostics}; | 47 | use crate::diagnostics::tests::{check_diagnostics, check_fix}; |
29 | 48 | ||
30 | #[test] | 49 | #[test] |
31 | fn test_wrap_return_type_option() { | 50 | fn test_wrap_return_type_option() { |
@@ -169,7 +188,7 @@ fn div(x: i32, y: i32) -> MyResult<i32> { | |||
169 | 188 | ||
170 | #[test] | 189 | #[test] |
171 | fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { | 190 | fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { |
172 | check_no_diagnostics( | 191 | check_diagnostics( |
173 | r#" | 192 | r#" |
174 | //- /main.rs crate:main deps:core | 193 | //- /main.rs crate:main deps:core |
175 | use core::result::Result::{self, Ok, Err}; | 194 | use core::result::Result::{self, Ok, Err}; |
@@ -189,7 +208,7 @@ pub mod option { | |||
189 | 208 | ||
190 | #[test] | 209 | #[test] |
191 | fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() { | 210 | fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() { |
192 | check_no_diagnostics( | 211 | check_diagnostics( |
193 | r#" | 212 | r#" |
194 | //- /main.rs crate:main deps:core | 213 | //- /main.rs crate:main deps:core |
195 | use core::result::Result::{self, Ok, Err}; | 214 | use core::result::Result::{self, Ok, Err}; |
diff --git a/crates/ide/src/diagnostics/missing_unsafe.rs b/crates/ide/src/diagnostics/missing_unsafe.rs new file mode 100644 index 000000000..5c47e8d0a --- /dev/null +++ b/crates/ide/src/diagnostics/missing_unsafe.rs | |||
@@ -0,0 +1,101 @@ | |||
1 | use crate::diagnostics::{Diagnostic, DiagnosticsContext}; | ||
2 | |||
3 | // Diagnostic: missing-unsafe | ||
4 | // | ||
5 | // This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block. | ||
6 | pub(super) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Diagnostic { | ||
7 | Diagnostic::new( | ||
8 | "missing-unsafe", | ||
9 | "this operation is unsafe and requires an unsafe function or block", | ||
10 | ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, | ||
11 | ) | ||
12 | } | ||
13 | |||
14 | #[cfg(test)] | ||
15 | mod tests { | ||
16 | use crate::diagnostics::tests::check_diagnostics; | ||
17 | |||
18 | #[test] | ||
19 | fn missing_unsafe_diagnostic_with_raw_ptr() { | ||
20 | check_diagnostics( | ||
21 | r#" | ||
22 | fn main() { | ||
23 | let x = &5 as *const usize; | ||
24 | unsafe { let y = *x; } | ||
25 | let z = *x; | ||
26 | } //^^ this operation is unsafe and requires an unsafe function or block | ||
27 | "#, | ||
28 | ) | ||
29 | } | ||
30 | |||
31 | #[test] | ||
32 | fn missing_unsafe_diagnostic_with_unsafe_call() { | ||
33 | check_diagnostics( | ||
34 | r#" | ||
35 | struct HasUnsafe; | ||
36 | |||
37 | impl HasUnsafe { | ||
38 | unsafe fn unsafe_fn(&self) { | ||
39 | let x = &5 as *const usize; | ||
40 | let y = *x; | ||
41 | } | ||
42 | } | ||
43 | |||
44 | unsafe fn unsafe_fn() { | ||
45 | let x = &5 as *const usize; | ||
46 | let y = *x; | ||
47 | } | ||
48 | |||
49 | fn main() { | ||
50 | unsafe_fn(); | ||
51 | //^^^^^^^^^^^ this operation is unsafe and requires an unsafe function or block | ||
52 | HasUnsafe.unsafe_fn(); | ||
53 | //^^^^^^^^^^^^^^^^^^^^^ this operation is unsafe and requires an unsafe function or block | ||
54 | unsafe { | ||
55 | unsafe_fn(); | ||
56 | HasUnsafe.unsafe_fn(); | ||
57 | } | ||
58 | } | ||
59 | "#, | ||
60 | ); | ||
61 | } | ||
62 | |||
63 | #[test] | ||
64 | fn missing_unsafe_diagnostic_with_static_mut() { | ||
65 | check_diagnostics( | ||
66 | r#" | ||
67 | struct Ty { | ||
68 | a: u8, | ||
69 | } | ||
70 | |||
71 | static mut STATIC_MUT: Ty = Ty { a: 0 }; | ||
72 | |||
73 | fn main() { | ||
74 | let x = STATIC_MUT.a; | ||
75 | //^^^^^^^^^^ this operation is unsafe and requires an unsafe function or block | ||
76 | unsafe { | ||
77 | let x = STATIC_MUT.a; | ||
78 | } | ||
79 | } | ||
80 | "#, | ||
81 | ); | ||
82 | } | ||
83 | |||
84 | #[test] | ||
85 | fn no_missing_unsafe_diagnostic_with_safe_intrinsic() { | ||
86 | check_diagnostics( | ||
87 | r#" | ||
88 | extern "rust-intrinsic" { | ||
89 | pub fn bitreverse(x: u32) -> u32; // Safe intrinsic | ||
90 | pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic | ||
91 | } | ||
92 | |||
93 | fn main() { | ||
94 | let _ = bitreverse(12); | ||
95 | let _ = floorf32(12.0); | ||
96 | //^^^^^^^^^^^^^^ this operation is unsafe and requires an unsafe function or block | ||
97 | } | ||
98 | "#, | ||
99 | ); | ||
100 | } | ||
101 | } | ||
diff --git a/crates/ide/src/diagnostics/fixes/create_field.rs b/crates/ide/src/diagnostics/no_such_field.rs index a5f457dce..edc63c246 100644 --- a/crates/ide/src/diagnostics/fixes/create_field.rs +++ b/crates/ide/src/diagnostics/no_such_field.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use hir::{db::AstDatabase, diagnostics::NoSuchField, HasSource, HirDisplay, Semantics}; | 1 | use hir::{db::AstDatabase, HasSource, HirDisplay, Semantics}; |
2 | use ide_db::{base_db::FileId, source_change::SourceChange, RootDatabase}; | 2 | use ide_db::{base_db::FileId, source_change::SourceChange, RootDatabase}; |
3 | use syntax::{ | 3 | use syntax::{ |
4 | ast::{self, edit::IndentLevel, make}, | 4 | ast::{self, edit::IndentLevel, make}, |
@@ -7,22 +7,29 @@ use syntax::{ | |||
7 | use text_edit::TextEdit; | 7 | use text_edit::TextEdit; |
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | diagnostics::{fix, DiagnosticWithFixes}, | 10 | diagnostics::{fix, Diagnostic, DiagnosticsContext}, |
11 | Assist, AssistResolveStrategy, | 11 | Assist, |
12 | }; | 12 | }; |
13 | impl DiagnosticWithFixes for NoSuchField { | 13 | |
14 | fn fixes( | 14 | // Diagnostic: no-such-field |
15 | &self, | 15 | // |
16 | sema: &Semantics<RootDatabase>, | 16 | // This diagnostic is triggered if created structure does not have field provided in record. |
17 | _resolve: &AssistResolveStrategy, | 17 | pub(super) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic { |
18 | ) -> Option<Vec<Assist>> { | 18 | Diagnostic::new( |
19 | let root = sema.db.parse_or_expand(self.file)?; | 19 | "no-such-field", |
20 | missing_record_expr_field_fixes( | 20 | "no such field", |
21 | &sema, | 21 | ctx.sema.diagnostics_display_range(d.field.clone().map(|it| it.into())).range, |
22 | self.file.original_file(sema.db), | 22 | ) |
23 | &self.field.to_node(&root), | 23 | .with_fixes(fixes(ctx, d)) |
24 | ) | 24 | } |
25 | } | 25 | |
26 | fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assist>> { | ||
27 | let root = ctx.sema.db.parse_or_expand(d.field.file_id)?; | ||
28 | missing_record_expr_field_fixes( | ||
29 | &ctx.sema, | ||
30 | d.field.file_id.original_file(ctx.sema.db), | ||
31 | &d.field.value.to_node(&root), | ||
32 | ) | ||
26 | } | 33 | } |
27 | 34 | ||
28 | fn missing_record_expr_field_fixes( | 35 | fn missing_record_expr_field_fixes( |
@@ -105,7 +112,130 @@ fn missing_record_expr_field_fixes( | |||
105 | 112 | ||
106 | #[cfg(test)] | 113 | #[cfg(test)] |
107 | mod tests { | 114 | mod tests { |
108 | use crate::diagnostics::tests::check_fix; | 115 | use crate::diagnostics::tests::{check_diagnostics, check_fix}; |
116 | |||
117 | #[test] | ||
118 | fn no_such_field_diagnostics() { | ||
119 | check_diagnostics( | ||
120 | r#" | ||
121 | struct S { foo: i32, bar: () } | ||
122 | impl S { | ||
123 | fn new() -> S { | ||
124 | S { | ||
125 | //^ Missing structure fields: | ||
126 | //| - bar | ||
127 | foo: 92, | ||
128 | baz: 62, | ||
129 | //^^^^^^^ no such field | ||
130 | } | ||
131 | } | ||
132 | } | ||
133 | "#, | ||
134 | ); | ||
135 | } | ||
136 | #[test] | ||
137 | fn no_such_field_with_feature_flag_diagnostics() { | ||
138 | check_diagnostics( | ||
139 | r#" | ||
140 | //- /lib.rs crate:foo cfg:feature=foo | ||
141 | struct MyStruct { | ||
142 | my_val: usize, | ||
143 | #[cfg(feature = "foo")] | ||
144 | bar: bool, | ||
145 | } | ||
146 | |||
147 | impl MyStruct { | ||
148 | #[cfg(feature = "foo")] | ||
149 | pub(crate) fn new(my_val: usize, bar: bool) -> Self { | ||
150 | Self { my_val, bar } | ||
151 | } | ||
152 | #[cfg(not(feature = "foo"))] | ||
153 | pub(crate) fn new(my_val: usize, _bar: bool) -> Self { | ||
154 | Self { my_val } | ||
155 | } | ||
156 | } | ||
157 | "#, | ||
158 | ); | ||
159 | } | ||
160 | |||
161 | #[test] | ||
162 | fn no_such_field_enum_with_feature_flag_diagnostics() { | ||
163 | check_diagnostics( | ||
164 | r#" | ||
165 | //- /lib.rs crate:foo cfg:feature=foo | ||
166 | enum Foo { | ||
167 | #[cfg(not(feature = "foo"))] | ||
168 | Buz, | ||
169 | #[cfg(feature = "foo")] | ||
170 | Bar, | ||
171 | Baz | ||
172 | } | ||
173 | |||
174 | fn test_fn(f: Foo) { | ||
175 | match f { | ||
176 | Foo::Bar => {}, | ||
177 | Foo::Baz => {}, | ||
178 | } | ||
179 | } | ||
180 | "#, | ||
181 | ); | ||
182 | } | ||
183 | |||
184 | #[test] | ||
185 | fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() { | ||
186 | check_diagnostics( | ||
187 | r#" | ||
188 | //- /lib.rs crate:foo cfg:feature=foo | ||
189 | struct S { | ||
190 | #[cfg(feature = "foo")] | ||
191 | foo: u32, | ||
192 | #[cfg(not(feature = "foo"))] | ||
193 | bar: u32, | ||
194 | } | ||
195 | |||
196 | impl S { | ||
197 | #[cfg(feature = "foo")] | ||
198 | fn new(foo: u32) -> Self { | ||
199 | Self { foo } | ||
200 | } | ||
201 | #[cfg(not(feature = "foo"))] | ||
202 | fn new(bar: u32) -> Self { | ||
203 | Self { bar } | ||
204 | } | ||
205 | fn new2(bar: u32) -> Self { | ||
206 | #[cfg(feature = "foo")] | ||
207 | { Self { foo: bar } } | ||
208 | #[cfg(not(feature = "foo"))] | ||
209 | { Self { bar } } | ||
210 | } | ||
211 | fn new2(val: u32) -> Self { | ||
212 | Self { | ||
213 | #[cfg(feature = "foo")] | ||
214 | foo: val, | ||
215 | #[cfg(not(feature = "foo"))] | ||
216 | bar: val, | ||
217 | } | ||
218 | } | ||
219 | } | ||
220 | "#, | ||
221 | ); | ||
222 | } | ||
223 | |||
224 | #[test] | ||
225 | fn no_such_field_with_type_macro() { | ||
226 | check_diagnostics( | ||
227 | r#" | ||
228 | macro_rules! Type { () => { u32 }; } | ||
229 | struct Foo { bar: Type![] } | ||
230 | |||
231 | impl Foo { | ||
232 | fn new() -> Self { | ||
233 | Foo { bar: 0 } | ||
234 | } | ||
235 | } | ||
236 | "#, | ||
237 | ); | ||
238 | } | ||
109 | 239 | ||
110 | #[test] | 240 | #[test] |
111 | fn test_add_field_from_usage() { | 241 | fn test_add_field_from_usage() { |
diff --git a/crates/ide/src/diagnostics/remove_this_semicolon.rs b/crates/ide/src/diagnostics/remove_this_semicolon.rs new file mode 100644 index 000000000..814cb0f8c --- /dev/null +++ b/crates/ide/src/diagnostics/remove_this_semicolon.rs | |||
@@ -0,0 +1,64 @@ | |||
1 | use hir::db::AstDatabase; | ||
2 | use ide_db::source_change::SourceChange; | ||
3 | use syntax::{ast, AstNode}; | ||
4 | use text_edit::TextEdit; | ||
5 | |||
6 | use crate::{ | ||
7 | diagnostics::{fix, Diagnostic, DiagnosticsContext}, | ||
8 | Assist, | ||
9 | }; | ||
10 | |||
11 | // Diagnostic: remove-this-semicolon | ||
12 | // | ||
13 | // This diagnostic is triggered when there's an erroneous `;` at the end of the block. | ||
14 | pub(super) fn remove_this_semicolon( | ||
15 | ctx: &DiagnosticsContext<'_>, | ||
16 | d: &hir::RemoveThisSemicolon, | ||
17 | ) -> Diagnostic { | ||
18 | Diagnostic::new( | ||
19 | "remove-this-semicolon", | ||
20 | "remove this semicolon", | ||
21 | ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, | ||
22 | ) | ||
23 | .with_fixes(fixes(ctx, d)) | ||
24 | } | ||
25 | |||
26 | fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::RemoveThisSemicolon) -> Option<Vec<Assist>> { | ||
27 | let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?; | ||
28 | |||
29 | let semicolon = d | ||
30 | .expr | ||
31 | .value | ||
32 | .to_node(&root) | ||
33 | .syntax() | ||
34 | .parent() | ||
35 | .and_then(ast::ExprStmt::cast) | ||
36 | .and_then(|expr| expr.semicolon_token())? | ||
37 | .text_range(); | ||
38 | |||
39 | let edit = TextEdit::delete(semicolon); | ||
40 | let source_change = | ||
41 | SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit); | ||
42 | |||
43 | Some(vec![fix("remove_semicolon", "Remove this semicolon", source_change, semicolon)]) | ||
44 | } | ||
45 | |||
46 | #[cfg(test)] | ||
47 | mod tests { | ||
48 | use crate::diagnostics::tests::{check_diagnostics, check_fix}; | ||
49 | |||
50 | #[test] | ||
51 | fn missing_semicolon() { | ||
52 | check_diagnostics( | ||
53 | r#" | ||
54 | fn test() -> i32 { 123; } | ||
55 | //^^^ remove this semicolon | ||
56 | "#, | ||
57 | ); | ||
58 | } | ||
59 | |||
60 | #[test] | ||
61 | fn remove_semicolon() { | ||
62 | check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#); | ||
63 | } | ||
64 | } | ||
diff --git a/crates/ide/src/diagnostics/replace_filter_map_next_with_find_map.rs b/crates/ide/src/diagnostics/replace_filter_map_next_with_find_map.rs new file mode 100644 index 000000000..f3b011495 --- /dev/null +++ b/crates/ide/src/diagnostics/replace_filter_map_next_with_find_map.rs | |||
@@ -0,0 +1,182 @@ | |||
1 | use hir::{db::AstDatabase, InFile}; | ||
2 | use ide_db::source_change::SourceChange; | ||
3 | use syntax::{ | ||
4 | ast::{self, ArgListOwner}, | ||
5 | AstNode, TextRange, | ||
6 | }; | ||
7 | use text_edit::TextEdit; | ||
8 | |||
9 | use crate::{ | ||
10 | diagnostics::{fix, Diagnostic, DiagnosticsContext}, | ||
11 | Assist, Severity, | ||
12 | }; | ||
13 | |||
14 | // Diagnostic: replace-filter-map-next-with-find-map | ||
15 | // | ||
16 | // This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`. | ||
17 | pub(super) fn replace_filter_map_next_with_find_map( | ||
18 | ctx: &DiagnosticsContext<'_>, | ||
19 | d: &hir::ReplaceFilterMapNextWithFindMap, | ||
20 | ) -> Diagnostic { | ||
21 | Diagnostic::new( | ||
22 | "replace-filter-map-next-with-find-map", | ||
23 | "replace filter_map(..).next() with find_map(..)", | ||
24 | ctx.sema.diagnostics_display_range(InFile::new(d.file, d.next_expr.clone().into())).range, | ||
25 | ) | ||
26 | .severity(Severity::WeakWarning) | ||
27 | .with_fixes(fixes(ctx, d)) | ||
28 | } | ||
29 | |||
30 | fn fixes( | ||
31 | ctx: &DiagnosticsContext<'_>, | ||
32 | d: &hir::ReplaceFilterMapNextWithFindMap, | ||
33 | ) -> Option<Vec<Assist>> { | ||
34 | let root = ctx.sema.db.parse_or_expand(d.file)?; | ||
35 | let next_expr = d.next_expr.to_node(&root); | ||
36 | let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?; | ||
37 | |||
38 | let filter_map_call = ast::MethodCallExpr::cast(next_call.receiver()?.syntax().clone())?; | ||
39 | let filter_map_name_range = filter_map_call.name_ref()?.ident_token()?.text_range(); | ||
40 | let filter_map_args = filter_map_call.arg_list()?; | ||
41 | |||
42 | let range_to_replace = | ||
43 | TextRange::new(filter_map_name_range.start(), next_expr.syntax().text_range().end()); | ||
44 | let replacement = format!("find_map{}", filter_map_args.syntax().text()); | ||
45 | let trigger_range = next_expr.syntax().text_range(); | ||
46 | |||
47 | let edit = TextEdit::replace(range_to_replace, replacement); | ||
48 | |||
49 | let source_change = SourceChange::from_text_edit(d.file.original_file(ctx.sema.db), edit); | ||
50 | |||
51 | Some(vec![fix( | ||
52 | "replace_with_find_map", | ||
53 | "Replace filter_map(..).next() with find_map()", | ||
54 | source_change, | ||
55 | trigger_range, | ||
56 | )]) | ||
57 | } | ||
58 | |||
59 | #[cfg(test)] | ||
60 | mod tests { | ||
61 | use crate::diagnostics::tests::check_fix; | ||
62 | |||
63 | // Register the required standard library types to make the tests work | ||
64 | #[track_caller] | ||
65 | fn check_diagnostics(ra_fixture: &str) { | ||
66 | let prefix = r#" | ||
67 | //- /main.rs crate:main deps:core | ||
68 | use core::iter::Iterator; | ||
69 | use core::option::Option::{self, Some, None}; | ||
70 | "#; | ||
71 | let suffix = r#" | ||
72 | //- /core/lib.rs crate:core | ||
73 | pub mod option { | ||
74 | pub enum Option<T> { Some(T), None } | ||
75 | } | ||
76 | pub mod iter { | ||
77 | pub trait Iterator { | ||
78 | type Item; | ||
79 | fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap } | ||
80 | fn next(&mut self) -> Option<Self::Item>; | ||
81 | } | ||
82 | pub struct FilterMap {} | ||
83 | impl Iterator for FilterMap { | ||
84 | type Item = i32; | ||
85 | fn next(&mut self) -> i32 { 7 } | ||
86 | } | ||
87 | } | ||
88 | "#; | ||
89 | crate::diagnostics::tests::check_diagnostics(&format!("{}{}{}", prefix, ra_fixture, suffix)) | ||
90 | } | ||
91 | |||
92 | #[test] | ||
93 | fn replace_filter_map_next_with_find_map2() { | ||
94 | check_diagnostics( | ||
95 | r#" | ||
96 | fn foo() { | ||
97 | let m = [1, 2, 3].iter().filter_map(|x| if *x == 2 { Some (4) } else { None }).next(); | ||
98 | } //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ replace filter_map(..).next() with find_map(..) | ||
99 | "#, | ||
100 | ); | ||
101 | } | ||
102 | |||
103 | #[test] | ||
104 | fn replace_filter_map_next_with_find_map_no_diagnostic_without_next() { | ||
105 | check_diagnostics( | ||
106 | r#" | ||
107 | fn foo() { | ||
108 | let m = [1, 2, 3] | ||
109 | .iter() | ||
110 | .filter_map(|x| if *x == 2 { Some (4) } else { None }) | ||
111 | .len(); | ||
112 | } | ||
113 | "#, | ||
114 | ); | ||
115 | } | ||
116 | |||
117 | #[test] | ||
118 | fn replace_filter_map_next_with_find_map_no_diagnostic_with_intervening_methods() { | ||
119 | check_diagnostics( | ||
120 | r#" | ||
121 | fn foo() { | ||
122 | let m = [1, 2, 3] | ||
123 | .iter() | ||
124 | .filter_map(|x| if *x == 2 { Some (4) } else { None }) | ||
125 | .map(|x| x + 2) | ||
126 | .len(); | ||
127 | } | ||
128 | "#, | ||
129 | ); | ||
130 | } | ||
131 | |||
132 | #[test] | ||
133 | fn replace_filter_map_next_with_find_map_no_diagnostic_if_not_in_chain() { | ||
134 | check_diagnostics( | ||
135 | r#" | ||
136 | fn foo() { | ||
137 | let m = [1, 2, 3] | ||
138 | .iter() | ||
139 | .filter_map(|x| if *x == 2 { Some (4) } else { None }); | ||
140 | let n = m.next(); | ||
141 | } | ||
142 | "#, | ||
143 | ); | ||
144 | } | ||
145 | |||
146 | #[test] | ||
147 | fn replace_with_wind_map() { | ||
148 | check_fix( | ||
149 | r#" | ||
150 | //- /main.rs crate:main deps:core | ||
151 | use core::iter::Iterator; | ||
152 | use core::option::Option::{self, Some, None}; | ||
153 | fn foo() { | ||
154 | let m = [1, 2, 3].iter().$0filter_map(|x| if *x == 2 { Some (4) } else { None }).next(); | ||
155 | } | ||
156 | //- /core/lib.rs crate:core | ||
157 | pub mod option { | ||
158 | pub enum Option<T> { Some(T), None } | ||
159 | } | ||
160 | pub mod iter { | ||
161 | pub trait Iterator { | ||
162 | type Item; | ||
163 | fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap } | ||
164 | fn next(&mut self) -> Option<Self::Item>; | ||
165 | } | ||
166 | pub struct FilterMap {} | ||
167 | impl Iterator for FilterMap { | ||
168 | type Item = i32; | ||
169 | fn next(&mut self) -> i32 { 7 } | ||
170 | } | ||
171 | } | ||
172 | "#, | ||
173 | r#" | ||
174 | use core::iter::Iterator; | ||
175 | use core::option::Option::{self, Some, None}; | ||
176 | fn foo() { | ||
177 | let m = [1, 2, 3].iter().find_map(|x| if *x == 2 { Some (4) } else { None }); | ||
178 | } | ||
179 | "#, | ||
180 | ) | ||
181 | } | ||
182 | } | ||
diff --git a/crates/ide/src/diagnostics/unimplemented_builtin_macro.rs b/crates/ide/src/diagnostics/unimplemented_builtin_macro.rs new file mode 100644 index 000000000..09faa3bbc --- /dev/null +++ b/crates/ide/src/diagnostics/unimplemented_builtin_macro.rs | |||
@@ -0,0 +1,19 @@ | |||
1 | use crate::{ | ||
2 | diagnostics::{Diagnostic, DiagnosticsContext}, | ||
3 | Severity, | ||
4 | }; | ||
5 | |||
6 | // Diagnostic: unimplemented-builtin-macro | ||
7 | // | ||
8 | // This diagnostic is shown for builtin macros which are not yet implemented by rust-analyzer | ||
9 | pub(super) fn unimplemented_builtin_macro( | ||
10 | ctx: &DiagnosticsContext<'_>, | ||
11 | d: &hir::UnimplementedBuiltinMacro, | ||
12 | ) -> Diagnostic { | ||
13 | Diagnostic::new( | ||
14 | "unimplemented-builtin-macro", | ||
15 | "unimplemented built-in macro".to_string(), | ||
16 | ctx.sema.diagnostics_display_range(d.node.clone()).range, | ||
17 | ) | ||
18 | .severity(Severity::WeakWarning) | ||
19 | } | ||
diff --git a/crates/ide/src/diagnostics/unlinked_file.rs b/crates/ide/src/diagnostics/unlinked_file.rs index 51fe0f360..a5b2e3399 100644 --- a/crates/ide/src/diagnostics/unlinked_file.rs +++ b/crates/ide/src/diagnostics/unlinked_file.rs | |||
@@ -1,11 +1,6 @@ | |||
1 | //! Diagnostic emitted for files that aren't part of any crate. | 1 | //! Diagnostic emitted for files that aren't part of any crate. |
2 | 2 | ||
3 | use hir::{ | 3 | use hir::db::DefDatabase; |
4 | db::DefDatabase, | ||
5 | diagnostics::{Diagnostic, DiagnosticCode}, | ||
6 | InFile, | ||
7 | }; | ||
8 | use ide_assists::AssistResolveStrategy; | ||
9 | use ide_db::{ | 4 | use ide_db::{ |
10 | base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt}, | 5 | base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt}, |
11 | source_change::SourceChange, | 6 | source_change::SourceChange, |
@@ -13,92 +8,77 @@ use ide_db::{ | |||
13 | }; | 8 | }; |
14 | use syntax::{ | 9 | use syntax::{ |
15 | ast::{self, ModuleItemOwner, NameOwner}, | 10 | ast::{self, ModuleItemOwner, NameOwner}, |
16 | AstNode, SyntaxNodePtr, | 11 | AstNode, TextRange, TextSize, |
17 | }; | 12 | }; |
18 | use text_edit::TextEdit; | 13 | use text_edit::TextEdit; |
19 | 14 | ||
20 | use crate::{ | 15 | use crate::{ |
21 | diagnostics::{fix, fixes::DiagnosticWithFixes}, | 16 | diagnostics::{fix, DiagnosticsContext}, |
22 | Assist, | 17 | Assist, Diagnostic, |
23 | }; | 18 | }; |
24 | 19 | ||
20 | #[derive(Debug)] | ||
21 | pub(crate) struct UnlinkedFile { | ||
22 | pub(crate) file: FileId, | ||
23 | } | ||
24 | |||
25 | // Diagnostic: unlinked-file | 25 | // Diagnostic: unlinked-file |
26 | // | 26 | // |
27 | // This diagnostic is shown for files that are not included in any crate, or files that are part of | 27 | // This diagnostic is shown for files that are not included in any crate, or files that are part of |
28 | // crates rust-analyzer failed to discover. The file will not have IDE features available. | 28 | // crates rust-analyzer failed to discover. The file will not have IDE features available. |
29 | #[derive(Debug)] | 29 | pub(super) fn unlinked_file(ctx: &DiagnosticsContext, d: &UnlinkedFile) -> Diagnostic { |
30 | pub(crate) struct UnlinkedFile { | 30 | // Limit diagnostic to the first few characters in the file. This matches how VS Code |
31 | pub(crate) file_id: FileId, | 31 | // renders it with the full span, but on other editors, and is less invasive. |
32 | pub(crate) node: SyntaxNodePtr, | 32 | let range = ctx.sema.db.parse(d.file).syntax_node().text_range(); |
33 | // FIXME: This is wrong if one of the first three characters is not ascii: `//Ы`. | ||
34 | let range = range.intersect(TextRange::up_to(TextSize::of("..."))).unwrap_or(range); | ||
35 | |||
36 | Diagnostic::new("unlinked-file", "file not included in module tree", range) | ||
37 | .with_fixes(fixes(ctx, d)) | ||
33 | } | 38 | } |
34 | 39 | ||
35 | impl Diagnostic for UnlinkedFile { | 40 | fn fixes(ctx: &DiagnosticsContext, d: &UnlinkedFile) -> Option<Vec<Assist>> { |
36 | fn code(&self) -> DiagnosticCode { | 41 | // If there's an existing module that could add `mod` or `pub mod` items to include the unlinked file, |
37 | DiagnosticCode("unlinked-file") | 42 | // suggest that as a fix. |
38 | } | ||
39 | 43 | ||
40 | fn message(&self) -> String { | 44 | let source_root = ctx.sema.db.source_root(ctx.sema.db.file_source_root(d.file)); |
41 | "file not included in module tree".to_string() | 45 | let our_path = source_root.path_for_file(&d.file)?; |
42 | } | 46 | let module_name = our_path.name_and_extension()?.0; |
43 | 47 | ||
44 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | 48 | // Candidates to look for: |
45 | InFile::new(self.file_id.into(), self.node.clone()) | 49 | // - `mod.rs` in the same folder |
46 | } | 50 | // - we also check `main.rs` and `lib.rs` |
51 | // - `$dir.rs` in the parent folder, where `$dir` is the directory containing `self.file_id` | ||
52 | let parent = our_path.parent()?; | ||
53 | let mut paths = vec![parent.join("mod.rs")?, parent.join("lib.rs")?, parent.join("main.rs")?]; | ||
47 | 54 | ||
48 | fn as_any(&self) -> &(dyn std::any::Any + Send + 'static) { | 55 | // `submod/bla.rs` -> `submod.rs` |
49 | self | 56 | if let Some(newmod) = (|| { |
57 | let name = parent.name_and_extension()?.0; | ||
58 | parent.parent()?.join(&format!("{}.rs", name)) | ||
59 | })() { | ||
60 | paths.push(newmod); | ||
50 | } | 61 | } |
51 | } | ||
52 | 62 | ||
53 | impl DiagnosticWithFixes for UnlinkedFile { | 63 | for path in &paths { |
54 | fn fixes( | 64 | if let Some(parent_id) = source_root.file_for_path(path) { |
55 | &self, | 65 | for krate in ctx.sema.db.relevant_crates(*parent_id).iter() { |
56 | sema: &hir::Semantics<RootDatabase>, | 66 | let crate_def_map = ctx.sema.db.crate_def_map(*krate); |
57 | _resolve: &AssistResolveStrategy, | 67 | for (_, module) in crate_def_map.modules() { |
58 | ) -> Option<Vec<Assist>> { | 68 | if module.origin.is_inline() { |
59 | // If there's an existing module that could add `mod` or `pub mod` items to include the unlinked file, | 69 | // We don't handle inline `mod parent {}`s, they use different paths. |
60 | // suggest that as a fix. | 70 | continue; |
61 | 71 | } | |
62 | let source_root = sema.db.source_root(sema.db.file_source_root(self.file_id)); | ||
63 | let our_path = source_root.path_for_file(&self.file_id)?; | ||
64 | let module_name = our_path.name_and_extension()?.0; | ||
65 | |||
66 | // Candidates to look for: | ||
67 | // - `mod.rs` in the same folder | ||
68 | // - we also check `main.rs` and `lib.rs` | ||
69 | // - `$dir.rs` in the parent folder, where `$dir` is the directory containing `self.file_id` | ||
70 | let parent = our_path.parent()?; | ||
71 | let mut paths = | ||
72 | vec![parent.join("mod.rs")?, parent.join("lib.rs")?, parent.join("main.rs")?]; | ||
73 | |||
74 | // `submod/bla.rs` -> `submod.rs` | ||
75 | if let Some(newmod) = (|| { | ||
76 | let name = parent.name_and_extension()?.0; | ||
77 | parent.parent()?.join(&format!("{}.rs", name)) | ||
78 | })() { | ||
79 | paths.push(newmod); | ||
80 | } | ||
81 | 72 | ||
82 | for path in &paths { | 73 | if module.origin.file_id() == Some(*parent_id) { |
83 | if let Some(parent_id) = source_root.file_for_path(path) { | 74 | return make_fixes(ctx.sema.db, *parent_id, module_name, d.file); |
84 | for krate in sema.db.relevant_crates(*parent_id).iter() { | ||
85 | let crate_def_map = sema.db.crate_def_map(*krate); | ||
86 | for (_, module) in crate_def_map.modules() { | ||
87 | if module.origin.is_inline() { | ||
88 | // We don't handle inline `mod parent {}`s, they use different paths. | ||
89 | continue; | ||
90 | } | ||
91 | |||
92 | if module.origin.file_id() == Some(*parent_id) { | ||
93 | return make_fixes(sema.db, *parent_id, module_name, self.file_id); | ||
94 | } | ||
95 | } | 75 | } |
96 | } | 76 | } |
97 | } | 77 | } |
98 | } | 78 | } |
99 | |||
100 | None | ||
101 | } | 79 | } |
80 | |||
81 | None | ||
102 | } | 82 | } |
103 | 83 | ||
104 | fn make_fixes( | 84 | fn make_fixes( |
@@ -181,3 +161,144 @@ fn make_fixes( | |||
181 | ), | 161 | ), |
182 | ]) | 162 | ]) |
183 | } | 163 | } |
164 | |||
165 | #[cfg(test)] | ||
166 | mod tests { | ||
167 | use crate::diagnostics::tests::{check_diagnostics, check_fix, check_fixes, check_no_fix}; | ||
168 | |||
169 | #[test] | ||
170 | fn unlinked_file_prepend_first_item() { | ||
171 | cov_mark::check!(unlinked_file_prepend_before_first_item); | ||
172 | // Only tests the first one for `pub mod` since the rest are the same | ||
173 | check_fixes( | ||
174 | r#" | ||
175 | //- /main.rs | ||
176 | fn f() {} | ||
177 | //- /foo.rs | ||
178 | $0 | ||
179 | "#, | ||
180 | vec![ | ||
181 | r#" | ||
182 | mod foo; | ||
183 | |||
184 | fn f() {} | ||
185 | "#, | ||
186 | r#" | ||
187 | pub mod foo; | ||
188 | |||
189 | fn f() {} | ||
190 | "#, | ||
191 | ], | ||
192 | ); | ||
193 | } | ||
194 | |||
195 | #[test] | ||
196 | fn unlinked_file_append_mod() { | ||
197 | cov_mark::check!(unlinked_file_append_to_existing_mods); | ||
198 | check_fix( | ||
199 | r#" | ||
200 | //- /main.rs | ||
201 | //! Comment on top | ||
202 | |||
203 | mod preexisting; | ||
204 | |||
205 | mod preexisting2; | ||
206 | |||
207 | struct S; | ||
208 | |||
209 | mod preexisting_bottom;) | ||
210 | //- /foo.rs | ||
211 | $0 | ||
212 | "#, | ||
213 | r#" | ||
214 | //! Comment on top | ||
215 | |||
216 | mod preexisting; | ||
217 | |||
218 | mod preexisting2; | ||
219 | mod foo; | ||
220 | |||
221 | struct S; | ||
222 | |||
223 | mod preexisting_bottom;) | ||
224 | "#, | ||
225 | ); | ||
226 | } | ||
227 | |||
228 | #[test] | ||
229 | fn unlinked_file_insert_in_empty_file() { | ||
230 | cov_mark::check!(unlinked_file_empty_file); | ||
231 | check_fix( | ||
232 | r#" | ||
233 | //- /main.rs | ||
234 | //- /foo.rs | ||
235 | $0 | ||
236 | "#, | ||
237 | r#" | ||
238 | mod foo; | ||
239 | "#, | ||
240 | ); | ||
241 | } | ||
242 | |||
243 | #[test] | ||
244 | fn unlinked_file_old_style_modrs() { | ||
245 | check_fix( | ||
246 | r#" | ||
247 | //- /main.rs | ||
248 | mod submod; | ||
249 | //- /submod/mod.rs | ||
250 | // in mod.rs | ||
251 | //- /submod/foo.rs | ||
252 | $0 | ||
253 | "#, | ||
254 | r#" | ||
255 | // in mod.rs | ||
256 | mod foo; | ||
257 | "#, | ||
258 | ); | ||
259 | } | ||
260 | |||
261 | #[test] | ||
262 | fn unlinked_file_new_style_mod() { | ||
263 | check_fix( | ||
264 | r#" | ||
265 | //- /main.rs | ||
266 | mod submod; | ||
267 | //- /submod.rs | ||
268 | //- /submod/foo.rs | ||
269 | $0 | ||
270 | "#, | ||
271 | r#" | ||
272 | mod foo; | ||
273 | "#, | ||
274 | ); | ||
275 | } | ||
276 | |||
277 | #[test] | ||
278 | fn unlinked_file_with_cfg_off() { | ||
279 | cov_mark::check!(unlinked_file_skip_fix_when_mod_already_exists); | ||
280 | check_no_fix( | ||
281 | r#" | ||
282 | //- /main.rs | ||
283 | #[cfg(never)] | ||
284 | mod foo; | ||
285 | |||
286 | //- /foo.rs | ||
287 | $0 | ||
288 | "#, | ||
289 | ); | ||
290 | } | ||
291 | |||
292 | #[test] | ||
293 | fn unlinked_file_with_cfg_on() { | ||
294 | check_diagnostics( | ||
295 | r#" | ||
296 | //- /main.rs | ||
297 | #[cfg(not(never))] | ||
298 | mod foo; | ||
299 | |||
300 | //- /foo.rs | ||
301 | "#, | ||
302 | ); | ||
303 | } | ||
304 | } | ||
diff --git a/crates/ide/src/diagnostics/unresolved_extern_crate.rs b/crates/ide/src/diagnostics/unresolved_extern_crate.rs new file mode 100644 index 000000000..2ea79c2ee --- /dev/null +++ b/crates/ide/src/diagnostics/unresolved_extern_crate.rs | |||
@@ -0,0 +1,49 @@ | |||
1 | use crate::diagnostics::{Diagnostic, DiagnosticsContext}; | ||
2 | |||
3 | // Diagnostic: unresolved-extern-crate | ||
4 | // | ||
5 | // This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate. | ||
6 | pub(super) fn unresolved_extern_crate( | ||
7 | ctx: &DiagnosticsContext<'_>, | ||
8 | d: &hir::UnresolvedExternCrate, | ||
9 | ) -> Diagnostic { | ||
10 | Diagnostic::new( | ||
11 | "unresolved-extern-crate", | ||
12 | "unresolved extern crate", | ||
13 | ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range, | ||
14 | ) | ||
15 | } | ||
16 | |||
17 | #[cfg(test)] | ||
18 | mod tests { | ||
19 | use crate::diagnostics::tests::check_diagnostics; | ||
20 | |||
21 | #[test] | ||
22 | fn unresolved_extern_crate() { | ||
23 | check_diagnostics( | ||
24 | r#" | ||
25 | //- /main.rs crate:main deps:core | ||
26 | extern crate core; | ||
27 | extern crate doesnotexist; | ||
28 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate | ||
29 | //- /lib.rs crate:core | ||
30 | "#, | ||
31 | ); | ||
32 | } | ||
33 | |||
34 | #[test] | ||
35 | fn extern_crate_self_as() { | ||
36 | cov_mark::check!(extern_crate_self_as); | ||
37 | check_diagnostics( | ||
38 | r#" | ||
39 | //- /lib.rs | ||
40 | extern crate doesnotexist; | ||
41 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate | ||
42 | // Should not error. | ||
43 | extern crate self as foo; | ||
44 | struct Foo; | ||
45 | use foo::Foo as Bar; | ||
46 | "#, | ||
47 | ); | ||
48 | } | ||
49 | } | ||
diff --git a/crates/ide/src/diagnostics/unresolved_import.rs b/crates/ide/src/diagnostics/unresolved_import.rs new file mode 100644 index 000000000..1cbf96ba1 --- /dev/null +++ b/crates/ide/src/diagnostics/unresolved_import.rs | |||
@@ -0,0 +1,90 @@ | |||
1 | use crate::diagnostics::{Diagnostic, DiagnosticsContext}; | ||
2 | |||
3 | // Diagnostic: unresolved-import | ||
4 | // | ||
5 | // This diagnostic is triggered if rust-analyzer is unable to resolve a path in | ||
6 | // a `use` declaration. | ||
7 | pub(super) fn unresolved_import( | ||
8 | ctx: &DiagnosticsContext<'_>, | ||
9 | d: &hir::UnresolvedImport, | ||
10 | ) -> Diagnostic { | ||
11 | Diagnostic::new( | ||
12 | "unresolved-import", | ||
13 | "unresolved import", | ||
14 | ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range, | ||
15 | ) | ||
16 | // This currently results in false positives in the following cases: | ||
17 | // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly) | ||
18 | // - `core::arch` (we don't handle `#[path = "../<path>"]` correctly) | ||
19 | // - proc macros and/or proc macro generated code | ||
20 | .experimental() | ||
21 | } | ||
22 | |||
23 | #[cfg(test)] | ||
24 | mod tests { | ||
25 | use crate::diagnostics::tests::check_diagnostics; | ||
26 | |||
27 | #[test] | ||
28 | fn unresolved_import() { | ||
29 | check_diagnostics( | ||
30 | r#" | ||
31 | use does_exist; | ||
32 | use does_not_exist; | ||
33 | //^^^^^^^^^^^^^^ unresolved import | ||
34 | |||
35 | mod does_exist {} | ||
36 | "#, | ||
37 | ); | ||
38 | } | ||
39 | |||
40 | #[test] | ||
41 | fn unresolved_import_in_use_tree() { | ||
42 | // Only the relevant part of a nested `use` item should be highlighted. | ||
43 | check_diagnostics( | ||
44 | r#" | ||
45 | use does_exist::{Exists, DoesntExist}; | ||
46 | //^^^^^^^^^^^ unresolved import | ||
47 | |||
48 | use {does_not_exist::*, does_exist}; | ||
49 | //^^^^^^^^^^^^^^^^^ unresolved import | ||
50 | |||
51 | use does_not_exist::{ | ||
52 | a, | ||
53 | //^ unresolved import | ||
54 | b, | ||
55 | //^ unresolved import | ||
56 | c, | ||
57 | //^ unresolved import | ||
58 | }; | ||
59 | |||
60 | mod does_exist { | ||
61 | pub struct Exists; | ||
62 | } | ||
63 | "#, | ||
64 | ); | ||
65 | } | ||
66 | |||
67 | #[test] | ||
68 | fn dedup_unresolved_import_from_unresolved_crate() { | ||
69 | check_diagnostics( | ||
70 | r#" | ||
71 | //- /main.rs crate:main | ||
72 | mod a { | ||
73 | extern crate doesnotexist; | ||
74 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate | ||
75 | |||
76 | // Should not error, since we already errored for the missing crate. | ||
77 | use doesnotexist::{self, bla, *}; | ||
78 | |||
79 | use crate::doesnotexist; | ||
80 | //^^^^^^^^^^^^^^^^^^^ unresolved import | ||
81 | } | ||
82 | |||
83 | mod m { | ||
84 | use super::doesnotexist; | ||
85 | //^^^^^^^^^^^^^^^^^^^ unresolved import | ||
86 | } | ||
87 | "#, | ||
88 | ); | ||
89 | } | ||
90 | } | ||
diff --git a/crates/ide/src/diagnostics/unresolved_macro_call.rs b/crates/ide/src/diagnostics/unresolved_macro_call.rs new file mode 100644 index 000000000..15b6a2730 --- /dev/null +++ b/crates/ide/src/diagnostics/unresolved_macro_call.rs | |||
@@ -0,0 +1,84 @@ | |||
1 | use hir::{db::AstDatabase, InFile}; | ||
2 | use syntax::{AstNode, SyntaxNodePtr}; | ||
3 | |||
4 | use crate::diagnostics::{Diagnostic, DiagnosticsContext}; | ||
5 | |||
6 | // Diagnostic: unresolved-macro-call | ||
7 | // | ||
8 | // This diagnostic is triggered if rust-analyzer is unable to resolve the path | ||
9 | // to a macro in a macro invocation. | ||
10 | pub(super) fn unresolved_macro_call( | ||
11 | ctx: &DiagnosticsContext<'_>, | ||
12 | d: &hir::UnresolvedMacroCall, | ||
13 | ) -> Diagnostic { | ||
14 | let last_path_segment = ctx.sema.db.parse_or_expand(d.macro_call.file_id).and_then(|root| { | ||
15 | d.macro_call | ||
16 | .value | ||
17 | .to_node(&root) | ||
18 | .path() | ||
19 | .and_then(|it| it.segment()) | ||
20 | .and_then(|it| it.name_ref()) | ||
21 | .map(|it| InFile::new(d.macro_call.file_id, SyntaxNodePtr::new(it.syntax()))) | ||
22 | }); | ||
23 | let diagnostics = last_path_segment.unwrap_or_else(|| d.macro_call.clone().map(|it| it.into())); | ||
24 | |||
25 | Diagnostic::new( | ||
26 | "unresolved-macro-call", | ||
27 | format!("unresolved macro `{}!`", d.path), | ||
28 | ctx.sema.diagnostics_display_range(diagnostics).range, | ||
29 | ) | ||
30 | .experimental() | ||
31 | } | ||
32 | |||
33 | #[cfg(test)] | ||
34 | mod tests { | ||
35 | use crate::diagnostics::tests::check_diagnostics; | ||
36 | |||
37 | #[test] | ||
38 | fn unresolved_macro_diag() { | ||
39 | check_diagnostics( | ||
40 | r#" | ||
41 | fn f() { | ||
42 | m!(); | ||
43 | } //^ unresolved macro `m!` | ||
44 | |||
45 | "#, | ||
46 | ); | ||
47 | } | ||
48 | |||
49 | #[test] | ||
50 | fn test_unresolved_macro_range() { | ||
51 | check_diagnostics( | ||
52 | r#" | ||
53 | foo::bar!(92); | ||
54 | //^^^ unresolved macro `foo::bar!` | ||
55 | "#, | ||
56 | ); | ||
57 | } | ||
58 | |||
59 | #[test] | ||
60 | fn unresolved_legacy_scope_macro() { | ||
61 | check_diagnostics( | ||
62 | r#" | ||
63 | macro_rules! m { () => {} } | ||
64 | |||
65 | m!(); m2!(); | ||
66 | //^^ unresolved macro `self::m2!` | ||
67 | "#, | ||
68 | ); | ||
69 | } | ||
70 | |||
71 | #[test] | ||
72 | fn unresolved_module_scope_macro() { | ||
73 | check_diagnostics( | ||
74 | r#" | ||
75 | mod mac { | ||
76 | #[macro_export] | ||
77 | macro_rules! m { () => {} } } | ||
78 | |||
79 | self::m!(); self::m2!(); | ||
80 | //^^ unresolved macro `self::m2!` | ||
81 | "#, | ||
82 | ); | ||
83 | } | ||
84 | } | ||
diff --git a/crates/ide/src/diagnostics/fixes/unresolved_module.rs b/crates/ide/src/diagnostics/unresolved_module.rs index b3d0283bb..977b46414 100644 --- a/crates/ide/src/diagnostics/fixes/unresolved_module.rs +++ b/crates/ide/src/diagnostics/unresolved_module.rs | |||
@@ -1,39 +1,62 @@ | |||
1 | use hir::{db::AstDatabase, diagnostics::UnresolvedModule, Semantics}; | 1 | use hir::db::AstDatabase; |
2 | use ide_assists::{Assist, AssistResolveStrategy}; | 2 | use ide_assists::Assist; |
3 | use ide_db::{base_db::AnchoredPathBuf, source_change::FileSystemEdit, RootDatabase}; | 3 | use ide_db::{base_db::AnchoredPathBuf, source_change::FileSystemEdit}; |
4 | use syntax::AstNode; | 4 | use syntax::AstNode; |
5 | 5 | ||
6 | use crate::diagnostics::{fix, DiagnosticWithFixes}; | 6 | use crate::diagnostics::{fix, Diagnostic, DiagnosticsContext}; |
7 | 7 | ||
8 | impl DiagnosticWithFixes for UnresolvedModule { | 8 | // Diagnostic: unresolved-module |
9 | fn fixes( | 9 | // |
10 | &self, | 10 | // This diagnostic is triggered if rust-analyzer is unable to discover referred module. |
11 | sema: &Semantics<RootDatabase>, | 11 | pub(super) fn unresolved_module( |
12 | _resolve: &AssistResolveStrategy, | 12 | ctx: &DiagnosticsContext<'_>, |
13 | ) -> Option<Vec<Assist>> { | 13 | d: &hir::UnresolvedModule, |
14 | let root = sema.db.parse_or_expand(self.file)?; | 14 | ) -> Diagnostic { |
15 | let unresolved_module = self.decl.to_node(&root); | 15 | Diagnostic::new( |
16 | Some(vec![fix( | 16 | "unresolved-module", |
17 | "create_module", | 17 | "unresolved module", |
18 | "Create module", | 18 | ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range, |
19 | FileSystemEdit::CreateFile { | 19 | ) |
20 | dst: AnchoredPathBuf { | 20 | .with_fixes(fixes(ctx, d)) |
21 | anchor: self.file.original_file(sema.db), | 21 | } |
22 | path: self.candidate.clone(), | 22 | |
23 | }, | 23 | fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule) -> Option<Vec<Assist>> { |
24 | initial_contents: "".to_string(), | 24 | let root = ctx.sema.db.parse_or_expand(d.decl.file_id)?; |
25 | } | 25 | let unresolved_module = d.decl.value.to_node(&root); |
26 | .into(), | 26 | Some(vec![fix( |
27 | unresolved_module.syntax().text_range(), | 27 | "create_module", |
28 | )]) | 28 | "Create module", |
29 | } | 29 | FileSystemEdit::CreateFile { |
30 | dst: AnchoredPathBuf { | ||
31 | anchor: d.decl.file_id.original_file(ctx.sema.db), | ||
32 | path: d.candidate.clone(), | ||
33 | }, | ||
34 | initial_contents: "".to_string(), | ||
35 | } | ||
36 | .into(), | ||
37 | unresolved_module.syntax().text_range(), | ||
38 | )]) | ||
30 | } | 39 | } |
31 | 40 | ||
32 | #[cfg(test)] | 41 | #[cfg(test)] |
33 | mod tests { | 42 | mod tests { |
34 | use expect_test::expect; | 43 | use expect_test::expect; |
35 | 44 | ||
36 | use crate::diagnostics::tests::check_expect; | 45 | use crate::diagnostics::tests::{check_diagnostics, check_expect}; |
46 | |||
47 | #[test] | ||
48 | fn unresolved_module() { | ||
49 | check_diagnostics( | ||
50 | r#" | ||
51 | //- /lib.rs | ||
52 | mod foo; | ||
53 | mod bar; | ||
54 | //^^^^^^^^ unresolved module | ||
55 | mod baz {} | ||
56 | //- /foo.rs | ||
57 | "#, | ||
58 | ); | ||
59 | } | ||
37 | 60 | ||
38 | #[test] | 61 | #[test] |
39 | fn test_unresolved_module_diagnostic() { | 62 | fn test_unresolved_module_diagnostic() { |
@@ -42,9 +65,14 @@ mod tests { | |||
42 | expect![[r#" | 65 | expect![[r#" |
43 | [ | 66 | [ |
44 | Diagnostic { | 67 | Diagnostic { |
68 | code: DiagnosticCode( | ||
69 | "unresolved-module", | ||
70 | ), | ||
45 | message: "unresolved module", | 71 | message: "unresolved module", |
46 | range: 0..8, | 72 | range: 0..8, |
47 | severity: Error, | 73 | severity: Error, |
74 | unused: false, | ||
75 | experimental: false, | ||
48 | fixes: Some( | 76 | fixes: Some( |
49 | [ | 77 | [ |
50 | Assist { | 78 | Assist { |
@@ -75,12 +103,6 @@ mod tests { | |||
75 | }, | 103 | }, |
76 | ], | 104 | ], |
77 | ), | 105 | ), |
78 | unused: false, | ||
79 | code: Some( | ||
80 | DiagnosticCode( | ||
81 | "unresolved-module", | ||
82 | ), | ||
83 | ), | ||
84 | }, | 106 | }, |
85 | ] | 107 | ] |
86 | "#]], | 108 | "#]], |
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 @@ | |||
1 | use 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`). | ||
15 | pub(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 | } | ||
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index ec3828ab2..57ae9455b 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs | |||
@@ -151,18 +151,18 @@ pub(crate) fn resolve_doc_path_for_def( | |||
151 | ) -> Option<hir::ModuleDef> { | 151 | ) -> Option<hir::ModuleDef> { |
152 | match def { | 152 | match def { |
153 | Definition::ModuleDef(def) => match def { | 153 | Definition::ModuleDef(def) => match def { |
154 | hir::ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns), | 154 | hir::ModuleDef::Module(it) => it.resolve_doc_path(db, link, ns), |
155 | hir::ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns), | 155 | hir::ModuleDef::Function(it) => it.resolve_doc_path(db, link, ns), |
156 | hir::ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns), | 156 | hir::ModuleDef::Adt(it) => it.resolve_doc_path(db, link, ns), |
157 | hir::ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns), | 157 | hir::ModuleDef::Variant(it) => it.resolve_doc_path(db, link, ns), |
158 | hir::ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns), | 158 | hir::ModuleDef::Const(it) => it.resolve_doc_path(db, link, ns), |
159 | hir::ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns), | 159 | hir::ModuleDef::Static(it) => it.resolve_doc_path(db, link, ns), |
160 | hir::ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns), | 160 | hir::ModuleDef::Trait(it) => it.resolve_doc_path(db, link, ns), |
161 | hir::ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns), | 161 | hir::ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, link, ns), |
162 | hir::ModuleDef::BuiltinType(_) => None, | 162 | hir::ModuleDef::BuiltinType(_) => None, |
163 | }, | 163 | }, |
164 | Definition::Macro(it) => it.resolve_doc_path(db, &link, ns), | 164 | Definition::Macro(it) => it.resolve_doc_path(db, link, ns), |
165 | Definition::Field(it) => it.resolve_doc_path(db, &link, ns), | 165 | Definition::Field(it) => it.resolve_doc_path(db, link, ns), |
166 | Definition::SelfType(_) | 166 | Definition::SelfType(_) |
167 | | Definition::Local(_) | 167 | | Definition::Local(_) |
168 | | Definition::GenericParam(_) | 168 | | Definition::GenericParam(_) |
@@ -192,7 +192,7 @@ pub(crate) fn doc_attributes( | |||
192 | ast::TupleField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))), | 192 | ast::TupleField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))), |
193 | ast::Macro(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Macro(def))), | 193 | ast::Macro(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Macro(def))), |
194 | // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))), | 194 | // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))), |
195 | _ => return None | 195 | _ => None |
196 | } | 196 | } |
197 | } | 197 | } |
198 | } | 198 | } |
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index eebae5ebe..cc43c5772 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs | |||
@@ -2,10 +2,7 @@ use std::iter; | |||
2 | 2 | ||
3 | use hir::Semantics; | 3 | use hir::Semantics; |
4 | use ide_db::RootDatabase; | 4 | use ide_db::RootDatabase; |
5 | use syntax::{ | 5 | use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*, SyntaxNode, WalkEvent, T}; |
6 | algo::find_node_at_offset, ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*, | ||
7 | SyntaxNode, WalkEvent, T, | ||
8 | }; | ||
9 | 6 | ||
10 | use crate::FilePosition; | 7 | use crate::FilePosition; |
11 | 8 | ||
@@ -28,16 +25,33 @@ pub struct ExpandedMacro { | |||
28 | pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { | 25 | pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { |
29 | let sema = Semantics::new(db); | 26 | let sema = Semantics::new(db); |
30 | let file = sema.parse(position.file_id); | 27 | let file = sema.parse(position.file_id); |
31 | let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset)?; | ||
32 | let mac = name_ref.syntax().ancestors().find_map(ast::MacroCall::cast)?; | ||
33 | 28 | ||
34 | let expanded = expand_macro_recur(&sema, &mac)?; | 29 | let tok = file.syntax().token_at_offset(position.offset).left_biased()?; |
30 | let mut expanded = None; | ||
31 | let mut name = None; | ||
32 | for node in tok.ancestors() { | ||
33 | if let Some(item) = ast::Item::cast(node.clone()) { | ||
34 | expanded = sema.expand_attr_macro(&item); | ||
35 | if expanded.is_some() { | ||
36 | // FIXME: add the macro name | ||
37 | // FIXME: make this recursive too | ||
38 | name = Some("?".to_string()); | ||
39 | break; | ||
40 | } | ||
41 | } | ||
42 | |||
43 | if let Some(mac) = ast::MacroCall::cast(node) { | ||
44 | name = Some(mac.path()?.segment()?.name_ref()?.to_string()); | ||
45 | expanded = expand_macro_recur(&sema, &mac); | ||
46 | break; | ||
47 | } | ||
48 | } | ||
35 | 49 | ||
36 | // FIXME: | 50 | // FIXME: |
37 | // macro expansion may lose all white space information | 51 | // macro expansion may lose all white space information |
38 | // But we hope someday we can use ra_fmt for that | 52 | // But we hope someday we can use ra_fmt for that |
39 | let expansion = insert_whitespaces(expanded); | 53 | let expansion = insert_whitespaces(expanded?); |
40 | Some(ExpandedMacro { name: name_ref.text().to_string(), expansion }) | 54 | Some(ExpandedMacro { name: name?, expansion }) |
41 | } | 55 | } |
42 | 56 | ||
43 | fn expand_macro_recur( | 57 | fn expand_macro_recur( |
diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs index 7032889ac..c7ec87edf 100644 --- a/crates/ide/src/extend_selection.rs +++ b/crates/ide/src/extend_selection.rs | |||
@@ -328,7 +328,7 @@ mod tests { | |||
328 | use super::*; | 328 | use super::*; |
329 | 329 | ||
330 | fn do_check(before: &str, afters: &[&str]) { | 330 | fn do_check(before: &str, afters: &[&str]) { |
331 | let (analysis, position) = fixture::position(&before); | 331 | let (analysis, position) = fixture::position(before); |
332 | let before = analysis.file_text(position.file_id).unwrap(); | 332 | let before = analysis.file_text(position.file_id).unwrap(); |
333 | let range = TextRange::empty(position.offset); | 333 | let range = TextRange::empty(position.offset); |
334 | let mut frange = FileRange { file_id: position.file_id, range }; | 334 | let mut frange = FileRange { file_id: position.file_id, range }; |
diff --git a/crates/ide/src/fixture.rs b/crates/ide/src/fixture.rs index 6780af617..38e2e866b 100644 --- a/crates/ide/src/fixture.rs +++ b/crates/ide/src/fixture.rs | |||
@@ -1,6 +1,5 @@ | |||
1 | //! Utilities for creating `Analysis` instances for tests. | 1 | //! Utilities for creating `Analysis` instances for tests. |
2 | use ide_db::base_db::fixture::ChangeFixture; | 2 | use ide_db::base_db::fixture::ChangeFixture; |
3 | use syntax::{TextRange, TextSize}; | ||
4 | use test_utils::extract_annotations; | 3 | use test_utils::extract_annotations; |
5 | 4 | ||
6 | use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; | 5 | use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; |
@@ -63,15 +62,8 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil | |||
63 | 62 | ||
64 | pub(crate) fn nav_target_annotation(ra_fixture: &str) -> (Analysis, FilePosition, FileRange) { | 63 | pub(crate) fn nav_target_annotation(ra_fixture: &str) -> (Analysis, FilePosition, FileRange) { |
65 | let (analysis, position, mut annotations) = annotations(ra_fixture); | 64 | let (analysis, position, mut annotations) = annotations(ra_fixture); |
66 | let (mut expected, data) = annotations.pop().unwrap(); | 65 | let (expected, data) = annotations.pop().unwrap(); |
67 | assert!(annotations.is_empty()); | 66 | assert!(annotations.is_empty()); |
68 | match data.as_str() { | 67 | assert_eq!(data, ""); |
69 | "" => (), | ||
70 | "file" => { | ||
71 | expected.range = | ||
72 | TextRange::up_to(TextSize::of(&*analysis.file_text(expected.file_id).unwrap())) | ||
73 | } | ||
74 | data => panic!("bad data: {}", data), | ||
75 | } | ||
76 | (analysis, position, expected) | 68 | (analysis, position, expected) |
77 | } | 69 | } |
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index a04333e63..8dd643a0f 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -1,10 +1,15 @@ | |||
1 | use std::convert::TryInto; | ||
2 | |||
1 | use either::Either; | 3 | use either::Either; |
2 | use hir::{InFile, Semantics}; | 4 | use hir::{AsAssocItem, InFile, ModuleDef, Semantics}; |
3 | use ide_db::{ | 5 | use ide_db::{ |
4 | defs::{NameClass, NameRefClass}, | 6 | base_db::{AnchoredPath, FileId, FileLoader}, |
7 | defs::{Definition, NameClass, NameRefClass}, | ||
5 | RootDatabase, | 8 | RootDatabase, |
6 | }; | 9 | }; |
7 | use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; | 10 | use syntax::{ |
11 | ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, TokenAtOffset, T, | ||
12 | }; | ||
8 | 13 | ||
9 | use crate::{ | 14 | use crate::{ |
10 | display::TryToNav, | 15 | display::TryToNav, |
@@ -32,20 +37,19 @@ pub(crate) fn goto_definition( | |||
32 | let original_token = pick_best(file.token_at_offset(position.offset))?; | 37 | let original_token = pick_best(file.token_at_offset(position.offset))?; |
33 | let token = sema.descend_into_macros(original_token.clone()); | 38 | let token = sema.descend_into_macros(original_token.clone()); |
34 | let parent = token.parent()?; | 39 | let parent = token.parent()?; |
35 | if let Some(_) = ast::Comment::cast(token) { | 40 | if let Some(_) = ast::Comment::cast(token.clone()) { |
36 | let (attributes, def) = doc_attributes(&sema, &parent)?; | 41 | let (attributes, def) = doc_attributes(&sema, &parent)?; |
37 | 42 | ||
38 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; | 43 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; |
39 | let (_, link, ns) = | 44 | let (_, link, ns) = |
40 | extract_definitions_from_markdown(docs.as_str()).into_iter().find(|(range, ..)| { | 45 | extract_definitions_from_markdown(docs.as_str()).into_iter().find(|(range, ..)| { |
41 | doc_mapping.map(range.clone()).map_or(false, |InFile { file_id, value: range }| { | 46 | doc_mapping.map(*range).map_or(false, |InFile { file_id, value: range }| { |
42 | file_id == position.file_id.into() && range.contains(position.offset) | 47 | file_id == position.file_id.into() && range.contains(position.offset) |
43 | }) | 48 | }) |
44 | })?; | 49 | })?; |
45 | let nav = resolve_doc_path_for_def(db, def, &link, ns)?.try_to_nav(db)?; | 50 | let nav = resolve_doc_path_for_def(db, def, &link, ns)?.try_to_nav(db)?; |
46 | return Some(RangeInfo::new(original_token.text_range(), vec![nav])); | 51 | return Some(RangeInfo::new(original_token.text_range(), vec![nav])); |
47 | } | 52 | } |
48 | |||
49 | let nav = match_ast! { | 53 | let nav = match_ast! { |
50 | match parent { | 54 | match parent { |
51 | ast::NameRef(name_ref) => { | 55 | ast::NameRef(name_ref) => { |
@@ -53,7 +57,8 @@ pub(crate) fn goto_definition( | |||
53 | }, | 57 | }, |
54 | ast::Name(name) => { | 58 | ast::Name(name) => { |
55 | let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); | 59 | let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); |
56 | def.try_to_nav(sema.db) | 60 | try_find_trait_item_definition(sema.db, &def) |
61 | .or_else(|| def.try_to_nav(sema.db)) | ||
57 | }, | 62 | }, |
58 | ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, <) { | 63 | ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, <) { |
59 | let def = name_class.referenced_or_defined(sema.db); | 64 | let def = name_class.referenced_or_defined(sema.db); |
@@ -61,6 +66,7 @@ pub(crate) fn goto_definition( | |||
61 | } else { | 66 | } else { |
62 | reference_definition(&sema, Either::Left(<)) | 67 | reference_definition(&sema, Either::Left(<)) |
63 | }, | 68 | }, |
69 | ast::TokenTree(tt) => try_lookup_include_path(sema.db, tt, token, position.file_id), | ||
64 | _ => return None, | 70 | _ => return None, |
65 | } | 71 | } |
66 | }; | 72 | }; |
@@ -68,6 +74,60 @@ pub(crate) fn goto_definition( | |||
68 | Some(RangeInfo::new(original_token.text_range(), nav.into_iter().collect())) | 74 | Some(RangeInfo::new(original_token.text_range(), nav.into_iter().collect())) |
69 | } | 75 | } |
70 | 76 | ||
77 | fn try_lookup_include_path( | ||
78 | db: &RootDatabase, | ||
79 | tt: ast::TokenTree, | ||
80 | token: SyntaxToken, | ||
81 | file_id: FileId, | ||
82 | ) -> Option<NavigationTarget> { | ||
83 | let path = ast::String::cast(token)?.value()?.into_owned(); | ||
84 | let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?; | ||
85 | let name = macro_call.path()?.segment()?.name_ref()?; | ||
86 | if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") { | ||
87 | return None; | ||
88 | } | ||
89 | let file_id = db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?; | ||
90 | let size = db.file_text(file_id).len().try_into().ok()?; | ||
91 | Some(NavigationTarget { | ||
92 | file_id, | ||
93 | full_range: TextRange::new(0.into(), size), | ||
94 | name: path.into(), | ||
95 | focus_range: None, | ||
96 | kind: None, | ||
97 | container_name: None, | ||
98 | description: None, | ||
99 | docs: None, | ||
100 | }) | ||
101 | } | ||
102 | |||
103 | /// finds the trait definition of an impl'd item | ||
104 | /// e.g. | ||
105 | /// ```rust | ||
106 | /// trait A { fn a(); } | ||
107 | /// struct S; | ||
108 | /// impl A for S { fn a(); } // <-- on this function, will get the location of a() in the trait | ||
109 | /// ``` | ||
110 | fn try_find_trait_item_definition(db: &RootDatabase, def: &Definition) -> Option<NavigationTarget> { | ||
111 | let name = def.name(db)?; | ||
112 | let assoc = match def { | ||
113 | Definition::ModuleDef(ModuleDef::Function(f)) => f.as_assoc_item(db), | ||
114 | Definition::ModuleDef(ModuleDef::Const(c)) => c.as_assoc_item(db), | ||
115 | Definition::ModuleDef(ModuleDef::TypeAlias(ty)) => ty.as_assoc_item(db), | ||
116 | _ => None, | ||
117 | }?; | ||
118 | |||
119 | let imp = match assoc.container(db) { | ||
120 | hir::AssocItemContainer::Impl(imp) => imp, | ||
121 | _ => return None, | ||
122 | }; | ||
123 | |||
124 | let trait_ = imp.trait_(db)?; | ||
125 | trait_ | ||
126 | .items(db) | ||
127 | .iter() | ||
128 | .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten()) | ||
129 | } | ||
130 | |||
71 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | 131 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { |
72 | return tokens.max_by_key(priority); | 132 | return tokens.max_by_key(priority); |
73 | fn priority(n: &SyntaxToken) -> usize { | 133 | fn priority(n: &SyntaxToken) -> usize { |
@@ -125,7 +185,7 @@ mod tests { | |||
125 | extern crate std$0; | 185 | extern crate std$0; |
126 | //- /std/lib.rs crate:std | 186 | //- /std/lib.rs crate:std |
127 | // empty | 187 | // empty |
128 | //^ file | 188 | //^file |
129 | "#, | 189 | "#, |
130 | ) | 190 | ) |
131 | } | 191 | } |
@@ -138,7 +198,7 @@ extern crate std$0; | |||
138 | extern crate std as abc$0; | 198 | extern crate std as abc$0; |
139 | //- /std/lib.rs crate:std | 199 | //- /std/lib.rs crate:std |
140 | // empty | 200 | // empty |
141 | //^ file | 201 | //^file |
142 | "#, | 202 | "#, |
143 | ) | 203 | ) |
144 | } | 204 | } |
@@ -193,7 +253,7 @@ mod $0foo; | |||
193 | 253 | ||
194 | //- /foo.rs | 254 | //- /foo.rs |
195 | // empty | 255 | // empty |
196 | //^ file | 256 | //^file |
197 | "#, | 257 | "#, |
198 | ); | 258 | ); |
199 | 259 | ||
@@ -204,7 +264,7 @@ mod $0foo; | |||
204 | 264 | ||
205 | //- /foo/mod.rs | 265 | //- /foo/mod.rs |
206 | // empty | 266 | // empty |
207 | //^ file | 267 | //^file |
208 | "#, | 268 | "#, |
209 | ); | 269 | ); |
210 | } | 270 | } |
@@ -335,7 +395,7 @@ use foo as bar$0; | |||
335 | 395 | ||
336 | //- /foo/lib.rs crate:foo | 396 | //- /foo/lib.rs crate:foo |
337 | // empty | 397 | // empty |
338 | //^ file | 398 | //^file |
339 | "#, | 399 | "#, |
340 | ); | 400 | ); |
341 | } | 401 | } |
@@ -1216,4 +1276,73 @@ fn f(e: Enum) { | |||
1216 | "#, | 1276 | "#, |
1217 | ); | 1277 | ); |
1218 | } | 1278 | } |
1279 | |||
1280 | #[test] | ||
1281 | fn goto_include() { | ||
1282 | check( | ||
1283 | r#" | ||
1284 | //- /main.rs | ||
1285 | fn main() { | ||
1286 | let str = include_str!("foo.txt$0"); | ||
1287 | } | ||
1288 | //- /foo.txt | ||
1289 | // empty | ||
1290 | //^file | ||
1291 | "#, | ||
1292 | ); | ||
1293 | } | ||
1294 | |||
1295 | #[test] | ||
1296 | fn goto_def_of_trait_impl_fn() { | ||
1297 | check( | ||
1298 | r#" | ||
1299 | trait Twait { | ||
1300 | fn a(); | ||
1301 | // ^ | ||
1302 | } | ||
1303 | |||
1304 | struct Stwuct; | ||
1305 | |||
1306 | impl Twait for Stwuct { | ||
1307 | fn a$0(); | ||
1308 | } | ||
1309 | "#, | ||
1310 | ); | ||
1311 | } | ||
1312 | |||
1313 | #[test] | ||
1314 | fn goto_def_of_trait_impl_const() { | ||
1315 | check( | ||
1316 | r#" | ||
1317 | trait Twait { | ||
1318 | const NOMS: bool; | ||
1319 | // ^^^^ | ||
1320 | } | ||
1321 | |||
1322 | struct Stwuct; | ||
1323 | |||
1324 | impl Twait for Stwuct { | ||
1325 | const NOMS$0: bool = true; | ||
1326 | } | ||
1327 | "#, | ||
1328 | ); | ||
1329 | } | ||
1330 | |||
1331 | #[test] | ||
1332 | fn goto_def_of_trait_impl_type_alias() { | ||
1333 | check( | ||
1334 | r#" | ||
1335 | trait Twait { | ||
1336 | type IsBad; | ||
1337 | // ^^^^^ | ||
1338 | } | ||
1339 | |||
1340 | struct Stwuct; | ||
1341 | |||
1342 | impl Twait for Stwuct { | ||
1343 | type IsBad$0 = !; | ||
1344 | } | ||
1345 | "#, | ||
1346 | ); | ||
1347 | } | ||
1219 | } | 1348 | } |
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index 43356a94e..0013820b4 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs | |||
@@ -52,13 +52,13 @@ pub(crate) fn goto_implementation( | |||
52 | hir::ModuleDef::Function(f) => { | 52 | hir::ModuleDef::Function(f) => { |
53 | let assoc = f.as_assoc_item(sema.db)?; | 53 | let assoc = f.as_assoc_item(sema.db)?; |
54 | let name = assoc.name(sema.db)?; | 54 | let name = assoc.name(sema.db)?; |
55 | let trait_ = assoc.containing_trait(sema.db)?; | 55 | let trait_ = assoc.containing_trait_or_trait_impl(sema.db)?; |
56 | impls_for_trait_item(&sema, trait_, name) | 56 | impls_for_trait_item(&sema, trait_, name) |
57 | } | 57 | } |
58 | hir::ModuleDef::Const(c) => { | 58 | hir::ModuleDef::Const(c) => { |
59 | let assoc = c.as_assoc_item(sema.db)?; | 59 | let assoc = c.as_assoc_item(sema.db)?; |
60 | let name = assoc.name(sema.db)?; | 60 | let name = assoc.name(sema.db)?; |
61 | let trait_ = assoc.containing_trait(sema.db)?; | 61 | let trait_ = assoc.containing_trait_or_trait_impl(sema.db)?; |
62 | impls_for_trait_item(&sema, trait_, name) | 62 | impls_for_trait_item(&sema, trait_, name) |
63 | } | 63 | } |
64 | _ => return None, | 64 | _ => return None, |
@@ -87,7 +87,7 @@ fn impls_for_trait_item( | |||
87 | .filter_map(|imp| { | 87 | .filter_map(|imp| { |
88 | let item = imp.items(sema.db).iter().find_map(|itm| { | 88 | let item = imp.items(sema.db).iter().find_map(|itm| { |
89 | let itm_name = itm.name(sema.db)?; | 89 | let itm_name = itm.name(sema.db)?; |
90 | (itm_name == fun_name).then(|| itm.clone()) | 90 | (itm_name == fun_name).then(|| *itm) |
91 | })?; | 91 | })?; |
92 | item.try_to_nav(sema.db) | 92 | item.try_to_nav(sema.db) |
93 | }) | 93 | }) |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 04598cd06..c08516805 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -6,12 +6,18 @@ use hir::{ | |||
6 | use ide_db::{ | 6 | use ide_db::{ |
7 | base_db::SourceDatabase, | 7 | base_db::SourceDatabase, |
8 | defs::{Definition, NameClass, NameRefClass}, | 8 | defs::{Definition, NameClass, NameRefClass}, |
9 | helpers::FamousDefs, | 9 | helpers::{ |
10 | generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES}, | ||
11 | FamousDefs, | ||
12 | }, | ||
10 | RootDatabase, | 13 | RootDatabase, |
11 | }; | 14 | }; |
12 | use itertools::Itertools; | 15 | use itertools::Itertools; |
13 | use stdx::format_to; | 16 | use stdx::format_to; |
14 | use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; | 17 | use syntax::{ |
18 | algo, ast, match_ast, AstNode, AstToken, Direction, SyntaxKind::*, SyntaxToken, TokenAtOffset, | ||
19 | T, | ||
20 | }; | ||
15 | 21 | ||
16 | use crate::{ | 22 | use crate::{ |
17 | display::{macro_label, TryToNav}, | 23 | display::{macro_label, TryToNav}, |
@@ -28,6 +34,7 @@ use crate::{ | |||
28 | #[derive(Clone, Debug, PartialEq, Eq)] | 34 | #[derive(Clone, Debug, PartialEq, Eq)] |
29 | pub struct HoverConfig { | 35 | pub struct HoverConfig { |
30 | pub implementations: bool, | 36 | pub implementations: bool, |
37 | pub references: bool, | ||
31 | pub run: bool, | 38 | pub run: bool, |
32 | pub debug: bool, | 39 | pub debug: bool, |
33 | pub goto_type_def: bool, | 40 | pub goto_type_def: bool, |
@@ -38,6 +45,7 @@ pub struct HoverConfig { | |||
38 | impl HoverConfig { | 45 | impl HoverConfig { |
39 | pub const NO_ACTIONS: Self = Self { | 46 | pub const NO_ACTIONS: Self = Self { |
40 | implementations: false, | 47 | implementations: false, |
48 | references: false, | ||
41 | run: false, | 49 | run: false, |
42 | debug: false, | 50 | debug: false, |
43 | goto_type_def: false, | 51 | goto_type_def: false, |
@@ -46,7 +54,7 @@ impl HoverConfig { | |||
46 | }; | 54 | }; |
47 | 55 | ||
48 | pub fn any(&self) -> bool { | 56 | pub fn any(&self) -> bool { |
49 | self.implementations || self.runnable() || self.goto_type_def | 57 | self.implementations || self.references || self.runnable() || self.goto_type_def |
50 | } | 58 | } |
51 | 59 | ||
52 | pub fn none(&self) -> bool { | 60 | pub fn none(&self) -> bool { |
@@ -62,6 +70,7 @@ impl HoverConfig { | |||
62 | pub enum HoverAction { | 70 | pub enum HoverAction { |
63 | Runnable(Runnable), | 71 | Runnable(Runnable), |
64 | Implementation(FilePosition), | 72 | Implementation(FilePosition), |
73 | Reference(FilePosition), | ||
65 | GoToType(Vec<HoverGotoTypeData>), | 74 | GoToType(Vec<HoverGotoTypeData>), |
66 | } | 75 | } |
67 | 76 | ||
@@ -115,13 +124,14 @@ pub(crate) fn hover( | |||
115 | |d| d.defined(db), | 124 | |d| d.defined(db), |
116 | ), | 125 | ), |
117 | 126 | ||
118 | _ => ast::Comment::cast(token.clone()) | 127 | _ => { |
119 | .and_then(|_| { | 128 | if ast::Comment::cast(token.clone()).is_some() { |
129 | cov_mark::hit!(no_highlight_on_comment_hover); | ||
120 | let (attributes, def) = doc_attributes(&sema, &node)?; | 130 | let (attributes, def) = doc_attributes(&sema, &node)?; |
121 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; | 131 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; |
122 | let (idl_range, link, ns) = | 132 | let (idl_range, link, ns) = |
123 | extract_definitions_from_markdown(docs.as_str()).into_iter().find_map(|(range, link, ns)| { | 133 | extract_definitions_from_markdown(docs.as_str()).into_iter().find_map(|(range, link, ns)| { |
124 | let InFile { file_id, value: range } = doc_mapping.map(range.clone())?; | 134 | let InFile { file_id, value: range } = doc_mapping.map(range)?; |
125 | if file_id == position.file_id.into() && range.contains(position.offset) { | 135 | if file_id == position.file_id.into() && range.contains(position.offset) { |
126 | Some((range, link, ns)) | 136 | Some((range, link, ns)) |
127 | } else { | 137 | } else { |
@@ -129,9 +139,13 @@ pub(crate) fn hover( | |||
129 | } | 139 | } |
130 | })?; | 140 | })?; |
131 | range = Some(idl_range); | 141 | range = Some(idl_range); |
132 | resolve_doc_path_for_def(db, def, &link, ns) | 142 | resolve_doc_path_for_def(db, def, &link, ns).map(Definition::ModuleDef) |
133 | }) | 143 | } else if let res@Some(_) = try_hover_for_attribute(&token) { |
134 | .map(Definition::ModuleDef), | 144 | return res; |
145 | } else { | ||
146 | None | ||
147 | } | ||
148 | }, | ||
135 | } | 149 | } |
136 | }; | 150 | }; |
137 | 151 | ||
@@ -148,6 +162,10 @@ pub(crate) fn hover( | |||
148 | res.actions.push(action); | 162 | res.actions.push(action); |
149 | } | 163 | } |
150 | 164 | ||
165 | if let Some(action) = show_fn_references_action(db, definition) { | ||
166 | res.actions.push(action); | ||
167 | } | ||
168 | |||
151 | if let Some(action) = runnable_action(&sema, definition, position.file_id) { | 169 | if let Some(action) = runnable_action(&sema, definition, position.file_id) { |
152 | res.actions.push(action); | 170 | res.actions.push(action); |
153 | } | 171 | } |
@@ -161,11 +179,6 @@ pub(crate) fn hover( | |||
161 | } | 179 | } |
162 | } | 180 | } |
163 | 181 | ||
164 | if token.kind() == syntax::SyntaxKind::COMMENT { | ||
165 | cov_mark::hit!(no_highlight_on_comment_hover); | ||
166 | return None; | ||
167 | } | ||
168 | |||
169 | if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) { | 182 | if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) { |
170 | return res; | 183 | return res; |
171 | } | 184 | } |
@@ -194,6 +207,51 @@ pub(crate) fn hover( | |||
194 | Some(RangeInfo::new(range, res)) | 207 | Some(RangeInfo::new(range, res)) |
195 | } | 208 | } |
196 | 209 | ||
210 | fn try_hover_for_attribute(token: &SyntaxToken) -> Option<RangeInfo<HoverResult>> { | ||
211 | let attr = token.ancestors().find_map(ast::Attr::cast)?; | ||
212 | let (path, tt) = attr.as_simple_call()?; | ||
213 | if !tt.syntax().text_range().contains(token.text_range().start()) { | ||
214 | return None; | ||
215 | } | ||
216 | let (is_clippy, lints) = match &*path { | ||
217 | "feature" => (false, FEATURES), | ||
218 | "allow" | "deny" | "forbid" | "warn" => { | ||
219 | let is_clippy = algo::non_trivia_sibling(token.clone().into(), Direction::Prev) | ||
220 | .filter(|t| t.kind() == T![:]) | ||
221 | .and_then(|t| algo::non_trivia_sibling(t, Direction::Prev)) | ||
222 | .filter(|t| t.kind() == T![:]) | ||
223 | .and_then(|t| algo::non_trivia_sibling(t, Direction::Prev)) | ||
224 | .map_or(false, |t| { | ||
225 | t.kind() == T![ident] && t.into_token().map_or(false, |t| t.text() == "clippy") | ||
226 | }); | ||
227 | if is_clippy { | ||
228 | (true, CLIPPY_LINTS) | ||
229 | } else { | ||
230 | (false, DEFAULT_LINTS) | ||
231 | } | ||
232 | } | ||
233 | _ => return None, | ||
234 | }; | ||
235 | |||
236 | let tmp; | ||
237 | let needle = if is_clippy { | ||
238 | tmp = format!("clippy::{}", token.text()); | ||
239 | &tmp | ||
240 | } else { | ||
241 | &*token.text() | ||
242 | }; | ||
243 | |||
244 | let lint = | ||
245 | lints.binary_search_by_key(&needle, |lint| lint.label).ok().map(|idx| &lints[idx])?; | ||
246 | Some(RangeInfo::new( | ||
247 | token.text_range(), | ||
248 | HoverResult { | ||
249 | markup: Markup::from(format!("```\n{}\n```\n___\n\n{}", lint.label, lint.description)), | ||
250 | ..Default::default() | ||
251 | }, | ||
252 | )) | ||
253 | } | ||
254 | |||
197 | fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { | 255 | fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { |
198 | fn to_action(nav_target: NavigationTarget) -> HoverAction { | 256 | fn to_action(nav_target: NavigationTarget) -> HoverAction { |
199 | HoverAction::Implementation(FilePosition { | 257 | HoverAction::Implementation(FilePosition { |
@@ -211,6 +269,18 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<Hov | |||
211 | adt.try_to_nav(db).map(to_action) | 269 | adt.try_to_nav(db).map(to_action) |
212 | } | 270 | } |
213 | 271 | ||
272 | fn show_fn_references_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { | ||
273 | match def { | ||
274 | Definition::ModuleDef(ModuleDef::Function(it)) => it.try_to_nav(db).map(|nav_target| { | ||
275 | HoverAction::Reference(FilePosition { | ||
276 | file_id: nav_target.file_id, | ||
277 | offset: nav_target.focus_or_full_range().start(), | ||
278 | }) | ||
279 | }), | ||
280 | _ => None, | ||
281 | } | ||
282 | } | ||
283 | |||
214 | fn runnable_action( | 284 | fn runnable_action( |
215 | sema: &Semantics<RootDatabase>, | 285 | sema: &Semantics<RootDatabase>, |
216 | def: Definition, | 286 | def: Definition, |
@@ -218,7 +288,7 @@ fn runnable_action( | |||
218 | ) -> Option<HoverAction> { | 288 | ) -> Option<HoverAction> { |
219 | match def { | 289 | match def { |
220 | Definition::ModuleDef(it) => match it { | 290 | Definition::ModuleDef(it) => match it { |
221 | ModuleDef::Module(it) => runnable_mod(&sema, it).map(|it| HoverAction::Runnable(it)), | 291 | ModuleDef::Module(it) => runnable_mod(sema, it).map(HoverAction::Runnable), |
222 | ModuleDef::Function(func) => { | 292 | ModuleDef::Function(func) => { |
223 | let src = func.source(sema.db)?; | 293 | let src = func.source(sema.db)?; |
224 | if src.file_id != file_id.into() { | 294 | if src.file_id != file_id.into() { |
@@ -227,7 +297,7 @@ fn runnable_action( | |||
227 | return None; | 297 | return None; |
228 | } | 298 | } |
229 | 299 | ||
230 | runnable_fn(&sema, func).map(HoverAction::Runnable) | 300 | runnable_fn(sema, func).map(HoverAction::Runnable) |
231 | } | 301 | } |
232 | _ => None, | 302 | _ => None, |
233 | }, | 303 | }, |
@@ -362,7 +432,7 @@ fn hover_for_definition( | |||
362 | return match def { | 432 | return match def { |
363 | Definition::Macro(it) => match &it.source(db)?.value { | 433 | Definition::Macro(it) => match &it.source(db)?.value { |
364 | Either::Left(mac) => { | 434 | Either::Left(mac) => { |
365 | let label = macro_label(&mac); | 435 | let label = macro_label(mac); |
366 | from_def_source_labeled(db, it, Some(label), mod_path) | 436 | from_def_source_labeled(db, it, Some(label), mod_path) |
367 | } | 437 | } |
368 | Either::Right(_) => { | 438 | Either::Right(_) => { |
@@ -446,7 +516,7 @@ fn hover_for_keyword( | |||
446 | if !token.kind().is_keyword() { | 516 | if !token.kind().is_keyword() { |
447 | return None; | 517 | return None; |
448 | } | 518 | } |
449 | let famous_defs = FamousDefs(&sema, sema.scope(&token.parent()?).krate()); | 519 | let famous_defs = FamousDefs(sema, sema.scope(&token.parent()?).krate()); |
450 | // std exposes {}_keyword modules with docstrings on the root to document keywords | 520 | // std exposes {}_keyword modules with docstrings on the root to document keywords |
451 | let keyword_mod = format!("{}_keyword", token.text()); | 521 | let keyword_mod = format!("{}_keyword", token.text()); |
452 | let doc_owner = find_std_module(&famous_defs, &keyword_mod)?; | 522 | let doc_owner = find_std_module(&famous_defs, &keyword_mod)?; |
@@ -2377,6 +2447,14 @@ fn foo_$0test() {} | |||
2377 | "#, | 2447 | "#, |
2378 | expect![[r#" | 2448 | expect![[r#" |
2379 | [ | 2449 | [ |
2450 | Reference( | ||
2451 | FilePosition { | ||
2452 | file_id: FileId( | ||
2453 | 0, | ||
2454 | ), | ||
2455 | offset: 11, | ||
2456 | }, | ||
2457 | ), | ||
2380 | Runnable( | 2458 | Runnable( |
2381 | Runnable { | 2459 | Runnable { |
2382 | nav: NavigationTarget { | 2460 | nav: NavigationTarget { |
@@ -3977,4 +4055,74 @@ pub fn foo() {} | |||
3977 | "#]], | 4055 | "#]], |
3978 | ) | 4056 | ) |
3979 | } | 4057 | } |
4058 | |||
4059 | #[test] | ||
4060 | fn hover_feature() { | ||
4061 | check( | ||
4062 | r#"#![feature(box_syntax$0)]"#, | ||
4063 | expect![[r##" | ||
4064 | *box_syntax* | ||
4065 | ``` | ||
4066 | box_syntax | ||
4067 | ``` | ||
4068 | ___ | ||
4069 | |||
4070 | # `box_syntax` | ||
4071 | |||
4072 | The tracking issue for this feature is: [#49733] | ||
4073 | |||
4074 | [#49733]: https://github.com/rust-lang/rust/issues/49733 | ||
4075 | |||
4076 | See also [`box_patterns`](box-patterns.md) | ||
4077 | |||
4078 | ------------------------ | ||
4079 | |||
4080 | Currently the only stable way to create a `Box` is via the `Box::new` method. | ||
4081 | Also it is not possible in stable Rust to destructure a `Box` in a match | ||
4082 | pattern. The unstable `box` keyword can be used to create a `Box`. An example | ||
4083 | usage would be: | ||
4084 | |||
4085 | ```rust | ||
4086 | #![feature(box_syntax)] | ||
4087 | |||
4088 | fn main() { | ||
4089 | let b = box 5; | ||
4090 | } | ||
4091 | ``` | ||
4092 | |||
4093 | "##]], | ||
4094 | ) | ||
4095 | } | ||
4096 | |||
4097 | #[test] | ||
4098 | fn hover_lint() { | ||
4099 | check( | ||
4100 | r#"#![allow(arithmetic_overflow$0)]"#, | ||
4101 | expect![[r#" | ||
4102 | *arithmetic_overflow* | ||
4103 | ``` | ||
4104 | arithmetic_overflow | ||
4105 | ``` | ||
4106 | ___ | ||
4107 | |||
4108 | arithmetic operation overflows | ||
4109 | "#]], | ||
4110 | ) | ||
4111 | } | ||
4112 | |||
4113 | #[test] | ||
4114 | fn hover_clippy_lint() { | ||
4115 | check( | ||
4116 | r#"#![allow(clippy::almost_swapped$0)]"#, | ||
4117 | expect![[r#" | ||
4118 | *almost_swapped* | ||
4119 | ``` | ||
4120 | clippy::almost_swapped | ||
4121 | ``` | ||
4122 | ___ | ||
4123 | |||
4124 | Checks for `foo = bar; bar = foo` sequences. | ||
4125 | "#]], | ||
4126 | ) | ||
4127 | } | ||
3980 | } | 4128 | } |
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 85f887737..9cd33d0e4 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs | |||
@@ -96,7 +96,7 @@ fn get_chaining_hints( | |||
96 | } | 96 | } |
97 | 97 | ||
98 | let krate = sema.scope(expr.syntax()).module().map(|it| it.krate()); | 98 | let krate = sema.scope(expr.syntax()).module().map(|it| it.krate()); |
99 | let famous_defs = FamousDefs(&sema, krate); | 99 | let famous_defs = FamousDefs(sema, krate); |
100 | 100 | ||
101 | let mut tokens = expr | 101 | let mut tokens = expr |
102 | .syntax() | 102 | .syntax() |
@@ -150,17 +150,11 @@ fn get_param_name_hints( | |||
150 | return None; | 150 | return None; |
151 | } | 151 | } |
152 | 152 | ||
153 | let args = match &expr { | 153 | let (callable, arg_list) = get_callable(sema, &expr)?; |
154 | ast::Expr::CallExpr(expr) => expr.arg_list()?.args(), | ||
155 | ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(), | ||
156 | _ => return None, | ||
157 | }; | ||
158 | |||
159 | let callable = get_callable(sema, &expr)?; | ||
160 | let hints = callable | 154 | let hints = callable |
161 | .params(sema.db) | 155 | .params(sema.db) |
162 | .into_iter() | 156 | .into_iter() |
163 | .zip(args) | 157 | .zip(arg_list.args()) |
164 | .filter_map(|((param, _ty), arg)| { | 158 | .filter_map(|((param, _ty), arg)| { |
165 | let param_name = match param? { | 159 | let param_name = match param? { |
166 | Either::Left(_) => "self".to_string(), | 160 | Either::Left(_) => "self".to_string(), |
@@ -171,7 +165,7 @@ fn get_param_name_hints( | |||
171 | }; | 165 | }; |
172 | Some((param_name, arg)) | 166 | Some((param_name, arg)) |
173 | }) | 167 | }) |
174 | .filter(|(param_name, arg)| should_show_param_name_hint(sema, &callable, param_name, &arg)) | 168 | .filter(|(param_name, arg)| !should_hide_param_name_hint(sema, &callable, param_name, arg)) |
175 | .map(|(param_name, arg)| InlayHint { | 169 | .map(|(param_name, arg)| InlayHint { |
176 | range: arg.syntax().text_range(), | 170 | range: arg.syntax().text_range(), |
177 | kind: InlayKind::ParameterHint, | 171 | kind: InlayKind::ParameterHint, |
@@ -193,7 +187,7 @@ fn get_bind_pat_hints( | |||
193 | } | 187 | } |
194 | 188 | ||
195 | let krate = sema.scope(pat.syntax()).module().map(|it| it.krate()); | 189 | let krate = sema.scope(pat.syntax()).module().map(|it| it.krate()); |
196 | let famous_defs = FamousDefs(&sema, krate); | 190 | let famous_defs = FamousDefs(sema, krate); |
197 | 191 | ||
198 | let ty = sema.type_of_pat(&pat.clone().into())?; | 192 | let ty = sema.type_of_pat(&pat.clone().into())?; |
199 | 193 | ||
@@ -289,15 +283,9 @@ fn should_not_display_type_hint( | |||
289 | for node in bind_pat.syntax().ancestors() { | 283 | for node in bind_pat.syntax().ancestors() { |
290 | match_ast! { | 284 | match_ast! { |
291 | match node { | 285 | match node { |
292 | ast::LetStmt(it) => { | 286 | ast::LetStmt(it) => return it.ty().is_some(), |
293 | return it.ty().is_some() | 287 | ast::Param(it) => return it.ty().is_some(), |
294 | }, | 288 | ast::MatchArm(_it) => return pat_is_enum_variant(db, bind_pat, pat_ty), |
295 | ast::Param(it) => { | ||
296 | return it.ty().is_some() | ||
297 | }, | ||
298 | ast::MatchArm(_it) => { | ||
299 | return pat_is_enum_variant(db, bind_pat, pat_ty); | ||
300 | }, | ||
301 | ast::IfExpr(it) => { | 289 | ast::IfExpr(it) => { |
302 | return it.condition().and_then(|condition| condition.pat()).is_some() | 290 | return it.condition().and_then(|condition| condition.pat()).is_some() |
303 | && pat_is_enum_variant(db, bind_pat, pat_ty); | 291 | && pat_is_enum_variant(db, bind_pat, pat_ty); |
@@ -311,9 +299,8 @@ fn should_not_display_type_hint( | |||
311 | // Type of expr should be iterable. | 299 | // Type of expr should be iterable. |
312 | return it.in_token().is_none() || | 300 | return it.in_token().is_none() || |
313 | it.iterable() | 301 | it.iterable() |
314 | .and_then(|iterable_expr|sema.type_of_expr(&iterable_expr)) | 302 | .and_then(|iterable_expr| sema.type_of_expr(&iterable_expr)) |
315 | .map(|iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit()) | 303 | .map_or(true, |iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit()) |
316 | .unwrap_or(true) | ||
317 | }, | 304 | }, |
318 | _ => (), | 305 | _ => (), |
319 | } | 306 | } |
@@ -322,76 +309,66 @@ fn should_not_display_type_hint( | |||
322 | false | 309 | false |
323 | } | 310 | } |
324 | 311 | ||
325 | fn should_show_param_name_hint( | 312 | fn should_hide_param_name_hint( |
326 | sema: &Semantics<RootDatabase>, | 313 | sema: &Semantics<RootDatabase>, |
327 | callable: &hir::Callable, | 314 | callable: &hir::Callable, |
328 | param_name: &str, | 315 | param_name: &str, |
329 | argument: &ast::Expr, | 316 | argument: &ast::Expr, |
330 | ) -> bool { | 317 | ) -> bool { |
318 | // These are to be tested in the `parameter_hint_heuristics` test | ||
319 | // hide when: | ||
320 | // - the parameter name is a suffix of the function's name | ||
321 | // - the argument is an enum whose name is equal to the parameter | ||
322 | // - exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix | ||
323 | // of argument with _ splitting it off | ||
324 | // - param starts with `ra_fixture` | ||
325 | // - param is a well known name in an unary function | ||
326 | |||
331 | let param_name = param_name.trim_start_matches('_'); | 327 | let param_name = param_name.trim_start_matches('_'); |
328 | if param_name.is_empty() { | ||
329 | return true; | ||
330 | } | ||
331 | |||
332 | let fn_name = match callable.kind() { | 332 | let fn_name = match callable.kind() { |
333 | hir::CallableKind::Function(it) => Some(it.name(sema.db).to_string()), | 333 | hir::CallableKind::Function(it) => Some(it.name(sema.db).to_string()), |
334 | hir::CallableKind::TupleStruct(_) | 334 | _ => None, |
335 | | hir::CallableKind::TupleEnumVariant(_) | ||
336 | | hir::CallableKind::Closure => None, | ||
337 | }; | 335 | }; |
338 | 336 | let fn_name = fn_name.as_deref(); | |
339 | if param_name.is_empty() | 337 | is_param_name_suffix_of_fn_name(param_name, callable, fn_name) |
340 | || Some(param_name) == fn_name.as_ref().map(|s| s.trim_start_matches('_')) | 338 | || is_enum_name_similar_to_param_name(sema, argument, param_name) |
341 | || is_argument_similar_to_param_name(sema, argument, param_name) | 339 | || is_argument_similar_to_param_name(argument, param_name) |
342 | || is_param_name_similar_to_fn_name(param_name, callable, fn_name.as_ref()) | ||
343 | || param_name.starts_with("ra_fixture") | 340 | || param_name.starts_with("ra_fixture") |
344 | { | 341 | || (callable.n_params() == 1 && is_obvious_param(param_name)) |
345 | return false; | ||
346 | } | ||
347 | |||
348 | // avoid displaying hints for common functions like map, filter, etc. | ||
349 | // or other obvious words used in std | ||
350 | !(callable.n_params() == 1 && is_obvious_param(param_name)) | ||
351 | } | 342 | } |
352 | 343 | ||
353 | fn is_argument_similar_to_param_name( | 344 | fn is_argument_similar_to_param_name(argument: &ast::Expr, param_name: &str) -> bool { |
354 | sema: &Semantics<RootDatabase>, | 345 | // check whether param_name and argument are the same or |
355 | argument: &ast::Expr, | 346 | // whether param_name is a prefix/suffix of argument(split at `_`) |
356 | param_name: &str, | 347 | let argument = match get_string_representation(argument) { |
357 | ) -> bool { | 348 | Some(argument) => argument, |
358 | if is_enum_name_similar_to_param_name(sema, argument, param_name) { | 349 | None => return false, |
350 | }; | ||
351 | |||
352 | let param_name = param_name.trim_start_matches('_'); | ||
353 | let argument = argument.trim_start_matches('_'); | ||
354 | if argument.strip_prefix(param_name).map_or(false, |s| s.starts_with('_')) { | ||
359 | return true; | 355 | return true; |
360 | } | 356 | } |
361 | match get_string_representation(argument) { | 357 | if argument.strip_suffix(param_name).map_or(false, |s| s.ends_with('_')) { |
362 | None => false, | 358 | return true; |
363 | Some(argument_string) => { | ||
364 | let num_leading_underscores = | ||
365 | argument_string.bytes().take_while(|&c| c == b'_').count(); | ||
366 | |||
367 | // Does the argument name begin with the parameter name? Ignore leading underscores. | ||
368 | let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores); | ||
369 | let starts_with_pattern = param_name.bytes().all( | ||
370 | |expected| matches!(arg_bytes.next(), Some(actual) if expected.eq_ignore_ascii_case(&actual)), | ||
371 | ); | ||
372 | |||
373 | if starts_with_pattern { | ||
374 | return true; | ||
375 | } | ||
376 | |||
377 | // Does the argument name end with the parameter name? | ||
378 | let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores); | ||
379 | param_name.bytes().rev().all( | ||
380 | |expected| matches!(arg_bytes.next_back(), Some(actual) if expected.eq_ignore_ascii_case(&actual)), | ||
381 | ) | ||
382 | } | ||
383 | } | 359 | } |
360 | argument == param_name | ||
384 | } | 361 | } |
385 | 362 | ||
386 | fn is_param_name_similar_to_fn_name( | 363 | /// Hide the parameter name of an unary function if it is a `_` - prefixed suffix of the function's name, or equal. |
364 | /// | ||
365 | /// `fn strip_suffix(suffix)` will be hidden. | ||
366 | /// `fn stripsuffix(suffix)` will not be hidden. | ||
367 | fn is_param_name_suffix_of_fn_name( | ||
387 | param_name: &str, | 368 | param_name: &str, |
388 | callable: &Callable, | 369 | callable: &Callable, |
389 | fn_name: Option<&String>, | 370 | fn_name: Option<&str>, |
390 | ) -> bool { | 371 | ) -> bool { |
391 | // if it's the only parameter, don't show it if: | ||
392 | // - is the same as the function name, or | ||
393 | // - the function ends with '_' + param_name | ||
394 | |||
395 | match (callable.n_params(), fn_name) { | 372 | match (callable.n_params(), fn_name) { |
396 | (1, Some(function)) => { | 373 | (1, Some(function)) => { |
397 | function == param_name | 374 | function == param_name |
@@ -419,12 +396,12 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> { | |||
419 | ast::Expr::MethodCallExpr(method_call_expr) => { | 396 | ast::Expr::MethodCallExpr(method_call_expr) => { |
420 | let name_ref = method_call_expr.name_ref()?; | 397 | let name_ref = method_call_expr.name_ref()?; |
421 | match name_ref.text().as_str() { | 398 | match name_ref.text().as_str() { |
422 | "clone" => method_call_expr.receiver().map(|rec| rec.to_string()), | 399 | "clone" | "as_ref" => method_call_expr.receiver().map(|rec| rec.to_string()), |
423 | name_ref => Some(name_ref.to_owned()), | 400 | name_ref => Some(name_ref.to_owned()), |
424 | } | 401 | } |
425 | } | 402 | } |
426 | ast::Expr::FieldExpr(field_expr) => Some(field_expr.name_ref()?.to_string()), | 403 | ast::Expr::FieldExpr(field_expr) => Some(field_expr.name_ref()?.to_string()), |
427 | ast::Expr::PathExpr(path_expr) => Some(path_expr.to_string()), | 404 | ast::Expr::PathExpr(path_expr) => Some(path_expr.path()?.segment()?.to_string()), |
428 | ast::Expr::PrefixExpr(prefix_expr) => get_string_representation(&prefix_expr.expr()?), | 405 | ast::Expr::PrefixExpr(prefix_expr) => get_string_representation(&prefix_expr.expr()?), |
429 | ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?), | 406 | ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?), |
430 | _ => None, | 407 | _ => None, |
@@ -432,15 +409,24 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> { | |||
432 | } | 409 | } |
433 | 410 | ||
434 | fn is_obvious_param(param_name: &str) -> bool { | 411 | fn is_obvious_param(param_name: &str) -> bool { |
412 | // avoid displaying hints for common functions like map, filter, etc. | ||
413 | // or other obvious words used in std | ||
435 | let is_obvious_param_name = | 414 | let is_obvious_param_name = |
436 | matches!(param_name, "predicate" | "value" | "pat" | "rhs" | "other"); | 415 | matches!(param_name, "predicate" | "value" | "pat" | "rhs" | "other"); |
437 | param_name.len() == 1 || is_obvious_param_name | 416 | param_name.len() == 1 || is_obvious_param_name |
438 | } | 417 | } |
439 | 418 | ||
440 | fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Callable> { | 419 | fn get_callable( |
420 | sema: &Semantics<RootDatabase>, | ||
421 | expr: &ast::Expr, | ||
422 | ) -> Option<(hir::Callable, ast::ArgList)> { | ||
441 | match expr { | 423 | match expr { |
442 | ast::Expr::CallExpr(expr) => sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db), | 424 | ast::Expr::CallExpr(expr) => { |
443 | ast::Expr::MethodCallExpr(expr) => sema.resolve_method_call_as_callable(expr), | 425 | sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db).zip(expr.arg_list()) |
426 | } | ||
427 | ast::Expr::MethodCallExpr(expr) => { | ||
428 | sema.resolve_method_call_as_callable(expr).zip(expr.arg_list()) | ||
429 | } | ||
444 | _ => None, | 430 | _ => None, |
445 | } | 431 | } |
446 | } | 432 | } |
@@ -464,6 +450,42 @@ mod tests { | |||
464 | check_with_config(TEST_CONFIG, ra_fixture); | 450 | check_with_config(TEST_CONFIG, ra_fixture); |
465 | } | 451 | } |
466 | 452 | ||
453 | fn check_params(ra_fixture: &str) { | ||
454 | check_with_config( | ||
455 | InlayHintsConfig { | ||
456 | parameter_hints: true, | ||
457 | type_hints: false, | ||
458 | chaining_hints: false, | ||
459 | max_length: None, | ||
460 | }, | ||
461 | ra_fixture, | ||
462 | ); | ||
463 | } | ||
464 | |||
465 | fn check_types(ra_fixture: &str) { | ||
466 | check_with_config( | ||
467 | InlayHintsConfig { | ||
468 | parameter_hints: false, | ||
469 | type_hints: true, | ||
470 | chaining_hints: false, | ||
471 | max_length: None, | ||
472 | }, | ||
473 | ra_fixture, | ||
474 | ); | ||
475 | } | ||
476 | |||
477 | fn check_chains(ra_fixture: &str) { | ||
478 | check_with_config( | ||
479 | InlayHintsConfig { | ||
480 | parameter_hints: false, | ||
481 | type_hints: false, | ||
482 | chaining_hints: true, | ||
483 | max_length: None, | ||
484 | }, | ||
485 | ra_fixture, | ||
486 | ); | ||
487 | } | ||
488 | |||
467 | fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) { | 489 | fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) { |
468 | let ra_fixture = | 490 | let ra_fixture = |
469 | format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); | 491 | format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); |
@@ -484,17 +506,30 @@ mod tests { | |||
484 | } | 506 | } |
485 | 507 | ||
486 | #[test] | 508 | #[test] |
487 | fn param_hints_only() { | 509 | fn hints_disabled() { |
488 | check_with_config( | 510 | check_with_config( |
489 | InlayHintsConfig { | 511 | InlayHintsConfig { |
490 | parameter_hints: true, | ||
491 | type_hints: false, | 512 | type_hints: false, |
513 | parameter_hints: false, | ||
492 | chaining_hints: false, | 514 | chaining_hints: false, |
493 | max_length: None, | 515 | max_length: None, |
494 | }, | 516 | }, |
495 | r#" | 517 | r#" |
496 | fn foo(a: i32, b: i32) -> i32 { a + b } | 518 | fn foo(a: i32, b: i32) -> i32 { a + b } |
497 | fn main() { | 519 | fn main() { |
520 | let _x = foo(4, 4); | ||
521 | }"#, | ||
522 | ); | ||
523 | } | ||
524 | |||
525 | // Parameter hint tests | ||
526 | |||
527 | #[test] | ||
528 | fn param_hints_only() { | ||
529 | check_params( | ||
530 | r#" | ||
531 | fn foo(a: i32, b: i32) -> i32 { a + b } | ||
532 | fn main() { | ||
498 | let _x = foo( | 533 | let _x = foo( |
499 | 4, | 534 | 4, |
500 | //^ a | 535 | //^ a |
@@ -507,13 +542,7 @@ fn main() { | |||
507 | 542 | ||
508 | #[test] | 543 | #[test] |
509 | fn param_name_similar_to_fn_name_still_hints() { | 544 | fn param_name_similar_to_fn_name_still_hints() { |
510 | check_with_config( | 545 | check_params( |
511 | InlayHintsConfig { | ||
512 | parameter_hints: true, | ||
513 | type_hints: false, | ||
514 | chaining_hints: false, | ||
515 | max_length: None, | ||
516 | }, | ||
517 | r#" | 546 | r#" |
518 | fn max(x: i32, y: i32) -> i32 { x + y } | 547 | fn max(x: i32, y: i32) -> i32 { x + y } |
519 | fn main() { | 548 | fn main() { |
@@ -529,13 +558,7 @@ fn main() { | |||
529 | 558 | ||
530 | #[test] | 559 | #[test] |
531 | fn param_name_similar_to_fn_name() { | 560 | fn param_name_similar_to_fn_name() { |
532 | check_with_config( | 561 | check_params( |
533 | InlayHintsConfig { | ||
534 | parameter_hints: true, | ||
535 | type_hints: false, | ||
536 | chaining_hints: false, | ||
537 | max_length: None, | ||
538 | }, | ||
539 | r#" | 562 | r#" |
540 | fn param_with_underscore(with_underscore: i32) -> i32 { with_underscore } | 563 | fn param_with_underscore(with_underscore: i32) -> i32 { with_underscore } |
541 | fn main() { | 564 | fn main() { |
@@ -544,17 +567,20 @@ fn main() { | |||
544 | ); | 567 | ); |
545 | }"#, | 568 | }"#, |
546 | ); | 569 | ); |
570 | check_params( | ||
571 | r#" | ||
572 | fn param_with_underscore(underscore: i32) -> i32 { underscore } | ||
573 | fn main() { | ||
574 | let _x = param_with_underscore( | ||
575 | 4, | ||
576 | ); | ||
577 | }"#, | ||
578 | ); | ||
547 | } | 579 | } |
548 | 580 | ||
549 | #[test] | 581 | #[test] |
550 | fn param_name_same_as_fn_name() { | 582 | fn param_name_same_as_fn_name() { |
551 | check_with_config( | 583 | check_params( |
552 | InlayHintsConfig { | ||
553 | parameter_hints: true, | ||
554 | type_hints: false, | ||
555 | chaining_hints: false, | ||
556 | max_length: None, | ||
557 | }, | ||
558 | r#" | 584 | r#" |
559 | fn foo(foo: i32) -> i32 { foo } | 585 | fn foo(foo: i32) -> i32 { foo } |
560 | fn main() { | 586 | fn main() { |
@@ -567,52 +593,206 @@ fn main() { | |||
567 | 593 | ||
568 | #[test] | 594 | #[test] |
569 | fn never_hide_param_when_multiple_params() { | 595 | fn never_hide_param_when_multiple_params() { |
570 | check_with_config( | 596 | check_params( |
571 | InlayHintsConfig { | ||
572 | parameter_hints: true, | ||
573 | type_hints: false, | ||
574 | chaining_hints: false, | ||
575 | max_length: None, | ||
576 | }, | ||
577 | r#" | 597 | r#" |
578 | fn foo(bar: i32, baz: i32) -> i32 { bar + baz } | 598 | fn foo(foo: i32, bar: i32) -> i32 { bar + baz } |
579 | fn main() { | 599 | fn main() { |
580 | let _x = foo( | 600 | let _x = foo( |
581 | 4, | 601 | 4, |
582 | //^ bar | 602 | //^ foo |
583 | 8, | 603 | 8, |
584 | //^ baz | 604 | //^ bar |
585 | ); | 605 | ); |
586 | }"#, | 606 | }"#, |
587 | ); | 607 | ); |
588 | } | 608 | } |
589 | 609 | ||
590 | #[test] | 610 | #[test] |
591 | fn hints_disabled() { | 611 | fn param_hints_look_through_as_ref_and_clone() { |
592 | check_with_config( | 612 | check_params( |
593 | InlayHintsConfig { | ||
594 | type_hints: false, | ||
595 | parameter_hints: false, | ||
596 | chaining_hints: false, | ||
597 | max_length: None, | ||
598 | }, | ||
599 | r#" | 613 | r#" |
600 | fn foo(a: i32, b: i32) -> i32 { a + b } | 614 | fn foo(bar: i32, baz: f32) {} |
615 | |||
601 | fn main() { | 616 | fn main() { |
602 | let _x = foo(4, 4); | 617 | let bar = 3; |
618 | let baz = &"baz"; | ||
619 | let fez = 1.0; | ||
620 | foo(bar.clone(), bar.clone()); | ||
621 | //^^^^^^^^^^^ baz | ||
622 | foo(bar.as_ref(), bar.as_ref()); | ||
623 | //^^^^^^^^^^^^ baz | ||
624 | } | ||
625 | "#, | ||
626 | ); | ||
627 | } | ||
628 | |||
629 | #[test] | ||
630 | fn self_param_hints() { | ||
631 | check_params( | ||
632 | r#" | ||
633 | struct Foo; | ||
634 | |||
635 | impl Foo { | ||
636 | fn foo(self: Self) {} | ||
637 | fn bar(self: &Self) {} | ||
638 | } | ||
639 | |||
640 | fn main() { | ||
641 | Foo::foo(Foo); | ||
642 | //^^^ self | ||
643 | Foo::bar(&Foo); | ||
644 | //^^^^ self | ||
645 | } | ||
646 | "#, | ||
647 | ) | ||
648 | } | ||
649 | |||
650 | #[test] | ||
651 | fn param_name_hints_show_for_literals() { | ||
652 | check_params( | ||
653 | r#"pub fn test(a: i32, b: i32) -> [i32; 2] { [a, b] } | ||
654 | fn main() { | ||
655 | test( | ||
656 | 0xa_b, | ||
657 | //^^^^^ a | ||
658 | 0xa_b, | ||
659 | //^^^^^ b | ||
660 | ); | ||
661 | }"#, | ||
662 | ) | ||
663 | } | ||
664 | |||
665 | #[test] | ||
666 | fn function_call_parameter_hint() { | ||
667 | check_params( | ||
668 | r#" | ||
669 | enum Option<T> { None, Some(T) } | ||
670 | use Option::*; | ||
671 | |||
672 | struct FileId {} | ||
673 | struct SmolStr {} | ||
674 | |||
675 | struct TextRange {} | ||
676 | struct SyntaxKind {} | ||
677 | struct NavigationTarget {} | ||
678 | |||
679 | struct Test {} | ||
680 | |||
681 | impl Test { | ||
682 | fn method(&self, mut param: i32) -> i32 { param * 2 } | ||
683 | |||
684 | fn from_syntax( | ||
685 | file_id: FileId, | ||
686 | name: SmolStr, | ||
687 | focus_range: Option<TextRange>, | ||
688 | full_range: TextRange, | ||
689 | kind: SyntaxKind, | ||
690 | docs: Option<String>, | ||
691 | ) -> NavigationTarget { | ||
692 | NavigationTarget {} | ||
693 | } | ||
694 | } | ||
695 | |||
696 | fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 { | ||
697 | foo + bar | ||
698 | } | ||
699 | |||
700 | fn main() { | ||
701 | let not_literal = 1; | ||
702 | let _: i32 = test_func(1, 2, "hello", 3, not_literal); | ||
703 | //^ foo ^ bar ^^^^^^^ msg ^^^^^^^^^^^ last | ||
704 | let t: Test = Test {}; | ||
705 | t.method(123); | ||
706 | //^^^ param | ||
707 | Test::method(&t, 3456); | ||
708 | //^^ self ^^^^ param | ||
709 | Test::from_syntax( | ||
710 | FileId {}, | ||
711 | //^^^^^^^^^ file_id | ||
712 | "impl".into(), | ||
713 | //^^^^^^^^^^^^^ name | ||
714 | None, | ||
715 | //^^^^ focus_range | ||
716 | TextRange {}, | ||
717 | //^^^^^^^^^^^^ full_range | ||
718 | SyntaxKind {}, | ||
719 | //^^^^^^^^^^^^^ kind | ||
720 | None, | ||
721 | //^^^^ docs | ||
722 | ); | ||
723 | }"#, | ||
724 | ); | ||
725 | } | ||
726 | |||
727 | #[test] | ||
728 | fn parameter_hint_heuristics() { | ||
729 | check_params( | ||
730 | r#" | ||
731 | fn check(ra_fixture_thing: &str) {} | ||
732 | |||
733 | fn map(f: i32) {} | ||
734 | fn filter(predicate: i32) {} | ||
735 | |||
736 | fn strip_suffix(suffix: &str) {} | ||
737 | fn stripsuffix(suffix: &str) {} | ||
738 | fn same(same: u32) {} | ||
739 | fn same2(_same2: u32) {} | ||
740 | |||
741 | fn enum_matches_param_name(completion_kind: CompletionKind) {} | ||
742 | |||
743 | fn foo(param: u32) {} | ||
744 | fn bar(param_eter: u32) {} | ||
745 | |||
746 | enum CompletionKind { | ||
747 | Keyword, | ||
748 | } | ||
749 | |||
750 | fn non_ident_pat((a, b): (u32, u32)) {} | ||
751 | |||
752 | fn main() { | ||
753 | check(""); | ||
754 | |||
755 | map(0); | ||
756 | filter(0); | ||
757 | |||
758 | strip_suffix(""); | ||
759 | stripsuffix(""); | ||
760 | //^^ suffix | ||
761 | same(0); | ||
762 | same2(0); | ||
763 | |||
764 | enum_matches_param_name(CompletionKind::Keyword); | ||
765 | |||
766 | let param = 0; | ||
767 | foo(param); | ||
768 | let param_end = 0; | ||
769 | foo(param_end); | ||
770 | let start_param = 0; | ||
771 | foo(start_param); | ||
772 | let param2 = 0; | ||
773 | foo(param2); | ||
774 | //^^^^^^ param | ||
775 | |||
776 | let param_eter = 0; | ||
777 | bar(param_eter); | ||
778 | let param_eter_end = 0; | ||
779 | bar(param_eter_end); | ||
780 | let start_param_eter = 0; | ||
781 | bar(start_param_eter); | ||
782 | let param_eter2 = 0; | ||
783 | bar(param_eter2); | ||
784 | //^^^^^^^^^^^ param_eter | ||
785 | |||
786 | non_ident_pat((0, 0)); | ||
603 | }"#, | 787 | }"#, |
604 | ); | 788 | ); |
605 | } | 789 | } |
606 | 790 | ||
791 | // Type-Hint tests | ||
792 | |||
607 | #[test] | 793 | #[test] |
608 | fn type_hints_only() { | 794 | fn type_hints_only() { |
609 | check_with_config( | 795 | check_types( |
610 | InlayHintsConfig { | ||
611 | type_hints: true, | ||
612 | parameter_hints: false, | ||
613 | chaining_hints: false, | ||
614 | max_length: None, | ||
615 | }, | ||
616 | r#" | 796 | r#" |
617 | fn foo(a: i32, b: i32) -> i32 { a + b } | 797 | fn foo(a: i32, b: i32) -> i32 { a + b } |
618 | fn main() { | 798 | fn main() { |
@@ -640,8 +820,115 @@ fn main() { | |||
640 | } | 820 | } |
641 | 821 | ||
642 | #[test] | 822 | #[test] |
823 | fn shorten_iterators_in_associated_params() { | ||
824 | check_types( | ||
825 | r#" | ||
826 | use core::iter; | ||
827 | |||
828 | pub struct SomeIter<T> {} | ||
829 | |||
830 | impl<T> SomeIter<T> { | ||
831 | pub fn new() -> Self { SomeIter {} } | ||
832 | pub fn push(&mut self, t: T) {} | ||
833 | } | ||
834 | |||
835 | impl<T> Iterator for SomeIter<T> { | ||
836 | type Item = T; | ||
837 | fn next(&mut self) -> Option<Self::Item> { | ||
838 | None | ||
839 | } | ||
840 | } | ||
841 | |||
842 | fn main() { | ||
843 | let mut some_iter = SomeIter::new(); | ||
844 | //^^^^^^^^^^^^^ SomeIter<Take<Repeat<i32>>> | ||
845 | some_iter.push(iter::repeat(2).take(2)); | ||
846 | let iter_of_iters = some_iter.take(2); | ||
847 | //^^^^^^^^^^^^^ impl Iterator<Item = impl Iterator<Item = i32>> | ||
848 | } | ||
849 | "#, | ||
850 | ); | ||
851 | } | ||
852 | |||
853 | #[test] | ||
854 | fn infer_call_method_return_associated_types_with_generic() { | ||
855 | check_types( | ||
856 | r#" | ||
857 | pub trait Default { | ||
858 | fn default() -> Self; | ||
859 | } | ||
860 | pub trait Foo { | ||
861 | type Bar: Default; | ||
862 | } | ||
863 | |||
864 | pub fn quux<T: Foo>() -> T::Bar { | ||
865 | let y = Default::default(); | ||
866 | //^ <T as Foo>::Bar | ||
867 | |||
868 | y | ||
869 | } | ||
870 | "#, | ||
871 | ); | ||
872 | } | ||
873 | |||
874 | #[test] | ||
875 | fn fn_hints() { | ||
876 | check_types( | ||
877 | r#" | ||
878 | trait Sized {} | ||
879 | |||
880 | fn foo() -> impl Fn() { loop {} } | ||
881 | fn foo1() -> impl Fn(f64) { loop {} } | ||
882 | fn foo2() -> impl Fn(f64, f64) { loop {} } | ||
883 | fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} } | ||
884 | fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} } | ||
885 | fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} } | ||
886 | fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} } | ||
887 | fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} } | ||
888 | |||
889 | fn main() { | ||
890 | let foo = foo(); | ||
891 | // ^^^ impl Fn() | ||
892 | let foo = foo1(); | ||
893 | // ^^^ impl Fn(f64) | ||
894 | let foo = foo2(); | ||
895 | // ^^^ impl Fn(f64, f64) | ||
896 | let foo = foo3(); | ||
897 | // ^^^ impl Fn(f64, f64) -> u32 | ||
898 | let foo = foo4(); | ||
899 | // ^^^ &dyn Fn(f64, f64) -> u32 | ||
900 | let foo = foo5(); | ||
901 | // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32 | ||
902 | let foo = foo6(); | ||
903 | // ^^^ impl Fn(f64, f64) -> u32 + Sized | ||
904 | let foo = foo7(); | ||
905 | // ^^^ *const (impl Fn(f64, f64) -> u32 + Sized) | ||
906 | } | ||
907 | "#, | ||
908 | ) | ||
909 | } | ||
910 | |||
911 | #[test] | ||
912 | fn unit_structs_have_no_type_hints() { | ||
913 | check_types( | ||
914 | r#" | ||
915 | enum Result<T, E> { Ok(T), Err(E) } | ||
916 | use Result::*; | ||
917 | |||
918 | struct SyntheticSyntax; | ||
919 | |||
920 | fn main() { | ||
921 | match Ok(()) { | ||
922 | Ok(_) => (), | ||
923 | Err(SyntheticSyntax) => (), | ||
924 | } | ||
925 | }"#, | ||
926 | ); | ||
927 | } | ||
928 | |||
929 | #[test] | ||
643 | fn let_statement() { | 930 | fn let_statement() { |
644 | check( | 931 | check_types( |
645 | r#" | 932 | r#" |
646 | #[derive(PartialEq)] | 933 | #[derive(PartialEq)] |
647 | enum Option<T> { None, Some(T) } | 934 | enum Option<T> { None, Some(T) } |
@@ -676,34 +963,8 @@ fn main() { | |||
676 | } | 963 | } |
677 | 964 | ||
678 | #[test] | 965 | #[test] |
679 | fn closure_parameters() { | ||
680 | check( | ||
681 | r#" | ||
682 | fn main() { | ||
683 | let mut start = 0; | ||
684 | //^^^^^^^^^ i32 | ||
685 | (0..2).for_each(|increment| { start += increment; }); | ||
686 | //^^^^^^^^^ i32 | ||
687 | |||
688 | let multiply = | ||
689 | //^^^^^^^^ |…| -> i32 | ||
690 | | a, b| a * b | ||
691 | //^ i32 ^ i32 | ||
692 | ; | ||
693 | |||
694 | let _: i32 = multiply(1, 2); | ||
695 | let multiply_ref = &multiply; | ||
696 | //^^^^^^^^^^^^ &|…| -> i32 | ||
697 | |||
698 | let return_42 = || 42; | ||
699 | //^^^^^^^^^ || -> i32 | ||
700 | }"#, | ||
701 | ); | ||
702 | } | ||
703 | |||
704 | #[test] | ||
705 | fn if_expr() { | 966 | fn if_expr() { |
706 | check( | 967 | check_types( |
707 | r#" | 968 | r#" |
708 | enum Option<T> { None, Some(T) } | 969 | enum Option<T> { None, Some(T) } |
709 | use Option::*; | 970 | use Option::*; |
@@ -735,7 +996,7 @@ fn main() { | |||
735 | 996 | ||
736 | #[test] | 997 | #[test] |
737 | fn while_expr() { | 998 | fn while_expr() { |
738 | check( | 999 | check_types( |
739 | r#" | 1000 | r#" |
740 | enum Option<T> { None, Some(T) } | 1001 | enum Option<T> { None, Some(T) } |
741 | use Option::*; | 1002 | use Option::*; |
@@ -753,7 +1014,7 @@ fn main() { | |||
753 | 1014 | ||
754 | #[test] | 1015 | #[test] |
755 | fn match_arm_list() { | 1016 | fn match_arm_list() { |
756 | check( | 1017 | check_types( |
757 | r#" | 1018 | r#" |
758 | enum Option<T> { None, Some(T) } | 1019 | enum Option<T> { None, Some(T) } |
759 | use Option::*; | 1020 | use Option::*; |
@@ -774,203 +1035,175 @@ fn main() { | |||
774 | } | 1035 | } |
775 | 1036 | ||
776 | #[test] | 1037 | #[test] |
777 | fn hint_truncation() { | 1038 | fn incomplete_for_no_hint() { |
778 | check_with_config( | 1039 | check_types( |
779 | InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG }, | ||
780 | r#" | 1040 | r#" |
781 | struct Smol<T>(T); | ||
782 | |||
783 | struct VeryLongOuterName<T>(T); | ||
784 | |||
785 | fn main() { | 1041 | fn main() { |
786 | let a = Smol(0u32); | 1042 | let data = &[1i32, 2, 3]; |
787 | //^ Smol<u32> | 1043 | //^^^^ &[i32; 3] |
788 | let b = VeryLongOuterName(0usize); | 1044 | for i |
789 | //^ VeryLongOuterName<…> | ||
790 | let c = Smol(Smol(0u32)) | ||
791 | //^ Smol<Smol<…>> | ||
792 | }"#, | 1045 | }"#, |
793 | ); | 1046 | ); |
794 | } | ||
795 | |||
796 | #[test] | ||
797 | fn function_call_parameter_hint() { | ||
798 | check( | 1047 | check( |
799 | r#" | 1048 | r#" |
800 | enum Option<T> { None, Some(T) } | 1049 | pub struct Vec<T> {} |
801 | use Option::*; | ||
802 | |||
803 | struct FileId {} | ||
804 | struct SmolStr {} | ||
805 | |||
806 | struct TextRange {} | ||
807 | struct SyntaxKind {} | ||
808 | struct NavigationTarget {} | ||
809 | |||
810 | struct Test {} | ||
811 | |||
812 | impl Test { | ||
813 | fn method(&self, mut param: i32) -> i32 { param * 2 } | ||
814 | 1050 | ||
815 | fn from_syntax( | 1051 | impl<T> Vec<T> { |
816 | file_id: FileId, | 1052 | pub fn new() -> Self { Vec {} } |
817 | name: SmolStr, | 1053 | pub fn push(&mut self, t: T) {} |
818 | focus_range: Option<TextRange>, | ||
819 | full_range: TextRange, | ||
820 | kind: SyntaxKind, | ||
821 | docs: Option<String>, | ||
822 | ) -> NavigationTarget { | ||
823 | NavigationTarget {} | ||
824 | } | ||
825 | } | 1054 | } |
826 | 1055 | ||
827 | fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 { | 1056 | impl<T> IntoIterator for Vec<T> { |
828 | foo + bar | 1057 | type Item=T; |
829 | } | 1058 | } |
830 | 1059 | ||
831 | fn main() { | 1060 | fn main() { |
832 | let not_literal = 1; | 1061 | let mut data = Vec::new(); |
833 | //^^^^^^^^^^^ i32 | 1062 | //^^^^^^^^ Vec<&str> |
834 | let _: i32 = test_func(1, 2, "hello", 3, not_literal); | 1063 | data.push("foo"); |
835 | //^ foo ^ bar ^^^^^^^ msg ^^^^^^^^^^^ last | 1064 | for i in |
836 | let t: Test = Test {}; | 1065 | |
837 | t.method(123); | 1066 | println!("Unit expr"); |
838 | //^^^ param | 1067 | } |
839 | Test::method(&t, 3456); | 1068 | "#, |
840 | //^^ self ^^^^ param | ||
841 | Test::from_syntax( | ||
842 | FileId {}, | ||
843 | //^^^^^^^^^ file_id | ||
844 | "impl".into(), | ||
845 | //^^^^^^^^^^^^^ name | ||
846 | None, | ||
847 | //^^^^ focus_range | ||
848 | TextRange {}, | ||
849 | //^^^^^^^^^^^^ full_range | ||
850 | SyntaxKind {}, | ||
851 | //^^^^^^^^^^^^^ kind | ||
852 | None, | ||
853 | //^^^^ docs | ||
854 | ); | ||
855 | }"#, | ||
856 | ); | 1069 | ); |
857 | } | 1070 | } |
858 | 1071 | ||
859 | #[test] | 1072 | #[test] |
860 | fn omitted_parameters_hints_heuristics() { | 1073 | fn complete_for_hint() { |
861 | check_with_config( | 1074 | check_types( |
862 | InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG }, | ||
863 | r#" | 1075 | r#" |
864 | fn map(f: i32) {} | 1076 | pub struct Vec<T> {} |
865 | fn filter(predicate: i32) {} | ||
866 | 1077 | ||
867 | struct TestVarContainer { | 1078 | impl<T> Vec<T> { |
868 | test_var: i32, | 1079 | pub fn new() -> Self { Vec {} } |
1080 | pub fn push(&mut self, t: T) {} | ||
869 | } | 1081 | } |
870 | 1082 | ||
871 | impl TestVarContainer { | 1083 | impl<T> IntoIterator for Vec<T> { |
872 | fn test_var(&self) -> i32 { | 1084 | type Item=T; |
873 | self.test_var | ||
874 | } | ||
875 | } | 1085 | } |
876 | 1086 | ||
877 | struct Test {} | 1087 | fn main() { |
878 | 1088 | let mut data = Vec::new(); | |
879 | impl Test { | 1089 | //^^^^^^^^ Vec<&str> |
880 | fn map(self, f: i32) -> Self { | 1090 | data.push("foo"); |
881 | self | 1091 | for i in data { |
882 | } | 1092 | //^ &str |
883 | 1093 | let z = i; | |
884 | fn filter(self, predicate: i32) -> Self { | 1094 | //^ &str |
885 | self | ||
886 | } | 1095 | } |
887 | 1096 | } | |
888 | fn field(self, value: i32) -> Self { | 1097 | "#, |
889 | self | 1098 | ); |
890 | } | 1099 | } |
891 | 1100 | ||
892 | fn no_hints_expected(&self, _: i32, test_var: i32) {} | 1101 | #[test] |
1102 | fn multi_dyn_trait_bounds() { | ||
1103 | check_types( | ||
1104 | r#" | ||
1105 | pub struct Vec<T> {} | ||
893 | 1106 | ||
894 | fn frob(&self, frob: bool) {} | 1107 | impl<T> Vec<T> { |
1108 | pub fn new() -> Self { Vec {} } | ||
895 | } | 1109 | } |
896 | 1110 | ||
897 | struct Param {} | 1111 | pub struct Box<T> {} |
898 | |||
899 | fn different_order(param: &Param) {} | ||
900 | fn different_order_mut(param: &mut Param) {} | ||
901 | fn has_underscore(_param: bool) {} | ||
902 | fn enum_matches_param_name(completion_kind: CompletionKind) {} | ||
903 | fn param_destructuring_omitted_1((a, b): (u32, u32)) {} | ||
904 | fn param_destructuring_omitted_2(TestVarContainer { test_var: _ }: TestVarContainer) {} | ||
905 | |||
906 | fn twiddle(twiddle: bool) {} | ||
907 | fn doo(_doo: bool) {} | ||
908 | 1112 | ||
909 | enum CompletionKind { | 1113 | trait Display {} |
910 | Keyword, | 1114 | trait Sync {} |
911 | } | ||
912 | 1115 | ||
913 | fn main() { | 1116 | fn main() { |
914 | let container: TestVarContainer = TestVarContainer { test_var: 42 }; | 1117 | let _v = Vec::<Box<&(dyn Display + Sync)>>::new(); |
915 | let test: Test = Test {}; | 1118 | //^^ Vec<Box<&(dyn Display + Sync)>> |
916 | 1119 | let _v = Vec::<Box<*const (dyn Display + Sync)>>::new(); | |
917 | map(22); | 1120 | //^^ Vec<Box<*const (dyn Display + Sync)>> |
918 | filter(33); | 1121 | let _v = Vec::<Box<dyn Display + Sync>>::new(); |
919 | 1122 | //^^ Vec<Box<dyn Display + Sync>> | |
920 | let test_processed: Test = test.map(1).filter(2).field(3); | 1123 | } |
1124 | "#, | ||
1125 | ); | ||
1126 | } | ||
921 | 1127 | ||
922 | let test_var: i32 = 55; | 1128 | #[test] |
923 | test_processed.no_hints_expected(22, test_var); | 1129 | fn shorten_iterator_hints() { |
924 | test_processed.no_hints_expected(33, container.test_var); | 1130 | check_types( |
925 | test_processed.no_hints_expected(44, container.test_var()); | 1131 | r#" |
926 | test_processed.frob(false); | 1132 | use core::iter; |
927 | 1133 | ||
928 | twiddle(true); | 1134 | struct MyIter; |
929 | doo(true); | ||
930 | 1135 | ||
931 | const TWIDDLE_UPPERCASE: bool = true; | 1136 | impl Iterator for MyIter { |
932 | twiddle(TWIDDLE_UPPERCASE); | 1137 | type Item = (); |
1138 | fn next(&mut self) -> Option<Self::Item> { | ||
1139 | None | ||
1140 | } | ||
1141 | } | ||
933 | 1142 | ||
934 | let mut param_begin: Param = Param {}; | 1143 | fn main() { |
935 | different_order(¶m_begin); | 1144 | let _x = MyIter; |
936 | different_order(&mut param_begin); | 1145 | //^^ MyIter |
1146 | let _x = iter::repeat(0); | ||
1147 | //^^ impl Iterator<Item = i32> | ||
1148 | fn generic<T: Clone>(t: T) { | ||
1149 | let _x = iter::repeat(t); | ||
1150 | //^^ impl Iterator<Item = T> | ||
1151 | let _chained = iter::repeat(t).take(10); | ||
1152 | //^^^^^^^^ impl Iterator<Item = T> | ||
1153 | } | ||
1154 | } | ||
1155 | "#, | ||
1156 | ); | ||
1157 | } | ||
937 | 1158 | ||
938 | let param: bool = true; | 1159 | #[test] |
939 | has_underscore(param); | 1160 | fn closures() { |
1161 | check( | ||
1162 | r#" | ||
1163 | fn main() { | ||
1164 | let mut start = 0; | ||
1165 | //^^^^^^^^^ i32 | ||
1166 | (0..2).for_each(|increment| { start += increment; }); | ||
1167 | //^^^^^^^^^ i32 | ||
940 | 1168 | ||
941 | enum_matches_param_name(CompletionKind::Keyword); | 1169 | let multiply = |
1170 | //^^^^^^^^ |…| -> i32 | ||
1171 | | a, b| a * b | ||
1172 | //^ i32 ^ i32 | ||
1173 | ; | ||
942 | 1174 | ||
943 | let a: f64 = 7.0; | 1175 | let _: i32 = multiply(1, 2); |
944 | let b: f64 = 4.0; | 1176 | let multiply_ref = &multiply; |
945 | let _: f64 = a.div_euclid(b); | 1177 | //^^^^^^^^^^^^ &|…| -> i32 |
946 | let _: f64 = a.abs_sub(b); | ||
947 | 1178 | ||
948 | let range: (u32, u32) = (3, 5); | 1179 | let return_42 = || 42; |
949 | param_destructuring_omitted_1(range); | 1180 | //^^^^^^^^^ || -> i32 |
950 | param_destructuring_omitted_2(container); | ||
951 | }"#, | 1181 | }"#, |
952 | ); | 1182 | ); |
953 | } | 1183 | } |
954 | 1184 | ||
955 | #[test] | 1185 | #[test] |
956 | fn unit_structs_have_no_type_hints() { | 1186 | fn hint_truncation() { |
957 | check_with_config( | 1187 | check_with_config( |
958 | InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG }, | 1188 | InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG }, |
959 | r#" | 1189 | r#" |
960 | enum Result<T, E> { Ok(T), Err(E) } | 1190 | struct Smol<T>(T); |
961 | use Result::*; | ||
962 | 1191 | ||
963 | struct SyntheticSyntax; | 1192 | struct VeryLongOuterName<T>(T); |
964 | 1193 | ||
965 | fn main() { | 1194 | fn main() { |
966 | match Ok(()) { | 1195 | let a = Smol(0u32); |
967 | Ok(_) => (), | 1196 | //^ Smol<u32> |
968 | Err(SyntheticSyntax) => (), | 1197 | let b = VeryLongOuterName(0usize); |
969 | } | 1198 | //^ VeryLongOuterName<…> |
1199 | let c = Smol(Smol(0u32)) | ||
1200 | //^ Smol<Smol<…>> | ||
970 | }"#, | 1201 | }"#, |
971 | ); | 1202 | ); |
972 | } | 1203 | } |
973 | 1204 | ||
1205 | // Chaining hint tests | ||
1206 | |||
974 | #[test] | 1207 | #[test] |
975 | fn chaining_hints_ignore_comments() { | 1208 | fn chaining_hints_ignore_comments() { |
976 | check_expect( | 1209 | check_expect( |
@@ -1013,13 +1246,7 @@ fn main() { | |||
1013 | 1246 | ||
1014 | #[test] | 1247 | #[test] |
1015 | fn chaining_hints_without_newlines() { | 1248 | fn chaining_hints_without_newlines() { |
1016 | check_with_config( | 1249 | check_chains( |
1017 | InlayHintsConfig { | ||
1018 | parameter_hints: false, | ||
1019 | type_hints: false, | ||
1020 | chaining_hints: true, | ||
1021 | max_length: None, | ||
1022 | }, | ||
1023 | r#" | 1250 | r#" |
1024 | struct A(B); | 1251 | struct A(B); |
1025 | impl A { fn into_b(self) -> B { self.0 } } | 1252 | impl A { fn into_b(self) -> B { self.0 } } |
@@ -1123,140 +1350,6 @@ fn main() { | |||
1123 | } | 1350 | } |
1124 | 1351 | ||
1125 | #[test] | 1352 | #[test] |
1126 | fn incomplete_for_no_hint() { | ||
1127 | check( | ||
1128 | r#" | ||
1129 | fn main() { | ||
1130 | let data = &[1i32, 2, 3]; | ||
1131 | //^^^^ &[i32; 3] | ||
1132 | for i | ||
1133 | }"#, | ||
1134 | ); | ||
1135 | check( | ||
1136 | r#" | ||
1137 | pub struct Vec<T> {} | ||
1138 | |||
1139 | impl<T> Vec<T> { | ||
1140 | pub fn new() -> Self { Vec {} } | ||
1141 | pub fn push(&mut self, t: T) {} | ||
1142 | } | ||
1143 | |||
1144 | impl<T> IntoIterator for Vec<T> { | ||
1145 | type Item=T; | ||
1146 | } | ||
1147 | |||
1148 | fn main() { | ||
1149 | let mut data = Vec::new(); | ||
1150 | //^^^^^^^^ Vec<&str> | ||
1151 | data.push("foo"); | ||
1152 | for i in | ||
1153 | |||
1154 | println!("Unit expr"); | ||
1155 | } | ||
1156 | "#, | ||
1157 | ); | ||
1158 | } | ||
1159 | |||
1160 | #[test] | ||
1161 | fn complete_for_hint() { | ||
1162 | check( | ||
1163 | r#" | ||
1164 | pub struct Vec<T> {} | ||
1165 | |||
1166 | impl<T> Vec<T> { | ||
1167 | pub fn new() -> Self { Vec {} } | ||
1168 | pub fn push(&mut self, t: T) {} | ||
1169 | } | ||
1170 | |||
1171 | impl<T> IntoIterator for Vec<T> { | ||
1172 | type Item=T; | ||
1173 | } | ||
1174 | |||
1175 | fn main() { | ||
1176 | let mut data = Vec::new(); | ||
1177 | //^^^^^^^^ Vec<&str> | ||
1178 | data.push("foo"); | ||
1179 | for i in data { | ||
1180 | //^ &str | ||
1181 | let z = i; | ||
1182 | //^ &str | ||
1183 | } | ||
1184 | } | ||
1185 | "#, | ||
1186 | ); | ||
1187 | } | ||
1188 | |||
1189 | #[test] | ||
1190 | fn multi_dyn_trait_bounds() { | ||
1191 | check_with_config( | ||
1192 | InlayHintsConfig { | ||
1193 | type_hints: true, | ||
1194 | parameter_hints: false, | ||
1195 | chaining_hints: false, | ||
1196 | max_length: None, | ||
1197 | }, | ||
1198 | r#" | ||
1199 | pub struct Vec<T> {} | ||
1200 | |||
1201 | impl<T> Vec<T> { | ||
1202 | pub fn new() -> Self { Vec {} } | ||
1203 | } | ||
1204 | |||
1205 | pub struct Box<T> {} | ||
1206 | |||
1207 | trait Display {} | ||
1208 | trait Sync {} | ||
1209 | |||
1210 | fn main() { | ||
1211 | let _v = Vec::<Box<&(dyn Display + Sync)>>::new(); | ||
1212 | //^^ Vec<Box<&(dyn Display + Sync)>> | ||
1213 | let _v = Vec::<Box<*const (dyn Display + Sync)>>::new(); | ||
1214 | //^^ Vec<Box<*const (dyn Display + Sync)>> | ||
1215 | let _v = Vec::<Box<dyn Display + Sync>>::new(); | ||
1216 | //^^ Vec<Box<dyn Display + Sync>> | ||
1217 | } | ||
1218 | "#, | ||
1219 | ); | ||
1220 | } | ||
1221 | |||
1222 | #[test] | ||
1223 | fn shorten_iterator_hints() { | ||
1224 | check_with_config( | ||
1225 | InlayHintsConfig { | ||
1226 | parameter_hints: false, | ||
1227 | type_hints: true, | ||
1228 | chaining_hints: false, | ||
1229 | max_length: None, | ||
1230 | }, | ||
1231 | r#" | ||
1232 | use core::iter; | ||
1233 | |||
1234 | struct MyIter; | ||
1235 | |||
1236 | impl Iterator for MyIter { | ||
1237 | type Item = (); | ||
1238 | fn next(&mut self) -> Option<Self::Item> { | ||
1239 | None | ||
1240 | } | ||
1241 | } | ||
1242 | |||
1243 | fn main() { | ||
1244 | let _x = MyIter; | ||
1245 | //^^ MyIter | ||
1246 | let _x = iter::repeat(0); | ||
1247 | //^^ impl Iterator<Item = i32> | ||
1248 | fn generic<T: Clone>(t: T) { | ||
1249 | let _x = iter::repeat(t); | ||
1250 | //^^ impl Iterator<Item = T> | ||
1251 | let _chained = iter::repeat(t).take(10); | ||
1252 | //^^^^^^^^ impl Iterator<Item = T> | ||
1253 | } | ||
1254 | } | ||
1255 | "#, | ||
1256 | ); | ||
1257 | } | ||
1258 | |||
1259 | #[test] | ||
1260 | fn shorten_iterator_chaining_hints() { | 1353 | fn shorten_iterator_chaining_hints() { |
1261 | check_expect( | 1354 | check_expect( |
1262 | InlayHintsConfig { | 1355 | InlayHintsConfig { |
@@ -1311,158 +1404,4 @@ fn main() { | |||
1311 | "#]], | 1404 | "#]], |
1312 | ); | 1405 | ); |
1313 | } | 1406 | } |
1314 | |||
1315 | #[test] | ||
1316 | fn shorten_iterators_in_associated_params() { | ||
1317 | check_with_config( | ||
1318 | InlayHintsConfig { | ||
1319 | parameter_hints: false, | ||
1320 | type_hints: true, | ||
1321 | chaining_hints: false, | ||
1322 | max_length: None, | ||
1323 | }, | ||
1324 | r#" | ||
1325 | use core::iter; | ||
1326 | |||
1327 | pub struct SomeIter<T> {} | ||
1328 | |||
1329 | impl<T> SomeIter<T> { | ||
1330 | pub fn new() -> Self { SomeIter {} } | ||
1331 | pub fn push(&mut self, t: T) {} | ||
1332 | } | ||
1333 | |||
1334 | impl<T> Iterator for SomeIter<T> { | ||
1335 | type Item = T; | ||
1336 | fn next(&mut self) -> Option<Self::Item> { | ||
1337 | None | ||
1338 | } | ||
1339 | } | ||
1340 | |||
1341 | fn main() { | ||
1342 | let mut some_iter = SomeIter::new(); | ||
1343 | //^^^^^^^^^^^^^ SomeIter<Take<Repeat<i32>>> | ||
1344 | some_iter.push(iter::repeat(2).take(2)); | ||
1345 | let iter_of_iters = some_iter.take(2); | ||
1346 | //^^^^^^^^^^^^^ impl Iterator<Item = impl Iterator<Item = i32>> | ||
1347 | } | ||
1348 | "#, | ||
1349 | ); | ||
1350 | } | ||
1351 | |||
1352 | #[test] | ||
1353 | fn hide_param_hints_for_clones() { | ||
1354 | check_with_config( | ||
1355 | InlayHintsConfig { | ||
1356 | parameter_hints: true, | ||
1357 | type_hints: false, | ||
1358 | chaining_hints: false, | ||
1359 | max_length: None, | ||
1360 | }, | ||
1361 | r#" | ||
1362 | fn foo(bar: i32, baz: String, qux: f32) {} | ||
1363 | |||
1364 | fn main() { | ||
1365 | let bar = 3; | ||
1366 | let baz = &"baz"; | ||
1367 | let fez = 1.0; | ||
1368 | foo(bar.clone(), baz.clone(), fez.clone()); | ||
1369 | //^^^^^^^^^^^ qux | ||
1370 | } | ||
1371 | "#, | ||
1372 | ); | ||
1373 | } | ||
1374 | |||
1375 | #[test] | ||
1376 | fn infer_call_method_return_associated_types_with_generic() { | ||
1377 | check( | ||
1378 | r#" | ||
1379 | pub trait Default { | ||
1380 | fn default() -> Self; | ||
1381 | } | ||
1382 | pub trait Foo { | ||
1383 | type Bar: Default; | ||
1384 | } | ||
1385 | |||
1386 | pub fn quux<T: Foo>() -> T::Bar { | ||
1387 | let y = Default::default(); | ||
1388 | //^ <T as Foo>::Bar | ||
1389 | |||
1390 | y | ||
1391 | } | ||
1392 | "#, | ||
1393 | ); | ||
1394 | } | ||
1395 | |||
1396 | #[test] | ||
1397 | fn self_param_hints() { | ||
1398 | check( | ||
1399 | r#" | ||
1400 | struct Foo; | ||
1401 | |||
1402 | impl Foo { | ||
1403 | fn foo(self: Self) {} | ||
1404 | fn bar(self: &Self) {} | ||
1405 | } | ||
1406 | |||
1407 | fn main() { | ||
1408 | Foo::foo(Foo); | ||
1409 | //^^^ self | ||
1410 | Foo::bar(&Foo); | ||
1411 | //^^^^ self | ||
1412 | } | ||
1413 | "#, | ||
1414 | ) | ||
1415 | } | ||
1416 | |||
1417 | #[test] | ||
1418 | fn fn_hints() { | ||
1419 | check( | ||
1420 | r#" | ||
1421 | trait Sized {} | ||
1422 | |||
1423 | fn foo() -> impl Fn() { loop {} } | ||
1424 | fn foo1() -> impl Fn(f64) { loop {} } | ||
1425 | fn foo2() -> impl Fn(f64, f64) { loop {} } | ||
1426 | fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} } | ||
1427 | fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} } | ||
1428 | fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} } | ||
1429 | fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} } | ||
1430 | fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} } | ||
1431 | |||
1432 | fn main() { | ||
1433 | let foo = foo(); | ||
1434 | // ^^^ impl Fn() | ||
1435 | let foo = foo1(); | ||
1436 | // ^^^ impl Fn(f64) | ||
1437 | let foo = foo2(); | ||
1438 | // ^^^ impl Fn(f64, f64) | ||
1439 | let foo = foo3(); | ||
1440 | // ^^^ impl Fn(f64, f64) -> u32 | ||
1441 | let foo = foo4(); | ||
1442 | // ^^^ &dyn Fn(f64, f64) -> u32 | ||
1443 | let foo = foo5(); | ||
1444 | // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32 | ||
1445 | let foo = foo6(); | ||
1446 | // ^^^ impl Fn(f64, f64) -> u32 + Sized | ||
1447 | let foo = foo7(); | ||
1448 | // ^^^ *const (impl Fn(f64, f64) -> u32 + Sized) | ||
1449 | } | ||
1450 | "#, | ||
1451 | ) | ||
1452 | } | ||
1453 | |||
1454 | #[test] | ||
1455 | fn param_name_hints_show_for_literals() { | ||
1456 | check( | ||
1457 | r#"pub fn test(a: i32, b: i32) -> [i32; 2] { [a, b] } | ||
1458 | fn main() { | ||
1459 | test( | ||
1460 | 0x0fab272b, | ||
1461 | //^^^^^^^^^^ a | ||
1462 | 0x0fab272b | ||
1463 | //^^^^^^^^^^ b | ||
1464 | ); | ||
1465 | }"#, | ||
1466 | ) | ||
1467 | } | ||
1468 | } | 1407 | } |
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs index c67ccd1a9..93d3760bf 100644 --- a/crates/ide/src/join_lines.rs +++ b/crates/ide/src/join_lines.rs | |||
@@ -60,7 +60,7 @@ fn remove_newlines(edit: &mut TextEditBuilder, token: &SyntaxToken, range: TextR | |||
60 | let pos: TextSize = (pos as u32).into(); | 60 | let pos: TextSize = (pos as u32).into(); |
61 | let offset = token.text_range().start() + range.start() + pos; | 61 | let offset = token.text_range().start() + range.start() + pos; |
62 | if !edit.invalidates_offset(offset) { | 62 | if !edit.invalidates_offset(offset) { |
63 | remove_newline(edit, &token, offset); | 63 | remove_newline(edit, token, offset); |
64 | } | 64 | } |
65 | } | 65 | } |
66 | } | 66 | } |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 97c9e5d2b..0511efae3 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -282,20 +282,20 @@ impl Analysis { | |||
282 | file_id: FileId, | 282 | file_id: FileId, |
283 | text_range: Option<TextRange>, | 283 | text_range: Option<TextRange>, |
284 | ) -> Cancellable<String> { | 284 | ) -> Cancellable<String> { |
285 | self.with_db(|db| syntax_tree::syntax_tree(&db, file_id, text_range)) | 285 | self.with_db(|db| syntax_tree::syntax_tree(db, file_id, text_range)) |
286 | } | 286 | } |
287 | 287 | ||
288 | pub fn view_hir(&self, position: FilePosition) -> Cancellable<String> { | 288 | pub fn view_hir(&self, position: FilePosition) -> Cancellable<String> { |
289 | self.with_db(|db| view_hir::view_hir(&db, position)) | 289 | self.with_db(|db| view_hir::view_hir(db, position)) |
290 | } | 290 | } |
291 | 291 | ||
292 | pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> { | 292 | pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> { |
293 | self.with_db(|db| view_item_tree::view_item_tree(&db, file_id)) | 293 | self.with_db(|db| view_item_tree::view_item_tree(db, file_id)) |
294 | } | 294 | } |
295 | 295 | ||
296 | /// Renders the crate graph to GraphViz "dot" syntax. | 296 | /// Renders the crate graph to GraphViz "dot" syntax. |
297 | pub fn view_crate_graph(&self) -> Cancellable<Result<String, String>> { | 297 | pub fn view_crate_graph(&self) -> Cancellable<Result<String, String>> { |
298 | self.with_db(|db| view_crate_graph::view_crate_graph(&db)) | 298 | self.with_db(|db| view_crate_graph::view_crate_graph(db)) |
299 | } | 299 | } |
300 | 300 | ||
301 | pub fn expand_macro(&self, position: FilePosition) -> Cancellable<Option<ExpandedMacro>> { | 301 | pub fn expand_macro(&self, position: FilePosition) -> Cancellable<Option<ExpandedMacro>> { |
@@ -315,7 +315,7 @@ impl Analysis { | |||
315 | /// up minor stuff like continuing the comment. | 315 | /// up minor stuff like continuing the comment. |
316 | /// The edit will be a snippet (with `$0`). | 316 | /// The edit will be a snippet (with `$0`). |
317 | pub fn on_enter(&self, position: FilePosition) -> Cancellable<Option<TextEdit>> { | 317 | pub fn on_enter(&self, position: FilePosition) -> Cancellable<Option<TextEdit>> { |
318 | self.with_db(|db| typing::on_enter(&db, position)) | 318 | self.with_db(|db| typing::on_enter(db, position)) |
319 | } | 319 | } |
320 | 320 | ||
321 | /// Returns an edit which should be applied after a character was typed. | 321 | /// Returns an edit which should be applied after a character was typed. |
@@ -331,7 +331,7 @@ impl Analysis { | |||
331 | if !typing::TRIGGER_CHARS.contains(char_typed) { | 331 | if !typing::TRIGGER_CHARS.contains(char_typed) { |
332 | return Ok(None); | 332 | return Ok(None); |
333 | } | 333 | } |
334 | self.with_db(|db| typing::on_char_typed(&db, position, char_typed)) | 334 | self.with_db(|db| typing::on_char_typed(db, position, char_typed)) |
335 | } | 335 | } |
336 | 336 | ||
337 | /// Returns a tree representation of symbols in the file. Useful to draw a | 337 | /// Returns a tree representation of symbols in the file. Useful to draw a |
diff --git a/crates/ide/src/prime_caches.rs b/crates/ide/src/prime_caches.rs index d912a01b8..36801c964 100644 --- a/crates/ide/src/prime_caches.rs +++ b/crates/ide/src/prime_caches.rs | |||
@@ -33,14 +33,15 @@ pub(crate) fn prime_caches(db: &RootDatabase, cb: &(dyn Fn(PrimeCachesProgress) | |||
33 | // FIXME: This would be easy to parallelize, since it's in the ideal ordering for that. | 33 | // FIXME: This would be easy to parallelize, since it's in the ideal ordering for that. |
34 | // Unfortunately rayon prevents panics from propagation out of a `scope`, which breaks | 34 | // Unfortunately rayon prevents panics from propagation out of a `scope`, which breaks |
35 | // cancellation, so we cannot use rayon. | 35 | // cancellation, so we cannot use rayon. |
36 | for (i, krate) in topo.iter().enumerate() { | 36 | for (i, &crate_id) in topo.iter().enumerate() { |
37 | let crate_name = graph[*krate].display_name.as_deref().unwrap_or_default().to_string(); | 37 | let crate_name = graph[crate_id].display_name.as_deref().unwrap_or_default().to_string(); |
38 | 38 | ||
39 | cb(PrimeCachesProgress::StartedOnCrate { | 39 | cb(PrimeCachesProgress::StartedOnCrate { |
40 | on_crate: crate_name, | 40 | on_crate: crate_name, |
41 | n_done: i, | 41 | n_done: i, |
42 | n_total: topo.len(), | 42 | n_total: topo.len(), |
43 | }); | 43 | }); |
44 | db.crate_def_map(*krate); | 44 | db.crate_def_map(crate_id); |
45 | db.import_map(crate_id); | ||
45 | } | 46 | } |
46 | } | 47 | } |
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index ae492a264..a0fdead2c 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -62,7 +62,7 @@ pub(crate) fn find_all_refs( | |||
62 | if let Some(name) = get_name_of_item_declaration(&syntax, position) { | 62 | if let Some(name) = get_name_of_item_declaration(&syntax, position) { |
63 | (NameClass::classify(sema, &name)?.referenced_or_defined(sema.db), true) | 63 | (NameClass::classify(sema, &name)?.referenced_or_defined(sema.db), true) |
64 | } else { | 64 | } else { |
65 | (find_def(&sema, &syntax, position)?, false) | 65 | (find_def(sema, &syntax, position)?, false) |
66 | }; | 66 | }; |
67 | 67 | ||
68 | let mut usages = def.usages(sema).set_scope(search_scope).include_self_refs().all(); | 68 | let mut usages = def.usages(sema).set_scope(search_scope).include_self_refs().all(); |
@@ -1380,4 +1380,24 @@ lib::foo!(); | |||
1380 | "#]], | 1380 | "#]], |
1381 | ); | 1381 | ); |
1382 | } | 1382 | } |
1383 | |||
1384 | #[test] | ||
1385 | fn macro_doesnt_reference_attribute_on_call() { | ||
1386 | check( | ||
1387 | r#" | ||
1388 | macro_rules! m { | ||
1389 | () => {}; | ||
1390 | } | ||
1391 | |||
1392 | #[proc_macro_test::attr_noop] | ||
1393 | m$0!(); | ||
1394 | |||
1395 | "#, | ||
1396 | expect![[r#" | ||
1397 | m Macro FileId(0) 0..32 13..14 | ||
1398 | |||
1399 | FileId(0) 64..65 | ||
1400 | "#]], | ||
1401 | ); | ||
1402 | } | ||
1383 | } | 1403 | } |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 01fe3a1a1..50cc1f963 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -64,7 +64,7 @@ pub(crate) fn prepare_rename( | |||
64 | } | 64 | } |
65 | }; | 65 | }; |
66 | let name_like = sema | 66 | let name_like = sema |
67 | .find_node_at_offset_with_descend(&syntax, position.offset) | 67 | .find_node_at_offset_with_descend(syntax, position.offset) |
68 | .ok_or_else(|| format_err!("No references found at position"))?; | 68 | .ok_or_else(|| format_err!("No references found at position"))?; |
69 | let node = match &name_like { | 69 | let node = match &name_like { |
70 | ast::NameLike::Name(it) => it.syntax(), | 70 | ast::NameLike::Name(it) => it.syntax(), |
@@ -104,7 +104,7 @@ pub(crate) fn rename_with_semantics( | |||
104 | 104 | ||
105 | let def = find_definition(sema, syntax, position)?; | 105 | let def = find_definition(sema, syntax, position)?; |
106 | match def { | 106 | match def { |
107 | Definition::ModuleDef(ModuleDef::Module(module)) => rename_mod(&sema, module, new_name), | 107 | Definition::ModuleDef(ModuleDef::Module(module)) => rename_mod(sema, module, new_name), |
108 | Definition::SelfType(_) => bail!("Cannot rename `Self`"), | 108 | Definition::SelfType(_) => bail!("Cannot rename `Self`"), |
109 | Definition::ModuleDef(ModuleDef::BuiltinType(_)) => bail!("Cannot rename builtin type"), | 109 | Definition::ModuleDef(ModuleDef::BuiltinType(_)) => bail!("Cannot rename builtin type"), |
110 | def => rename_reference(sema, def, new_name), | 110 | def => rename_reference(sema, def, new_name), |
@@ -170,7 +170,17 @@ fn find_definition( | |||
170 | NameClass::classify(sema, &name).map(|class| class.referenced_or_defined(sema.db)) | 170 | NameClass::classify(sema, &name).map(|class| class.referenced_or_defined(sema.db)) |
171 | } | 171 | } |
172 | ast::NameLike::NameRef(name_ref) => { | 172 | ast::NameLike::NameRef(name_ref) => { |
173 | NameRefClass::classify(sema, &name_ref).map(|class| class.referenced(sema.db)) | 173 | if let Some(def) = |
174 | NameRefClass::classify(sema, &name_ref).map(|class| class.referenced(sema.db)) | ||
175 | { | ||
176 | // if the name differs from the definitions name it has to be an alias | ||
177 | if def.name(sema.db).map_or(false, |it| it.to_string() != name_ref.text()) { | ||
178 | bail!("Renaming aliases is currently unsupported"); | ||
179 | } | ||
180 | Some(def) | ||
181 | } else { | ||
182 | None | ||
183 | } | ||
174 | } | 184 | } |
175 | ast::NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime) | 185 | ast::NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime) |
176 | .map(|class| NameRefClass::referenced(class, sema.db)) | 186 | .map(|class| NameRefClass::referenced(class, sema.db)) |
@@ -229,7 +239,7 @@ fn rename_mod( | |||
229 | 239 | ||
230 | fn rename_reference( | 240 | fn rename_reference( |
231 | sema: &Semantics<RootDatabase>, | 241 | sema: &Semantics<RootDatabase>, |
232 | def: Definition, | 242 | mut def: Definition, |
233 | new_name: &str, | 243 | new_name: &str, |
234 | ) -> RenameResult<SourceChange> { | 244 | ) -> RenameResult<SourceChange> { |
235 | let ident_kind = check_identifier(new_name)?; | 245 | let ident_kind = check_identifier(new_name)?; |
@@ -275,14 +285,45 @@ fn rename_reference( | |||
275 | } | 285 | } |
276 | } | 286 | } |
277 | 287 | ||
288 | def = match def { | ||
289 | // HACK: resolve trait impl items to the item def of the trait definition | ||
290 | // so that we properly resolve all trait item references | ||
291 | Definition::ModuleDef(mod_def) => mod_def | ||
292 | .as_assoc_item(sema.db) | ||
293 | .and_then(|it| it.containing_trait_impl(sema.db)) | ||
294 | .and_then(|it| { | ||
295 | it.items(sema.db).into_iter().find_map(|it| match (it, mod_def) { | ||
296 | (hir::AssocItem::Function(trait_func), ModuleDef::Function(func)) | ||
297 | if trait_func.name(sema.db) == func.name(sema.db) => | ||
298 | { | ||
299 | Some(Definition::ModuleDef(ModuleDef::Function(trait_func))) | ||
300 | } | ||
301 | (hir::AssocItem::Const(trait_konst), ModuleDef::Const(konst)) | ||
302 | if trait_konst.name(sema.db) == konst.name(sema.db) => | ||
303 | { | ||
304 | Some(Definition::ModuleDef(ModuleDef::Const(trait_konst))) | ||
305 | } | ||
306 | ( | ||
307 | hir::AssocItem::TypeAlias(trait_type_alias), | ||
308 | ModuleDef::TypeAlias(type_alias), | ||
309 | ) if trait_type_alias.name(sema.db) == type_alias.name(sema.db) => { | ||
310 | Some(Definition::ModuleDef(ModuleDef::TypeAlias(trait_type_alias))) | ||
311 | } | ||
312 | _ => None, | ||
313 | }) | ||
314 | }) | ||
315 | .unwrap_or(def), | ||
316 | _ => def, | ||
317 | }; | ||
278 | let usages = def.usages(sema).all(); | 318 | let usages = def.usages(sema).all(); |
319 | |||
279 | if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { | 320 | if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { |
280 | cov_mark::hit!(rename_underscore_multiple); | 321 | cov_mark::hit!(rename_underscore_multiple); |
281 | bail!("Cannot rename reference to `_` as it is being referenced multiple times"); | 322 | bail!("Cannot rename reference to `_` as it is being referenced multiple times"); |
282 | } | 323 | } |
283 | let mut source_change = SourceChange::default(); | 324 | let mut source_change = SourceChange::default(); |
284 | source_change.extend(usages.iter().map(|(&file_id, references)| { | 325 | source_change.extend(usages.iter().map(|(&file_id, references)| { |
285 | (file_id, source_edit_from_references(&references, def, new_name)) | 326 | (file_id, source_edit_from_references(references, def, new_name)) |
286 | })); | 327 | })); |
287 | 328 | ||
288 | let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; | 329 | let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; |
@@ -372,7 +413,7 @@ fn rename_self_to_param( | |||
372 | let mut source_change = SourceChange::default(); | 413 | let mut source_change = SourceChange::default(); |
373 | source_change.insert_source_edit(file_id.original_file(sema.db), edit); | 414 | source_change.insert_source_edit(file_id.original_file(sema.db), edit); |
374 | source_change.extend(usages.iter().map(|(&file_id, references)| { | 415 | source_change.extend(usages.iter().map(|(&file_id, references)| { |
375 | (file_id, source_edit_from_references(&references, def, new_name)) | 416 | (file_id, source_edit_from_references(references, def, new_name)) |
376 | })); | 417 | })); |
377 | Ok(source_change) | 418 | Ok(source_change) |
378 | } | 419 | } |
@@ -385,7 +426,7 @@ fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Opt | |||
385 | None | 426 | None |
386 | } | 427 | } |
387 | 428 | ||
388 | let impl_def = self_param.syntax().ancestors().find_map(|it| ast::Impl::cast(it))?; | 429 | let impl_def = self_param.syntax().ancestors().find_map(ast::Impl::cast)?; |
389 | let type_name = target_type_name(&impl_def)?; | 430 | let type_name = target_type_name(&impl_def)?; |
390 | 431 | ||
391 | let mut replacement_text = String::from(new_name); | 432 | let mut replacement_text = String::from(new_name); |
@@ -1907,4 +1948,157 @@ impl Fo0 where Self: {} | |||
1907 | "#, | 1948 | "#, |
1908 | ); | 1949 | ); |
1909 | } | 1950 | } |
1951 | |||
1952 | #[test] | ||
1953 | fn test_rename_fails_on_aliases() { | ||
1954 | check( | ||
1955 | "Baz", | ||
1956 | r#" | ||
1957 | struct Foo; | ||
1958 | use Foo as Bar$0; | ||
1959 | "#, | ||
1960 | "error: Renaming aliases is currently unsupported", | ||
1961 | ); | ||
1962 | check( | ||
1963 | "Baz", | ||
1964 | r#" | ||
1965 | struct Foo; | ||
1966 | use Foo as Bar; | ||
1967 | use Bar$0; | ||
1968 | "#, | ||
1969 | "error: Renaming aliases is currently unsupported", | ||
1970 | ); | ||
1971 | } | ||
1972 | |||
1973 | #[test] | ||
1974 | fn test_rename_trait_method() { | ||
1975 | let res = r" | ||
1976 | trait Foo { | ||
1977 | fn foo(&self) { | ||
1978 | self.foo(); | ||
1979 | } | ||
1980 | } | ||
1981 | |||
1982 | impl Foo for () { | ||
1983 | fn foo(&self) { | ||
1984 | self.foo(); | ||
1985 | } | ||
1986 | }"; | ||
1987 | check( | ||
1988 | "foo", | ||
1989 | r#" | ||
1990 | trait Foo { | ||
1991 | fn bar$0(&self) { | ||
1992 | self.bar(); | ||
1993 | } | ||
1994 | } | ||
1995 | |||
1996 | impl Foo for () { | ||
1997 | fn bar(&self) { | ||
1998 | self.bar(); | ||
1999 | } | ||
2000 | }"#, | ||
2001 | res, | ||
2002 | ); | ||
2003 | check( | ||
2004 | "foo", | ||
2005 | r#" | ||
2006 | trait Foo { | ||
2007 | fn bar(&self) { | ||
2008 | self.bar$0(); | ||
2009 | } | ||
2010 | } | ||
2011 | |||
2012 | impl Foo for () { | ||
2013 | fn bar(&self) { | ||
2014 | self.bar(); | ||
2015 | } | ||
2016 | }"#, | ||
2017 | res, | ||
2018 | ); | ||
2019 | check( | ||
2020 | "foo", | ||
2021 | r#" | ||
2022 | trait Foo { | ||
2023 | fn bar(&self) { | ||
2024 | self.bar(); | ||
2025 | } | ||
2026 | } | ||
2027 | |||
2028 | impl Foo for () { | ||
2029 | fn bar$0(&self) { | ||
2030 | self.bar(); | ||
2031 | } | ||
2032 | }"#, | ||
2033 | res, | ||
2034 | ); | ||
2035 | check( | ||
2036 | "foo", | ||
2037 | r#" | ||
2038 | trait Foo { | ||
2039 | fn bar(&self) { | ||
2040 | self.bar(); | ||
2041 | } | ||
2042 | } | ||
2043 | |||
2044 | impl Foo for () { | ||
2045 | fn bar(&self) { | ||
2046 | self.bar$0(); | ||
2047 | } | ||
2048 | }"#, | ||
2049 | res, | ||
2050 | ); | ||
2051 | } | ||
2052 | |||
2053 | #[test] | ||
2054 | fn test_rename_trait_const() { | ||
2055 | let res = r" | ||
2056 | trait Foo { | ||
2057 | const FOO: (); | ||
2058 | } | ||
2059 | |||
2060 | impl Foo for () { | ||
2061 | const FOO: (); | ||
2062 | } | ||
2063 | fn f() { <()>::FOO; }"; | ||
2064 | check( | ||
2065 | "FOO", | ||
2066 | r#" | ||
2067 | trait Foo { | ||
2068 | const BAR$0: (); | ||
2069 | } | ||
2070 | |||
2071 | impl Foo for () { | ||
2072 | const BAR: (); | ||
2073 | } | ||
2074 | fn f() { <()>::BAR; }"#, | ||
2075 | res, | ||
2076 | ); | ||
2077 | check( | ||
2078 | "FOO", | ||
2079 | r#" | ||
2080 | trait Foo { | ||
2081 | const BAR: (); | ||
2082 | } | ||
2083 | |||
2084 | impl Foo for () { | ||
2085 | const BAR$0: (); | ||
2086 | } | ||
2087 | fn f() { <()>::BAR; }"#, | ||
2088 | res, | ||
2089 | ); | ||
2090 | check( | ||
2091 | "FOO", | ||
2092 | r#" | ||
2093 | trait Foo { | ||
2094 | const BAR: (); | ||
2095 | } | ||
2096 | |||
2097 | impl Foo for () { | ||
2098 | const BAR: (); | ||
2099 | } | ||
2100 | fn f() { <()>::BAR$0; }"#, | ||
2101 | res, | ||
2102 | ); | ||
2103 | } | ||
1910 | } | 2104 | } |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 552054951..03faabadc 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -158,7 +158,7 @@ fn find_related_tests( | |||
158 | search_scope: Option<SearchScope>, | 158 | search_scope: Option<SearchScope>, |
159 | tests: &mut FxHashSet<Runnable>, | 159 | tests: &mut FxHashSet<Runnable>, |
160 | ) { | 160 | ) { |
161 | if let Some(refs) = references::find_all_refs(&sema, position, search_scope) { | 161 | if let Some(refs) = references::find_all_refs(sema, position, search_scope) { |
162 | for (file_id, refs) in refs.references { | 162 | for (file_id, refs) in refs.references { |
163 | let file = sema.parse(file_id); | 163 | let file = sema.parse(file_id); |
164 | let file = file.syntax(); | 164 | let file = file.syntax(); |
@@ -169,10 +169,10 @@ fn find_related_tests( | |||
169 | }); | 169 | }); |
170 | 170 | ||
171 | for fn_def in functions { | 171 | for fn_def in functions { |
172 | if let Some(runnable) = as_test_runnable(&sema, &fn_def) { | 172 | if let Some(runnable) = as_test_runnable(sema, &fn_def) { |
173 | // direct test | 173 | // direct test |
174 | tests.insert(runnable); | 174 | tests.insert(runnable); |
175 | } else if let Some(module) = parent_test_module(&sema, &fn_def) { | 175 | } else if let Some(module) = parent_test_module(sema, &fn_def) { |
176 | // indirect test | 176 | // indirect test |
177 | find_related_tests_in_module(sema, &fn_def, &module, tests); | 177 | find_related_tests_in_module(sema, &fn_def, &module, tests); |
178 | } | 178 | } |
@@ -203,7 +203,7 @@ fn find_related_tests_in_module( | |||
203 | } | 203 | } |
204 | 204 | ||
205 | fn as_test_runnable(sema: &Semantics<RootDatabase>, fn_def: &ast::Fn) -> Option<Runnable> { | 205 | fn as_test_runnable(sema: &Semantics<RootDatabase>, fn_def: &ast::Fn) -> Option<Runnable> { |
206 | if test_related_attribute(&fn_def).is_some() { | 206 | if test_related_attribute(fn_def).is_some() { |
207 | let function = sema.to_def(fn_def)?; | 207 | let function = sema.to_def(fn_def)?; |
208 | runnable_fn(sema, function) | 208 | runnable_fn(sema, function) |
209 | } else { | 209 | } else { |
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 79c2f4a1e..e186b82b7 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs | |||
@@ -192,6 +192,7 @@ fn traverse( | |||
192 | let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); | 192 | let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); |
193 | 193 | ||
194 | let mut current_macro_call: Option<ast::MacroCall> = None; | 194 | let mut current_macro_call: Option<ast::MacroCall> = None; |
195 | let mut current_attr_macro_call = None; | ||
195 | let mut current_macro: Option<ast::Macro> = None; | 196 | let mut current_macro: Option<ast::Macro> = None; |
196 | let mut macro_highlighter = MacroHighlighter::default(); | 197 | let mut macro_highlighter = MacroHighlighter::default(); |
197 | let mut inside_attribute = false; | 198 | let mut inside_attribute = false; |
@@ -227,6 +228,19 @@ fn traverse( | |||
227 | } | 228 | } |
228 | _ => (), | 229 | _ => (), |
229 | } | 230 | } |
231 | match event.clone().map(|it| it.into_node().and_then(ast::Item::cast)) { | ||
232 | WalkEvent::Enter(Some(item)) => { | ||
233 | if sema.is_attr_macro_call(&item) { | ||
234 | current_attr_macro_call = Some(item); | ||
235 | } | ||
236 | } | ||
237 | WalkEvent::Leave(Some(item)) => { | ||
238 | if current_attr_macro_call == Some(item) { | ||
239 | current_attr_macro_call = None; | ||
240 | } | ||
241 | } | ||
242 | _ => (), | ||
243 | } | ||
230 | 244 | ||
231 | match event.clone().map(|it| it.into_node().and_then(ast::Macro::cast)) { | 245 | match event.clone().map(|it| it.into_node().and_then(ast::Macro::cast)) { |
232 | WalkEvent::Enter(Some(mac)) => { | 246 | WalkEvent::Enter(Some(mac)) => { |
@@ -286,6 +300,22 @@ fn traverse( | |||
286 | } | 300 | } |
287 | None => token.into(), | 301 | None => token.into(), |
288 | } | 302 | } |
303 | } else if current_attr_macro_call.is_some() { | ||
304 | let token = match element.clone().into_token() { | ||
305 | Some(it) => it, | ||
306 | _ => continue, | ||
307 | }; | ||
308 | let token = sema.descend_into_macros(token.clone()); | ||
309 | match token.parent() { | ||
310 | Some(parent) => { | ||
311 | // We only care Name and Name_ref | ||
312 | match (token.kind(), parent.kind()) { | ||
313 | (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(), | ||
314 | _ => token.into(), | ||
315 | } | ||
316 | } | ||
317 | None => token.into(), | ||
318 | } | ||
289 | } else { | 319 | } else { |
290 | element.clone() | 320 | element.clone() |
291 | }; | 321 | }; |
@@ -293,7 +323,7 @@ fn traverse( | |||
293 | if let Some(token) = element.as_token().cloned().and_then(ast::String::cast) { | 323 | if let Some(token) = element.as_token().cloned().and_then(ast::String::cast) { |
294 | if token.is_raw() { | 324 | if token.is_raw() { |
295 | let expanded = element_to_highlight.as_token().unwrap().clone(); | 325 | let expanded = element_to_highlight.as_token().unwrap().clone(); |
296 | if inject::ra_fixture(hl, &sema, token, expanded).is_some() { | 326 | if inject::ra_fixture(hl, sema, token, expanded).is_some() { |
297 | continue; | 327 | continue; |
298 | } | 328 | } |
299 | } | 329 | } |
@@ -304,7 +334,7 @@ fn traverse( | |||
304 | } | 334 | } |
305 | 335 | ||
306 | if let Some((mut highlight, binding_hash)) = highlight::element( | 336 | if let Some((mut highlight, binding_hash)) = highlight::element( |
307 | &sema, | 337 | sema, |
308 | krate, | 338 | krate, |
309 | &mut bindings_shadow_count, | 339 | &mut bindings_shadow_count, |
310 | syntactic_name_ref_highlighting, | 340 | syntactic_name_ref_highlighting, |
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index 9503c936d..7a53268e8 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs | |||
@@ -131,6 +131,9 @@ pub(super) fn element( | |||
131 | } | 131 | } |
132 | STRING | BYTE_STRING => HlTag::StringLiteral.into(), | 132 | STRING | BYTE_STRING => HlTag::StringLiteral.into(), |
133 | ATTR => HlTag::Attribute.into(), | 133 | ATTR => HlTag::Attribute.into(), |
134 | INT_NUMBER if element.ancestors().nth(1).map_or(false, |it| it.kind() == FIELD_EXPR) => { | ||
135 | SymbolKind::Field.into() | ||
136 | } | ||
134 | INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(), | 137 | INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(), |
135 | BYTE => HlTag::ByteLiteral.into(), | 138 | BYTE => HlTag::ByteLiteral.into(), |
136 | CHAR => HlTag::CharLiteral.into(), | 139 | CHAR => HlTag::CharLiteral.into(), |
@@ -446,12 +449,12 @@ fn highlight_method_call( | |||
446 | krate: Option<hir::Crate>, | 449 | krate: Option<hir::Crate>, |
447 | method_call: &ast::MethodCallExpr, | 450 | method_call: &ast::MethodCallExpr, |
448 | ) -> Option<Highlight> { | 451 | ) -> Option<Highlight> { |
449 | let func = sema.resolve_method_call(&method_call)?; | 452 | let func = sema.resolve_method_call(method_call)?; |
450 | 453 | ||
451 | let mut h = SymbolKind::Function.into(); | 454 | let mut h = SymbolKind::Function.into(); |
452 | h |= HlMod::Associated; | 455 | h |= HlMod::Associated; |
453 | 456 | ||
454 | if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) { | 457 | if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(method_call) { |
455 | h |= HlMod::Unsafe; | 458 | h |= HlMod::Unsafe; |
456 | } | 459 | } |
457 | if func.is_async(sema.db) { | 460 | if func.is_async(sema.db) { |
@@ -523,11 +526,9 @@ fn highlight_name_ref_by_syntax( | |||
523 | }; | 526 | }; |
524 | 527 | ||
525 | match parent.kind() { | 528 | match parent.kind() { |
526 | METHOD_CALL_EXPR => { | 529 | METHOD_CALL_EXPR => ast::MethodCallExpr::cast(parent) |
527 | return ast::MethodCallExpr::cast(parent) | 530 | .and_then(|it| highlight_method_call(sema, krate, &it)) |
528 | .and_then(|it| highlight_method_call(sema, krate, &it)) | 531 | .unwrap_or_else(|| SymbolKind::Function.into()), |
529 | .unwrap_or_else(|| SymbolKind::Function.into()); | ||
530 | } | ||
531 | FIELD_EXPR => { | 532 | FIELD_EXPR => { |
532 | let h = HlTag::Symbol(SymbolKind::Field); | 533 | let h = HlTag::Symbol(SymbolKind::Field); |
533 | let is_union = ast::FieldExpr::cast(parent) | 534 | let is_union = ast::FieldExpr::cast(parent) |
diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs index 5327af845..478facfee 100644 --- a/crates/ide/src/syntax_highlighting/html.rs +++ b/crates/ide/src/syntax_highlighting/html.rs | |||
@@ -23,7 +23,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo | |||
23 | let hl_ranges = highlight(db, file_id, None, false); | 23 | let hl_ranges = highlight(db, file_id, None, false); |
24 | let text = parse.tree().syntax().to_string(); | 24 | let text = parse.tree().syntax().to_string(); |
25 | let mut buf = String::new(); | 25 | let mut buf = String::new(); |
26 | buf.push_str(&STYLE); | 26 | buf.push_str(STYLE); |
27 | buf.push_str("<pre><code>"); | 27 | buf.push_str("<pre><code>"); |
28 | for r in &hl_ranges { | 28 | for r in &hl_ranges { |
29 | let chunk = html_escape(&text[r.range]); | 29 | let chunk = html_escape(&text[r.range]); |
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 4269d339e..ec43c8579 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs | |||
@@ -23,7 +23,7 @@ pub(super) fn ra_fixture( | |||
23 | literal: ast::String, | 23 | literal: ast::String, |
24 | expanded: SyntaxToken, | 24 | expanded: SyntaxToken, |
25 | ) -> Option<()> { | 25 | ) -> Option<()> { |
26 | let active_parameter = ActiveParameter::at_token(&sema, expanded)?; | 26 | let active_parameter = ActiveParameter::at_token(sema, expanded)?; |
27 | if !active_parameter.ident().map_or(false, |name| name.text().starts_with("ra_fixture")) { | 27 | if !active_parameter.ident().map_or(false, |name| name.text().starts_with("ra_fixture")) { |
28 | return None; | 28 | return None; |
29 | } | 29 | } |
@@ -124,7 +124,7 @@ pub(super) fn doc_comment( | |||
124 | } | 124 | } |
125 | 125 | ||
126 | for attr in attributes.by_key("doc").attrs() { | 126 | for attr in attributes.by_key("doc").attrs() { |
127 | let InFile { file_id, value: src } = attrs_source_map.source_of(&attr); | 127 | let InFile { file_id, value: src } = attrs_source_map.source_of(attr); |
128 | if file_id != node.file_id { | 128 | if file_id != node.file_id { |
129 | continue; | 129 | continue; |
130 | } | 130 | } |
@@ -232,7 +232,7 @@ fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option<ast::Stri | |||
232 | string.text().get(1..string.text().len() - 1).map_or(false, |it| it == text) | 232 | string.text().get(1..string.text().len() - 1).map_or(false, |it| it == text) |
233 | }) | 233 | }) |
234 | } | 234 | } |
235 | _ => return None, | 235 | _ => None, |
236 | } | 236 | } |
237 | } | 237 | } |
238 | 238 | ||
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index 0264e39a3..59f1e8e4c 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html | |||
@@ -148,6 +148,10 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
148 | <span class="brace">}</span> | 148 | <span class="brace">}</span> |
149 | <span class="brace">}</span> | 149 | <span class="brace">}</span> |
150 | 150 | ||
151 | <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">dont_color_me_braces</span> <span class="brace">{</span> | ||
152 | <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="brace">{</span><span class="numeric_literal">0</span><span class="brace">}</span> | ||
153 | <span class="brace">}</span> | ||
154 | |||
151 | <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span> | 155 | <span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span> |
152 | <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="brace">{</span> | 156 | <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="brace">{</span> |
153 | <span class="punctuation">$</span>expr | 157 | <span class="punctuation">$</span>expr |
@@ -171,6 +175,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
171 | <span class="comment">// comment</span> | 175 | <span class="comment">// comment</span> |
172 | <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> | 176 | <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> |
173 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello, {}!"</span><span class="comma">,</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span> | 177 | <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello, {}!"</span><span class="comma">,</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
178 | <span class="macro">dont_color_me_braces!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> | ||
174 | 179 | ||
175 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> <span class="operator">=</span> <span class="unresolved_reference">Vec</span><span class="operator">::</span><span class="unresolved_reference">new</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> | 180 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> <span class="operator">=</span> <span class="unresolved_reference">Vec</span><span class="operator">::</span><span class="unresolved_reference">new</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
176 | <span class="keyword control">if</span> <span class="bool_literal">true</span> <span class="brace">{</span> | 181 | <span class="keyword control">if</span> <span class="bool_literal">true</span> <span class="brace">{</span> |
@@ -210,8 +215,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
210 | <span class="keyword">let</span> <span class="variable callable declaration">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="semicolon">;</span> | 215 | <span class="keyword">let</span> <span class="variable callable declaration">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="semicolon">;</span> |
211 | <span class="keyword">let</span> <span class="variable callable declaration">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function associated">baz</span><span class="semicolon">;</span> | 216 | <span class="keyword">let</span> <span class="variable callable declaration">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function associated">baz</span><span class="semicolon">;</span> |
212 | 217 | ||
213 | <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="semicolon">;</span> | 218 | <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="comma">,</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
214 | <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="semicolon">;</span> | 219 | <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="operator">.</span><span class="field">0</span><span class="semicolon">;</span> |
215 | 220 | ||
216 | <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="logical">!</span><span class="bool_literal">true</span><span class="semicolon">;</span> | 221 | <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="logical">!</span><span class="bool_literal">true</span><span class="semicolon">;</span> |
217 | 222 | ||
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 662b53481..f7d8334a0 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -122,6 +122,10 @@ def_fn! { | |||
122 | } | 122 | } |
123 | } | 123 | } |
124 | 124 | ||
125 | macro_rules! dont_color_me_braces { | ||
126 | () => {0} | ||
127 | } | ||
128 | |||
125 | macro_rules! noop { | 129 | macro_rules! noop { |
126 | ($expr:expr) => { | 130 | ($expr:expr) => { |
127 | $expr | 131 | $expr |
@@ -145,6 +149,7 @@ macro without_args { | |||
145 | // comment | 149 | // comment |
146 | fn main() { | 150 | fn main() { |
147 | println!("Hello, {}!", 92); | 151 | println!("Hello, {}!", 92); |
152 | dont_color_me_braces!(); | ||
148 | 153 | ||
149 | let mut vec = Vec::new(); | 154 | let mut vec = Vec::new(); |
150 | if true { | 155 | if true { |
@@ -184,8 +189,8 @@ fn main() { | |||
184 | let a = |x| x; | 189 | let a = |x| x; |
185 | let bar = Foo::baz; | 190 | let bar = Foo::baz; |
186 | 191 | ||
187 | let baz = -42; | 192 | let baz = (-42,); |
188 | let baz = -baz; | 193 | let baz = -baz.0; |
189 | 194 | ||
190 | let _ = !true; | 195 | let _ = !true; |
191 | 196 | ||
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs index 81c4d95b1..5cba9d11d 100644 --- a/crates/ide/src/typing/on_enter.rs +++ b/crates/ide/src/typing/on_enter.rs | |||
@@ -88,12 +88,12 @@ fn on_enter_in_comment( | |||
88 | if comment.text().ends_with(' ') { | 88 | if comment.text().ends_with(' ') { |
89 | cov_mark::hit!(continues_end_of_line_comment_with_space); | 89 | cov_mark::hit!(continues_end_of_line_comment_with_space); |
90 | remove_trailing_whitespace = true; | 90 | remove_trailing_whitespace = true; |
91 | } else if !followed_by_comment(&comment) { | 91 | } else if !followed_by_comment(comment) { |
92 | return None; | 92 | return None; |
93 | } | 93 | } |
94 | } | 94 | } |
95 | 95 | ||
96 | let indent = node_indent(&file, comment.syntax())?; | 96 | let indent = node_indent(file, comment.syntax())?; |
97 | let inserted = format!("\n{}{} $0", indent, prefix); | 97 | let inserted = format!("\n{}{} $0", indent, prefix); |
98 | let delete = if remove_trailing_whitespace { | 98 | let delete = if remove_trailing_whitespace { |
99 | let trimmed_len = comment.text().trim_end().len() as u32; | 99 | let trimmed_len = comment.text().trim_end().len() as u32; |
@@ -188,7 +188,7 @@ mod tests { | |||
188 | use crate::fixture; | 188 | use crate::fixture; |
189 | 189 | ||
190 | fn apply_on_enter(before: &str) -> Option<String> { | 190 | fn apply_on_enter(before: &str) -> Option<String> { |
191 | let (analysis, position) = fixture::position(&before); | 191 | let (analysis, position) = fixture::position(before); |
192 | let result = analysis.on_enter(position).unwrap()?; | 192 | let result = analysis.on_enter(position).unwrap()?; |
193 | 193 | ||
194 | let mut actual = analysis.file_text(position.file_id).unwrap().to_string(); | 194 | let mut actual = analysis.file_text(position.file_id).unwrap().to_string(); |
diff --git a/crates/ide_assists/Cargo.toml b/crates/ide_assists/Cargo.toml index a83acb191..0d0d1605e 100644 --- a/crates/ide_assists/Cargo.toml +++ b/crates/ide_assists/Cargo.toml | |||
@@ -10,7 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = { version = "1.1", features = ["thread-local"] } | 13 | cov-mark = "2.0.0-pre.1" |
14 | rustc-hash = "1.1.0" | 14 | rustc-hash = "1.1.0" |
15 | itertools = "0.10.0" | 15 | itertools = "0.10.0" |
16 | either = "1.6.1" | 16 | either = "1.6.1" |
diff --git a/crates/ide_assists/src/assist_context.rs b/crates/ide_assists/src/assist_context.rs index 8fc40f9bd..36a2bf89a 100644 --- a/crates/ide_assists/src/assist_context.rs +++ b/crates/ide_assists/src/assist_context.rs | |||
@@ -291,8 +291,7 @@ impl AssistBuilder { | |||
291 | algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) | 291 | algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) |
292 | } | 292 | } |
293 | pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) { | 293 | pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) { |
294 | let file_system_edit = | 294 | let file_system_edit = FileSystemEdit::CreateFile { dst, initial_contents: content.into() }; |
295 | FileSystemEdit::CreateFile { dst: dst, initial_contents: content.into() }; | ||
296 | self.source_change.push_file_system_edit(file_system_edit); | 295 | self.source_change.push_file_system_edit(file_system_edit); |
297 | } | 296 | } |
298 | 297 | ||
diff --git a/crates/ide_assists/src/handlers/apply_demorgan.rs b/crates/ide_assists/src/handlers/apply_demorgan.rs index 5c936a510..c93959e66 100644 --- a/crates/ide_assists/src/handlers/apply_demorgan.rs +++ b/crates/ide_assists/src/handlers/apply_demorgan.rs | |||
@@ -78,12 +78,12 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<( | |||
78 | terms.sort_by_key(|t| t.syntax().text_range().start()); | 78 | terms.sort_by_key(|t| t.syntax().text_range().start()); |
79 | let mut terms = VecDeque::from(terms); | 79 | let mut terms = VecDeque::from(terms); |
80 | 80 | ||
81 | let paren_expr = expr.syntax().parent().and_then(|parent| ast::ParenExpr::cast(parent)); | 81 | let paren_expr = expr.syntax().parent().and_then(ast::ParenExpr::cast); |
82 | 82 | ||
83 | let neg_expr = paren_expr | 83 | let neg_expr = paren_expr |
84 | .clone() | 84 | .clone() |
85 | .and_then(|paren_expr| paren_expr.syntax().parent()) | 85 | .and_then(|paren_expr| paren_expr.syntax().parent()) |
86 | .and_then(|parent| ast::PrefixExpr::cast(parent)) | 86 | .and_then(ast::PrefixExpr::cast) |
87 | .and_then(|prefix_expr| { | 87 | .and_then(|prefix_expr| { |
88 | if prefix_expr.op_kind().unwrap() == ast::PrefixOp::Not { | 88 | if prefix_expr.op_kind().unwrap() == ast::PrefixOp::Not { |
89 | Some(prefix_expr) | 89 | Some(prefix_expr) |
diff --git a/crates/ide_assists/src/handlers/change_visibility.rs b/crates/ide_assists/src/handlers/change_visibility.rs index d7e39b2ae..ed936667f 100644 --- a/crates/ide_assists/src/handlers/change_visibility.rs +++ b/crates/ide_assists/src/handlers/change_visibility.rs | |||
@@ -1,7 +1,9 @@ | |||
1 | use syntax::{ | 1 | use syntax::{ |
2 | ast::{self, NameOwner, VisibilityOwner}, | 2 | ast::{self, NameOwner, VisibilityOwner}, |
3 | AstNode, | 3 | AstNode, |
4 | SyntaxKind::{CONST, ENUM, FN, MODULE, STATIC, STRUCT, TRAIT, TYPE_ALIAS, VISIBILITY}, | 4 | SyntaxKind::{ |
5 | CONST, ENUM, FN, MACRO_DEF, MODULE, STATIC, STRUCT, TRAIT, TYPE_ALIAS, USE, VISIBILITY, | ||
6 | }, | ||
5 | T, | 7 | T, |
6 | }; | 8 | }; |
7 | 9 | ||
@@ -37,12 +39,15 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
37 | | T![enum] | 39 | | T![enum] |
38 | | T![trait] | 40 | | T![trait] |
39 | | T![type] | 41 | | T![type] |
42 | | T![use] | ||
43 | | T![macro] | ||
40 | ) | 44 | ) |
41 | }); | 45 | }); |
42 | 46 | ||
43 | let (offset, target) = if let Some(keyword) = item_keyword { | 47 | let (offset, target) = if let Some(keyword) = item_keyword { |
44 | let parent = keyword.parent()?; | 48 | let parent = keyword.parent()?; |
45 | let def_kws = vec![CONST, STATIC, TYPE_ALIAS, FN, MODULE, STRUCT, ENUM, TRAIT]; | 49 | let def_kws = |
50 | vec![CONST, STATIC, TYPE_ALIAS, FN, MODULE, STRUCT, ENUM, TRAIT, USE, MACRO_DEF]; | ||
46 | // Parent is not a definition, can't add visibility | 51 | // Parent is not a definition, can't add visibility |
47 | if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) { | 52 | if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) { |
48 | return None; | 53 | return None; |
@@ -122,6 +127,8 @@ mod tests { | |||
122 | check_assist(change_visibility, "$0trait Foo {}", "pub(crate) trait Foo {}"); | 127 | check_assist(change_visibility, "$0trait Foo {}", "pub(crate) trait Foo {}"); |
123 | check_assist(change_visibility, "m$0od {}", "pub(crate) mod {}"); | 128 | check_assist(change_visibility, "m$0od {}", "pub(crate) mod {}"); |
124 | check_assist(change_visibility, "unsafe f$0n foo() {}", "pub(crate) unsafe fn foo() {}"); | 129 | check_assist(change_visibility, "unsafe f$0n foo() {}", "pub(crate) unsafe fn foo() {}"); |
130 | check_assist(change_visibility, "$0macro foo() {}", "pub(crate) macro foo() {}"); | ||
131 | check_assist(change_visibility, "$0use foo;", "pub(crate) use foo;"); | ||
125 | } | 132 | } |
126 | 133 | ||
127 | #[test] | 134 | #[test] |
diff --git a/crates/ide_assists/src/handlers/convert_comment_block.rs b/crates/ide_assists/src/handlers/convert_comment_block.rs index d202a85f9..749e8685b 100644 --- a/crates/ide_assists/src/handlers/convert_comment_block.rs +++ b/crates/ide_assists/src/handlers/convert_comment_block.rs | |||
@@ -88,7 +88,7 @@ fn line_to_block(acc: &mut Assists, comment: ast::Comment) -> Option<()> { | |||
88 | // We pick a single indentation level for the whole block comment based on the | 88 | // We pick a single indentation level for the whole block comment based on the |
89 | // comment where the assist was invoked. This will be prepended to the | 89 | // comment where the assist was invoked. This will be prepended to the |
90 | // contents of each line comment when they're put into the block comment. | 90 | // contents of each line comment when they're put into the block comment. |
91 | let indentation = IndentLevel::from_token(&comment.syntax()); | 91 | let indentation = IndentLevel::from_token(comment.syntax()); |
92 | 92 | ||
93 | let block_comment_body = | 93 | let block_comment_body = |
94 | comments.into_iter().map(|c| line_comment_text(indentation, c)).join("\n"); | 94 | comments.into_iter().map(|c| line_comment_text(indentation, c)).join("\n"); |
@@ -167,7 +167,7 @@ fn line_comment_text(indentation: IndentLevel, comm: ast::Comment) -> String { | |||
167 | if contents.is_empty() { | 167 | if contents.is_empty() { |
168 | contents.to_owned() | 168 | contents.to_owned() |
169 | } else { | 169 | } else { |
170 | indentation.to_string() + &contents | 170 | indentation.to_string() + contents |
171 | } | 171 | } |
172 | } | 172 | } |
173 | 173 | ||
diff --git a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs index 70949ca35..fc5a17f05 100644 --- a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs | |||
@@ -1,3 +1,4 @@ | |||
1 | use either::Either; | ||
1 | use ide_db::defs::{Definition, NameRefClass}; | 2 | use ide_db::defs::{Definition, NameRefClass}; |
2 | use syntax::{ | 3 | use syntax::{ |
3 | ast::{self, AstNode, GenericParamsOwner, VisibilityOwner}, | 4 | ast::{self, AstNode, GenericParamsOwner, VisibilityOwner}, |
@@ -8,7 +9,7 @@ use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, | |||
8 | 9 | ||
9 | // Assist: convert_tuple_struct_to_named_struct | 10 | // Assist: convert_tuple_struct_to_named_struct |
10 | // | 11 | // |
11 | // Converts tuple struct to struct with named fields. | 12 | // Converts tuple struct to struct with named fields, and analogously for tuple enum variants. |
12 | // | 13 | // |
13 | // ``` | 14 | // ``` |
14 | // struct Point$0(f32, f32); | 15 | // struct Point$0(f32, f32); |
@@ -49,14 +50,21 @@ pub(crate) fn convert_tuple_struct_to_named_struct( | |||
49 | acc: &mut Assists, | 50 | acc: &mut Assists, |
50 | ctx: &AssistContext, | 51 | ctx: &AssistContext, |
51 | ) -> Option<()> { | 52 | ) -> Option<()> { |
52 | let strukt = ctx.find_node_at_offset::<ast::Struct>()?; | 53 | let strukt = ctx |
53 | let tuple_fields = match strukt.field_list()? { | 54 | .find_node_at_offset::<ast::Struct>() |
55 | .map(Either::Left) | ||
56 | .or_else(|| ctx.find_node_at_offset::<ast::Variant>().map(Either::Right))?; | ||
57 | let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?; | ||
58 | let tuple_fields = match field_list { | ||
54 | ast::FieldList::TupleFieldList(it) => it, | 59 | ast::FieldList::TupleFieldList(it) => it, |
55 | ast::FieldList::RecordFieldList(_) => return None, | 60 | ast::FieldList::RecordFieldList(_) => return None, |
56 | }; | 61 | }; |
57 | let strukt_def = ctx.sema.to_def(&strukt)?; | 62 | let strukt_def = match &strukt { |
63 | Either::Left(s) => Either::Left(ctx.sema.to_def(s)?), | ||
64 | Either::Right(v) => Either::Right(ctx.sema.to_def(v)?), | ||
65 | }; | ||
66 | let target = strukt.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range(); | ||
58 | 67 | ||
59 | let target = strukt.syntax().text_range(); | ||
60 | acc.add( | 68 | acc.add( |
61 | AssistId("convert_tuple_struct_to_named_struct", AssistKind::RefactorRewrite), | 69 | AssistId("convert_tuple_struct_to_named_struct", AssistKind::RefactorRewrite), |
62 | "Convert to named struct", | 70 | "Convert to named struct", |
@@ -73,7 +81,7 @@ pub(crate) fn convert_tuple_struct_to_named_struct( | |||
73 | fn edit_struct_def( | 81 | fn edit_struct_def( |
74 | ctx: &AssistContext, | 82 | ctx: &AssistContext, |
75 | edit: &mut AssistBuilder, | 83 | edit: &mut AssistBuilder, |
76 | strukt: &ast::Struct, | 84 | strukt: &Either<ast::Struct, ast::Variant>, |
77 | tuple_fields: ast::TupleFieldList, | 85 | tuple_fields: ast::TupleFieldList, |
78 | names: Vec<ast::Name>, | 86 | names: Vec<ast::Name>, |
79 | ) { | 87 | ) { |
@@ -86,27 +94,40 @@ fn edit_struct_def( | |||
86 | 94 | ||
87 | edit.edit_file(ctx.frange.file_id); | 95 | edit.edit_file(ctx.frange.file_id); |
88 | 96 | ||
89 | if let Some(w) = strukt.where_clause() { | 97 | if let Either::Left(strukt) = strukt { |
90 | edit.delete(w.syntax().text_range()); | 98 | if let Some(w) = strukt.where_clause() { |
91 | edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_newline().text()); | 99 | edit.delete(w.syntax().text_range()); |
92 | edit.insert(tuple_fields_text_range.start(), w.syntax().text()); | 100 | edit.insert( |
93 | edit.insert(tuple_fields_text_range.start(), ","); | 101 | tuple_fields_text_range.start(), |
94 | edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_newline().text()); | 102 | ast::make::tokens::single_newline().text(), |
103 | ); | ||
104 | edit.insert(tuple_fields_text_range.start(), w.syntax().text()); | ||
105 | edit.insert(tuple_fields_text_range.start(), ","); | ||
106 | edit.insert( | ||
107 | tuple_fields_text_range.start(), | ||
108 | ast::make::tokens::single_newline().text(), | ||
109 | ); | ||
110 | } else { | ||
111 | edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text()); | ||
112 | } | ||
113 | strukt.semicolon_token().map(|t| edit.delete(t.text_range())); | ||
95 | } else { | 114 | } else { |
96 | edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text()); | 115 | edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text()); |
97 | } | 116 | } |
98 | 117 | ||
99 | edit.replace(tuple_fields_text_range, record_fields.to_string()); | 118 | edit.replace(tuple_fields_text_range, record_fields.to_string()); |
100 | strukt.semicolon_token().map(|t| edit.delete(t.text_range())); | ||
101 | } | 119 | } |
102 | 120 | ||
103 | fn edit_struct_references( | 121 | fn edit_struct_references( |
104 | ctx: &AssistContext, | 122 | ctx: &AssistContext, |
105 | edit: &mut AssistBuilder, | 123 | edit: &mut AssistBuilder, |
106 | strukt: hir::Struct, | 124 | strukt: Either<hir::Struct, hir::Variant>, |
107 | names: &[ast::Name], | 125 | names: &[ast::Name], |
108 | ) { | 126 | ) { |
109 | let strukt_def = Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(strukt))); | 127 | let strukt_def = match strukt { |
128 | Either::Left(s) => Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(s))), | ||
129 | Either::Right(v) => Definition::ModuleDef(hir::ModuleDef::Variant(v)), | ||
130 | }; | ||
110 | let usages = strukt_def.usages(&ctx.sema).include_self_refs().all(); | 131 | let usages = strukt_def.usages(&ctx.sema).include_self_refs().all(); |
111 | 132 | ||
112 | let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> { | 133 | let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> { |
@@ -513,4 +534,305 @@ where | |||
513 | "#, | 534 | "#, |
514 | ); | 535 | ); |
515 | } | 536 | } |
537 | #[test] | ||
538 | fn not_applicable_other_than_tuple_variant() { | ||
539 | check_assist_not_applicable( | ||
540 | convert_tuple_struct_to_named_struct, | ||
541 | r#"enum Enum { Variant$0 { value: usize } };"#, | ||
542 | ); | ||
543 | check_assist_not_applicable( | ||
544 | convert_tuple_struct_to_named_struct, | ||
545 | r#"enum Enum { Variant$0 }"#, | ||
546 | ); | ||
547 | } | ||
548 | |||
549 | #[test] | ||
550 | fn convert_simple_variant() { | ||
551 | check_assist( | ||
552 | convert_tuple_struct_to_named_struct, | ||
553 | r#" | ||
554 | enum A { | ||
555 | $0Variant(usize), | ||
556 | } | ||
557 | |||
558 | impl A { | ||
559 | fn new(value: usize) -> A { | ||
560 | A::Variant(value) | ||
561 | } | ||
562 | |||
563 | fn new_with_default() -> A { | ||
564 | A::new(Default::default()) | ||
565 | } | ||
566 | |||
567 | fn value(self) -> usize { | ||
568 | match self { | ||
569 | A::Variant(value) => value, | ||
570 | } | ||
571 | } | ||
572 | }"#, | ||
573 | r#" | ||
574 | enum A { | ||
575 | Variant { field1: usize }, | ||
576 | } | ||
577 | |||
578 | impl A { | ||
579 | fn new(value: usize) -> A { | ||
580 | A::Variant { field1: value } | ||
581 | } | ||
582 | |||
583 | fn new_with_default() -> A { | ||
584 | A::new(Default::default()) | ||
585 | } | ||
586 | |||
587 | fn value(self) -> usize { | ||
588 | match self { | ||
589 | A::Variant { field1: value } => value, | ||
590 | } | ||
591 | } | ||
592 | }"#, | ||
593 | ); | ||
594 | } | ||
595 | |||
596 | #[test] | ||
597 | fn convert_variant_referenced_via_self_kw() { | ||
598 | check_assist( | ||
599 | convert_tuple_struct_to_named_struct, | ||
600 | r#" | ||
601 | enum A { | ||
602 | $0Variant(usize), | ||
603 | } | ||
604 | |||
605 | impl A { | ||
606 | fn new(value: usize) -> A { | ||
607 | Self::Variant(value) | ||
608 | } | ||
609 | |||
610 | fn new_with_default() -> A { | ||
611 | Self::new(Default::default()) | ||
612 | } | ||
613 | |||
614 | fn value(self) -> usize { | ||
615 | match self { | ||
616 | Self::Variant(value) => value, | ||
617 | } | ||
618 | } | ||
619 | }"#, | ||
620 | r#" | ||
621 | enum A { | ||
622 | Variant { field1: usize }, | ||
623 | } | ||
624 | |||
625 | impl A { | ||
626 | fn new(value: usize) -> A { | ||
627 | Self::Variant { field1: value } | ||
628 | } | ||
629 | |||
630 | fn new_with_default() -> A { | ||
631 | Self::new(Default::default()) | ||
632 | } | ||
633 | |||
634 | fn value(self) -> usize { | ||
635 | match self { | ||
636 | Self::Variant { field1: value } => value, | ||
637 | } | ||
638 | } | ||
639 | }"#, | ||
640 | ); | ||
641 | } | ||
642 | |||
643 | #[test] | ||
644 | fn convert_destructured_variant() { | ||
645 | check_assist( | ||
646 | convert_tuple_struct_to_named_struct, | ||
647 | r#" | ||
648 | enum A { | ||
649 | $0Variant(usize), | ||
650 | } | ||
651 | |||
652 | impl A { | ||
653 | fn into_inner(self) -> usize { | ||
654 | let A::Variant(first) = self; | ||
655 | first | ||
656 | } | ||
657 | |||
658 | fn into_inner_via_self(self) -> usize { | ||
659 | let Self::Variant(first) = self; | ||
660 | first | ||
661 | } | ||
662 | }"#, | ||
663 | r#" | ||
664 | enum A { | ||
665 | Variant { field1: usize }, | ||
666 | } | ||
667 | |||
668 | impl A { | ||
669 | fn into_inner(self) -> usize { | ||
670 | let A::Variant { field1: first } = self; | ||
671 | first | ||
672 | } | ||
673 | |||
674 | fn into_inner_via_self(self) -> usize { | ||
675 | let Self::Variant { field1: first } = self; | ||
676 | first | ||
677 | } | ||
678 | }"#, | ||
679 | ); | ||
680 | } | ||
681 | |||
682 | #[test] | ||
683 | fn convert_variant_with_wrapped_references() { | ||
684 | check_assist( | ||
685 | convert_tuple_struct_to_named_struct, | ||
686 | r#" | ||
687 | enum Inner { | ||
688 | $0Variant(usize), | ||
689 | } | ||
690 | enum Outer { | ||
691 | Variant(Inner), | ||
692 | } | ||
693 | |||
694 | impl Outer { | ||
695 | fn new() -> Self { | ||
696 | Self::Variant(Inner::Variant(42)) | ||
697 | } | ||
698 | |||
699 | fn into_inner_destructed(self) -> u32 { | ||
700 | let Outer::Variant(Inner::Variant(x)) = self; | ||
701 | x | ||
702 | } | ||
703 | }"#, | ||
704 | r#" | ||
705 | enum Inner { | ||
706 | Variant { field1: usize }, | ||
707 | } | ||
708 | enum Outer { | ||
709 | Variant(Inner), | ||
710 | } | ||
711 | |||
712 | impl Outer { | ||
713 | fn new() -> Self { | ||
714 | Self::Variant(Inner::Variant { field1: 42 }) | ||
715 | } | ||
716 | |||
717 | fn into_inner_destructed(self) -> u32 { | ||
718 | let Outer::Variant(Inner::Variant { field1: x }) = self; | ||
719 | x | ||
720 | } | ||
721 | }"#, | ||
722 | ); | ||
723 | |||
724 | check_assist( | ||
725 | convert_tuple_struct_to_named_struct, | ||
726 | r#" | ||
727 | enum Inner { | ||
728 | Variant(usize), | ||
729 | } | ||
730 | enum Outer { | ||
731 | $0Variant(Inner), | ||
732 | } | ||
733 | |||
734 | impl Outer { | ||
735 | fn new() -> Self { | ||
736 | Self::Variant(Inner::Variant(42)) | ||
737 | } | ||
738 | |||
739 | fn into_inner_destructed(self) -> u32 { | ||
740 | let Outer::Variant(Inner::Variant(x)) = self; | ||
741 | x | ||
742 | } | ||
743 | }"#, | ||
744 | r#" | ||
745 | enum Inner { | ||
746 | Variant(usize), | ||
747 | } | ||
748 | enum Outer { | ||
749 | Variant { field1: Inner }, | ||
750 | } | ||
751 | |||
752 | impl Outer { | ||
753 | fn new() -> Self { | ||
754 | Self::Variant { field1: Inner::Variant(42) } | ||
755 | } | ||
756 | |||
757 | fn into_inner_destructed(self) -> u32 { | ||
758 | let Outer::Variant { field1: Inner::Variant(x) } = self; | ||
759 | x | ||
760 | } | ||
761 | }"#, | ||
762 | ); | ||
763 | } | ||
764 | |||
765 | #[test] | ||
766 | fn convert_variant_with_multi_file_references() { | ||
767 | check_assist( | ||
768 | convert_tuple_struct_to_named_struct, | ||
769 | r#" | ||
770 | //- /main.rs | ||
771 | struct Inner; | ||
772 | enum A { | ||
773 | $0Variant(Inner), | ||
774 | } | ||
775 | |||
776 | mod foo; | ||
777 | |||
778 | //- /foo.rs | ||
779 | use crate::{A, Inner}; | ||
780 | fn f() { | ||
781 | let a = A::Variant(Inner); | ||
782 | } | ||
783 | "#, | ||
784 | r#" | ||
785 | //- /main.rs | ||
786 | struct Inner; | ||
787 | enum A { | ||
788 | Variant { field1: Inner }, | ||
789 | } | ||
790 | |||
791 | mod foo; | ||
792 | |||
793 | //- /foo.rs | ||
794 | use crate::{A, Inner}; | ||
795 | fn f() { | ||
796 | let a = A::Variant { field1: Inner }; | ||
797 | } | ||
798 | "#, | ||
799 | ); | ||
800 | } | ||
801 | |||
802 | #[test] | ||
803 | fn convert_directly_used_variant() { | ||
804 | check_assist( | ||
805 | convert_tuple_struct_to_named_struct, | ||
806 | r#" | ||
807 | //- /main.rs | ||
808 | struct Inner; | ||
809 | enum A { | ||
810 | $0Variant(Inner), | ||
811 | } | ||
812 | |||
813 | mod foo; | ||
814 | |||
815 | //- /foo.rs | ||
816 | use crate::{A::Variant, Inner}; | ||
817 | fn f() { | ||
818 | let a = Variant(Inner); | ||
819 | } | ||
820 | "#, | ||
821 | r#" | ||
822 | //- /main.rs | ||
823 | struct Inner; | ||
824 | enum A { | ||
825 | Variant { field1: Inner }, | ||
826 | } | ||
827 | |||
828 | mod foo; | ||
829 | |||
830 | //- /foo.rs | ||
831 | use crate::{A::Variant, Inner}; | ||
832 | fn f() { | ||
833 | let a = Variant { field1: Inner }; | ||
834 | } | ||
835 | "#, | ||
836 | ); | ||
837 | } | ||
516 | } | 838 | } |
diff --git a/crates/ide_assists/src/handlers/early_return.rs b/crates/ide_assists/src/handlers/early_return.rs index 5eb6a57f0..ef4a7cb50 100644 --- a/crates/ide_assists/src/handlers/early_return.rs +++ b/crates/ide_assists/src/handlers/early_return.rs | |||
@@ -108,7 +108,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
108 | "Convert to guarded return", | 108 | "Convert to guarded return", |
109 | target, | 109 | target, |
110 | |edit| { | 110 | |edit| { |
111 | let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); | 111 | let if_indent_level = IndentLevel::from_node(if_expr.syntax()); |
112 | let new_block = match if_let_pat { | 112 | let new_block = match if_let_pat { |
113 | None => { | 113 | None => { |
114 | // If. | 114 | // If. |
@@ -174,7 +174,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
174 | .take_while(|i| *i != end_of_then), | 174 | .take_while(|i| *i != end_of_then), |
175 | ); | 175 | ); |
176 | replace_children( | 176 | replace_children( |
177 | &parent_block.syntax(), | 177 | parent_block.syntax(), |
178 | RangeInclusive::new( | 178 | RangeInclusive::new( |
179 | if_expr.clone().syntax().clone().into(), | 179 | if_expr.clone().syntax().clone().into(), |
180 | if_expr.syntax().clone().into(), | 180 | if_expr.syntax().clone().into(), |
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs index a2dba915c..f2be091f4 100644 --- a/crates/ide_assists/src/handlers/extract_function.rs +++ b/crates/ide_assists/src/handlers/extract_function.rs | |||
@@ -76,7 +76,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
76 | let module = ctx.sema.scope(&insert_after).module()?; | 76 | let module = ctx.sema.scope(&insert_after).module()?; |
77 | 77 | ||
78 | let vars_defined_in_body_and_outlive = | 78 | let vars_defined_in_body_and_outlive = |
79 | vars_defined_in_body_and_outlive(ctx, &body, &node.parent().as_ref().unwrap_or(&node)); | 79 | vars_defined_in_body_and_outlive(ctx, &body, node.parent().as_ref().unwrap_or(&node)); |
80 | let ret_ty = body_return_ty(ctx, &body)?; | 80 | let ret_ty = body_return_ty(ctx, &body)?; |
81 | 81 | ||
82 | // FIXME: we compute variables that outlive here just to check `never!` condition | 82 | // FIXME: we compute variables that outlive here just to check `never!` condition |
@@ -782,7 +782,7 @@ fn expr_require_exclusive_access(ctx: &AssistContext, expr: &ast::Expr) -> Optio | |||
782 | Some(false) | 782 | Some(false) |
783 | } | 783 | } |
784 | 784 | ||
785 | /// Container of local varaible usages | 785 | /// Container of local variable usages |
786 | /// | 786 | /// |
787 | /// Semanticall same as `UsageSearchResult`, but provides more convenient interface | 787 | /// Semanticall same as `UsageSearchResult`, but provides more convenient interface |
788 | struct LocalUsages(ide_db::search::UsageSearchResult); | 788 | struct LocalUsages(ide_db::search::UsageSearchResult); |
@@ -808,7 +808,7 @@ trait HasTokenAtOffset { | |||
808 | 808 | ||
809 | impl HasTokenAtOffset for SyntaxNode { | 809 | impl HasTokenAtOffset for SyntaxNode { |
810 | fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken> { | 810 | fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken> { |
811 | SyntaxNode::token_at_offset(&self, offset) | 811 | SyntaxNode::token_at_offset(self, offset) |
812 | } | 812 | } |
813 | } | 813 | } |
814 | 814 | ||
@@ -854,7 +854,7 @@ fn vars_defined_in_body_and_outlive( | |||
854 | body: &FunctionBody, | 854 | body: &FunctionBody, |
855 | parent: &SyntaxNode, | 855 | parent: &SyntaxNode, |
856 | ) -> Vec<OutlivedLocal> { | 856 | ) -> Vec<OutlivedLocal> { |
857 | let vars_defined_in_body = vars_defined_in_body(&body, ctx); | 857 | let vars_defined_in_body = vars_defined_in_body(body, ctx); |
858 | vars_defined_in_body | 858 | vars_defined_in_body |
859 | .into_iter() | 859 | .into_iter() |
860 | .filter_map(|var| var_outlives_body(ctx, body, var, parent)) | 860 | .filter_map(|var| var_outlives_body(ctx, body, var, parent)) |
@@ -868,7 +868,7 @@ fn is_defined_before( | |||
868 | src: &hir::InFile<Either<ast::IdentPat, ast::SelfParam>>, | 868 | src: &hir::InFile<Either<ast::IdentPat, ast::SelfParam>>, |
869 | ) -> bool { | 869 | ) -> bool { |
870 | src.file_id.original_file(ctx.db()) == ctx.frange.file_id | 870 | src.file_id.original_file(ctx.db()) == ctx.frange.file_id |
871 | && !body.contains_node(&either_syntax(&src.value)) | 871 | && !body.contains_node(either_syntax(&src.value)) |
872 | } | 872 | } |
873 | 873 | ||
874 | fn either_syntax(value: &Either<ast::IdentPat, ast::SelfParam>) -> &SyntaxNode { | 874 | fn either_syntax(value: &Either<ast::IdentPat, ast::SelfParam>) -> &SyntaxNode { |
diff --git a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs index 007aba23d..d3ff7b65c 100644 --- a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -11,14 +11,19 @@ use ide_db::{ | |||
11 | search::FileReference, | 11 | search::FileReference, |
12 | RootDatabase, | 12 | RootDatabase, |
13 | }; | 13 | }; |
14 | use itertools::Itertools; | ||
14 | use rustc_hash::FxHashSet; | 15 | use rustc_hash::FxHashSet; |
15 | use syntax::{ | 16 | use syntax::{ |
16 | algo::find_node_at_offset, | 17 | ast::{ |
17 | ast::{self, make, AstNode, NameOwner, VisibilityOwner}, | 18 | self, make, AstNode, AttrsOwner, GenericParamsOwner, NameOwner, TypeBoundsOwner, |
18 | ted, SyntaxNode, T, | 19 | VisibilityOwner, |
20 | }, | ||
21 | match_ast, | ||
22 | ted::{self, Position}, | ||
23 | SyntaxNode, T, | ||
19 | }; | 24 | }; |
20 | 25 | ||
21 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 26 | use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists}; |
22 | 27 | ||
23 | // Assist: extract_struct_from_enum_variant | 28 | // Assist: extract_struct_from_enum_variant |
24 | // | 29 | // |
@@ -70,11 +75,10 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
70 | continue; | 75 | continue; |
71 | } | 76 | } |
72 | builder.edit_file(file_id); | 77 | builder.edit_file(file_id); |
73 | let source_file = builder.make_mut(ctx.sema.parse(file_id)); | ||
74 | let processed = process_references( | 78 | let processed = process_references( |
75 | ctx, | 79 | ctx, |
80 | builder, | ||
76 | &mut visited_modules_set, | 81 | &mut visited_modules_set, |
77 | source_file.syntax(), | ||
78 | &enum_module_def, | 82 | &enum_module_def, |
79 | &variant_hir_name, | 83 | &variant_hir_name, |
80 | references, | 84 | references, |
@@ -84,13 +88,12 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
84 | }); | 88 | }); |
85 | } | 89 | } |
86 | builder.edit_file(ctx.frange.file_id); | 90 | builder.edit_file(ctx.frange.file_id); |
87 | let source_file = builder.make_mut(ctx.sema.parse(ctx.frange.file_id)); | ||
88 | let variant = builder.make_mut(variant.clone()); | 91 | let variant = builder.make_mut(variant.clone()); |
89 | if let Some(references) = def_file_references { | 92 | if let Some(references) = def_file_references { |
90 | let processed = process_references( | 93 | let processed = process_references( |
91 | ctx, | 94 | ctx, |
95 | builder, | ||
92 | &mut visited_modules_set, | 96 | &mut visited_modules_set, |
93 | source_file.syntax(), | ||
94 | &enum_module_def, | 97 | &enum_module_def, |
95 | &variant_hir_name, | 98 | &variant_hir_name, |
96 | references, | 99 | references, |
@@ -100,12 +103,12 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
100 | }); | 103 | }); |
101 | } | 104 | } |
102 | 105 | ||
103 | let def = create_struct_def(variant_name.clone(), &field_list, enum_ast.visibility()); | 106 | let def = create_struct_def(variant_name.clone(), &field_list, &enum_ast); |
104 | let start_offset = &variant.parent_enum().syntax().clone(); | 107 | let start_offset = &variant.parent_enum().syntax().clone(); |
105 | ted::insert_raw(ted::Position::before(start_offset), def.syntax()); | 108 | ted::insert_raw(ted::Position::before(start_offset), def.syntax()); |
106 | ted::insert_raw(ted::Position::before(start_offset), &make::tokens::blank_line()); | 109 | ted::insert_raw(ted::Position::before(start_offset), &make::tokens::blank_line()); |
107 | 110 | ||
108 | update_variant(&variant); | 111 | update_variant(&variant, enum_ast.generic_param_list()); |
109 | }, | 112 | }, |
110 | ) | 113 | ) |
111 | } | 114 | } |
@@ -149,7 +152,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va | |||
149 | fn create_struct_def( | 152 | fn create_struct_def( |
150 | variant_name: ast::Name, | 153 | variant_name: ast::Name, |
151 | field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>, | 154 | field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>, |
152 | visibility: Option<ast::Visibility>, | 155 | enum_: &ast::Enum, |
153 | ) -> ast::Struct { | 156 | ) -> ast::Struct { |
154 | let pub_vis = make::visibility_pub(); | 157 | let pub_vis = make::visibility_pub(); |
155 | 158 | ||
@@ -184,12 +187,38 @@ fn create_struct_def( | |||
184 | } | 187 | } |
185 | }; | 188 | }; |
186 | 189 | ||
187 | make::struct_(visibility, variant_name, None, field_list).clone_for_update() | 190 | // FIXME: This uses all the generic params of the enum, but the variant might not use all of them. |
191 | let strukt = | ||
192 | make::struct_(enum_.visibility(), variant_name, enum_.generic_param_list(), field_list) | ||
193 | .clone_for_update(); | ||
194 | |||
195 | // copy attributes | ||
196 | ted::insert_all( | ||
197 | Position::first_child_of(strukt.syntax()), | ||
198 | enum_.attrs().map(|it| it.syntax().clone_for_update().into()).collect(), | ||
199 | ); | ||
200 | strukt | ||
188 | } | 201 | } |
189 | 202 | ||
190 | fn update_variant(variant: &ast::Variant) -> Option<()> { | 203 | fn update_variant(variant: &ast::Variant, generic: Option<ast::GenericParamList>) -> Option<()> { |
191 | let name = variant.name()?; | 204 | let name = variant.name()?; |
192 | let tuple_field = make::tuple_field(None, make::ty(&name.text())); | 205 | let ty = match generic { |
206 | // FIXME: This uses all the generic params of the enum, but the variant might not use all of them. | ||
207 | Some(gpl) => { | ||
208 | let gpl = gpl.clone_for_update(); | ||
209 | gpl.generic_params().for_each(|gp| { | ||
210 | match gp { | ||
211 | ast::GenericParam::LifetimeParam(it) => it.type_bound_list(), | ||
212 | ast::GenericParam::TypeParam(it) => it.type_bound_list(), | ||
213 | ast::GenericParam::ConstParam(_) => return, | ||
214 | } | ||
215 | .map(|it| it.remove()); | ||
216 | }); | ||
217 | make::ty(&format!("{}<{}>", name.text(), gpl.generic_params().join(", "))) | ||
218 | } | ||
219 | None => make::ty(&name.text()), | ||
220 | }; | ||
221 | let tuple_field = make::tuple_field(None, ty); | ||
193 | let replacement = make::variant( | 222 | let replacement = make::variant( |
194 | name, | 223 | name, |
195 | Some(ast::FieldList::TupleFieldList(make::tuple_field_list(iter::once(tuple_field)))), | 224 | Some(ast::FieldList::TupleFieldList(make::tuple_field_list(iter::once(tuple_field)))), |
@@ -208,18 +237,17 @@ fn apply_references( | |||
208 | if let Some((scope, path)) = import { | 237 | if let Some((scope, path)) = import { |
209 | insert_use(&scope, mod_path_to_ast(&path), insert_use_cfg); | 238 | insert_use(&scope, mod_path_to_ast(&path), insert_use_cfg); |
210 | } | 239 | } |
211 | ted::insert_raw( | 240 | // deep clone to prevent cycle |
212 | ted::Position::before(segment.syntax()), | 241 | let path = make::path_from_segments(iter::once(segment.clone_subtree()), false); |
213 | make::path_from_text(&format!("{}", segment)).clone_for_update().syntax(), | 242 | ted::insert_raw(ted::Position::before(segment.syntax()), path.clone_for_update().syntax()); |
214 | ); | ||
215 | ted::insert_raw(ted::Position::before(segment.syntax()), make::token(T!['('])); | 243 | ted::insert_raw(ted::Position::before(segment.syntax()), make::token(T!['('])); |
216 | ted::insert_raw(ted::Position::after(&node), make::token(T![')'])); | 244 | ted::insert_raw(ted::Position::after(&node), make::token(T![')'])); |
217 | } | 245 | } |
218 | 246 | ||
219 | fn process_references( | 247 | fn process_references( |
220 | ctx: &AssistContext, | 248 | ctx: &AssistContext, |
249 | builder: &mut AssistBuilder, | ||
221 | visited_modules: &mut FxHashSet<Module>, | 250 | visited_modules: &mut FxHashSet<Module>, |
222 | source_file: &SyntaxNode, | ||
223 | enum_module_def: &ModuleDef, | 251 | enum_module_def: &ModuleDef, |
224 | variant_hir_name: &Name, | 252 | variant_hir_name: &Name, |
225 | refs: Vec<FileReference>, | 253 | refs: Vec<FileReference>, |
@@ -228,8 +256,9 @@ fn process_references( | |||
228 | // and corresponding nodes up front | 256 | // and corresponding nodes up front |
229 | refs.into_iter() | 257 | refs.into_iter() |
230 | .flat_map(|reference| { | 258 | .flat_map(|reference| { |
231 | let (segment, scope_node, module) = | 259 | let (segment, scope_node, module) = reference_to_node(&ctx.sema, reference)?; |
232 | reference_to_node(&ctx.sema, source_file, reference)?; | 260 | let segment = builder.make_mut(segment); |
261 | let scope_node = builder.make_syntax_mut(scope_node); | ||
233 | if !visited_modules.contains(&module) { | 262 | if !visited_modules.contains(&module) { |
234 | let mod_path = module.find_use_path_prefixed( | 263 | let mod_path = module.find_use_path_prefixed( |
235 | ctx.sema.db, | 264 | ctx.sema.db, |
@@ -251,23 +280,22 @@ fn process_references( | |||
251 | 280 | ||
252 | fn reference_to_node( | 281 | fn reference_to_node( |
253 | sema: &hir::Semantics<RootDatabase>, | 282 | sema: &hir::Semantics<RootDatabase>, |
254 | source_file: &SyntaxNode, | ||
255 | reference: FileReference, | 283 | reference: FileReference, |
256 | ) -> Option<(ast::PathSegment, SyntaxNode, hir::Module)> { | 284 | ) -> Option<(ast::PathSegment, SyntaxNode, hir::Module)> { |
257 | let offset = reference.range.start(); | 285 | let segment = |
258 | if let Some(path_expr) = find_node_at_offset::<ast::PathExpr>(source_file, offset) { | 286 | reference.name.as_name_ref()?.syntax().parent().and_then(ast::PathSegment::cast)?; |
259 | // tuple variant | 287 | let parent = segment.parent_path().syntax().parent()?; |
260 | Some((path_expr.path()?.segment()?, path_expr.syntax().parent()?)) | 288 | let expr_or_pat = match_ast! { |
261 | } else if let Some(record_expr) = find_node_at_offset::<ast::RecordExpr>(source_file, offset) { | 289 | match parent { |
262 | // record variant | 290 | ast::PathExpr(_it) => parent.parent()?, |
263 | Some((record_expr.path()?.segment()?, record_expr.syntax().clone())) | 291 | ast::RecordExpr(_it) => parent, |
264 | } else { | 292 | ast::TupleStructPat(_it) => parent, |
265 | None | 293 | ast::RecordPat(_it) => parent, |
266 | } | 294 | _ => return None, |
267 | .and_then(|(segment, expr)| { | 295 | } |
268 | let module = sema.scope(&expr).module()?; | 296 | }; |
269 | Some((segment, expr, module)) | 297 | let module = sema.scope(&expr_or_pat).module()?; |
270 | }) | 298 | Some((segment, expr_or_pat, module)) |
271 | } | 299 | } |
272 | 300 | ||
273 | #[cfg(test)] | 301 | #[cfg(test)] |
@@ -278,6 +306,12 @@ mod tests { | |||
278 | 306 | ||
279 | use super::*; | 307 | use super::*; |
280 | 308 | ||
309 | fn check_not_applicable(ra_fixture: &str) { | ||
310 | let fixture = | ||
311 | format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); | ||
312 | check_assist_not_applicable(extract_struct_from_enum_variant, &fixture) | ||
313 | } | ||
314 | |||
281 | #[test] | 315 | #[test] |
282 | fn test_extract_struct_several_fields_tuple() { | 316 | fn test_extract_struct_several_fields_tuple() { |
283 | check_assist( | 317 | check_assist( |
@@ -312,6 +346,32 @@ enum A { One(One) }"#, | |||
312 | } | 346 | } |
313 | 347 | ||
314 | #[test] | 348 | #[test] |
349 | fn test_extract_struct_carries_over_generics() { | ||
350 | check_assist( | ||
351 | extract_struct_from_enum_variant, | ||
352 | r"enum En<T> { Var { a: T$0 } }", | ||
353 | r#"struct Var<T>{ pub a: T } | ||
354 | |||
355 | enum En<T> { Var(Var<T>) }"#, | ||
356 | ); | ||
357 | } | ||
358 | |||
359 | #[test] | ||
360 | fn test_extract_struct_carries_over_attributes() { | ||
361 | check_assist( | ||
362 | extract_struct_from_enum_variant, | ||
363 | r#"#[derive(Debug)] | ||
364 | #[derive(Clone)] | ||
365 | enum Enum { Variant{ field: u32$0 } }"#, | ||
366 | r#"#[derive(Debug)]#[derive(Clone)] struct Variant{ pub field: u32 } | ||
367 | |||
368 | #[derive(Debug)] | ||
369 | #[derive(Clone)] | ||
370 | enum Enum { Variant(Variant) }"#, | ||
371 | ); | ||
372 | } | ||
373 | |||
374 | #[test] | ||
315 | fn test_extract_struct_keep_comments_and_attrs_one_field_named() { | 375 | fn test_extract_struct_keep_comments_and_attrs_one_field_named() { |
316 | check_assist( | 376 | check_assist( |
317 | extract_struct_from_enum_variant, | 377 | extract_struct_from_enum_variant, |
@@ -496,7 +556,7 @@ enum E { | |||
496 | } | 556 | } |
497 | 557 | ||
498 | fn f() { | 558 | fn f() { |
499 | let e = E::V { i: 9, j: 2 }; | 559 | let E::V { i, j } = E::V { i: 9, j: 2 }; |
500 | } | 560 | } |
501 | "#, | 561 | "#, |
502 | r#" | 562 | r#" |
@@ -507,7 +567,34 @@ enum E { | |||
507 | } | 567 | } |
508 | 568 | ||
509 | fn f() { | 569 | fn f() { |
510 | let e = E::V(V { i: 9, j: 2 }); | 570 | let E::V(V { i, j }) = E::V(V { i: 9, j: 2 }); |
571 | } | ||
572 | "#, | ||
573 | ) | ||
574 | } | ||
575 | |||
576 | #[test] | ||
577 | fn extract_record_fix_references2() { | ||
578 | check_assist( | ||
579 | extract_struct_from_enum_variant, | ||
580 | r#" | ||
581 | enum E { | ||
582 | $0V(i32, i32) | ||
583 | } | ||
584 | |||
585 | fn f() { | ||
586 | let E::V(i, j) = E::V(9, 2); | ||
587 | } | ||
588 | "#, | ||
589 | r#" | ||
590 | struct V(pub i32, pub i32); | ||
591 | |||
592 | enum E { | ||
593 | V(V) | ||
594 | } | ||
595 | |||
596 | fn f() { | ||
597 | let E::V(V(i, j)) = E::V(V(9, 2)); | ||
511 | } | 598 | } |
512 | "#, | 599 | "#, |
513 | ) | 600 | ) |
@@ -610,12 +697,6 @@ fn foo() { | |||
610 | ); | 697 | ); |
611 | } | 698 | } |
612 | 699 | ||
613 | fn check_not_applicable(ra_fixture: &str) { | ||
614 | let fixture = | ||
615 | format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); | ||
616 | check_assist_not_applicable(extract_struct_from_enum_variant, &fixture) | ||
617 | } | ||
618 | |||
619 | #[test] | 700 | #[test] |
620 | fn test_extract_enum_not_applicable_for_element_with_no_fields() { | 701 | fn test_extract_enum_not_applicable_for_element_with_no_fields() { |
621 | check_not_applicable("enum A { $0One }"); | 702 | check_not_applicable("enum A { $0One }"); |
diff --git a/crates/ide_assists/src/handlers/extract_type_alias.rs b/crates/ide_assists/src/handlers/extract_type_alias.rs index 998e0de7b..eac8857c6 100644 --- a/crates/ide_assists/src/handlers/extract_type_alias.rs +++ b/crates/ide_assists/src/handlers/extract_type_alias.rs | |||
@@ -1,4 +1,7 @@ | |||
1 | use syntax::ast::{self, AstNode}; | 1 | use syntax::{ |
2 | ast::{self, edit::IndentLevel, AstNode}, | ||
3 | match_ast, | ||
4 | }; | ||
2 | 5 | ||
3 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
4 | 7 | ||
@@ -25,12 +28,15 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext) -> Opti | |||
25 | } | 28 | } |
26 | 29 | ||
27 | let node = ctx.find_node_at_range::<ast::Type>()?; | 30 | let node = ctx.find_node_at_range::<ast::Type>()?; |
28 | let insert = ctx | 31 | let item = ctx.find_node_at_offset::<ast::Item>()?; |
29 | .find_node_at_offset::<ast::Impl>() | 32 | let insert = match_ast! { |
30 | .map(|imp| imp.syntax().clone()) | 33 | match (item.syntax().parent()?) { |
31 | .or_else(|| ctx.find_node_at_offset::<ast::Item>().map(|item| item.syntax().clone()))? | 34 | ast::AssocItemList(it) => it.syntax().parent()?, |
32 | .text_range() | 35 | _ => item.syntax().clone(), |
33 | .start(); | 36 | } |
37 | }; | ||
38 | let indent = IndentLevel::from_node(&insert); | ||
39 | let insert = insert.text_range().start(); | ||
34 | let target = node.syntax().text_range(); | 40 | let target = node.syntax().text_range(); |
35 | 41 | ||
36 | acc.add( | 42 | acc.add( |
@@ -42,10 +48,14 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext) -> Opti | |||
42 | builder.replace(target, "Type"); | 48 | builder.replace(target, "Type"); |
43 | match ctx.config.snippet_cap { | 49 | match ctx.config.snippet_cap { |
44 | Some(cap) => { | 50 | Some(cap) => { |
45 | builder.insert_snippet(cap, insert, format!("type $0Type = {};\n\n", node)); | 51 | builder.insert_snippet( |
52 | cap, | ||
53 | insert, | ||
54 | format!("type $0Type = {};\n\n{}", node, indent), | ||
55 | ); | ||
46 | } | 56 | } |
47 | None => { | 57 | None => { |
48 | builder.insert(insert, format!("type Type = {};\n\n", node)); | 58 | builder.insert(insert, format!("type Type = {};\n\n{}", node, indent)); |
49 | } | 59 | } |
50 | } | 60 | } |
51 | }, | 61 | }, |
@@ -153,9 +163,9 @@ struct S { | |||
153 | } | 163 | } |
154 | 164 | ||
155 | #[test] | 165 | #[test] |
156 | fn extract_from_impl() { | 166 | fn extract_from_impl_or_trait() { |
157 | // When invoked in an impl, extracted type alias should be placed next to the impl, not | 167 | // When invoked in an impl/trait, extracted type alias should be placed next to the |
158 | // inside. | 168 | // impl/trait, not inside. |
159 | check_assist( | 169 | check_assist( |
160 | extract_type_alias, | 170 | extract_type_alias, |
161 | r#" | 171 | r#" |
@@ -171,5 +181,39 @@ impl S { | |||
171 | } | 181 | } |
172 | "#, | 182 | "#, |
173 | ); | 183 | ); |
184 | check_assist( | ||
185 | extract_type_alias, | ||
186 | r#" | ||
187 | trait Tr { | ||
188 | fn f() -> $0(u8, u8)$0 {} | ||
189 | } | ||
190 | "#, | ||
191 | r#" | ||
192 | type $0Type = (u8, u8); | ||
193 | |||
194 | trait Tr { | ||
195 | fn f() -> Type {} | ||
196 | } | ||
197 | "#, | ||
198 | ); | ||
199 | } | ||
200 | |||
201 | #[test] | ||
202 | fn indentation() { | ||
203 | check_assist( | ||
204 | extract_type_alias, | ||
205 | r#" | ||
206 | mod m { | ||
207 | fn f() -> $0u8$0 {} | ||
208 | } | ||
209 | "#, | ||
210 | r#" | ||
211 | mod m { | ||
212 | type $0Type = u8; | ||
213 | |||
214 | fn f() -> Type {} | ||
215 | } | ||
216 | "#, | ||
217 | ); | ||
174 | } | 218 | } |
175 | } | 219 | } |
diff --git a/crates/ide_assists/src/handlers/extract_variable.rs b/crates/ide_assists/src/handlers/extract_variable.rs index ae084c86c..46b54a5f5 100644 --- a/crates/ide_assists/src/handlers/extract_variable.rs +++ b/crates/ide_assists/src/handlers/extract_variable.rs | |||
@@ -36,6 +36,11 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
36 | return None; | 36 | return None; |
37 | } | 37 | } |
38 | let to_extract = node.ancestors().find_map(valid_target_expr)?; | 38 | let to_extract = node.ancestors().find_map(valid_target_expr)?; |
39 | if let Some(ty) = ctx.sema.type_of_expr(&to_extract) { | ||
40 | if ty.is_unit() { | ||
41 | return None; | ||
42 | } | ||
43 | } | ||
39 | let anchor = Anchor::from(&to_extract)?; | 44 | let anchor = Anchor::from(&to_extract)?; |
40 | let indent = anchor.syntax().prev_sibling_or_token()?.as_token()?.clone(); | 45 | let indent = anchor.syntax().prev_sibling_or_token()?.as_token()?.clone(); |
41 | let target = to_extract.syntax().text_range(); | 46 | let target = to_extract.syntax().text_range(); |
@@ -275,15 +280,23 @@ fn foo() { | |||
275 | check_assist( | 280 | check_assist( |
276 | extract_variable, | 281 | extract_variable, |
277 | r#" | 282 | r#" |
278 | fn foo() { | 283 | fn foo() -> i32 { |
279 | $0bar(1 + 1)$0 | 284 | $0bar(1 + 1)$0 |
280 | } | 285 | } |
286 | |||
287 | fn bar(i: i32) -> i32 { | ||
288 | i | ||
289 | } | ||
281 | "#, | 290 | "#, |
282 | r#" | 291 | r#" |
283 | fn foo() { | 292 | fn foo() -> i32 { |
284 | let $0bar = bar(1 + 1); | 293 | let $0bar = bar(1 + 1); |
285 | bar | 294 | bar |
286 | } | 295 | } |
296 | |||
297 | fn bar(i: i32) -> i32 { | ||
298 | i | ||
299 | } | ||
287 | "#, | 300 | "#, |
288 | ) | 301 | ) |
289 | } | 302 | } |
@@ -796,6 +809,22 @@ fn foo() { | |||
796 | check_assist_not_applicable(extract_variable, "fn main() { loop { $0break$0; }; }"); | 809 | check_assist_not_applicable(extract_variable, "fn main() { loop { $0break$0; }; }"); |
797 | } | 810 | } |
798 | 811 | ||
812 | #[test] | ||
813 | fn test_extract_var_unit_expr_not_applicable() { | ||
814 | check_assist_not_applicable( | ||
815 | extract_variable, | ||
816 | r#" | ||
817 | fn foo() { | ||
818 | let mut i = 3; | ||
819 | $0if i >= 0 { | ||
820 | i += 1; | ||
821 | } else { | ||
822 | i -= 1; | ||
823 | }$0 | ||
824 | }"#, | ||
825 | ); | ||
826 | } | ||
827 | |||
799 | // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic | 828 | // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic |
800 | #[test] | 829 | #[test] |
801 | fn extract_var_target() { | 830 | fn extract_var_target() { |
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs index 3d2cd739a..5a43bdd6f 100644 --- a/crates/ide_assists/src/handlers/fill_match_arms.rs +++ b/crates/ide_assists/src/handlers/fill_match_arms.rs | |||
@@ -202,7 +202,7 @@ impl ExtendedEnum { | |||
202 | fn variants(self, db: &RootDatabase) -> Vec<ExtendedVariant> { | 202 | fn variants(self, db: &RootDatabase) -> Vec<ExtendedVariant> { |
203 | match self { | 203 | match self { |
204 | ExtendedEnum::Enum(e) => { | 204 | ExtendedEnum::Enum(e) => { |
205 | e.variants(db).into_iter().map(|x| ExtendedVariant::Variant(x)).collect::<Vec<_>>() | 205 | e.variants(db).into_iter().map(ExtendedVariant::Variant).collect::<Vec<_>>() |
206 | } | 206 | } |
207 | ExtendedEnum::Bool => { | 207 | ExtendedEnum::Bool => { |
208 | Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False]) | 208 | Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False]) |
@@ -212,7 +212,7 @@ impl ExtendedEnum { | |||
212 | } | 212 | } |
213 | 213 | ||
214 | fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> { | 214 | fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> { |
215 | sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { | 215 | sema.type_of_expr(expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { |
216 | Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)), | 216 | Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)), |
217 | _ => { | 217 | _ => { |
218 | if ty.is_bool() { | 218 | if ty.is_bool() { |
@@ -228,7 +228,7 @@ fn resolve_tuple_of_enum_def( | |||
228 | sema: &Semantics<RootDatabase>, | 228 | sema: &Semantics<RootDatabase>, |
229 | expr: &ast::Expr, | 229 | expr: &ast::Expr, |
230 | ) -> Option<Vec<ExtendedEnum>> { | 230 | ) -> Option<Vec<ExtendedEnum>> { |
231 | sema.type_of_expr(&expr)? | 231 | sema.type_of_expr(expr)? |
232 | .tuple_fields(sema.db) | 232 | .tuple_fields(sema.db) |
233 | .iter() | 233 | .iter() |
234 | .map(|ty| { | 234 | .map(|ty| { |
diff --git a/crates/ide_assists/src/handlers/fix_visibility.rs b/crates/ide_assists/src/handlers/fix_visibility.rs index 89f7b2c2c..9b432e92f 100644 --- a/crates/ide_assists/src/handlers/fix_visibility.rs +++ b/crates/ide_assists/src/handlers/fix_visibility.rs | |||
@@ -43,7 +43,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O | |||
43 | _ => return None, | 43 | _ => return None, |
44 | }; | 44 | }; |
45 | 45 | ||
46 | let current_module = ctx.sema.scope(&path.syntax()).module()?; | 46 | let current_module = ctx.sema.scope(path.syntax()).module()?; |
47 | let target_module = def.module(ctx.db())?; | 47 | let target_module = def.module(ctx.db())?; |
48 | 48 | ||
49 | let vis = target_module.visibility_of(ctx.db(), &def)?; | 49 | let vis = target_module.visibility_of(ctx.db(), &def)?; |
diff --git a/crates/ide_assists/src/handlers/generate_enum_is_method.rs b/crates/ide_assists/src/handlers/generate_enum_is_method.rs index a9f71a703..24939f262 100644 --- a/crates/ide_assists/src/handlers/generate_enum_is_method.rs +++ b/crates/ide_assists/src/handlers/generate_enum_is_method.rs | |||
@@ -47,7 +47,7 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> | |||
47 | let fn_name = format!("is_{}", &to_lower_snake_case(&variant_name.text())); | 47 | let fn_name = format!("is_{}", &to_lower_snake_case(&variant_name.text())); |
48 | 48 | ||
49 | // Return early if we've found an existing new fn | 49 | // Return early if we've found an existing new fn |
50 | let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; | 50 | let impl_def = find_struct_impl(ctx, &parent_enum, &fn_name)?; |
51 | 51 | ||
52 | let target = variant.syntax().text_range(); | 52 | let target = variant.syntax().text_range(); |
53 | acc.add( | 53 | acc.add( |
diff --git a/crates/ide_assists/src/handlers/generate_enum_projection_method.rs b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs index e2f572ba3..986fb2315 100644 --- a/crates/ide_assists/src/handlers/generate_enum_projection_method.rs +++ b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs | |||
@@ -136,7 +136,7 @@ fn generate_enum_projection_method( | |||
136 | format!("{}_{}", props.fn_name_prefix, &to_lower_snake_case(&variant_name.text())); | 136 | format!("{}_{}", props.fn_name_prefix, &to_lower_snake_case(&variant_name.text())); |
137 | 137 | ||
138 | // Return early if we've found an existing new fn | 138 | // Return early if we've found an existing new fn |
139 | let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; | 139 | let impl_def = find_struct_impl(ctx, &parent_enum, &fn_name)?; |
140 | 140 | ||
141 | let target = variant.syntax().text_range(); | 141 | let target = variant.syntax().text_range(); |
142 | acc.add(AssistId(assist_id, AssistKind::Generate), assist_description, target, |builder| { | 142 | acc.add(AssistId(assist_id, AssistKind::Generate), assist_description, target, |builder| { |
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs index bc9fc524b..706c995ac 100644 --- a/crates/ide_assists/src/handlers/generate_function.rs +++ b/crates/ide_assists/src/handlers/generate_function.rs | |||
@@ -59,7 +59,7 @@ pub(crate) fn generate_function(acc: &mut Assists, ctx: &AssistContext) -> Optio | |||
59 | None => None, | 59 | None => None, |
60 | }; | 60 | }; |
61 | 61 | ||
62 | let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; | 62 | let function_builder = FunctionBuilder::from_call(ctx, &call, &path, target_module)?; |
63 | 63 | ||
64 | let target = call.syntax().text_range(); | 64 | let target = call.syntax().text_range(); |
65 | acc.add( | 65 | acc.add( |
@@ -128,12 +128,12 @@ impl FunctionBuilder { | |||
128 | file = in_file; | 128 | file = in_file; |
129 | target | 129 | target |
130 | } | 130 | } |
131 | None => next_space_for_fn_after_call_site(&call)?, | 131 | None => next_space_for_fn_after_call_site(call)?, |
132 | }; | 132 | }; |
133 | let needs_pub = target_module.is_some(); | 133 | let needs_pub = target_module.is_some(); |
134 | let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?; | 134 | let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?; |
135 | let fn_name = fn_name(&path)?; | 135 | let fn_name = fn_name(path)?; |
136 | let (type_params, params) = fn_args(ctx, target_module, &call)?; | 136 | let (type_params, params) = fn_args(ctx, target_module, call)?; |
137 | 137 | ||
138 | // should_render_snippet intends to express a rough level of confidence about | 138 | // should_render_snippet intends to express a rough level of confidence about |
139 | // the correctness of the return type. | 139 | // the correctness of the return type. |
diff --git a/crates/ide_assists/src/handlers/generate_getter.rs b/crates/ide_assists/src/handlers/generate_getter.rs index 09971226e..cc020c92c 100644 --- a/crates/ide_assists/src/handlers/generate_getter.rs +++ b/crates/ide_assists/src/handlers/generate_getter.rs | |||
@@ -75,7 +75,7 @@ pub(crate) fn generate_getter_impl( | |||
75 | if mutable { | 75 | if mutable { |
76 | format_to!(fn_name, "_mut"); | 76 | format_to!(fn_name, "_mut"); |
77 | } | 77 | } |
78 | let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?; | 78 | let impl_def = find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?; |
79 | 79 | ||
80 | let (id, label) = if mutable { | 80 | let (id, label) = if mutable { |
81 | ("generate_getter_mut", "Generate a mut getter method") | 81 | ("generate_getter_mut", "Generate a mut getter method") |
diff --git a/crates/ide_assists/src/handlers/generate_new.rs b/crates/ide_assists/src/handlers/generate_new.rs index 959a1f86c..b65e8387b 100644 --- a/crates/ide_assists/src/handlers/generate_new.rs +++ b/crates/ide_assists/src/handlers/generate_new.rs | |||
@@ -36,7 +36,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
36 | }; | 36 | }; |
37 | 37 | ||
38 | // Return early if we've found an existing new fn | 38 | // Return early if we've found an existing new fn |
39 | let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), "new")?; | 39 | let impl_def = find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), "new")?; |
40 | 40 | ||
41 | let target = strukt.syntax().text_range(); | 41 | let target = strukt.syntax().text_range(); |
42 | acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| { | 42 | acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| { |
diff --git a/crates/ide_assists/src/handlers/generate_setter.rs b/crates/ide_assists/src/handlers/generate_setter.rs index 288cf745d..5bdf6b3f4 100644 --- a/crates/ide_assists/src/handlers/generate_setter.rs +++ b/crates/ide_assists/src/handlers/generate_setter.rs | |||
@@ -39,7 +39,7 @@ pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
39 | // Return early if we've found an existing fn | 39 | // Return early if we've found an existing fn |
40 | let fn_name = to_lower_snake_case(&field_name.to_string()); | 40 | let fn_name = to_lower_snake_case(&field_name.to_string()); |
41 | let impl_def = find_struct_impl( | 41 | let impl_def = find_struct_impl( |
42 | &ctx, | 42 | ctx, |
43 | &ast::Adt::Struct(strukt.clone()), | 43 | &ast::Adt::Struct(strukt.clone()), |
44 | format!("set_{}", fn_name).as_str(), | 44 | format!("set_{}", fn_name).as_str(), |
45 | )?; | 45 | )?; |
diff --git a/crates/ide_assists/src/handlers/inline_local_variable.rs b/crates/ide_assists/src/handlers/inline_local_variable.rs index f5dafc8cb..2441dbb8b 100644 --- a/crates/ide_assists/src/handlers/inline_local_variable.rs +++ b/crates/ide_assists/src/handlers/inline_local_variable.rs | |||
@@ -182,6 +182,10 @@ fn inline_usage(ctx: &AssistContext) -> Option<InlineData> { | |||
182 | PathResolution::Local(local) => local, | 182 | PathResolution::Local(local) => local, |
183 | _ => return None, | 183 | _ => return None, |
184 | }; | 184 | }; |
185 | if local.is_mut(ctx.sema.db) { | ||
186 | cov_mark::hit!(test_not_inline_mut_variable_use); | ||
187 | return None; | ||
188 | } | ||
185 | 189 | ||
186 | let bind_pat = match local.source(ctx.db()).value { | 190 | let bind_pat = match local.source(ctx.db()).value { |
187 | Either::Left(ident) => ident, | 191 | Either::Left(ident) => ident, |
@@ -427,6 +431,19 @@ fn foo() { | |||
427 | } | 431 | } |
428 | 432 | ||
429 | #[test] | 433 | #[test] |
434 | fn test_not_inline_mut_variable_use() { | ||
435 | cov_mark::check!(test_not_inline_mut_variable_use); | ||
436 | check_assist_not_applicable( | ||
437 | inline_local_variable, | ||
438 | r" | ||
439 | fn foo() { | ||
440 | let mut a = 1 + 1; | ||
441 | a$0 + 1; | ||
442 | }", | ||
443 | ); | ||
444 | } | ||
445 | |||
446 | #[test] | ||
430 | fn test_call_expr() { | 447 | fn test_call_expr() { |
431 | check_assist( | 448 | check_assist( |
432 | inline_local_variable, | 449 | inline_local_variable, |
diff --git a/crates/ide_assists/src/handlers/remove_dbg.rs b/crates/ide_assists/src/handlers/remove_dbg.rs index c8226550f..b20fe992d 100644 --- a/crates/ide_assists/src/handlers/remove_dbg.rs +++ b/crates/ide_assists/src/handlers/remove_dbg.rs | |||
@@ -85,7 +85,7 @@ fn whitespace_start(it: SyntaxElement) -> Option<TextSize> { | |||
85 | } | 85 | } |
86 | 86 | ||
87 | fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> { | 87 | fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> { |
88 | let contents = get_valid_macrocall_contents(¯o_call, "dbg")?; | 88 | let contents = get_valid_macrocall_contents(macro_call, "dbg")?; |
89 | let macro_text_with_brackets = macro_call.token_tree()?.syntax().text(); | 89 | let macro_text_with_brackets = macro_call.token_tree()?.syntax().text(); |
90 | let macro_text_in_brackets = macro_text_with_brackets.slice(TextRange::new( | 90 | let macro_text_in_brackets = macro_text_with_brackets.slice(TextRange::new( |
91 | TextSize::of('('), | 91 | TextSize::of('('), |
diff --git a/crates/ide_assists/src/handlers/remove_unused_param.rs b/crates/ide_assists/src/handlers/remove_unused_param.rs index 2699d2861..fabfe7e93 100644 --- a/crates/ide_assists/src/handlers/remove_unused_param.rs +++ b/crates/ide_assists/src/handlers/remove_unused_param.rs | |||
@@ -37,8 +37,20 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Opt | |||
37 | _ => return None, | 37 | _ => return None, |
38 | }; | 38 | }; |
39 | let func = param.syntax().ancestors().find_map(ast::Fn::cast)?; | 39 | let func = param.syntax().ancestors().find_map(ast::Fn::cast)?; |
40 | let param_position = func.param_list()?.params().position(|it| it == param)?; | ||
41 | 40 | ||
41 | // check if fn is in impl Trait for .. | ||
42 | if func | ||
43 | .syntax() | ||
44 | .parent() // AssocItemList | ||
45 | .and_then(|x| x.parent()) | ||
46 | .and_then(ast::Impl::cast) | ||
47 | .map_or(false, |imp| imp.trait_().is_some()) | ||
48 | { | ||
49 | cov_mark::hit!(trait_impl); | ||
50 | return None; | ||
51 | } | ||
52 | |||
53 | let param_position = func.param_list()?.params().position(|it| it == param)?; | ||
42 | let fn_def = { | 54 | let fn_def = { |
43 | let func = ctx.sema.to_def(&func)?; | 55 | let func = ctx.sema.to_def(&func)?; |
44 | Definition::ModuleDef(func.into()) | 56 | Definition::ModuleDef(func.into()) |
@@ -254,6 +266,22 @@ fn main() { foo(9, 2) } | |||
254 | } | 266 | } |
255 | 267 | ||
256 | #[test] | 268 | #[test] |
269 | fn trait_impl() { | ||
270 | cov_mark::check!(trait_impl); | ||
271 | check_assist_not_applicable( | ||
272 | remove_unused_param, | ||
273 | r#" | ||
274 | trait Trait { | ||
275 | fn foo(x: i32); | ||
276 | } | ||
277 | impl Trait for () { | ||
278 | fn foo($0x: i32) {} | ||
279 | } | ||
280 | "#, | ||
281 | ); | ||
282 | } | ||
283 | |||
284 | #[test] | ||
257 | fn remove_across_files() { | 285 | fn remove_across_files() { |
258 | check_assist( | 286 | check_assist( |
259 | remove_unused_param, | 287 | remove_unused_param, |
diff --git a/crates/ide_assists/src/handlers/reorder_fields.rs b/crates/ide_assists/src/handlers/reorder_fields.rs index 933acead1..f6a926042 100644 --- a/crates/ide_assists/src/handlers/reorder_fields.rs +++ b/crates/ide_assists/src/handlers/reorder_fields.rs | |||
@@ -28,7 +28,7 @@ pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<( | |||
28 | .or_else(|| ctx.find_node_at_offset::<ast::RecordPat>().map(Either::Right))?; | 28 | .or_else(|| ctx.find_node_at_offset::<ast::RecordPat>().map(Either::Right))?; |
29 | 29 | ||
30 | let path = record.as_ref().either(|it| it.path(), |it| it.path())?; | 30 | let path = record.as_ref().either(|it| it.path(), |it| it.path())?; |
31 | let ranks = compute_fields_ranks(&path, &ctx)?; | 31 | let ranks = compute_fields_ranks(&path, ctx)?; |
32 | let get_rank_of_field = | 32 | let get_rank_of_field = |
33 | |of: Option<_>| *ranks.get(&of.unwrap_or_default()).unwrap_or(&usize::MAX); | 33 | |of: Option<_>| *ranks.get(&of.unwrap_or_default()).unwrap_or(&usize::MAX); |
34 | 34 | ||
diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs index 10d9cec31..f9474c9f5 100644 --- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs | |||
@@ -112,7 +112,7 @@ fn add_assist( | |||
112 | let insert_pos = adt.syntax().text_range().end(); | 112 | let insert_pos = adt.syntax().text_range().end(); |
113 | let impl_def_with_items = | 113 | let impl_def_with_items = |
114 | impl_def_from_trait(&ctx.sema, &annotated_name, trait_, trait_path); | 114 | impl_def_from_trait(&ctx.sema, &annotated_name, trait_, trait_path); |
115 | update_attribute(builder, &input, &trait_name, &attr); | 115 | update_attribute(builder, input, &trait_name, attr); |
116 | let trait_path = format!("{}", trait_path); | 116 | let trait_path = format!("{}", trait_path); |
117 | match (ctx.config.snippet_cap, impl_def_with_items) { | 117 | match (ctx.config.snippet_cap, impl_def_with_items) { |
118 | (None, _) => { | 118 | (None, _) => { |
diff --git a/crates/ide_assists/src/handlers/replace_if_let_with_match.rs b/crates/ide_assists/src/handlers/replace_if_let_with_match.rs index aee880625..9404aa26d 100644 --- a/crates/ide_assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ide_assists/src/handlers/replace_if_let_with_match.rs | |||
@@ -169,7 +169,7 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext) | |||
169 | } | 169 | } |
170 | 170 | ||
171 | fn is_pat_wildcard_or_sad(sema: &hir::Semantics<RootDatabase>, pat: &ast::Pat) -> bool { | 171 | fn is_pat_wildcard_or_sad(sema: &hir::Semantics<RootDatabase>, pat: &ast::Pat) -> bool { |
172 | sema.type_of_pat(&pat) | 172 | sema.type_of_pat(pat) |
173 | .and_then(|ty| TryEnum::from_ty(sema, &ty)) | 173 | .and_then(|ty| TryEnum::from_ty(sema, &ty)) |
174 | .map(|it| it.sad_pattern().syntax().text() == pat.syntax().text()) | 174 | .map(|it| it.sad_pattern().syntax().text() == pat.syntax().text()) |
175 | .unwrap_or_else(|| matches!(pat, ast::Pat::WildcardPat(_))) | 175 | .unwrap_or_else(|| matches!(pat, ast::Pat::WildcardPat(_))) |
diff --git a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs index 2f1da82c7..140e27356 100644 --- a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs +++ b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs | |||
@@ -123,7 +123,7 @@ impl TailReturnCollector { | |||
123 | fn handle_exprs(&mut self, expr: &Expr, collect_break: bool) { | 123 | fn handle_exprs(&mut self, expr: &Expr, collect_break: bool) { |
124 | match expr { | 124 | match expr { |
125 | Expr::BlockExpr(block_expr) => { | 125 | Expr::BlockExpr(block_expr) => { |
126 | self.collect_jump_exprs(&block_expr, collect_break); | 126 | self.collect_jump_exprs(block_expr, collect_break); |
127 | } | 127 | } |
128 | Expr::ReturnExpr(ret_expr) => { | 128 | Expr::ReturnExpr(ret_expr) => { |
129 | if let Some(ret_expr_arg) = &ret_expr.expr() { | 129 | if let Some(ret_expr_arg) = &ret_expr.expr() { |
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index 16af72927..331a6df2b 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs | |||
@@ -47,14 +47,14 @@ impl AssistKind { | |||
47 | } | 47 | } |
48 | 48 | ||
49 | match self { | 49 | match self { |
50 | AssistKind::None | AssistKind::Generate => return true, | 50 | AssistKind::None | AssistKind::Generate => true, |
51 | AssistKind::Refactor => match other { | 51 | AssistKind::Refactor => match other { |
52 | AssistKind::RefactorExtract | 52 | AssistKind::RefactorExtract |
53 | | AssistKind::RefactorInline | 53 | | AssistKind::RefactorInline |
54 | | AssistKind::RefactorRewrite => return true, | 54 | | AssistKind::RefactorRewrite => true, |
55 | _ => return false, | 55 | _ => false, |
56 | }, | 56 | }, |
57 | _ => return false, | 57 | _ => false, |
58 | } | 58 | } |
59 | } | 59 | } |
60 | 60 | ||
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs index 2b7c2d581..bdf9cb71c 100644 --- a/crates/ide_assists/src/tests.rs +++ b/crates/ide_assists/src/tests.rs | |||
@@ -74,7 +74,7 @@ pub(crate) fn check_assist_unresolved(assist: Handler, ra_fixture: &str) { | |||
74 | #[track_caller] | 74 | #[track_caller] |
75 | fn check_doc_test(assist_id: &str, before: &str, after: &str) { | 75 | fn check_doc_test(assist_id: &str, before: &str, after: &str) { |
76 | let after = trim_indent(after); | 76 | let after = trim_indent(after); |
77 | let (db, file_id, selection) = RootDatabase::with_range_or_offset(&before); | 77 | let (db, file_id, selection) = RootDatabase::with_range_or_offset(before); |
78 | let before = db.file_text(file_id).to_string(); | 78 | let before = db.file_text(file_id).to_string(); |
79 | let frange = FileRange { file_id, range: selection.into() }; | 79 | let frange = FileRange { file_id, range: selection.into() }; |
80 | 80 | ||
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs index 30128a24a..068df005b 100644 --- a/crates/ide_assists/src/utils.rs +++ b/crates/ide_assists/src/utils.rs | |||
@@ -492,7 +492,7 @@ pub(crate) fn add_method_to_adt( | |||
492 | let start_offset = impl_def | 492 | let start_offset = impl_def |
493 | .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) | 493 | .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) |
494 | .unwrap_or_else(|| { | 494 | .unwrap_or_else(|| { |
495 | buf = generate_impl_text(&adt, &buf); | 495 | buf = generate_impl_text(adt, &buf); |
496 | adt.syntax().text_range().end() | 496 | adt.syntax().text_range().end() |
497 | }); | 497 | }); |
498 | 498 | ||
diff --git a/crates/ide_assists/src/utils/suggest_name.rs b/crates/ide_assists/src/utils/suggest_name.rs index b3aabeab3..cb8bc8b2f 100644 --- a/crates/ide_assists/src/utils/suggest_name.rs +++ b/crates/ide_assists/src/utils/suggest_name.rs | |||
@@ -187,7 +187,7 @@ fn from_method_call(expr: &ast::Expr) -> Option<String> { | |||
187 | } | 187 | } |
188 | } | 188 | } |
189 | 189 | ||
190 | normalize(&name) | 190 | normalize(name) |
191 | } | 191 | } |
192 | 192 | ||
193 | fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> { | 193 | fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> { |
diff --git a/crates/ide_completion/Cargo.toml b/crates/ide_completion/Cargo.toml index ba81c9e04..3c45fe1cb 100644 --- a/crates/ide_completion/Cargo.toml +++ b/crates/ide_completion/Cargo.toml | |||
@@ -10,7 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = { version = "1.1", features = ["thread-local"] } | 13 | cov-mark = "2.0.0-pre.1" |
14 | itertools = "0.10.0" | 14 | itertools = "0.10.0" |
15 | log = "0.4.8" | 15 | log = "0.4.8" |
16 | rustc-hash = "1.1.0" | 16 | rustc-hash = "1.1.0" |
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs index ffdcdc930..bd90cefb2 100644 --- a/crates/ide_completion/src/completions.rs +++ b/crates/ide_completion/src/completions.rs | |||
@@ -6,7 +6,6 @@ pub(crate) mod flyimport; | |||
6 | pub(crate) mod fn_param; | 6 | pub(crate) mod fn_param; |
7 | pub(crate) mod keyword; | 7 | pub(crate) mod keyword; |
8 | pub(crate) mod lifetime; | 8 | pub(crate) mod lifetime; |
9 | pub(crate) mod macro_in_item_position; | ||
10 | pub(crate) mod mod_; | 9 | pub(crate) mod mod_; |
11 | pub(crate) mod pattern; | 10 | pub(crate) mod pattern; |
12 | pub(crate) mod postfix; | 11 | pub(crate) mod postfix; |
@@ -30,7 +29,7 @@ use crate::{ | |||
30 | macro_::render_macro, | 29 | macro_::render_macro, |
31 | pattern::{render_struct_pat, render_variant_pat}, | 30 | pattern::{render_struct_pat, render_variant_pat}, |
32 | render_field, render_resolution, render_tuple_field, | 31 | render_field, render_resolution, render_tuple_field, |
33 | type_alias::render_type_alias, | 32 | type_alias::{render_type_alias, render_type_alias_with_eq}, |
34 | RenderContext, | 33 | RenderContext, |
35 | }, | 34 | }, |
36 | CompletionContext, CompletionItem, CompletionItemKind, | 35 | CompletionContext, CompletionItem, CompletionItemKind, |
@@ -57,10 +56,16 @@ impl Builder { | |||
57 | } | 56 | } |
58 | 57 | ||
59 | impl Completions { | 58 | impl Completions { |
60 | pub(crate) fn add(&mut self, item: CompletionItem) { | 59 | fn add(&mut self, item: CompletionItem) { |
61 | self.buf.push(item) | 60 | self.buf.push(item) |
62 | } | 61 | } |
63 | 62 | ||
63 | fn add_opt(&mut self, item: Option<CompletionItem>) { | ||
64 | if let Some(item) = item { | ||
65 | self.buf.push(item) | ||
66 | } | ||
67 | } | ||
68 | |||
64 | pub(crate) fn add_all<I>(&mut self, items: I) | 69 | pub(crate) fn add_all<I>(&mut self, items: I) |
65 | where | 70 | where |
66 | I: IntoIterator, | 71 | I: IntoIterator, |
@@ -104,9 +109,10 @@ impl Completions { | |||
104 | local_name: hir::Name, | 109 | local_name: hir::Name, |
105 | resolution: &hir::ScopeDef, | 110 | resolution: &hir::ScopeDef, |
106 | ) { | 111 | ) { |
107 | if let Some(item) = render_resolution(RenderContext::new(ctx), local_name, resolution) { | 112 | if ctx.expects_type() && resolution.is_value_def() { |
108 | self.add(item); | 113 | return; |
109 | } | 114 | } |
115 | self.add_opt(render_resolution(RenderContext::new(ctx), local_name, resolution)); | ||
110 | } | 116 | } |
111 | 117 | ||
112 | pub(crate) fn add_macro( | 118 | pub(crate) fn add_macro( |
@@ -119,9 +125,7 @@ impl Completions { | |||
119 | Some(it) => it, | 125 | Some(it) => it, |
120 | None => return, | 126 | None => return, |
121 | }; | 127 | }; |
122 | if let Some(item) = render_macro(RenderContext::new(ctx), None, name, macro_) { | 128 | self.add_opt(render_macro(RenderContext::new(ctx), None, name, macro_)); |
123 | self.add(item); | ||
124 | } | ||
125 | } | 129 | } |
126 | 130 | ||
127 | pub(crate) fn add_function( | 131 | pub(crate) fn add_function( |
@@ -130,9 +134,10 @@ impl Completions { | |||
130 | func: hir::Function, | 134 | func: hir::Function, |
131 | local_name: Option<hir::Name>, | 135 | local_name: Option<hir::Name>, |
132 | ) { | 136 | ) { |
133 | if let Some(item) = render_fn(RenderContext::new(ctx), None, local_name, func) { | 137 | if ctx.expects_type() { |
134 | self.add(item) | 138 | return; |
135 | } | 139 | } |
140 | self.add_opt(render_fn(RenderContext::new(ctx), None, local_name, func)); | ||
136 | } | 141 | } |
137 | 142 | ||
138 | pub(crate) fn add_method( | 143 | pub(crate) fn add_method( |
@@ -142,10 +147,7 @@ impl Completions { | |||
142 | receiver: Option<hir::Name>, | 147 | receiver: Option<hir::Name>, |
143 | local_name: Option<hir::Name>, | 148 | local_name: Option<hir::Name>, |
144 | ) { | 149 | ) { |
145 | if let Some(item) = render_method(RenderContext::new(ctx), None, receiver, local_name, func) | 150 | self.add_opt(render_method(RenderContext::new(ctx), None, receiver, local_name, func)); |
146 | { | ||
147 | self.add(item) | ||
148 | } | ||
149 | } | 151 | } |
150 | 152 | ||
151 | pub(crate) fn add_variant_pat( | 153 | pub(crate) fn add_variant_pat( |
@@ -154,9 +156,7 @@ impl Completions { | |||
154 | variant: hir::Variant, | 156 | variant: hir::Variant, |
155 | local_name: Option<hir::Name>, | 157 | local_name: Option<hir::Name>, |
156 | ) { | 158 | ) { |
157 | if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, local_name, None) { | 159 | self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, local_name, None)); |
158 | self.add(item); | ||
159 | } | ||
160 | } | 160 | } |
161 | 161 | ||
162 | pub(crate) fn add_qualified_variant_pat( | 162 | pub(crate) fn add_qualified_variant_pat( |
@@ -165,9 +165,7 @@ impl Completions { | |||
165 | variant: hir::Variant, | 165 | variant: hir::Variant, |
166 | path: hir::ModPath, | 166 | path: hir::ModPath, |
167 | ) { | 167 | ) { |
168 | if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, None, Some(path)) { | 168 | self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, Some(path))); |
169 | self.add(item); | ||
170 | } | ||
171 | } | 169 | } |
172 | 170 | ||
173 | pub(crate) fn add_struct_pat( | 171 | pub(crate) fn add_struct_pat( |
@@ -176,21 +174,26 @@ impl Completions { | |||
176 | strukt: hir::Struct, | 174 | strukt: hir::Struct, |
177 | local_name: Option<hir::Name>, | 175 | local_name: Option<hir::Name>, |
178 | ) { | 176 | ) { |
179 | if let Some(item) = render_struct_pat(RenderContext::new(ctx), strukt, local_name) { | 177 | self.add_opt(render_struct_pat(RenderContext::new(ctx), strukt, local_name)); |
180 | self.add(item); | ||
181 | } | ||
182 | } | 178 | } |
183 | 179 | ||
184 | pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) { | 180 | pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) { |
185 | if let Some(item) = render_const(RenderContext::new(ctx), constant) { | 181 | if ctx.expects_type() { |
186 | self.add(item); | 182 | return; |
187 | } | 183 | } |
184 | self.add_opt(render_const(RenderContext::new(ctx), constant)); | ||
188 | } | 185 | } |
189 | 186 | ||
190 | pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) { | 187 | pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) { |
191 | if let Some(item) = render_type_alias(RenderContext::new(ctx), type_alias) { | 188 | self.add_opt(render_type_alias(RenderContext::new(ctx), type_alias)); |
192 | self.add(item) | 189 | } |
193 | } | 190 | |
191 | pub(crate) fn add_type_alias_with_eq( | ||
192 | &mut self, | ||
193 | ctx: &CompletionContext, | ||
194 | type_alias: hir::TypeAlias, | ||
195 | ) { | ||
196 | self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias)); | ||
194 | } | 197 | } |
195 | 198 | ||
196 | pub(crate) fn add_qualified_enum_variant( | 199 | pub(crate) fn add_qualified_enum_variant( |
@@ -209,6 +212,9 @@ impl Completions { | |||
209 | variant: hir::Variant, | 212 | variant: hir::Variant, |
210 | local_name: Option<hir::Name>, | 213 | local_name: Option<hir::Name>, |
211 | ) { | 214 | ) { |
215 | if ctx.expects_type() { | ||
216 | return; | ||
217 | } | ||
212 | let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None); | 218 | let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None); |
213 | self.add(item); | 219 | self.add(item); |
214 | } | 220 | } |
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs index c48bb9e66..6df569c2a 100644 --- a/crates/ide_completion/src/completions/attribute.rs +++ b/crates/ide_completion/src/completions/attribute.rs | |||
@@ -3,20 +3,20 @@ | |||
3 | //! This module uses a bit of static metadata to provide completions | 3 | //! This module uses a bit of static metadata to provide completions |
4 | //! for built-in attributes. | 4 | //! for built-in attributes. |
5 | 5 | ||
6 | use hir::HasAttrs; | ||
7 | use ide_db::helpers::generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES}; | ||
6 | use once_cell::sync::Lazy; | 8 | use once_cell::sync::Lazy; |
7 | use rustc_hash::{FxHashMap, FxHashSet}; | 9 | use rustc_hash::{FxHashMap, FxHashSet}; |
8 | use syntax::{algo::non_trivia_sibling, ast, AstNode, Direction, NodeOrToken, SyntaxKind, T}; | 10 | use syntax::{algo::non_trivia_sibling, ast, AstNode, Direction, NodeOrToken, SyntaxKind, T}; |
9 | 11 | ||
10 | use crate::{ | 12 | use crate::{ |
11 | context::CompletionContext, | 13 | context::CompletionContext, |
12 | generated_lint_completions::{CLIPPY_LINTS, FEATURES}, | ||
13 | item::{CompletionItem, CompletionItemKind, CompletionKind}, | 14 | item::{CompletionItem, CompletionItemKind, CompletionKind}, |
14 | Completions, | 15 | Completions, |
15 | }; | 16 | }; |
16 | 17 | ||
17 | mod derive; | 18 | mod derive; |
18 | mod lint; | 19 | mod lint; |
19 | pub(crate) use self::lint::LintCompletion; | ||
20 | 20 | ||
21 | pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | 21 | pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { |
22 | let attribute = ctx.attribute_under_caret.as_ref()?; | 22 | let attribute = ctx.attribute_under_caret.as_ref()?; |
@@ -25,7 +25,7 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) | |||
25 | "derive" => derive::complete_derive(acc, ctx, token_tree), | 25 | "derive" => derive::complete_derive(acc, ctx, token_tree), |
26 | "feature" => lint::complete_lint(acc, ctx, token_tree, FEATURES), | 26 | "feature" => lint::complete_lint(acc, ctx, token_tree, FEATURES), |
27 | "allow" | "warn" | "deny" | "forbid" => { | 27 | "allow" | "warn" | "deny" | "forbid" => { |
28 | lint::complete_lint(acc, ctx, token_tree.clone(), lint::DEFAULT_LINT_COMPLETIONS); | 28 | lint::complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINTS); |
29 | lint::complete_lint(acc, ctx, token_tree, CLIPPY_LINTS); | 29 | lint::complete_lint(acc, ctx, token_tree, CLIPPY_LINTS); |
30 | } | 30 | } |
31 | _ => (), | 31 | _ => (), |
@@ -69,7 +69,7 @@ fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attrib | |||
69 | } | 69 | } |
70 | 70 | ||
71 | if is_inner || !attr_completion.prefer_inner { | 71 | if is_inner || !attr_completion.prefer_inner { |
72 | acc.add(item.build()); | 72 | item.add_to(acc); |
73 | } | 73 | } |
74 | }; | 74 | }; |
75 | 75 | ||
@@ -82,6 +82,24 @@ fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attrib | |||
82 | None if is_inner => ATTRIBUTES.iter().for_each(add_completion), | 82 | None if is_inner => ATTRIBUTES.iter().for_each(add_completion), |
83 | None => ATTRIBUTES.iter().filter(|compl| !compl.prefer_inner).for_each(add_completion), | 83 | None => ATTRIBUTES.iter().filter(|compl| !compl.prefer_inner).for_each(add_completion), |
84 | } | 84 | } |
85 | |||
86 | // FIXME: write a test for this when we can | ||
87 | ctx.scope.process_all_names(&mut |name, scope_def| { | ||
88 | if let hir::ScopeDef::MacroDef(mac) = scope_def { | ||
89 | if mac.kind() == hir::MacroKind::Attr { | ||
90 | let mut item = CompletionItem::new( | ||
91 | CompletionKind::Attribute, | ||
92 | ctx.source_range(), | ||
93 | name.to_string(), | ||
94 | ); | ||
95 | item.kind(CompletionItemKind::Attribute); | ||
96 | if let Some(docs) = mac.docs(ctx.sema.db) { | ||
97 | item.documentation(docs); | ||
98 | } | ||
99 | item.add_to(acc); | ||
100 | } | ||
101 | } | ||
102 | }); | ||
85 | } | 103 | } |
86 | 104 | ||
87 | struct AttrCompletion { | 105 | struct AttrCompletion { |
@@ -201,7 +219,7 @@ static KIND_TO_ATTRIBUTES: Lazy<FxHashMap<SyntaxKind, &[&str]>> = Lazy::new(|| { | |||
201 | }); | 219 | }); |
202 | const EXPR_ATTRIBUTES: &[&str] = attrs!(); | 220 | const EXPR_ATTRIBUTES: &[&str] = attrs!(); |
203 | 221 | ||
204 | /// https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index | 222 | /// <https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index> |
205 | // Keep these sorted for the binary search! | 223 | // Keep these sorted for the binary search! |
206 | const ATTRIBUTES: &[AttrCompletion] = &[ | 224 | const ATTRIBUTES: &[AttrCompletion] = &[ |
207 | attr("allow(…)", Some("allow"), Some("allow(${0:lint})")), | 225 | attr("allow(…)", Some("allow"), Some("allow(${0:lint})")), |
diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs index 0bc3eab98..d526824fb 100644 --- a/crates/ide_completion/src/completions/attribute/derive.rs +++ b/crates/ide_completion/src/completions/attribute/derive.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! Completion for derives | 1 | //! Completion for derives |
2 | use hir::HasAttrs; | ||
2 | use itertools::Itertools; | 3 | use itertools::Itertools; |
3 | use rustc_hash::FxHashSet; | 4 | use rustc_hash::FxHashMap; |
4 | use syntax::ast; | 5 | use syntax::ast; |
5 | 6 | ||
6 | use crate::{ | 7 | use crate::{ |
@@ -15,66 +16,64 @@ pub(super) fn complete_derive( | |||
15 | derive_input: ast::TokenTree, | 16 | derive_input: ast::TokenTree, |
16 | ) { | 17 | ) { |
17 | if let Some(existing_derives) = super::parse_comma_sep_input(derive_input) { | 18 | if let Some(existing_derives) = super::parse_comma_sep_input(derive_input) { |
18 | for derive_completion in DEFAULT_DERIVE_COMPLETIONS | 19 | for (derive, docs) in get_derive_names_in_scope(ctx) { |
19 | .iter() | 20 | let (label, lookup) = if let Some(derive_completion) = DEFAULT_DERIVE_COMPLETIONS |
20 | .filter(|completion| !existing_derives.contains(completion.label)) | 21 | .iter() |
21 | { | 22 | .find(|derive_completion| derive_completion.label == derive) |
22 | let mut components = vec![derive_completion.label]; | 23 | { |
23 | components.extend( | 24 | let mut components = vec![derive_completion.label]; |
24 | derive_completion | 25 | components.extend( |
25 | .dependencies | 26 | derive_completion |
26 | .iter() | 27 | .dependencies |
27 | .filter(|&&dependency| !existing_derives.contains(dependency)), | 28 | .iter() |
28 | ); | 29 | .filter(|&&dependency| !existing_derives.contains(dependency)), |
29 | let lookup = components.join(", "); | 30 | ); |
30 | let label = components.iter().rev().join(", "); | 31 | let lookup = components.join(", "); |
32 | let label = components.iter().rev().join(", "); | ||
33 | (label, Some(lookup)) | ||
34 | } else { | ||
35 | (derive, None) | ||
36 | }; | ||
31 | let mut item = | 37 | let mut item = |
32 | CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label); | 38 | CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label); |
33 | item.lookup_by(lookup).kind(CompletionItemKind::Attribute); | ||
34 | item.add_to(acc); | ||
35 | } | ||
36 | |||
37 | for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) { | ||
38 | let mut item = CompletionItem::new( | ||
39 | CompletionKind::Attribute, | ||
40 | ctx.source_range(), | ||
41 | custom_derive_name, | ||
42 | ); | ||
43 | item.kind(CompletionItemKind::Attribute); | 39 | item.kind(CompletionItemKind::Attribute); |
40 | if let Some(docs) = docs { | ||
41 | item.documentation(docs); | ||
42 | } | ||
43 | if let Some(lookup) = lookup { | ||
44 | item.lookup_by(lookup); | ||
45 | } | ||
44 | item.add_to(acc); | 46 | item.add_to(acc); |
45 | } | 47 | } |
46 | } | 48 | } |
47 | } | 49 | } |
48 | 50 | ||
49 | fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> { | 51 | fn get_derive_names_in_scope( |
50 | let mut result = FxHashSet::default(); | 52 | ctx: &CompletionContext, |
53 | ) -> FxHashMap<String, Option<hir::Documentation>> { | ||
54 | let mut result = FxHashMap::default(); | ||
51 | ctx.scope.process_all_names(&mut |name, scope_def| { | 55 | ctx.scope.process_all_names(&mut |name, scope_def| { |
52 | if let hir::ScopeDef::MacroDef(mac) = scope_def { | 56 | if let hir::ScopeDef::MacroDef(mac) = scope_def { |
53 | if mac.kind() == hir::MacroKind::Derive { | 57 | if mac.kind() == hir::MacroKind::Derive { |
54 | result.insert(name.to_string()); | 58 | result.insert(name.to_string(), mac.docs(ctx.db)); |
55 | } | 59 | } |
56 | } | 60 | } |
57 | }); | 61 | }); |
58 | result | 62 | result |
59 | } | 63 | } |
60 | 64 | ||
61 | struct DeriveCompletion { | 65 | struct DeriveDependencies { |
62 | label: &'static str, | 66 | label: &'static str, |
63 | dependencies: &'static [&'static str], | 67 | dependencies: &'static [&'static str], |
64 | } | 68 | } |
65 | 69 | ||
66 | /// Standard Rust derives and the information about their dependencies | 70 | /// Standard Rust derives that have dependencies |
67 | /// (the dependencies are needed so that the main derive don't break the compilation when added) | 71 | /// (the dependencies are needed so that the main derive don't break the compilation when added) |
68 | const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[ | 72 | const DEFAULT_DERIVE_COMPLETIONS: &[DeriveDependencies] = &[ |
69 | DeriveCompletion { label: "Clone", dependencies: &[] }, | 73 | DeriveDependencies { label: "Copy", dependencies: &["Clone"] }, |
70 | DeriveCompletion { label: "Copy", dependencies: &["Clone"] }, | 74 | DeriveDependencies { label: "Eq", dependencies: &["PartialEq"] }, |
71 | DeriveCompletion { label: "Debug", dependencies: &[] }, | 75 | DeriveDependencies { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] }, |
72 | DeriveCompletion { label: "Default", dependencies: &[] }, | 76 | DeriveDependencies { label: "PartialOrd", dependencies: &["PartialEq"] }, |
73 | DeriveCompletion { label: "Hash", dependencies: &[] }, | ||
74 | DeriveCompletion { label: "PartialEq", dependencies: &[] }, | ||
75 | DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] }, | ||
76 | DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] }, | ||
77 | DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] }, | ||
78 | ]; | 77 | ]; |
79 | 78 | ||
80 | #[cfg(test)] | 79 | #[cfg(test)] |
@@ -94,6 +93,7 @@ mod tests { | |||
94 | } | 93 | } |
95 | 94 | ||
96 | #[test] | 95 | #[test] |
96 | #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures | ||
97 | fn empty_derive() { | 97 | fn empty_derive() { |
98 | check( | 98 | check( |
99 | r#"#[derive($0)] struct Test;"#, | 99 | r#"#[derive($0)] struct Test;"#, |
@@ -112,6 +112,7 @@ mod tests { | |||
112 | } | 112 | } |
113 | 113 | ||
114 | #[test] | 114 | #[test] |
115 | #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures | ||
115 | fn derive_with_input() { | 116 | fn derive_with_input() { |
116 | check( | 117 | check( |
117 | r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#, | 118 | r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#, |
@@ -129,6 +130,7 @@ mod tests { | |||
129 | } | 130 | } |
130 | 131 | ||
131 | #[test] | 132 | #[test] |
133 | #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures | ||
132 | fn derive_with_input2() { | 134 | fn derive_with_input2() { |
133 | check( | 135 | check( |
134 | r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#, | 136 | r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#, |
diff --git a/crates/ide_completion/src/completions/attribute/lint.rs b/crates/ide_completion/src/completions/attribute/lint.rs index 403630dce..ca99e9759 100644 --- a/crates/ide_completion/src/completions/attribute/lint.rs +++ b/crates/ide_completion/src/completions/attribute/lint.rs | |||
@@ -1,4 +1,5 @@ | |||
1 | //! Completion for lints | 1 | //! Completion for lints |
2 | use ide_db::helpers::generated_lints::Lint; | ||
2 | use syntax::ast; | 3 | use syntax::ast; |
3 | 4 | ||
4 | use crate::{ | 5 | use crate::{ |
@@ -11,7 +12,7 @@ pub(super) fn complete_lint( | |||
11 | acc: &mut Completions, | 12 | acc: &mut Completions, |
12 | ctx: &CompletionContext, | 13 | ctx: &CompletionContext, |
13 | derive_input: ast::TokenTree, | 14 | derive_input: ast::TokenTree, |
14 | lints_completions: &[LintCompletion], | 15 | lints_completions: &[Lint], |
15 | ) { | 16 | ) { |
16 | if let Some(existing_lints) = super::parse_comma_sep_input(derive_input) { | 17 | if let Some(existing_lints) = super::parse_comma_sep_input(derive_input) { |
17 | for lint_completion in lints_completions | 18 | for lint_completion in lints_completions |
@@ -23,136 +24,13 @@ pub(super) fn complete_lint( | |||
23 | ctx.source_range(), | 24 | ctx.source_range(), |
24 | lint_completion.label, | 25 | lint_completion.label, |
25 | ); | 26 | ); |
26 | item.kind(CompletionItemKind::Attribute).detail(lint_completion.description); | 27 | item.kind(CompletionItemKind::Attribute) |
28 | .documentation(hir::Documentation::new(lint_completion.description.to_owned())); | ||
27 | item.add_to(acc) | 29 | item.add_to(acc) |
28 | } | 30 | } |
29 | } | 31 | } |
30 | } | 32 | } |
31 | 33 | ||
32 | pub(crate) struct LintCompletion { | ||
33 | pub(crate) label: &'static str, | ||
34 | pub(crate) description: &'static str, | ||
35 | } | ||
36 | |||
37 | #[rustfmt::skip] | ||
38 | pub(super) const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[ | ||
39 | LintCompletion { label: "absolute_paths_not_starting_with_crate", description: r#"fully qualified paths that start with a module name instead of `crate`, `self`, or an extern crate name"# }, | ||
40 | LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# }, | ||
41 | LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# }, | ||
42 | LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# }, | ||
43 | LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# }, | ||
44 | LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# }, | ||
45 | LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# }, | ||
46 | LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# }, | ||
47 | LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# }, | ||
48 | LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# }, | ||
49 | LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# }, | ||
50 | LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# }, | ||
51 | LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# }, | ||
52 | LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# }, | ||
53 | LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# }, | ||
54 | LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# }, | ||
55 | LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# }, | ||
56 | LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# }, | ||
57 | LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# }, | ||
58 | LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# }, | ||
59 | LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# }, | ||
60 | LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# }, | ||
61 | LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# }, | ||
62 | LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# }, | ||
63 | LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# }, | ||
64 | LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# }, | ||
65 | LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# }, | ||
66 | LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# }, | ||
67 | LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# }, | ||
68 | LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# }, | ||
69 | LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# }, | ||
70 | LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# }, | ||
71 | LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# }, | ||
72 | LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# }, | ||
73 | LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# }, | ||
74 | LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# }, | ||
75 | LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# }, | ||
76 | LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# }, | ||
77 | LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# }, | ||
78 | LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# }, | ||
79 | LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# }, | ||
80 | LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# }, | ||
81 | LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# }, | ||
82 | LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# }, | ||
83 | LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# }, | ||
84 | LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# }, | ||
85 | LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# }, | ||
86 | LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# }, | ||
87 | LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# }, | ||
88 | LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# }, | ||
89 | LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# }, | ||
90 | LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# }, | ||
91 | LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# }, | ||
92 | LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# }, | ||
93 | LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# }, | ||
94 | LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# }, | ||
95 | LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# }, | ||
96 | LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# }, | ||
97 | LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# }, | ||
98 | LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# }, | ||
99 | LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# }, | ||
100 | LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# }, | ||
101 | LintCompletion { label: "path_statements", description: r#"path statements with no effect"# }, | ||
102 | LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# }, | ||
103 | LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# }, | ||
104 | LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# }, | ||
105 | LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# }, | ||
106 | LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# }, | ||
107 | LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# }, | ||
108 | LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# }, | ||
109 | LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# }, | ||
110 | LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# }, | ||
111 | LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# }, | ||
112 | LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# }, | ||
113 | LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# }, | ||
114 | LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# }, | ||
115 | LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# }, | ||
116 | LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# }, | ||
117 | LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# }, | ||
118 | LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# }, | ||
119 | LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# }, | ||
120 | LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# }, | ||
121 | LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# }, | ||
122 | LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# }, | ||
123 | LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# }, | ||
124 | LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# }, | ||
125 | LintCompletion { label: "unused_imports", description: r#"imports that are never used"# }, | ||
126 | LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# }, | ||
127 | LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# }, | ||
128 | LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# }, | ||
129 | LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# }, | ||
130 | LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# }, | ||
131 | LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# }, | ||
132 | LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# }, | ||
133 | LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# }, | ||
134 | LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# }, | ||
135 | LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# }, | ||
136 | LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# }, | ||
137 | LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# }, | ||
138 | LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# }, | ||
139 | LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# }, | ||
140 | LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# }, | ||
141 | LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# }, | ||
142 | LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# }, | ||
143 | LintCompletion { label: "macro_expanded_macro_exports_accessed_by_absolute_paths", description: r#"macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths"# }, | ||
144 | LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# }, | ||
145 | LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# }, | ||
146 | LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# }, | ||
147 | LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# }, | ||
148 | LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# }, | ||
149 | LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# }, | ||
150 | LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# }, | ||
151 | LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# }, | ||
152 | LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# }, | ||
153 | LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# }, | ||
154 | ]; | ||
155 | |||
156 | #[cfg(test)] | 34 | #[cfg(test)] |
157 | mod tests { | 35 | mod tests { |
158 | 36 | ||
@@ -184,4 +62,13 @@ mod tests { | |||
184 | r#"#[allow(keyword_idents, deprecated)] struct Test;"#, | 62 | r#"#[allow(keyword_idents, deprecated)] struct Test;"#, |
185 | ) | 63 | ) |
186 | } | 64 | } |
65 | |||
66 | #[test] | ||
67 | fn check_feature() { | ||
68 | check_edit( | ||
69 | "box_syntax", | ||
70 | r#"#[feature(box_$0)] struct Test;"#, | ||
71 | r#"#[feature(box_syntax)] struct Test;"#, | ||
72 | ) | ||
73 | } | ||
187 | } | 74 | } |
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs index e0a7021fd..9552875c1 100644 --- a/crates/ide_completion/src/completions/dot.rs +++ b/crates/ide_completion/src/completions/dot.rs | |||
@@ -4,7 +4,7 @@ use either::Either; | |||
4 | use hir::{HasVisibility, ScopeDef}; | 4 | use hir::{HasVisibility, ScopeDef}; |
5 | use rustc_hash::FxHashSet; | 5 | use rustc_hash::FxHashSet; |
6 | 6 | ||
7 | use crate::{context::CompletionContext, Completions}; | 7 | use crate::{context::CompletionContext, patterns::ImmediateLocation, Completions}; |
8 | 8 | ||
9 | /// Complete dot accesses, i.e. fields or methods. | 9 | /// Complete dot accesses, i.e. fields or methods. |
10 | pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | 10 | pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { |
@@ -13,12 +13,12 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | |||
13 | _ => return complete_undotted_self(acc, ctx), | 13 | _ => return complete_undotted_self(acc, ctx), |
14 | }; | 14 | }; |
15 | 15 | ||
16 | let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { | 16 | let receiver_ty = match ctx.sema.type_of_expr(dot_receiver) { |
17 | Some(ty) => ty, | 17 | Some(ty) => ty, |
18 | _ => return, | 18 | _ => return, |
19 | }; | 19 | }; |
20 | 20 | ||
21 | if ctx.is_call { | 21 | if matches!(ctx.completion_location, Some(ImmediateLocation::MethodCall { .. })) { |
22 | cov_mark::hit!(test_no_struct_field_completion_for_method_call); | 22 | cov_mark::hit!(test_no_struct_field_completion_for_method_call); |
23 | } else { | 23 | } else { |
24 | complete_fields(ctx, &receiver_ty, |field, ty| match field { | 24 | complete_fields(ctx, &receiver_ty, |field, ty| match field { |
@@ -33,7 +33,7 @@ fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) { | |||
33 | if !ctx.config.enable_self_on_the_fly { | 33 | if !ctx.config.enable_self_on_the_fly { |
34 | return; | 34 | return; |
35 | } | 35 | } |
36 | if !ctx.is_trivial_path || ctx.is_path_disallowed() { | 36 | if !ctx.is_trivial_path() || ctx.is_path_disallowed() { |
37 | return; | 37 | return; |
38 | } | 38 | } |
39 | ctx.scope.process_all_names(&mut |name, def| { | 39 | ctx.scope.process_all_names(&mut |name, def| { |
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs index d72bf13d3..30b8d44bd 100644 --- a/crates/ide_completion/src/completions/flyimport.rs +++ b/crates/ide_completion/src/completions/flyimport.rs | |||
@@ -1,10 +1,10 @@ | |||
1 | //! Feature: completion with imports-on-the-fly | 1 | //! Feature: completion with imports-on-the-fly |
2 | //! | 2 | //! |
3 | //! When completing names in the current scope, proposes additional imports from other modules or crates, | 3 | //! When completing names in the current scope, proposes additional imports from other modules or crates, |
4 | //! if they can be qualified in the scope and their name contains all symbols from the completion input. | 4 | //! if they can be qualified in the scope, and their name contains all symbols from the completion input. |
5 | //! | 5 | //! |
6 | //! To be considered applicable, the name must contain all input symbols in the given order, not necessarily adjacent. | 6 | //! To be considered applicable, the name must contain all input symbols in the given order, not necessarily adjacent. |
7 | //! If any input symbol is not lowercased, the name must contain all symbols in exact case; otherwise the contaning is checked case-insensitively. | 7 | //! If any input symbol is not lowercased, the name must contain all symbols in exact case; otherwise the containing is checked case-insensitively. |
8 | //! | 8 | //! |
9 | //! ``` | 9 | //! ``` |
10 | //! fn main() { | 10 | //! fn main() { |
@@ -23,8 +23,8 @@ | |||
23 | //! ``` | 23 | //! ``` |
24 | //! | 24 | //! |
25 | //! Also completes associated items, that require trait imports. | 25 | //! Also completes associated items, that require trait imports. |
26 | //! If any unresolved and/or partially-qualified path predeces the input, it will be taken into account. | 26 | //! If any unresolved and/or partially-qualified path precedes the input, it will be taken into account. |
27 | //! Currently, only the imports with their import path ending with the whole qialifier will be proposed | 27 | //! Currently, only the imports with their import path ending with the whole qualifier will be proposed |
28 | //! (no fuzzy matching for qualifier). | 28 | //! (no fuzzy matching for qualifier). |
29 | //! | 29 | //! |
30 | //! ``` | 30 | //! ``` |
@@ -61,14 +61,14 @@ | |||
61 | //! } | 61 | //! } |
62 | //! ``` | 62 | //! ``` |
63 | //! | 63 | //! |
64 | //! NOTE: currently, if an assoc item comes from a trait that's not currently imported and it also has an unresolved and/or partially-qualified path, | 64 | //! NOTE: currently, if an assoc item comes from a trait that's not currently imported, and it also has an unresolved and/or partially-qualified path, |
65 | //! no imports will be proposed. | 65 | //! no imports will be proposed. |
66 | //! | 66 | //! |
67 | //! .Fuzzy search details | 67 | //! .Fuzzy search details |
68 | //! | 68 | //! |
69 | //! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only | 69 | //! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only |
70 | //! (i.e. in `HashMap` in the `std::collections::HashMap` path). | 70 | //! (i.e. in `HashMap` in the `std::collections::HashMap` path). |
71 | //! For the same reasons, avoids searching for any path imports for inputs with their length less that 2 symbols | 71 | //! For the same reasons, avoids searching for any path imports for inputs with their length less than 2 symbols |
72 | //! (but shows all associated items for any input length). | 72 | //! (but shows all associated items for any input length). |
73 | //! | 73 | //! |
74 | //! .Import configuration | 74 | //! .Import configuration |
@@ -79,18 +79,17 @@ | |||
79 | //! .LSP and performance implications | 79 | //! .LSP and performance implications |
80 | //! | 80 | //! |
81 | //! The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits` | 81 | //! The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits` |
82 | //! (case sensitive) resolve client capability in its client capabilities. | 82 | //! (case-sensitive) resolve client capability in its client capabilities. |
83 | //! This way the server is able to defer the costly computations, doing them for a selected completion item only. | 83 | //! This way the server is able to defer the costly computations, doing them for a selected completion item only. |
84 | //! For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones, | 84 | //! For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones, |
85 | //! which might be slow ergo the feature is automatically disabled. | 85 | //! which might be slow ergo the feature is automatically disabled. |
86 | //! | 86 | //! |
87 | //! .Feature toggle | 87 | //! .Feature toggle |
88 | //! | 88 | //! |
89 | //! The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.enableAutoimportCompletions` flag. | 89 | //! The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.autoimport.enable` flag. |
90 | //! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding | 90 | //! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corresponding |
91 | //! capability enabled. | 91 | //! capability enabled. |
92 | 92 | ||
93 | use hir::ModPath; | ||
94 | use ide_db::helpers::{ | 93 | use ide_db::helpers::{ |
95 | import_assets::{ImportAssets, ImportCandidate}, | 94 | import_assets::{ImportAssets, ImportCandidate}, |
96 | insert_use::ImportScope, | 95 | insert_use::ImportScope, |
@@ -161,13 +160,13 @@ pub(crate) fn position_for_import<'a>( | |||
161 | ) -> Option<&'a SyntaxNode> { | 160 | ) -> Option<&'a SyntaxNode> { |
162 | Some(match import_candidate { | 161 | Some(match import_candidate { |
163 | Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(), | 162 | Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(), |
164 | Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual.as_ref()?.syntax(), | 163 | Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual()?.syntax(), |
165 | Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(), | 164 | Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(), |
166 | None => ctx | 165 | None => ctx |
167 | .name_ref_syntax | 166 | .name_ref_syntax |
168 | .as_ref() | 167 | .as_ref() |
169 | .map(|name_ref| name_ref.syntax()) | 168 | .map(|name_ref| name_ref.syntax()) |
170 | .or_else(|| ctx.path_qual.as_ref().map(|path| path.syntax())) | 169 | .or_else(|| ctx.path_qual().map(|path| path.syntax())) |
171 | .or_else(|| ctx.dot_receiver().map(|expr| expr.syntax()))?, | 170 | .or_else(|| ctx.dot_receiver().map(|expr| expr.syntax()))?, |
172 | }) | 171 | }) |
173 | } | 172 | } |
@@ -190,7 +189,7 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs | |||
190 | }; | 189 | }; |
191 | let assets_for_path = ImportAssets::for_fuzzy_path( | 190 | let assets_for_path = ImportAssets::for_fuzzy_path( |
192 | current_module, | 191 | current_module, |
193 | ctx.path_qual.clone(), | 192 | ctx.path_qual().cloned(), |
194 | fuzzy_name, | 193 | fuzzy_name, |
195 | &ctx.sema, | 194 | &ctx.sema, |
196 | approximate_node, | 195 | approximate_node, |
@@ -208,7 +207,7 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs | |||
208 | } | 207 | } |
209 | 208 | ||
210 | fn compute_fuzzy_completion_order_key( | 209 | fn compute_fuzzy_completion_order_key( |
211 | proposed_mod_path: &ModPath, | 210 | proposed_mod_path: &hir::ModPath, |
212 | user_input_lowercased: &str, | 211 | user_input_lowercased: &str, |
213 | ) -> usize { | 212 | ) -> usize { |
214 | cov_mark::hit!(certain_fuzzy_order_test); | 213 | cov_mark::hit!(certain_fuzzy_order_test); |
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs index 1a7a484a4..ba13d3707 100644 --- a/crates/ide_completion/src/completions/keyword.rs +++ b/crates/ide_completion/src/completions/keyword.rs | |||
@@ -5,8 +5,8 @@ use std::iter; | |||
5 | use syntax::{SyntaxKind, T}; | 5 | use syntax::{SyntaxKind, T}; |
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
8 | patterns::ImmediateLocation, CompletionContext, CompletionItem, CompletionItemKind, | 8 | context::PathCompletionContext, patterns::ImmediateLocation, CompletionContext, CompletionItem, |
9 | CompletionKind, Completions, | 9 | CompletionItemKind, CompletionKind, Completions, |
10 | }; | 10 | }; |
11 | 11 | ||
12 | pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) { | 12 | pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) { |
@@ -19,11 +19,12 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC | |||
19 | }; | 19 | }; |
20 | 20 | ||
21 | if ctx.use_item_syntax.is_some() { | 21 | if ctx.use_item_syntax.is_some() { |
22 | if ctx.path_qual.is_none() { | 22 | let qual = ctx.path_qual(); |
23 | if qual.is_none() { | ||
23 | kw_completion("crate::").add_to(acc); | 24 | kw_completion("crate::").add_to(acc); |
24 | } | 25 | } |
25 | kw_completion("self").add_to(acc); | 26 | kw_completion("self").add_to(acc); |
26 | if iter::successors(ctx.path_qual.clone(), |p| p.qualifier()) | 27 | if iter::successors(qual.cloned(), |p| p.qualifier()) |
27 | .all(|p| p.segment().and_then(|s| s.super_token()).is_some()) | 28 | .all(|p| p.segment().and_then(|s| s.super_token()).is_some()) |
28 | { | 29 | { |
29 | kw_completion("super::").add_to(acc); | 30 | kw_completion("super::").add_to(acc); |
@@ -127,8 +128,15 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
127 | add_keyword("mut", "mut "); | 128 | add_keyword("mut", "mut "); |
128 | } | 129 | } |
129 | 130 | ||
130 | if ctx.in_loop_body { | 131 | let (can_be_stmt, in_loop_body) = match ctx.path_context { |
131 | if ctx.can_be_stmt { | 132 | Some(PathCompletionContext { |
133 | is_trivial_path: true, can_be_stmt, in_loop_body, .. | ||
134 | }) => (can_be_stmt, in_loop_body), | ||
135 | _ => return, | ||
136 | }; | ||
137 | |||
138 | if in_loop_body { | ||
139 | if can_be_stmt { | ||
132 | add_keyword("continue", "continue;"); | 140 | add_keyword("continue", "continue;"); |
133 | add_keyword("break", "break;"); | 141 | add_keyword("break", "break;"); |
134 | } else { | 142 | } else { |
@@ -137,9 +145,6 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
137 | } | 145 | } |
138 | } | 146 | } |
139 | 147 | ||
140 | if !ctx.is_trivial_path { | ||
141 | return; | ||
142 | } | ||
143 | let fn_def = match &ctx.function_def { | 148 | let fn_def = match &ctx.function_def { |
144 | Some(it) => it, | 149 | Some(it) => it, |
145 | None => return, | 150 | None => return, |
@@ -147,7 +152,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
147 | 152 | ||
148 | add_keyword( | 153 | add_keyword( |
149 | "return", | 154 | "return", |
150 | match (ctx.can_be_stmt, fn_def.ret_type().is_some()) { | 155 | match (can_be_stmt, fn_def.ret_type().is_some()) { |
151 | (true, true) => "return $0;", | 156 | (true, true) => "return $0;", |
152 | (true, false) => "return;", | 157 | (true, false) => "return;", |
153 | (false, true) => "return $0", | 158 | (false, true) => "return $0", |
diff --git a/crates/ide_completion/src/completions/macro_in_item_position.rs b/crates/ide_completion/src/completions/macro_in_item_position.rs deleted file mode 100644 index 781b96ff1..000000000 --- a/crates/ide_completion/src/completions/macro_in_item_position.rs +++ /dev/null | |||
@@ -1,48 +0,0 @@ | |||
1 | //! Completes macro invocations used in item position. | ||
2 | |||
3 | use crate::{CompletionContext, Completions}; | ||
4 | |||
5 | // Ideally this should be removed and moved into `(un)qualified_path` respectively | ||
6 | pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) { | ||
7 | // Show only macros in top level. | ||
8 | if !ctx.expects_item() { | ||
9 | return; | ||
10 | } | ||
11 | |||
12 | ctx.scope.process_all_names(&mut |name, res| { | ||
13 | if let hir::ScopeDef::MacroDef(mac) = res { | ||
14 | acc.add_macro(ctx, Some(name.clone()), mac); | ||
15 | } | ||
16 | // FIXME: This should be done in qualified_path/unqualified_path instead? | ||
17 | if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res { | ||
18 | acc.add_resolution(ctx, name, &res); | ||
19 | } | ||
20 | }) | ||
21 | } | ||
22 | |||
23 | #[cfg(test)] | ||
24 | mod tests { | ||
25 | use expect_test::{expect, Expect}; | ||
26 | |||
27 | use crate::{test_utils::completion_list, CompletionKind}; | ||
28 | |||
29 | fn check(ra_fixture: &str, expect: Expect) { | ||
30 | let actual = completion_list(ra_fixture, CompletionKind::Reference); | ||
31 | expect.assert_eq(&actual) | ||
32 | } | ||
33 | |||
34 | #[test] | ||
35 | fn completes_macros_as_item() { | ||
36 | check( | ||
37 | r#" | ||
38 | macro_rules! foo { () => {} } | ||
39 | fn foo() {} | ||
40 | |||
41 | $0 | ||
42 | "#, | ||
43 | expect![[r#" | ||
44 | ma foo!(…) macro_rules! foo | ||
45 | "#]], | ||
46 | ) | ||
47 | } | ||
48 | } | ||
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs index 8a728c67e..1daa8595a 100644 --- a/crates/ide_completion/src/completions/pattern.rs +++ b/crates/ide_completion/src/completions/pattern.rs | |||
@@ -39,7 +39,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | |||
39 | | hir::ModuleDef::Module(..) => refutable, | 39 | | hir::ModuleDef::Module(..) => refutable, |
40 | _ => false, | 40 | _ => false, |
41 | }, | 41 | }, |
42 | hir::ScopeDef::MacroDef(_) => true, | 42 | hir::ScopeDef::MacroDef(mac) => mac.is_fn_like(), |
43 | hir::ScopeDef::ImplSelfType(impl_) => match impl_.self_ty(ctx.db).as_adt() { | 43 | hir::ScopeDef::ImplSelfType(impl_) => match impl_.self_ty(ctx.db).as_adt() { |
44 | Some(hir::Adt::Struct(strukt)) => { | 44 | Some(hir::Adt::Struct(strukt)) => { |
45 | acc.add_struct_pat(ctx, strukt, Some(name.clone())); | 45 | acc.add_struct_pat(ctx, strukt, Some(name.clone())); |
@@ -102,6 +102,28 @@ fn foo() { | |||
102 | } | 102 | } |
103 | 103 | ||
104 | #[test] | 104 | #[test] |
105 | fn does_not_complete_non_fn_macros() { | ||
106 | check( | ||
107 | r#" | ||
108 | macro_rules! m { ($e:expr) => { $e } } | ||
109 | enum E { X } | ||
110 | |||
111 | #[rustc_builtin_macro] | ||
112 | macro Clone {} | ||
113 | |||
114 | fn foo() { | ||
115 | match E::X { $0 } | ||
116 | } | ||
117 | "#, | ||
118 | expect![[r#" | ||
119 | ev E::X () | ||
120 | en E | ||
121 | ma m!(…) macro_rules! m | ||
122 | "#]], | ||
123 | ); | ||
124 | } | ||
125 | |||
126 | #[test] | ||
105 | fn completes_in_simple_macro_call() { | 127 | fn completes_in_simple_macro_call() { |
106 | check( | 128 | check( |
107 | r#" | 129 | r#" |
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs index 86bbb58e2..9f98b21be 100644 --- a/crates/ide_completion/src/completions/postfix.rs +++ b/crates/ide_completion/src/completions/postfix.rs | |||
@@ -24,7 +24,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
24 | } | 24 | } |
25 | 25 | ||
26 | let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.completion_location { | 26 | let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.completion_location { |
27 | Some(ImmediateLocation::MethodCall { receiver: Some(it) }) => (it, false), | 27 | Some(ImmediateLocation::MethodCall { receiver: Some(it), .. }) => (it, false), |
28 | Some(ImmediateLocation::FieldAccess { | 28 | Some(ImmediateLocation::FieldAccess { |
29 | receiver: Some(it), | 29 | receiver: Some(it), |
30 | receiver_is_ambiguous_float_literal, | 30 | receiver_is_ambiguous_float_literal, |
@@ -34,7 +34,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
34 | 34 | ||
35 | let receiver_text = get_receiver_text(dot_receiver, receiver_is_ambiguous_float_literal); | 35 | let receiver_text = get_receiver_text(dot_receiver, receiver_is_ambiguous_float_literal); |
36 | 36 | ||
37 | let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { | 37 | let receiver_ty = match ctx.sema.type_of_expr(dot_receiver) { |
38 | Some(it) => it, | 38 | Some(it) => it, |
39 | None => return, | 39 | None => return, |
40 | }; | 40 | }; |
@@ -50,7 +50,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
50 | postfix_snippet( | 50 | postfix_snippet( |
51 | ctx, | 51 | ctx, |
52 | cap, | 52 | cap, |
53 | &dot_receiver, | 53 | dot_receiver, |
54 | "ifl", | 54 | "ifl", |
55 | "if let Ok {}", | 55 | "if let Ok {}", |
56 | &format!("if let Ok($1) = {} {{\n $0\n}}", receiver_text), | 56 | &format!("if let Ok($1) = {} {{\n $0\n}}", receiver_text), |
@@ -60,7 +60,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
60 | postfix_snippet( | 60 | postfix_snippet( |
61 | ctx, | 61 | ctx, |
62 | cap, | 62 | cap, |
63 | &dot_receiver, | 63 | dot_receiver, |
64 | "while", | 64 | "while", |
65 | "while let Ok {}", | 65 | "while let Ok {}", |
66 | &format!("while let Ok($1) = {} {{\n $0\n}}", receiver_text), | 66 | &format!("while let Ok($1) = {} {{\n $0\n}}", receiver_text), |
@@ -71,7 +71,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
71 | postfix_snippet( | 71 | postfix_snippet( |
72 | ctx, | 72 | ctx, |
73 | cap, | 73 | cap, |
74 | &dot_receiver, | 74 | dot_receiver, |
75 | "ifl", | 75 | "ifl", |
76 | "if let Some {}", | 76 | "if let Some {}", |
77 | &format!("if let Some($1) = {} {{\n $0\n}}", receiver_text), | 77 | &format!("if let Some($1) = {} {{\n $0\n}}", receiver_text), |
@@ -81,7 +81,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
81 | postfix_snippet( | 81 | postfix_snippet( |
82 | ctx, | 82 | ctx, |
83 | cap, | 83 | cap, |
84 | &dot_receiver, | 84 | dot_receiver, |
85 | "while", | 85 | "while", |
86 | "while let Some {}", | 86 | "while let Some {}", |
87 | &format!("while let Some($1) = {} {{\n $0\n}}", receiver_text), | 87 | &format!("while let Some($1) = {} {{\n $0\n}}", receiver_text), |
@@ -93,7 +93,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
93 | postfix_snippet( | 93 | postfix_snippet( |
94 | ctx, | 94 | ctx, |
95 | cap, | 95 | cap, |
96 | &dot_receiver, | 96 | dot_receiver, |
97 | "if", | 97 | "if", |
98 | "if expr {}", | 98 | "if expr {}", |
99 | &format!("if {} {{\n $0\n}}", receiver_text), | 99 | &format!("if {} {{\n $0\n}}", receiver_text), |
@@ -102,22 +102,22 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
102 | postfix_snippet( | 102 | postfix_snippet( |
103 | ctx, | 103 | ctx, |
104 | cap, | 104 | cap, |
105 | &dot_receiver, | 105 | dot_receiver, |
106 | "while", | 106 | "while", |
107 | "while expr {}", | 107 | "while expr {}", |
108 | &format!("while {} {{\n $0\n}}", receiver_text), | 108 | &format!("while {} {{\n $0\n}}", receiver_text), |
109 | ) | 109 | ) |
110 | .add_to(acc); | 110 | .add_to(acc); |
111 | postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text)) | 111 | postfix_snippet(ctx, cap, dot_receiver, "not", "!expr", &format!("!{}", receiver_text)) |
112 | .add_to(acc); | 112 | .add_to(acc); |
113 | } | 113 | } |
114 | 114 | ||
115 | postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text)) | 115 | postfix_snippet(ctx, cap, dot_receiver, "ref", "&expr", &format!("&{}", receiver_text)) |
116 | .add_to(acc); | 116 | .add_to(acc); |
117 | postfix_snippet( | 117 | postfix_snippet( |
118 | ctx, | 118 | ctx, |
119 | cap, | 119 | cap, |
120 | &dot_receiver, | 120 | dot_receiver, |
121 | "refm", | 121 | "refm", |
122 | "&mut expr", | 122 | "&mut expr", |
123 | &format!("&mut {}", receiver_text), | 123 | &format!("&mut {}", receiver_text), |
diff --git a/crates/ide_completion/src/completions/postfix/format_like.rs b/crates/ide_completion/src/completions/postfix/format_like.rs index 0dcb3e898..2dc13c293 100644 --- a/crates/ide_completion/src/completions/postfix/format_like.rs +++ b/crates/ide_completion/src/completions/postfix/format_like.rs | |||
@@ -4,15 +4,15 @@ | |||
4 | // | 4 | // |
5 | // The following postfix snippets are available: | 5 | // The following postfix snippets are available: |
6 | // | 6 | // |
7 | // - `format` -> `format!(...)` | 7 | // * `format` -> `format!(...)` |
8 | // - `panic` -> `panic!(...)` | 8 | // * `panic` -> `panic!(...)` |
9 | // - `println` -> `println!(...)` | 9 | // * `println` -> `println!(...)` |
10 | // - `log`: | 10 | // * `log`: |
11 | // + `logd` -> `log::debug!(...)` | 11 | // ** `logd` -> `log::debug!(...)` |
12 | // + `logt` -> `log::trace!(...)` | 12 | // ** `logt` -> `log::trace!(...)` |
13 | // + `logi` -> `log::info!(...)` | 13 | // ** `logi` -> `log::info!(...)` |
14 | // + `logw` -> `log::warn!(...)` | 14 | // ** `logw` -> `log::warn!(...)` |
15 | // + `loge` -> `log::error!(...)` | 15 | // ** `loge` -> `log::error!(...)` |
16 | // | 16 | // |
17 | // image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[] | 17 | // image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[] |
18 | 18 | ||
@@ -53,7 +53,7 @@ pub(crate) fn add_format_like_completions( | |||
53 | for (label, macro_name) in KINDS { | 53 | for (label, macro_name) in KINDS { |
54 | let snippet = parser.into_suggestion(macro_name); | 54 | let snippet = parser.into_suggestion(macro_name); |
55 | 55 | ||
56 | postfix_snippet(ctx, cap, &dot_receiver, label, macro_name, &snippet).add_to(acc); | 56 | postfix_snippet(ctx, cap, dot_receiver, label, macro_name, &snippet).add_to(acc); |
57 | } | 57 | } |
58 | } | 58 | } |
59 | } | 59 | } |
@@ -91,7 +91,7 @@ enum State { | |||
91 | impl FormatStrParser { | 91 | impl FormatStrParser { |
92 | pub(crate) fn new(input: String) -> Self { | 92 | pub(crate) fn new(input: String) -> Self { |
93 | Self { | 93 | Self { |
94 | input: input, | 94 | input, |
95 | output: String::new(), | 95 | output: String::new(), |
96 | extracted_expressions: Vec::new(), | 96 | extracted_expressions: Vec::new(), |
97 | state: State::NotExpr, | 97 | state: State::NotExpr, |
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs index de58ce1cd..6083537b7 100644 --- a/crates/ide_completion/src/completions/qualified_path.rs +++ b/crates/ide_completion/src/completions/qualified_path.rs | |||
@@ -7,25 +7,28 @@ use syntax::AstNode; | |||
7 | use crate::{CompletionContext, Completions}; | 7 | use crate::{CompletionContext, Completions}; |
8 | 8 | ||
9 | pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { | 9 | pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { |
10 | if ctx.is_path_disallowed() || ctx.expects_item() { | 10 | if ctx.is_path_disallowed() { |
11 | return; | 11 | return; |
12 | } | 12 | } |
13 | let path = match &ctx.path_qual { | 13 | let path = match ctx.path_qual() { |
14 | Some(path) => path.clone(), | 14 | Some(path) => path, |
15 | None => return, | 15 | None => return, |
16 | }; | 16 | }; |
17 | 17 | ||
18 | let resolution = match ctx.sema.resolve_path(&path) { | 18 | let resolution = match ctx.sema.resolve_path(path) { |
19 | Some(res) => res, | 19 | Some(res) => res, |
20 | None => return, | 20 | None => return, |
21 | }; | 21 | }; |
22 | let context_module = ctx.scope.module(); | 22 | let context_module = ctx.scope.module(); |
23 | if ctx.expects_assoc_item() { | 23 | |
24 | if ctx.expects_item() || ctx.expects_assoc_item() { | ||
24 | if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution { | 25 | if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution { |
25 | let module_scope = module.scope(ctx.db, context_module); | 26 | let module_scope = module.scope(ctx.db, context_module); |
26 | for (name, def) in module_scope { | 27 | for (name, def) in module_scope { |
27 | if let hir::ScopeDef::MacroDef(macro_def) = def { | 28 | if let hir::ScopeDef::MacroDef(macro_def) = def { |
28 | acc.add_macro(ctx, Some(name.clone()), macro_def); | 29 | if macro_def.is_fn_like() { |
30 | acc.add_macro(ctx, Some(name.clone()), macro_def); | ||
31 | } | ||
29 | } | 32 | } |
30 | if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def { | 33 | if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def { |
31 | acc.add_resolution(ctx, name, &def); | 34 | acc.add_resolution(ctx, name, &def); |
@@ -57,6 +60,13 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
57 | } | 60 | } |
58 | } | 61 | } |
59 | 62 | ||
63 | if let hir::ScopeDef::MacroDef(macro_def) = def { | ||
64 | if !macro_def.is_fn_like() { | ||
65 | // Don't suggest attribute macros and derives. | ||
66 | continue; | ||
67 | } | ||
68 | } | ||
69 | |||
60 | acc.add_resolution(ctx, name, &def); | 70 | acc.add_resolution(ctx, name, &def); |
61 | } | 71 | } |
62 | } | 72 | } |
@@ -198,6 +208,36 @@ mod tests { | |||
198 | } | 208 | } |
199 | 209 | ||
200 | #[test] | 210 | #[test] |
211 | fn dont_complete_values_in_type_pos() { | ||
212 | check( | ||
213 | r#" | ||
214 | const FOO: () = (); | ||
215 | static BAR: () = (); | ||
216 | struct Baz; | ||
217 | fn foo() { | ||
218 | let _: self::$0; | ||
219 | } | ||
220 | "#, | ||
221 | expect![[r#" | ||
222 | st Baz | ||
223 | "#]], | ||
224 | ); | ||
225 | } | ||
226 | |||
227 | #[test] | ||
228 | fn dont_complete_enum_variants_in_type_pos() { | ||
229 | check( | ||
230 | r#" | ||
231 | enum Foo { Bar } | ||
232 | fn foo() { | ||
233 | let _: Foo::$0; | ||
234 | } | ||
235 | "#, | ||
236 | expect![[r#""#]], | ||
237 | ); | ||
238 | } | ||
239 | |||
240 | #[test] | ||
201 | fn dont_complete_current_use_in_braces_with_glob() { | 241 | fn dont_complete_current_use_in_braces_with_glob() { |
202 | check( | 242 | check( |
203 | r#" | 243 | r#" |
@@ -617,6 +657,32 @@ fn main() { let _ = crate::$0 } | |||
617 | } | 657 | } |
618 | 658 | ||
619 | #[test] | 659 | #[test] |
660 | fn does_not_complete_non_fn_macros() { | ||
661 | check( | ||
662 | r#" | ||
663 | mod m { | ||
664 | #[rustc_builtin_macro] | ||
665 | pub macro Clone {} | ||
666 | } | ||
667 | |||
668 | fn f() {m::$0} | ||
669 | "#, | ||
670 | expect![[r#""#]], | ||
671 | ); | ||
672 | check( | ||
673 | r#" | ||
674 | mod m { | ||
675 | #[rustc_builtin_macro] | ||
676 | pub macro bench {} | ||
677 | } | ||
678 | |||
679 | fn f() {m::$0} | ||
680 | "#, | ||
681 | expect![[r#""#]], | ||
682 | ); | ||
683 | } | ||
684 | |||
685 | #[test] | ||
620 | fn completes_in_assoc_item_list() { | 686 | fn completes_in_assoc_item_list() { |
621 | check( | 687 | check( |
622 | r#" | 688 | r#" |
@@ -631,17 +697,17 @@ impl MyStruct { | |||
631 | "#, | 697 | "#, |
632 | expect![[r##" | 698 | expect![[r##" |
633 | md bar | 699 | md bar |
634 | ma foo! #[macro_export] macro_rules! foo | 700 | ma foo!(…) #[macro_export] macro_rules! foo |
635 | "##]], | 701 | "##]], |
636 | ); | 702 | ); |
637 | } | 703 | } |
638 | 704 | ||
639 | #[test] | 705 | #[test] |
640 | #[ignore] // FIXME doesn't complete anything atm | ||
641 | fn completes_in_item_list() { | 706 | fn completes_in_item_list() { |
642 | check( | 707 | check( |
643 | r#" | 708 | r#" |
644 | struct MyStruct {} | 709 | struct MyStruct {} |
710 | #[macro_export] | ||
645 | macro_rules! foo {} | 711 | macro_rules! foo {} |
646 | mod bar {} | 712 | mod bar {} |
647 | 713 | ||
@@ -649,7 +715,7 @@ crate::$0 | |||
649 | "#, | 715 | "#, |
650 | expect![[r#" | 716 | expect![[r#" |
651 | md bar | 717 | md bar |
652 | ma foo! macro_rules! foo | 718 | ma foo!(…) #[macro_export] macro_rules! foo |
653 | "#]], | 719 | "#]], |
654 | ) | 720 | ) |
655 | } | 721 | } |
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs index 6e6a6eb92..b9862de67 100644 --- a/crates/ide_completion/src/completions/snippet.rs +++ b/crates/ide_completion/src/completions/snippet.rs | |||
@@ -3,8 +3,8 @@ | |||
3 | use ide_db::helpers::SnippetCap; | 3 | use ide_db::helpers::SnippetCap; |
4 | 4 | ||
5 | use crate::{ | 5 | use crate::{ |
6 | item::Builder, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, | 6 | context::PathCompletionContext, item::Builder, CompletionContext, CompletionItem, |
7 | Completions, | 7 | CompletionItemKind, CompletionKind, Completions, |
8 | }; | 8 | }; |
9 | 9 | ||
10 | fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder { | 10 | fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder { |
@@ -14,15 +14,21 @@ fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) | |||
14 | } | 14 | } |
15 | 15 | ||
16 | pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { | 16 | pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { |
17 | if !(ctx.is_trivial_path && ctx.function_def.is_some()) { | 17 | if ctx.function_def.is_none() { |
18 | return; | 18 | return; |
19 | } | 19 | } |
20 | |||
21 | let can_be_stmt = match ctx.path_context { | ||
22 | Some(PathCompletionContext { is_trivial_path: true, can_be_stmt, .. }) => can_be_stmt, | ||
23 | _ => return, | ||
24 | }; | ||
25 | |||
20 | let cap = match ctx.config.snippet_cap { | 26 | let cap = match ctx.config.snippet_cap { |
21 | Some(it) => it, | 27 | Some(it) => it, |
22 | None => return, | 28 | None => return, |
23 | }; | 29 | }; |
24 | 30 | ||
25 | if ctx.can_be_stmt { | 31 | if can_be_stmt { |
26 | snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); | 32 | snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); |
27 | snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc); | 33 | snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc); |
28 | } | 34 | } |
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs index 968c0254d..a60e5f43c 100644 --- a/crates/ide_completion/src/completions/trait_impl.rs +++ b/crates/ide_completion/src/completions/trait_impl.rs | |||
@@ -34,20 +34,13 @@ | |||
34 | use hir::{self, HasAttrs, HasSource}; | 34 | use hir::{self, HasAttrs, HasSource}; |
35 | use ide_db::{traits::get_missing_assoc_items, SymbolKind}; | 35 | use ide_db::{traits::get_missing_assoc_items, SymbolKind}; |
36 | use syntax::{ | 36 | use syntax::{ |
37 | ast::{self, edit, Impl}, | 37 | ast::{self, edit}, |
38 | display::function_declaration, | 38 | display::function_declaration, |
39 | AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, T, | 39 | AstNode, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, T, |
40 | }; | 40 | }; |
41 | use text_edit::TextEdit; | 41 | use text_edit::TextEdit; |
42 | 42 | ||
43 | use crate::{ | 43 | use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions}; |
44 | CompletionContext, | ||
45 | CompletionItem, | ||
46 | CompletionItemKind, | ||
47 | CompletionKind, | ||
48 | Completions, | ||
49 | // display::function_declaration, | ||
50 | }; | ||
51 | 44 | ||
52 | #[derive(Debug, PartialEq, Eq)] | 45 | #[derive(Debug, PartialEq, Eq)] |
53 | enum ImplCompletionKind { | 46 | enum ImplCompletionKind { |
@@ -58,7 +51,7 @@ enum ImplCompletionKind { | |||
58 | } | 51 | } |
59 | 52 | ||
60 | pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { | 53 | pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { |
61 | if let Some((kind, trigger, impl_def)) = completion_match(ctx) { | 54 | if let Some((kind, trigger, impl_def)) = completion_match(ctx.token.clone()) { |
62 | get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| match item { | 55 | get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| match item { |
63 | hir::AssocItem::Function(fn_item) | 56 | hir::AssocItem::Function(fn_item) |
64 | if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn => | 57 | if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn => |
@@ -80,8 +73,7 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext | |||
80 | } | 73 | } |
81 | } | 74 | } |
82 | 75 | ||
83 | fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, SyntaxNode, Impl)> { | 76 | fn completion_match(mut token: SyntaxToken) -> Option<(ImplCompletionKind, SyntaxNode, ast::Impl)> { |
84 | let mut token = ctx.token.clone(); | ||
85 | // For keyword without name like `impl .. { fn $0 }`, the current position is inside | 77 | // For keyword without name like `impl .. { fn $0 }`, the current position is inside |
86 | // the whitespace token, which is outside `FN` syntax node. | 78 | // the whitespace token, which is outside `FN` syntax node. |
87 | // We need to follow the previous token in this case. | 79 | // We need to follow the previous token in this case. |
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index bd955aa85..952f052a1 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs | |||
@@ -1,30 +1,32 @@ | |||
1 | //! Completion of names from the current scope, e.g. locals and imported items. | 1 | //! Completion of names from the current scope, e.g. locals and imported items. |
2 | 2 | ||
3 | use hir::ScopeDef; | 3 | use hir::ScopeDef; |
4 | use syntax::{ast, AstNode}; | ||
4 | 5 | ||
5 | use crate::{CompletionContext, Completions}; | 6 | use crate::{patterns::ImmediateLocation, CompletionContext, Completions}; |
6 | 7 | ||
7 | pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { | 8 | pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { |
8 | if !ctx.is_trivial_path { | 9 | if ctx.is_path_disallowed() || !ctx.is_trivial_path() { |
9 | return; | ||
10 | } | ||
11 | if ctx.is_path_disallowed() || ctx.expects_item() { | ||
12 | return; | 10 | return; |
13 | } | 11 | } |
14 | 12 | ||
15 | if ctx.expects_assoc_item() { | 13 | if ctx.expects_item() || ctx.expects_assoc_item() { |
16 | ctx.scope.process_all_names(&mut |name, def| { | 14 | // only show macros in {Assoc}ItemList |
17 | if let ScopeDef::MacroDef(macro_def) = def { | 15 | ctx.scope.process_all_names(&mut |name, res| { |
18 | acc.add_macro(ctx, Some(name.clone()), macro_def); | 16 | if let hir::ScopeDef::MacroDef(mac) = res { |
17 | if mac.is_fn_like() { | ||
18 | acc.add_macro(ctx, Some(name.clone()), mac); | ||
19 | } | ||
19 | } | 20 | } |
20 | if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def { | 21 | if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res { |
21 | acc.add_resolution(ctx, name, &def); | 22 | acc.add_resolution(ctx, name, &res); |
22 | } | 23 | } |
23 | }); | 24 | }); |
24 | return; | 25 | return; |
25 | } | 26 | } |
26 | 27 | ||
27 | if ctx.expects_use_tree() { | 28 | if ctx.expects_use_tree() { |
29 | // only show modules in a fresh UseTree | ||
28 | cov_mark::hit!(only_completes_modules_in_import); | 30 | cov_mark::hit!(only_completes_modules_in_import); |
29 | ctx.scope.process_all_names(&mut |name, res| { | 31 | ctx.scope.process_all_names(&mut |name, res| { |
30 | if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res { | 32 | if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res { |
@@ -42,12 +44,32 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
42 | }); | 44 | }); |
43 | } | 45 | } |
44 | 46 | ||
47 | if let Some(ImmediateLocation::GenericArgList(arg_list)) = &ctx.completion_location { | ||
48 | if let Some(path_seg) = arg_list.syntax().parent().and_then(ast::PathSegment::cast) { | ||
49 | if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(trait_))) = | ||
50 | ctx.sema.resolve_path(&path_seg.parent_path()) | ||
51 | { | ||
52 | trait_.items(ctx.sema.db).into_iter().for_each(|it| { | ||
53 | if let hir::AssocItem::TypeAlias(alias) = it { | ||
54 | acc.add_type_alias_with_eq(ctx, alias) | ||
55 | } | ||
56 | }); | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | |||
45 | ctx.scope.process_all_names(&mut |name, res| { | 61 | ctx.scope.process_all_names(&mut |name, res| { |
46 | if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res { | 62 | if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res { |
47 | cov_mark::hit!(skip_lifetime_completion); | 63 | cov_mark::hit!(skip_lifetime_completion); |
48 | return; | 64 | return; |
49 | } | 65 | } |
50 | acc.add_resolution(ctx, name, &res); | 66 | let add_resolution = match res { |
67 | ScopeDef::MacroDef(mac) => mac.is_fn_like(), | ||
68 | _ => true, | ||
69 | }; | ||
70 | if add_resolution { | ||
71 | acc.add_resolution(ctx, name, &res); | ||
72 | } | ||
51 | }); | 73 | }); |
52 | } | 74 | } |
53 | 75 | ||
@@ -70,6 +92,28 @@ mod tests { | |||
70 | } | 92 | } |
71 | 93 | ||
72 | #[test] | 94 | #[test] |
95 | fn dont_complete_values_in_type_pos() { | ||
96 | check( | ||
97 | r#" | ||
98 | const FOO: () = (); | ||
99 | static BAR: () = (); | ||
100 | enum Foo { | ||
101 | Bar | ||
102 | } | ||
103 | struct Baz; | ||
104 | fn foo() { | ||
105 | let local = (); | ||
106 | let _: $0; | ||
107 | } | ||
108 | "#, | ||
109 | expect![[r#" | ||
110 | en Foo | ||
111 | st Baz | ||
112 | "#]], | ||
113 | ); | ||
114 | } | ||
115 | |||
116 | #[test] | ||
73 | fn only_completes_modules_in_import() { | 117 | fn only_completes_modules_in_import() { |
74 | cov_mark::check!(only_completes_modules_in_import); | 118 | cov_mark::check!(only_completes_modules_in_import); |
75 | check( | 119 | check( |
@@ -340,7 +384,6 @@ fn x() -> $0 | |||
340 | "#, | 384 | "#, |
341 | expect![[r#" | 385 | expect![[r#" |
342 | st Foo | 386 | st Foo |
343 | fn x() fn() | ||
344 | "#]], | 387 | "#]], |
345 | ); | 388 | ); |
346 | } | 389 | } |
@@ -392,7 +435,6 @@ pub mod prelude { | |||
392 | } | 435 | } |
393 | "#, | 436 | "#, |
394 | expect![[r#" | 437 | expect![[r#" |
395 | fn foo() fn() | ||
396 | md std | 438 | md std |
397 | st Option | 439 | st Option |
398 | "#]], | 440 | "#]], |
@@ -428,6 +470,44 @@ mod macros { | |||
428 | } | 470 | } |
429 | 471 | ||
430 | #[test] | 472 | #[test] |
473 | fn does_not_complete_non_fn_macros() { | ||
474 | check( | ||
475 | r#" | ||
476 | #[rustc_builtin_macro] | ||
477 | pub macro Clone {} | ||
478 | |||
479 | fn f() {$0} | ||
480 | "#, | ||
481 | expect![[r#" | ||
482 | fn f() fn() | ||
483 | "#]], | ||
484 | ); | ||
485 | check( | ||
486 | r#" | ||
487 | #[rustc_builtin_macro] | ||
488 | pub macro Clone {} | ||
489 | |||
490 | struct S; | ||
491 | impl S { | ||
492 | $0 | ||
493 | } | ||
494 | "#, | ||
495 | expect![[r#""#]], | ||
496 | ); | ||
497 | check( | ||
498 | r#" | ||
499 | #[rustc_builtin_macro] | ||
500 | pub macro bench {} | ||
501 | |||
502 | fn f() {$0} | ||
503 | "#, | ||
504 | expect![[r#" | ||
505 | fn f() fn() | ||
506 | "#]], | ||
507 | ); | ||
508 | } | ||
509 | |||
510 | #[test] | ||
431 | fn completes_std_prelude_if_core_is_defined() { | 511 | fn completes_std_prelude_if_core_is_defined() { |
432 | check( | 512 | check( |
433 | r#" | 513 | r#" |
@@ -449,7 +529,6 @@ pub mod prelude { | |||
449 | } | 529 | } |
450 | "#, | 530 | "#, |
451 | expect![[r#" | 531 | expect![[r#" |
452 | fn foo() fn() | ||
453 | md std | 532 | md std |
454 | md core | 533 | md core |
455 | st String | 534 | st String |
@@ -510,7 +589,6 @@ macro_rules! foo { () => {} } | |||
510 | fn main() { let x: $0 } | 589 | fn main() { let x: $0 } |
511 | "#, | 590 | "#, |
512 | expect![[r#" | 591 | expect![[r#" |
513 | fn main() fn() | ||
514 | ma foo!(…) macro_rules! foo | 592 | ma foo!(…) macro_rules! foo |
515 | "#]], | 593 | "#]], |
516 | ); | 594 | ); |
@@ -693,12 +771,11 @@ impl MyStruct { | |||
693 | "#, | 771 | "#, |
694 | expect![[r#" | 772 | expect![[r#" |
695 | md bar | 773 | md bar |
696 | ma foo! macro_rules! foo | 774 | ma foo!(…) macro_rules! foo |
697 | "#]], | 775 | "#]], |
698 | ) | 776 | ) |
699 | } | 777 | } |
700 | 778 | ||
701 | // FIXME: The completions here currently come from `macro_in_item_position`, but they shouldn't | ||
702 | #[test] | 779 | #[test] |
703 | fn completes_in_item_list() { | 780 | fn completes_in_item_list() { |
704 | check( | 781 | check( |
@@ -715,4 +792,21 @@ $0 | |||
715 | "#]], | 792 | "#]], |
716 | ) | 793 | ) |
717 | } | 794 | } |
795 | |||
796 | #[test] | ||
797 | fn completes_assoc_types_in_dynimpl_trait() { | ||
798 | check( | ||
799 | r#" | ||
800 | trait Foo { | ||
801 | type Bar; | ||
802 | } | ||
803 | |||
804 | fn foo(_: impl Foo<B$0>) {} | ||
805 | "#, | ||
806 | expect![[r#" | ||
807 | ta Bar = type Bar; | ||
808 | tt Foo | ||
809 | "#]], | ||
810 | ); | ||
811 | } | ||
718 | } | 812 | } |
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 6f685c02f..4c3929a26 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -29,6 +29,34 @@ pub(crate) enum PatternRefutability { | |||
29 | Irrefutable, | 29 | Irrefutable, |
30 | } | 30 | } |
31 | 31 | ||
32 | #[derive(Debug)] | ||
33 | pub(super) enum PathKind { | ||
34 | Expr, | ||
35 | Type, | ||
36 | } | ||
37 | |||
38 | #[derive(Debug)] | ||
39 | pub(crate) struct PathCompletionContext { | ||
40 | /// If this is a call with () already there | ||
41 | call_kind: Option<CallKind>, | ||
42 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. | ||
43 | pub(super) is_trivial_path: bool, | ||
44 | /// If not a trivial path, the prefix (qualifier). | ||
45 | pub(super) qualifier: Option<ast::Path>, | ||
46 | pub(super) kind: Option<PathKind>, | ||
47 | /// Whether the path segment has type args or not. | ||
48 | pub(super) has_type_args: bool, | ||
49 | /// `true` if we are a statement or a last expr in the block. | ||
50 | pub(super) can_be_stmt: bool, | ||
51 | pub(super) in_loop_body: bool, | ||
52 | } | ||
53 | |||
54 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
55 | pub(crate) enum CallKind { | ||
56 | Pat, | ||
57 | Mac, | ||
58 | Expr, | ||
59 | } | ||
32 | /// `CompletionContext` is created early during completion to figure out, where | 60 | /// `CompletionContext` is created early during completion to figure out, where |
33 | /// exactly is the cursor, syntax-wise. | 61 | /// exactly is the cursor, syntax-wise. |
34 | #[derive(Debug)] | 62 | #[derive(Debug)] |
@@ -45,14 +73,13 @@ pub(crate) struct CompletionContext<'a> { | |||
45 | pub(super) krate: Option<hir::Crate>, | 73 | pub(super) krate: Option<hir::Crate>, |
46 | pub(super) expected_name: Option<NameOrNameRef>, | 74 | pub(super) expected_name: Option<NameOrNameRef>, |
47 | pub(super) expected_type: Option<Type>, | 75 | pub(super) expected_type: Option<Type>, |
48 | pub(super) name_ref_syntax: Option<ast::NameRef>, | ||
49 | |||
50 | pub(super) use_item_syntax: Option<ast::Use>, | ||
51 | 76 | ||
52 | /// The parent function of the cursor position if it exists. | 77 | /// The parent function of the cursor position if it exists. |
53 | pub(super) function_def: Option<ast::Fn>, | 78 | pub(super) function_def: Option<ast::Fn>, |
54 | /// The parent impl of the cursor position if it exists. | 79 | /// The parent impl of the cursor position if it exists. |
55 | pub(super) impl_def: Option<ast::Impl>, | 80 | pub(super) impl_def: Option<ast::Impl>, |
81 | pub(super) name_ref_syntax: Option<ast::NameRef>, | ||
82 | pub(super) use_item_syntax: Option<ast::Use>, | ||
56 | 83 | ||
57 | // potentially set if we are completing a lifetime | 84 | // potentially set if we are completing a lifetime |
58 | pub(super) lifetime_syntax: Option<ast::Lifetime>, | 85 | pub(super) lifetime_syntax: Option<ast::Lifetime>, |
@@ -67,29 +94,12 @@ pub(crate) struct CompletionContext<'a> { | |||
67 | pub(super) completion_location: Option<ImmediateLocation>, | 94 | pub(super) completion_location: Option<ImmediateLocation>, |
68 | pub(super) prev_sibling: Option<ImmediatePrevSibling>, | 95 | pub(super) prev_sibling: Option<ImmediatePrevSibling>, |
69 | pub(super) attribute_under_caret: Option<ast::Attr>, | 96 | pub(super) attribute_under_caret: Option<ast::Attr>, |
97 | pub(super) previous_token: Option<SyntaxToken>, | ||
70 | 98 | ||
71 | /// FIXME: `ActiveParameter` is string-based, which is very very wrong | 99 | pub(super) path_context: Option<PathCompletionContext>, |
72 | pub(super) active_parameter: Option<ActiveParameter>, | 100 | pub(super) active_parameter: Option<ActiveParameter>, |
73 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. | ||
74 | pub(super) is_trivial_path: bool, | ||
75 | /// If not a trivial path, the prefix (qualifier). | ||
76 | pub(super) path_qual: Option<ast::Path>, | ||
77 | /// `true` if we are a statement or a last expr in the block. | ||
78 | pub(super) can_be_stmt: bool, | ||
79 | /// `true` if we expect an expression at the cursor position. | ||
80 | pub(super) is_expr: bool, | ||
81 | /// If this is a call (method or function) in particular, i.e. the () are already there. | ||
82 | pub(super) is_call: bool, | ||
83 | /// Like `is_call`, but for tuple patterns. | ||
84 | pub(super) is_pattern_call: bool, | ||
85 | /// If this is a macro call, i.e. the () are already there. | ||
86 | pub(super) is_macro_call: bool, | ||
87 | pub(super) is_path_type: bool, | ||
88 | pub(super) has_type_args: bool, | ||
89 | pub(super) locals: Vec<(String, Local)>, | 101 | pub(super) locals: Vec<(String, Local)>, |
90 | 102 | ||
91 | pub(super) previous_token: Option<SyntaxToken>, | ||
92 | pub(super) in_loop_body: bool, | ||
93 | pub(super) incomplete_let: bool, | 103 | pub(super) incomplete_let: bool, |
94 | 104 | ||
95 | no_completion_required: bool, | 105 | no_completion_required: bool, |
@@ -136,36 +146,27 @@ impl<'a> CompletionContext<'a> { | |||
136 | original_token, | 146 | original_token, |
137 | token, | 147 | token, |
138 | krate, | 148 | krate, |
139 | lifetime_allowed: false, | ||
140 | expected_name: None, | 149 | expected_name: None, |
141 | expected_type: None, | 150 | expected_type: None, |
151 | function_def: None, | ||
152 | impl_def: None, | ||
142 | name_ref_syntax: None, | 153 | name_ref_syntax: None, |
154 | use_item_syntax: None, | ||
143 | lifetime_syntax: None, | 155 | lifetime_syntax: None, |
144 | lifetime_param_syntax: None, | 156 | lifetime_param_syntax: None, |
145 | function_def: None, | 157 | lifetime_allowed: false, |
146 | use_item_syntax: None, | ||
147 | impl_def: None, | ||
148 | active_parameter: ActiveParameter::at(db, position), | ||
149 | is_label_ref: false, | 158 | is_label_ref: false, |
150 | is_param: false, | ||
151 | is_pat_or_const: None, | 159 | is_pat_or_const: None, |
152 | is_trivial_path: false, | 160 | is_param: false, |
153 | path_qual: None, | ||
154 | can_be_stmt: false, | ||
155 | is_expr: false, | ||
156 | is_call: false, | ||
157 | is_pattern_call: false, | ||
158 | is_macro_call: false, | ||
159 | is_path_type: false, | ||
160 | has_type_args: false, | ||
161 | previous_token: None, | ||
162 | in_loop_body: false, | ||
163 | completion_location: None, | 161 | completion_location: None, |
164 | prev_sibling: None, | 162 | prev_sibling: None, |
165 | no_completion_required: false, | ||
166 | incomplete_let: false, | ||
167 | attribute_under_caret: None, | 163 | attribute_under_caret: None, |
164 | previous_token: None, | ||
165 | path_context: None, | ||
166 | active_parameter: ActiveParameter::at(db, position), | ||
168 | locals, | 167 | locals, |
168 | incomplete_let: false, | ||
169 | no_completion_required: false, | ||
169 | }; | 170 | }; |
170 | 171 | ||
171 | let mut original_file = original_file.syntax().clone(); | 172 | let mut original_file = original_file.syntax().clone(); |
@@ -250,14 +251,14 @@ impl<'a> CompletionContext<'a> { | |||
250 | pub(crate) fn has_dot_receiver(&self) -> bool { | 251 | pub(crate) fn has_dot_receiver(&self) -> bool { |
251 | matches!( | 252 | matches!( |
252 | &self.completion_location, | 253 | &self.completion_location, |
253 | Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver }) | 254 | Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver,.. }) |
254 | if receiver.is_some() | 255 | if receiver.is_some() |
255 | ) | 256 | ) |
256 | } | 257 | } |
257 | 258 | ||
258 | pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> { | 259 | pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> { |
259 | match &self.completion_location { | 260 | match &self.completion_location { |
260 | Some(ImmediateLocation::MethodCall { receiver }) | 261 | Some(ImmediateLocation::MethodCall { receiver, .. }) |
261 | | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(), | 262 | | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(), |
262 | _ => None, | 263 | _ => None, |
263 | } | 264 | } |
@@ -275,11 +276,6 @@ impl<'a> CompletionContext<'a> { | |||
275 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) | 276 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) |
276 | } | 277 | } |
277 | 278 | ||
278 | // fn expects_value(&self) -> bool { | ||
279 | pub(crate) fn expects_expression(&self) -> bool { | ||
280 | self.is_expr | ||
281 | } | ||
282 | |||
283 | pub(crate) fn has_block_expr_parent(&self) -> bool { | 279 | pub(crate) fn has_block_expr_parent(&self) -> bool { |
284 | matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) | 280 | matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) |
285 | } | 281 | } |
@@ -316,6 +312,26 @@ impl<'a> CompletionContext<'a> { | |||
316 | ) || self.attribute_under_caret.is_some() | 312 | ) || self.attribute_under_caret.is_some() |
317 | } | 313 | } |
318 | 314 | ||
315 | pub(crate) fn expects_expression(&self) -> bool { | ||
316 | matches!(self.path_context, Some(PathCompletionContext { kind: Some(PathKind::Expr), .. })) | ||
317 | } | ||
318 | |||
319 | pub(crate) fn expects_type(&self) -> bool { | ||
320 | matches!(self.path_context, Some(PathCompletionContext { kind: Some(PathKind::Type), .. })) | ||
321 | } | ||
322 | |||
323 | pub(crate) fn path_call_kind(&self) -> Option<CallKind> { | ||
324 | self.path_context.as_ref().and_then(|it| it.call_kind) | ||
325 | } | ||
326 | |||
327 | pub(crate) fn is_trivial_path(&self) -> bool { | ||
328 | matches!(self.path_context, Some(PathCompletionContext { is_trivial_path: true, .. })) | ||
329 | } | ||
330 | |||
331 | pub(crate) fn path_qual(&self) -> Option<&ast::Path> { | ||
332 | self.path_context.as_ref().and_then(|it| it.qualifier.as_ref()) | ||
333 | } | ||
334 | |||
319 | fn fill_impl_def(&mut self) { | 335 | fn fill_impl_def(&mut self) { |
320 | self.impl_def = self | 336 | self.impl_def = self |
321 | .sema | 337 | .sema |
@@ -364,7 +380,7 @@ impl<'a> CompletionContext<'a> { | |||
364 | (|| { | 380 | (|| { |
365 | let expr_field = self.token.prev_sibling_or_token()? | 381 | let expr_field = self.token.prev_sibling_or_token()? |
366 | .into_node() | 382 | .into_node() |
367 | .and_then(|node| ast::RecordExprField::cast(node))?; | 383 | .and_then(ast::RecordExprField::cast)?; |
368 | let (_, _, ty) = self.sema.resolve_record_field(&expr_field)?; | 384 | let (_, _, ty) = self.sema.resolve_record_field(&expr_field)?; |
369 | Some(( | 385 | Some(( |
370 | Some(ty), | 386 | Some(ty), |
@@ -441,7 +457,6 @@ impl<'a> CompletionContext<'a> { | |||
441 | let for_is_prev2 = for_is_prev2(syntax_element.clone()); | 457 | let for_is_prev2 = for_is_prev2(syntax_element.clone()); |
442 | (fn_is_prev && !inside_impl_trait_block) || for_is_prev2 | 458 | (fn_is_prev && !inside_impl_trait_block) || for_is_prev2 |
443 | }; | 459 | }; |
444 | self.in_loop_body = is_in_loop_body(syntax_element.clone()); | ||
445 | 460 | ||
446 | self.incomplete_let = | 461 | self.incomplete_let = |
447 | syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| { | 462 | syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| { |
@@ -452,7 +467,7 @@ impl<'a> CompletionContext<'a> { | |||
452 | self.expected_type = expected_type; | 467 | self.expected_type = expected_type; |
453 | self.expected_name = expected_name; | 468 | self.expected_name = expected_name; |
454 | 469 | ||
455 | let name_like = match find_node_at_offset(&&file_with_fake_ident, offset) { | 470 | let name_like = match find_node_at_offset(&file_with_fake_ident, offset) { |
456 | Some(it) => it, | 471 | Some(it) => it, |
457 | None => return, | 472 | None => return, |
458 | }; | 473 | }; |
@@ -549,10 +564,6 @@ impl<'a> CompletionContext<'a> { | |||
549 | self.name_ref_syntax = | 564 | self.name_ref_syntax = |
550 | find_node_at_offset(original_file, name_ref.syntax().text_range().start()); | 565 | find_node_at_offset(original_file, name_ref.syntax().text_range().start()); |
551 | 566 | ||
552 | if matches!(self.completion_location, Some(ImmediateLocation::ItemList)) { | ||
553 | return; | ||
554 | } | ||
555 | |||
556 | self.use_item_syntax = | 567 | self.use_item_syntax = |
557 | self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast); | 568 | self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast); |
558 | 569 | ||
@@ -567,23 +578,43 @@ impl<'a> CompletionContext<'a> { | |||
567 | None => return, | 578 | None => return, |
568 | }; | 579 | }; |
569 | 580 | ||
570 | if let Some(segment) = ast::PathSegment::cast(parent.clone()) { | 581 | if let Some(segment) = ast::PathSegment::cast(parent) { |
582 | let path_ctx = self.path_context.get_or_insert(PathCompletionContext { | ||
583 | call_kind: None, | ||
584 | is_trivial_path: false, | ||
585 | qualifier: None, | ||
586 | has_type_args: false, | ||
587 | can_be_stmt: false, | ||
588 | in_loop_body: false, | ||
589 | kind: None, | ||
590 | }); | ||
591 | path_ctx.in_loop_body = is_in_loop_body(name_ref.syntax()); | ||
571 | let path = segment.parent_path(); | 592 | let path = segment.parent_path(); |
572 | self.is_call = path | ||
573 | .syntax() | ||
574 | .parent() | ||
575 | .and_then(ast::PathExpr::cast) | ||
576 | .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) | ||
577 | .is_some(); | ||
578 | self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); | ||
579 | self.is_pattern_call = | ||
580 | path.syntax().parent().and_then(ast::TupleStructPat::cast).is_some(); | ||
581 | 593 | ||
582 | self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); | 594 | if let Some(p) = path.syntax().parent() { |
583 | self.has_type_args = segment.generic_arg_list().is_some(); | 595 | path_ctx.call_kind = match_ast! { |
596 | match p { | ||
597 | ast::PathExpr(it) => it.syntax().parent().and_then(ast::CallExpr::cast).map(|_| CallKind::Expr), | ||
598 | ast::MacroCall(it) => it.excl_token().and(Some(CallKind::Mac)), | ||
599 | ast::TupleStructPat(_it) => Some(CallKind::Pat), | ||
600 | _ => None | ||
601 | } | ||
602 | }; | ||
603 | } | ||
604 | |||
605 | if let Some(parent) = path.syntax().parent() { | ||
606 | path_ctx.kind = match_ast! { | ||
607 | match parent { | ||
608 | ast::PathType(_it) => Some(PathKind::Type), | ||
609 | ast::PathExpr(_it) => Some(PathKind::Expr), | ||
610 | _ => None, | ||
611 | } | ||
612 | }; | ||
613 | } | ||
614 | path_ctx.has_type_args = segment.generic_arg_list().is_some(); | ||
584 | 615 | ||
585 | if let Some(path) = path_or_use_tree_qualifier(&path) { | 616 | if let Some(path) = path_or_use_tree_qualifier(&path) { |
586 | self.path_qual = path | 617 | path_ctx.qualifier = path |
587 | .segment() | 618 | .segment() |
588 | .and_then(|it| { | 619 | .and_then(|it| { |
589 | find_node_with_range::<ast::PathSegment>( | 620 | find_node_with_range::<ast::PathSegment>( |
@@ -601,11 +632,11 @@ impl<'a> CompletionContext<'a> { | |||
601 | } | 632 | } |
602 | } | 633 | } |
603 | 634 | ||
604 | self.is_trivial_path = true; | 635 | path_ctx.is_trivial_path = true; |
605 | 636 | ||
606 | // Find either enclosing expr statement (thing with `;`) or a | 637 | // Find either enclosing expr statement (thing with `;`) or a |
607 | // block. If block, check that we are the last expr. | 638 | // block. If block, check that we are the last expr. |
608 | self.can_be_stmt = name_ref | 639 | path_ctx.can_be_stmt = name_ref |
609 | .syntax() | 640 | .syntax() |
610 | .ancestors() | 641 | .ancestors() |
611 | .find_map(|node| { | 642 | .find_map(|node| { |
@@ -621,10 +652,7 @@ impl<'a> CompletionContext<'a> { | |||
621 | None | 652 | None |
622 | }) | 653 | }) |
623 | .unwrap_or(false); | 654 | .unwrap_or(false); |
624 | self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); | ||
625 | } | 655 | } |
626 | self.is_call |= | ||
627 | matches!(self.completion_location, Some(ImmediateLocation::MethodCall { .. })); | ||
628 | } | 656 | } |
629 | } | 657 | } |
630 | 658 | ||
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs index 1152a9850..18983aa01 100644 --- a/crates/ide_completion/src/lib.rs +++ b/crates/ide_completion/src/lib.rs | |||
@@ -4,7 +4,6 @@ mod config; | |||
4 | mod item; | 4 | mod item; |
5 | mod context; | 5 | mod context; |
6 | mod patterns; | 6 | mod patterns; |
7 | mod generated_lint_completions; | ||
8 | #[cfg(test)] | 7 | #[cfg(test)] |
9 | mod test_utils; | 8 | mod test_utils; |
10 | mod render; | 9 | mod render; |
@@ -159,7 +158,6 @@ pub fn completions( | |||
159 | completions::record::complete_record(&mut acc, &ctx); | 158 | completions::record::complete_record(&mut acc, &ctx); |
160 | completions::pattern::complete_pattern(&mut acc, &ctx); | 159 | completions::pattern::complete_pattern(&mut acc, &ctx); |
161 | completions::postfix::complete_postfix(&mut acc, &ctx); | 160 | completions::postfix::complete_postfix(&mut acc, &ctx); |
162 | completions::macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); | ||
163 | completions::trait_impl::complete_trait_impl(&mut acc, &ctx); | 161 | completions::trait_impl::complete_trait_impl(&mut acc, &ctx); |
164 | completions::mod_::complete_mod(&mut acc, &ctx); | 162 | completions::mod_::complete_mod(&mut acc, &ctx); |
165 | completions::flyimport::import_on_the_fly(&mut acc, &ctx); | 163 | completions::flyimport::import_on_the_fly(&mut acc, &ctx); |
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs index 080898aef..72e67e3c4 100644 --- a/crates/ide_completion/src/patterns.rs +++ b/crates/ide_completion/src/patterns.rs | |||
@@ -4,7 +4,7 @@ use hir::Semantics; | |||
4 | use ide_db::RootDatabase; | 4 | use ide_db::RootDatabase; |
5 | use syntax::{ | 5 | use syntax::{ |
6 | algo::non_trivia_sibling, | 6 | algo::non_trivia_sibling, |
7 | ast::{self, LoopBodyOwner}, | 7 | ast::{self, ArgListOwner, LoopBodyOwner}, |
8 | match_ast, AstNode, Direction, SyntaxElement, | 8 | match_ast, AstNode, Direction, SyntaxElement, |
9 | SyntaxKind::*, | 9 | SyntaxKind::*, |
10 | SyntaxNode, SyntaxToken, TextRange, TextSize, T, | 10 | SyntaxNode, SyntaxToken, TextRange, TextSize, T, |
@@ -39,6 +39,7 @@ pub(crate) enum ImmediateLocation { | |||
39 | // Original file ast node | 39 | // Original file ast node |
40 | MethodCall { | 40 | MethodCall { |
41 | receiver: Option<ast::Expr>, | 41 | receiver: Option<ast::Expr>, |
42 | has_parens: bool, | ||
42 | }, | 43 | }, |
43 | // Original file ast node | 44 | // Original file ast node |
44 | FieldAccess { | 45 | FieldAccess { |
@@ -46,6 +47,9 @@ pub(crate) enum ImmediateLocation { | |||
46 | receiver_is_ambiguous_float_literal: bool, | 47 | receiver_is_ambiguous_float_literal: bool, |
47 | }, | 48 | }, |
48 | // Original file ast node | 49 | // Original file ast node |
50 | // Only set from a type arg | ||
51 | GenericArgList(ast::GenericArgList), | ||
52 | // Original file ast node | ||
49 | /// The record expr of the field name we are completing | 53 | /// The record expr of the field name we are completing |
50 | RecordExpr(ast::RecordExpr), | 54 | RecordExpr(ast::RecordExpr), |
51 | // Original file ast node | 55 | // Original file ast node |
@@ -111,12 +115,12 @@ pub(crate) fn determine_location( | |||
111 | ) -> Option<ImmediateLocation> { | 115 | ) -> Option<ImmediateLocation> { |
112 | let node = match name_like { | 116 | let node = match name_like { |
113 | ast::NameLike::NameRef(name_ref) => { | 117 | ast::NameLike::NameRef(name_ref) => { |
114 | if ast::RecordExprField::for_field_name(&name_ref).is_some() { | 118 | if ast::RecordExprField::for_field_name(name_ref).is_some() { |
115 | return sema | 119 | return sema |
116 | .find_node_at_offset_with_macros(original_file, offset) | 120 | .find_node_at_offset_with_macros(original_file, offset) |
117 | .map(ImmediateLocation::RecordExpr); | 121 | .map(ImmediateLocation::RecordExpr); |
118 | } | 122 | } |
119 | if ast::RecordPatField::for_field_name_ref(&name_ref).is_some() { | 123 | if ast::RecordPatField::for_field_name_ref(name_ref).is_some() { |
120 | return sema | 124 | return sema |
121 | .find_node_at_offset_with_macros(original_file, offset) | 125 | .find_node_at_offset_with_macros(original_file, offset) |
122 | .map(ImmediateLocation::RecordPat); | 126 | .map(ImmediateLocation::RecordPat); |
@@ -124,7 +128,7 @@ pub(crate) fn determine_location( | |||
124 | maximize_name_ref(name_ref) | 128 | maximize_name_ref(name_ref) |
125 | } | 129 | } |
126 | ast::NameLike::Name(name) => { | 130 | ast::NameLike::Name(name) => { |
127 | if ast::RecordPatField::for_field_name(&name).is_some() { | 131 | if ast::RecordPatField::for_field_name(name).is_some() { |
128 | return sema | 132 | return sema |
129 | .find_node_at_offset_with_macros(original_file, offset) | 133 | .find_node_at_offset_with_macros(original_file, offset) |
130 | .map(ImmediateLocation::RecordPat); | 134 | .map(ImmediateLocation::RecordPat); |
@@ -158,7 +162,6 @@ pub(crate) fn determine_location( | |||
158 | } | 162 | } |
159 | } | 163 | } |
160 | }; | 164 | }; |
161 | |||
162 | let res = match_ast! { | 165 | let res = match_ast! { |
163 | match parent { | 166 | match parent { |
164 | ast::IdentPat(_it) => ImmediateLocation::IdentPat, | 167 | ast::IdentPat(_it) => ImmediateLocation::IdentPat, |
@@ -173,6 +176,9 @@ pub(crate) fn determine_location( | |||
173 | Some(TRAIT) => ImmediateLocation::Trait, | 176 | Some(TRAIT) => ImmediateLocation::Trait, |
174 | _ => return None, | 177 | _ => return None, |
175 | }, | 178 | }, |
179 | ast::GenericArgList(_it) => sema | ||
180 | .find_node_at_offset_with_macros(original_file, offset) | ||
181 | .map(ImmediateLocation::GenericArgList)?, | ||
176 | ast::Module(it) => { | 182 | ast::Module(it) => { |
177 | if it.item_list().is_none() { | 183 | if it.item_list().is_none() { |
178 | ImmediateLocation::ModDeclaration(it) | 184 | ImmediateLocation::ModDeclaration(it) |
@@ -204,6 +210,7 @@ pub(crate) fn determine_location( | |||
204 | .receiver() | 210 | .receiver() |
205 | .map(|e| e.syntax().text_range()) | 211 | .map(|e| e.syntax().text_range()) |
206 | .and_then(|r| find_node_with_range(original_file, r)), | 212 | .and_then(|r| find_node_with_range(original_file, r)), |
213 | has_parens: it.arg_list().map_or(false, |it| it.l_paren_token().is_some()) | ||
207 | }, | 214 | }, |
208 | _ => return None, | 215 | _ => return None, |
209 | } | 216 | } |
@@ -252,7 +259,7 @@ fn test_inside_impl_trait_block() { | |||
252 | } | 259 | } |
253 | 260 | ||
254 | pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> { | 261 | pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> { |
255 | element.into_token().and_then(|it| previous_non_trivia_token(it)) | 262 | element.into_token().and_then(previous_non_trivia_token) |
256 | } | 263 | } |
257 | 264 | ||
258 | /// Check if the token previous to the previous one is `for`. | 265 | /// Check if the token previous to the previous one is `for`. |
@@ -260,8 +267,8 @@ pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> { | |||
260 | pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool { | 267 | pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool { |
261 | element | 268 | element |
262 | .into_token() | 269 | .into_token() |
263 | .and_then(|it| previous_non_trivia_token(it)) | 270 | .and_then(previous_non_trivia_token) |
264 | .and_then(|it| previous_non_trivia_token(it)) | 271 | .and_then(previous_non_trivia_token) |
265 | .filter(|it| it.kind() == T![for]) | 272 | .filter(|it| it.kind() == T![for]) |
266 | .is_some() | 273 | .is_some() |
267 | } | 274 | } |
@@ -270,9 +277,8 @@ fn test_for_is_prev2() { | |||
270 | check_pattern_is_applicable(r"for i i$0", for_is_prev2); | 277 | check_pattern_is_applicable(r"for i i$0", for_is_prev2); |
271 | } | 278 | } |
272 | 279 | ||
273 | pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { | 280 | pub(crate) fn is_in_loop_body(node: &SyntaxNode) -> bool { |
274 | element | 281 | node.ancestors() |
275 | .ancestors() | ||
276 | .take_while(|it| it.kind() != FN && it.kind() != CLOSURE_EXPR) | 282 | .take_while(|it| it.kind() != FN && it.kind() != CLOSURE_EXPR) |
277 | .find_map(|it| { | 283 | .find_map(|it| { |
278 | let loop_body = match_ast! { | 284 | let loop_body = match_ast! { |
@@ -283,7 +289,7 @@ pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { | |||
283 | _ => None, | 289 | _ => None, |
284 | } | 290 | } |
285 | }; | 291 | }; |
286 | loop_body.filter(|it| it.syntax().text_range().contains_range(element.text_range())) | 292 | loop_body.filter(|it| it.syntax().text_range().contains_range(node.text_range())) |
287 | }) | 293 | }) |
288 | .is_some() | 294 | .is_some() |
289 | } | 295 | } |
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index a49a60711..d8ca18c73 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs | |||
@@ -18,54 +18,56 @@ use ide_db::{ | |||
18 | use syntax::TextRange; | 18 | use syntax::TextRange; |
19 | 19 | ||
20 | use crate::{ | 20 | use crate::{ |
21 | context::{PathCompletionContext, PathKind}, | ||
21 | item::{CompletionRelevanceTypeMatch, ImportEdit}, | 22 | item::{CompletionRelevanceTypeMatch, ImportEdit}, |
22 | render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}, | 23 | render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}, |
23 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, | 24 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, |
24 | }; | 25 | }; |
25 | 26 | ||
26 | pub(crate) fn render_field<'a>( | 27 | pub(crate) fn render_field( |
27 | ctx: RenderContext<'a>, | 28 | ctx: RenderContext<'_>, |
28 | receiver: Option<hir::Name>, | 29 | receiver: Option<hir::Name>, |
29 | field: hir::Field, | 30 | field: hir::Field, |
30 | ty: &hir::Type, | 31 | ty: &hir::Type, |
31 | ) -> CompletionItem { | 32 | ) -> CompletionItem { |
32 | Render::new(ctx).render_field(receiver, field, ty) | 33 | render_field_(ctx, receiver, field, ty) |
33 | } | 34 | } |
34 | 35 | ||
35 | pub(crate) fn render_tuple_field<'a>( | 36 | pub(crate) fn render_tuple_field( |
36 | ctx: RenderContext<'a>, | 37 | ctx: RenderContext<'_>, |
37 | receiver: Option<hir::Name>, | 38 | receiver: Option<hir::Name>, |
38 | field: usize, | 39 | field: usize, |
39 | ty: &hir::Type, | 40 | ty: &hir::Type, |
40 | ) -> CompletionItem { | 41 | ) -> CompletionItem { |
41 | Render::new(ctx).render_tuple_field(receiver, field, ty) | 42 | render_tuple_field_(ctx, receiver, field, ty) |
42 | } | 43 | } |
43 | 44 | ||
44 | pub(crate) fn render_resolution<'a>( | 45 | pub(crate) fn render_resolution( |
45 | ctx: RenderContext<'a>, | 46 | ctx: RenderContext<'_>, |
46 | local_name: hir::Name, | 47 | local_name: hir::Name, |
47 | resolution: &hir::ScopeDef, | 48 | resolution: &hir::ScopeDef, |
48 | ) -> Option<CompletionItem> { | 49 | ) -> Option<CompletionItem> { |
49 | Render::new(ctx).render_resolution(local_name, None, resolution) | 50 | render_resolution_(ctx, local_name, None, resolution) |
50 | } | 51 | } |
51 | 52 | ||
52 | pub(crate) fn render_resolution_with_import<'a>( | 53 | pub(crate) fn render_resolution_with_import( |
53 | ctx: RenderContext<'a>, | 54 | ctx: RenderContext<'_>, |
54 | import_edit: ImportEdit, | 55 | import_edit: ImportEdit, |
55 | ) -> Option<CompletionItem> { | 56 | ) -> Option<CompletionItem> { |
56 | let resolution = hir::ScopeDef::from(import_edit.import.original_item); | 57 | let resolution = hir::ScopeDef::from(import_edit.import.original_item); |
58 | if ctx.completion.expects_type() && resolution.is_value_def() { | ||
59 | return None; | ||
60 | } | ||
57 | let local_name = match resolution { | 61 | let local_name = match resolution { |
58 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db), | 62 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db), |
59 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?, | 63 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?, |
60 | hir::ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db), | 64 | hir::ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db), |
61 | _ => item_name(ctx.db(), import_edit.import.original_item)?, | 65 | _ => item_name(ctx.db(), import_edit.import.original_item)?, |
62 | }; | 66 | }; |
63 | Render::new(ctx).render_resolution(local_name, Some(import_edit), &resolution).map( | 67 | render_resolution_(ctx, local_name, Some(import_edit), &resolution).map(|mut item| { |
64 | |mut item| { | 68 | item.completion_kind = CompletionKind::Magic; |
65 | item.completion_kind = CompletionKind::Magic; | 69 | item |
66 | item | 70 | }) |
67 | }, | ||
68 | ) | ||
69 | } | 71 | } |
70 | 72 | ||
71 | /// Interface for data and methods required for items rendering. | 73 | /// Interface for data and methods required for items rendering. |
@@ -84,7 +86,7 @@ impl<'a> RenderContext<'a> { | |||
84 | } | 86 | } |
85 | 87 | ||
86 | fn db(&self) -> &'a RootDatabase { | 88 | fn db(&self) -> &'a RootDatabase { |
87 | &self.completion.db | 89 | self.completion.db |
88 | } | 90 | } |
89 | 91 | ||
90 | fn source_range(&self) -> TextRange { | 92 | fn source_range(&self) -> TextRange { |
@@ -109,7 +111,10 @@ impl<'a> RenderContext<'a> { | |||
109 | hir::AssocItem::TypeAlias(it) => self.is_deprecated(it), | 111 | hir::AssocItem::TypeAlias(it) => self.is_deprecated(it), |
110 | }; | 112 | }; |
111 | is_assoc_deprecated | 113 | is_assoc_deprecated |
112 | || assoc.containing_trait(db).map(|trait_| self.is_deprecated(trait_)).unwrap_or(false) | 114 | || assoc |
115 | .containing_trait_or_trait_impl(db) | ||
116 | .map(|trait_| self.is_deprecated(trait_)) | ||
117 | .unwrap_or(false) | ||
113 | } | 118 | } |
114 | 119 | ||
115 | fn docs(&self, node: impl HasAttrs) -> Option<hir::Documentation> { | 120 | fn docs(&self, node: impl HasAttrs) -> Option<hir::Documentation> { |
@@ -117,215 +122,191 @@ impl<'a> RenderContext<'a> { | |||
117 | } | 122 | } |
118 | } | 123 | } |
119 | 124 | ||
120 | /// Generic renderer for completion items. | 125 | fn render_field_( |
121 | #[derive(Debug)] | 126 | ctx: RenderContext<'_>, |
122 | struct Render<'a> { | 127 | receiver: Option<hir::Name>, |
123 | ctx: RenderContext<'a>, | 128 | field: hir::Field, |
124 | } | 129 | ty: &hir::Type, |
125 | 130 | ) -> CompletionItem { | |
126 | impl<'a> Render<'a> { | 131 | let is_deprecated = ctx.is_deprecated(field); |
127 | fn new(ctx: RenderContext<'a>) -> Render<'a> { | 132 | let name = field.name(ctx.db()).to_string(); |
128 | Render { ctx } | 133 | let mut item = CompletionItem::new( |
134 | CompletionKind::Reference, | ||
135 | ctx.source_range(), | ||
136 | receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name)), | ||
137 | ); | ||
138 | |||
139 | item.set_relevance(CompletionRelevance { | ||
140 | type_match: compute_type_match(ctx.completion, ty), | ||
141 | exact_name_match: compute_exact_name_match(ctx.completion, &name), | ||
142 | ..CompletionRelevance::default() | ||
143 | }); | ||
144 | item.kind(SymbolKind::Field) | ||
145 | .detail(ty.display(ctx.db()).to_string()) | ||
146 | .set_documentation(field.docs(ctx.db())) | ||
147 | .set_deprecated(is_deprecated) | ||
148 | .lookup_by(name); | ||
149 | |||
150 | if let Some(_ref_match) = compute_ref_match(ctx.completion, ty) { | ||
151 | // FIXME | ||
152 | // For now we don't properly calculate the edits for ref match | ||
153 | // completions on struct fields, so we've disabled them. See #8058. | ||
129 | } | 154 | } |
130 | 155 | ||
131 | fn render_field( | 156 | item.build() |
132 | &self, | 157 | } |
133 | receiver: Option<hir::Name>, | ||
134 | field: hir::Field, | ||
135 | ty: &hir::Type, | ||
136 | ) -> CompletionItem { | ||
137 | let is_deprecated = self.ctx.is_deprecated(field); | ||
138 | let name = field.name(self.ctx.db()).to_string(); | ||
139 | let mut item = CompletionItem::new( | ||
140 | CompletionKind::Reference, | ||
141 | self.ctx.source_range(), | ||
142 | receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name)), | ||
143 | ); | ||
144 | item.kind(SymbolKind::Field) | ||
145 | .detail(ty.display(self.ctx.db()).to_string()) | ||
146 | .set_documentation(field.docs(self.ctx.db())) | ||
147 | .set_deprecated(is_deprecated); | ||
148 | |||
149 | item.set_relevance(CompletionRelevance { | ||
150 | type_match: compute_type_match(self.ctx.completion, ty), | ||
151 | exact_name_match: compute_exact_name_match(self.ctx.completion, &name), | ||
152 | ..CompletionRelevance::default() | ||
153 | }); | ||
154 | |||
155 | if let Some(_ref_match) = compute_ref_match(self.ctx.completion, ty) { | ||
156 | // FIXME | ||
157 | // For now we don't properly calculate the edits for ref match | ||
158 | // completions on struct fields, so we've disabled them. See #8058. | ||
159 | } | ||
160 | 158 | ||
161 | item.build() | 159 | fn render_tuple_field_( |
162 | } | 160 | ctx: RenderContext<'_>, |
161 | receiver: Option<hir::Name>, | ||
162 | field: usize, | ||
163 | ty: &hir::Type, | ||
164 | ) -> CompletionItem { | ||
165 | let mut item = CompletionItem::new( | ||
166 | CompletionKind::Reference, | ||
167 | ctx.source_range(), | ||
168 | receiver.map_or_else(|| field.to_string(), |receiver| format!("{}.{}", receiver, field)), | ||
169 | ); | ||
163 | 170 | ||
164 | fn render_tuple_field( | 171 | item.kind(SymbolKind::Field) |
165 | &self, | 172 | .detail(ty.display(ctx.db()).to_string()) |
166 | receiver: Option<hir::Name>, | 173 | .lookup_by(field.to_string()); |
167 | field: usize, | ||
168 | ty: &hir::Type, | ||
169 | ) -> CompletionItem { | ||
170 | let mut item = CompletionItem::new( | ||
171 | CompletionKind::Reference, | ||
172 | self.ctx.source_range(), | ||
173 | receiver | ||
174 | .map_or_else(|| field.to_string(), |receiver| format!("{}.{}", receiver, field)), | ||
175 | ); | ||
176 | 174 | ||
177 | item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string()); | 175 | item.build() |
176 | } | ||
178 | 177 | ||
179 | item.build() | 178 | fn render_resolution_( |
180 | } | 179 | ctx: RenderContext<'_>, |
180 | local_name: hir::Name, | ||
181 | import_to_add: Option<ImportEdit>, | ||
182 | resolution: &hir::ScopeDef, | ||
183 | ) -> Option<CompletionItem> { | ||
184 | let _p = profile::span("render_resolution"); | ||
185 | use hir::ModuleDef::*; | ||
181 | 186 | ||
182 | fn render_resolution( | 187 | let completion_kind = match resolution { |
183 | self, | 188 | hir::ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType, |
184 | local_name: hir::Name, | 189 | _ => CompletionKind::Reference, |
185 | import_to_add: Option<ImportEdit>, | 190 | }; |
186 | resolution: &hir::ScopeDef, | ||
187 | ) -> Option<CompletionItem> { | ||
188 | let _p = profile::span("render_resolution"); | ||
189 | use hir::ModuleDef::*; | ||
190 | |||
191 | let completion_kind = match resolution { | ||
192 | hir::ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType, | ||
193 | _ => CompletionKind::Reference, | ||
194 | }; | ||
195 | 191 | ||
196 | let kind = match resolution { | 192 | let kind = match resolution { |
197 | hir::ScopeDef::ModuleDef(Function(func)) => { | 193 | hir::ScopeDef::ModuleDef(Function(func)) => { |
198 | return render_fn(self.ctx, import_to_add, Some(local_name), *func); | 194 | return render_fn(ctx, import_to_add, Some(local_name), *func); |
199 | } | 195 | } |
200 | hir::ScopeDef::ModuleDef(Variant(_)) | 196 | hir::ScopeDef::ModuleDef(Variant(_)) if ctx.completion.is_pat_or_const.is_some() => { |
201 | if self.ctx.completion.is_pat_or_const.is_some() => | 197 | CompletionItemKind::SymbolKind(SymbolKind::Variant) |
202 | { | 198 | } |
203 | CompletionItemKind::SymbolKind(SymbolKind::Variant) | 199 | hir::ScopeDef::ModuleDef(Variant(var)) => { |
204 | } | 200 | let item = render_variant(ctx, import_to_add, Some(local_name), *var, None); |
205 | hir::ScopeDef::ModuleDef(Variant(var)) => { | 201 | return Some(item); |
206 | let item = render_variant(self.ctx, import_to_add, Some(local_name), *var, None); | 202 | } |
207 | return Some(item); | 203 | hir::ScopeDef::MacroDef(mac) => { |
208 | } | 204 | let item = render_macro(ctx, import_to_add, local_name, *mac); |
209 | hir::ScopeDef::MacroDef(mac) => { | 205 | return item; |
210 | let item = render_macro(self.ctx, import_to_add, local_name, *mac); | 206 | } |
211 | return item; | ||
212 | } | ||
213 | 207 | ||
214 | hir::ScopeDef::ModuleDef(Module(..)) => { | 208 | hir::ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module), |
215 | CompletionItemKind::SymbolKind(SymbolKind::Module) | 209 | hir::ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt { |
216 | } | 210 | hir::Adt::Struct(_) => SymbolKind::Struct, |
217 | hir::ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt { | 211 | hir::Adt::Union(_) => SymbolKind::Union, |
218 | hir::Adt::Struct(_) => SymbolKind::Struct, | 212 | hir::Adt::Enum(_) => SymbolKind::Enum, |
219 | hir::Adt::Union(_) => SymbolKind::Union, | 213 | }), |
220 | hir::Adt::Enum(_) => SymbolKind::Enum, | 214 | hir::ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const), |
221 | }), | 215 | hir::ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static), |
222 | hir::ScopeDef::ModuleDef(Const(..)) => { | 216 | hir::ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait), |
223 | CompletionItemKind::SymbolKind(SymbolKind::Const) | 217 | hir::ScopeDef::ModuleDef(TypeAlias(..)) => { |
224 | } | 218 | CompletionItemKind::SymbolKind(SymbolKind::TypeAlias) |
225 | hir::ScopeDef::ModuleDef(Static(..)) => { | 219 | } |
226 | CompletionItemKind::SymbolKind(SymbolKind::Static) | 220 | hir::ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, |
227 | } | 221 | hir::ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param { |
228 | hir::ScopeDef::ModuleDef(Trait(..)) => { | 222 | hir::GenericParam::TypeParam(_) => SymbolKind::TypeParam, |
229 | CompletionItemKind::SymbolKind(SymbolKind::Trait) | 223 | hir::GenericParam::LifetimeParam(_) => SymbolKind::LifetimeParam, |
230 | } | 224 | hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam, |
231 | hir::ScopeDef::ModuleDef(TypeAlias(..)) => { | 225 | }), |
232 | CompletionItemKind::SymbolKind(SymbolKind::TypeAlias) | 226 | hir::ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local), |
233 | } | 227 | hir::ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label), |
234 | hir::ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, | 228 | hir::ScopeDef::AdtSelfType(..) | hir::ScopeDef::ImplSelfType(..) => { |
235 | hir::ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param { | 229 | CompletionItemKind::SymbolKind(SymbolKind::SelfParam) |
236 | hir::GenericParam::TypeParam(_) => SymbolKind::TypeParam, | 230 | } |
237 | hir::GenericParam::LifetimeParam(_) => SymbolKind::LifetimeParam, | 231 | hir::ScopeDef::Unknown => { |
238 | hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam, | 232 | let mut item = CompletionItem::new( |
239 | }), | 233 | CompletionKind::Reference, |
240 | hir::ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local), | 234 | ctx.source_range(), |
241 | hir::ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label), | 235 | local_name.to_string(), |
242 | hir::ScopeDef::AdtSelfType(..) | hir::ScopeDef::ImplSelfType(..) => { | 236 | ); |
243 | CompletionItemKind::SymbolKind(SymbolKind::SelfParam) | 237 | item.kind(CompletionItemKind::UnresolvedReference).add_import(import_to_add); |
244 | } | 238 | return Some(item.build()); |
245 | hir::ScopeDef::Unknown => { | 239 | } |
246 | let mut item = CompletionItem::new( | 240 | }; |
247 | CompletionKind::Reference, | ||
248 | self.ctx.source_range(), | ||
249 | local_name.to_string(), | ||
250 | ); | ||
251 | item.kind(CompletionItemKind::UnresolvedReference).add_import(import_to_add); | ||
252 | return Some(item.build()); | ||
253 | } | ||
254 | }; | ||
255 | 241 | ||
256 | let local_name = local_name.to_string(); | 242 | let local_name = local_name.to_string(); |
257 | let mut item = | 243 | let mut item = CompletionItem::new(completion_kind, ctx.source_range(), local_name.clone()); |
258 | CompletionItem::new(completion_kind, self.ctx.source_range(), local_name.clone()); | 244 | if let hir::ScopeDef::Local(local) = resolution { |
259 | if let hir::ScopeDef::Local(local) = resolution { | 245 | let ty = local.ty(ctx.db()); |
260 | let ty = local.ty(self.ctx.db()); | 246 | if !ty.is_unknown() { |
261 | if !ty.is_unknown() { | 247 | item.detail(ty.display(ctx.db()).to_string()); |
262 | item.detail(ty.display(self.ctx.db()).to_string()); | 248 | } |
263 | } | ||
264 | 249 | ||
265 | item.set_relevance(CompletionRelevance { | 250 | item.set_relevance(CompletionRelevance { |
266 | type_match: compute_type_match(self.ctx.completion, &ty), | 251 | type_match: compute_type_match(ctx.completion, &ty), |
267 | exact_name_match: compute_exact_name_match(self.ctx.completion, &local_name), | 252 | exact_name_match: compute_exact_name_match(ctx.completion, &local_name), |
268 | is_local: true, | 253 | is_local: true, |
269 | ..CompletionRelevance::default() | 254 | ..CompletionRelevance::default() |
270 | }); | 255 | }); |
271 | 256 | ||
272 | if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ty) { | 257 | if let Some(ref_match) = compute_ref_match(ctx.completion, &ty) { |
273 | item.ref_match(ref_match); | 258 | item.ref_match(ref_match); |
274 | } | 259 | } |
275 | }; | 260 | }; |
276 | 261 | ||
277 | // Add `<>` for generic types | 262 | // Add `<>` for generic types |
278 | if self.ctx.completion.is_path_type | 263 | if matches!( |
279 | && !self.ctx.completion.has_type_args | 264 | ctx.completion.path_context, |
280 | && self.ctx.completion.config.add_call_parenthesis | 265 | Some(PathCompletionContext { kind: Some(PathKind::Type), has_type_args: false, .. }) |
281 | { | 266 | ) && ctx.completion.config.add_call_parenthesis |
282 | if let Some(cap) = self.ctx.snippet_cap() { | 267 | { |
283 | let has_non_default_type_params = match resolution { | 268 | if let Some(cap) = ctx.snippet_cap() { |
284 | hir::ScopeDef::ModuleDef(Adt(it)) => { | 269 | let has_non_default_type_params = match resolution { |
285 | it.has_non_default_type_params(self.ctx.db()) | 270 | hir::ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db()), |
286 | } | 271 | hir::ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db()), |
287 | hir::ScopeDef::ModuleDef(TypeAlias(it)) => { | 272 | _ => false, |
288 | it.has_non_default_type_params(self.ctx.db()) | 273 | }; |
289 | } | 274 | if has_non_default_type_params { |
290 | _ => false, | 275 | cov_mark::hit!(inserts_angle_brackets_for_generics); |
291 | }; | 276 | item.lookup_by(local_name.clone()) |
292 | if has_non_default_type_params { | 277 | .label(format!("{}<…>", local_name)) |
293 | cov_mark::hit!(inserts_angle_brackets_for_generics); | 278 | .insert_snippet(cap, format!("{}<$0>", local_name)); |
294 | item.lookup_by(local_name.clone()) | ||
295 | .label(format!("{}<…>", local_name)) | ||
296 | .insert_snippet(cap, format!("{}<$0>", local_name)); | ||
297 | } | ||
298 | } | 279 | } |
299 | } | 280 | } |
300 | item.kind(kind) | ||
301 | .add_import(import_to_add) | ||
302 | .set_documentation(self.docs(resolution)) | ||
303 | .set_deprecated(self.is_deprecated(resolution)); | ||
304 | Some(item.build()) | ||
305 | } | 281 | } |
282 | item.kind(kind) | ||
283 | .add_import(import_to_add) | ||
284 | .set_documentation(scope_def_docs(ctx.db(), resolution)) | ||
285 | .set_deprecated(scope_def_is_deprecated(&ctx, resolution)); | ||
286 | Some(item.build()) | ||
287 | } | ||
306 | 288 | ||
307 | fn docs(&self, resolution: &hir::ScopeDef) -> Option<hir::Documentation> { | 289 | fn scope_def_docs(db: &RootDatabase, resolution: &hir::ScopeDef) -> Option<hir::Documentation> { |
308 | use hir::ModuleDef::*; | 290 | use hir::ModuleDef::*; |
309 | match resolution { | 291 | match resolution { |
310 | hir::ScopeDef::ModuleDef(Module(it)) => it.docs(self.ctx.db()), | 292 | hir::ScopeDef::ModuleDef(Module(it)) => it.docs(db), |
311 | hir::ScopeDef::ModuleDef(Adt(it)) => it.docs(self.ctx.db()), | 293 | hir::ScopeDef::ModuleDef(Adt(it)) => it.docs(db), |
312 | hir::ScopeDef::ModuleDef(Variant(it)) => it.docs(self.ctx.db()), | 294 | hir::ScopeDef::ModuleDef(Variant(it)) => it.docs(db), |
313 | hir::ScopeDef::ModuleDef(Const(it)) => it.docs(self.ctx.db()), | 295 | hir::ScopeDef::ModuleDef(Const(it)) => it.docs(db), |
314 | hir::ScopeDef::ModuleDef(Static(it)) => it.docs(self.ctx.db()), | 296 | hir::ScopeDef::ModuleDef(Static(it)) => it.docs(db), |
315 | hir::ScopeDef::ModuleDef(Trait(it)) => it.docs(self.ctx.db()), | 297 | hir::ScopeDef::ModuleDef(Trait(it)) => it.docs(db), |
316 | hir::ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(self.ctx.db()), | 298 | hir::ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(db), |
317 | _ => None, | 299 | _ => None, |
318 | } | ||
319 | } | 300 | } |
301 | } | ||
320 | 302 | ||
321 | fn is_deprecated(&self, resolution: &hir::ScopeDef) -> bool { | 303 | fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: &hir::ScopeDef) -> bool { |
322 | match resolution { | 304 | match resolution { |
323 | hir::ScopeDef::ModuleDef(it) => self.ctx.is_deprecated_assoc_item(*it), | 305 | hir::ScopeDef::ModuleDef(it) => ctx.is_deprecated_assoc_item(*it), |
324 | hir::ScopeDef::MacroDef(it) => self.ctx.is_deprecated(*it), | 306 | hir::ScopeDef::MacroDef(it) => ctx.is_deprecated(*it), |
325 | hir::ScopeDef::GenericParam(it) => self.ctx.is_deprecated(*it), | 307 | hir::ScopeDef::GenericParam(it) => ctx.is_deprecated(*it), |
326 | hir::ScopeDef::AdtSelfType(it) => self.ctx.is_deprecated(*it), | 308 | hir::ScopeDef::AdtSelfType(it) => ctx.is_deprecated(*it), |
327 | _ => false, | 309 | _ => false, |
328 | } | ||
329 | } | 310 | } |
330 | } | 311 | } |
331 | 312 | ||
@@ -1026,6 +1007,7 @@ fn go(world: &WorldSnapshot) { go(w$0) } | |||
1026 | 1007 | ||
1027 | #[test] | 1008 | #[test] |
1028 | fn too_many_arguments() { | 1009 | fn too_many_arguments() { |
1010 | cov_mark::check!(too_many_arguments); | ||
1029 | check_relevance( | 1011 | check_relevance( |
1030 | r#" | 1012 | r#" |
1031 | struct Foo; | 1013 | struct Foo; |
diff --git a/crates/ide_completion/src/render/builder_ext.rs b/crates/ide_completion/src/render/builder_ext.rs index 6d062b3b9..c54752d30 100644 --- a/crates/ide_completion/src/render/builder_ext.rs +++ b/crates/ide_completion/src/render/builder_ext.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use itertools::Itertools; | 3 | use itertools::Itertools; |
4 | 4 | ||
5 | use crate::{item::Builder, CompletionContext}; | 5 | use crate::{context::CallKind, item::Builder, patterns::ImmediateLocation, CompletionContext}; |
6 | 6 | ||
7 | #[derive(Debug)] | 7 | #[derive(Debug)] |
8 | pub(super) enum Params { | 8 | pub(super) enum Params { |
@@ -32,10 +32,12 @@ impl Builder { | |||
32 | cov_mark::hit!(no_parens_in_use_item); | 32 | cov_mark::hit!(no_parens_in_use_item); |
33 | return false; | 33 | return false; |
34 | } | 34 | } |
35 | if ctx.is_pattern_call { | 35 | if matches!(ctx.path_call_kind(), Some(CallKind::Expr) | Some(CallKind::Pat)) |
36 | return false; | 36 | | matches!( |
37 | } | 37 | ctx.completion_location, |
38 | if ctx.is_call { | 38 | Some(ImmediateLocation::MethodCall { has_parens: true, .. }) |
39 | ) | ||
40 | { | ||
39 | return false; | 41 | return false; |
40 | } | 42 | } |
41 | 43 | ||
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs index 1abeed96d..1357b9f4a 100644 --- a/crates/ide_completion/src/render/function.rs +++ b/crates/ide_completion/src/render/function.rs | |||
@@ -58,29 +58,29 @@ impl<'a> FunctionRender<'a> { | |||
58 | Some(FunctionRender { ctx, name, receiver, func: fn_, ast_node, is_method }) | 58 | Some(FunctionRender { ctx, name, receiver, func: fn_, ast_node, is_method }) |
59 | } | 59 | } |
60 | 60 | ||
61 | fn render(mut self, import_to_add: Option<ImportEdit>) -> CompletionItem { | 61 | fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { |
62 | let params = self.params(); | 62 | let params = self.params(); |
63 | if let Some(receiver) = &self.receiver { | 63 | let call = if let Some(receiver) = &self.receiver { |
64 | self.name = format!("{}.{}", receiver, &self.name) | 64 | format!("{}.{}", receiver, &self.name) |
65 | } | 65 | } else { |
66 | let mut item = CompletionItem::new( | 66 | self.name.clone() |
67 | CompletionKind::Reference, | 67 | }; |
68 | self.ctx.source_range(), | 68 | let mut item = |
69 | self.name.clone(), | 69 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), call.clone()); |
70 | ); | ||
71 | item.kind(self.kind()) | 70 | item.kind(self.kind()) |
72 | .set_documentation(self.ctx.docs(self.func)) | 71 | .set_documentation(self.ctx.docs(self.func)) |
73 | .set_deprecated( | 72 | .set_deprecated( |
74 | self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func), | 73 | self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func), |
75 | ) | 74 | ) |
76 | .detail(self.detail()) | 75 | .detail(self.detail()) |
77 | .add_call_parens(self.ctx.completion, self.name.clone(), params) | 76 | .add_call_parens(self.ctx.completion, call.clone(), params) |
78 | .add_import(import_to_add); | 77 | .add_import(import_to_add) |
78 | .lookup_by(self.name); | ||
79 | 79 | ||
80 | let ret_type = self.func.ret_type(self.ctx.db()); | 80 | let ret_type = self.func.ret_type(self.ctx.db()); |
81 | item.set_relevance(CompletionRelevance { | 81 | item.set_relevance(CompletionRelevance { |
82 | type_match: compute_type_match(self.ctx.completion, &ret_type), | 82 | type_match: compute_type_match(self.ctx.completion, &ret_type), |
83 | exact_name_match: compute_exact_name_match(self.ctx.completion, &self.name), | 83 | exact_name_match: compute_exact_name_match(self.ctx.completion, &call), |
84 | ..CompletionRelevance::default() | 84 | ..CompletionRelevance::default() |
85 | }); | 85 | }); |
86 | 86 | ||
@@ -263,7 +263,7 @@ fn bar(s: &S) { | |||
263 | ); | 263 | ); |
264 | 264 | ||
265 | check_edit( | 265 | check_edit( |
266 | "self.foo", | 266 | "foo", |
267 | r#" | 267 | r#" |
268 | struct S {} | 268 | struct S {} |
269 | impl S { | 269 | impl S { |
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs index 0dfba8acc..429d937c8 100644 --- a/crates/ide_completion/src/render/macro_.rs +++ b/crates/ide_completion/src/render/macro_.rs | |||
@@ -5,6 +5,7 @@ use ide_db::SymbolKind; | |||
5 | use syntax::display::macro_label; | 5 | use syntax::display::macro_label; |
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
8 | context::CallKind, | ||
8 | item::{CompletionItem, CompletionKind, ImportEdit}, | 9 | item::{CompletionItem, CompletionKind, ImportEdit}, |
9 | render::RenderContext, | 10 | render::RenderContext, |
10 | }; | 11 | }; |
@@ -68,7 +69,8 @@ impl<'a> MacroRender<'a> { | |||
68 | } | 69 | } |
69 | 70 | ||
70 | fn needs_bang(&self) -> bool { | 71 | fn needs_bang(&self) -> bool { |
71 | self.ctx.completion.use_item_syntax.is_none() && !self.ctx.completion.is_macro_call | 72 | self.ctx.completion.use_item_syntax.is_none() |
73 | && !matches!(self.ctx.completion.path_call_kind(), Some(CallKind::Mac)) | ||
72 | } | 74 | } |
73 | 75 | ||
74 | fn label(&self) -> String { | 76 | fn label(&self) -> String { |
diff --git a/crates/ide_completion/src/render/pattern.rs b/crates/ide_completion/src/render/pattern.rs index b4e80f424..3717a0409 100644 --- a/crates/ide_completion/src/render/pattern.rs +++ b/crates/ide_completion/src/render/pattern.rs | |||
@@ -75,10 +75,10 @@ fn render_pat( | |||
75 | ) -> Option<String> { | 75 | ) -> Option<String> { |
76 | let mut pat = match kind { | 76 | let mut pat = match kind { |
77 | StructKind::Tuple if ctx.snippet_cap().is_some() => { | 77 | StructKind::Tuple if ctx.snippet_cap().is_some() => { |
78 | render_tuple_as_pat(&fields, &name, fields_omitted) | 78 | render_tuple_as_pat(fields, name, fields_omitted) |
79 | } | 79 | } |
80 | StructKind::Record => { | 80 | StructKind::Record => { |
81 | render_record_as_pat(ctx.db(), ctx.snippet_cap(), &fields, &name, fields_omitted) | 81 | render_record_as_pat(ctx.db(), ctx.snippet_cap(), fields, name, fields_omitted) |
82 | } | 82 | } |
83 | _ => return None, | 83 | _ => return None, |
84 | }; | 84 | }; |
@@ -86,7 +86,7 @@ fn render_pat( | |||
86 | if ctx.completion.is_param { | 86 | if ctx.completion.is_param { |
87 | pat.push(':'); | 87 | pat.push(':'); |
88 | pat.push(' '); | 88 | pat.push(' '); |
89 | pat.push_str(&name); | 89 | pat.push_str(name); |
90 | } | 90 | } |
91 | if ctx.snippet_cap().is_some() { | 91 | if ctx.snippet_cap().is_some() { |
92 | pat.push_str("$0"); | 92 | pat.push_str("$0"); |
diff --git a/crates/ide_completion/src/render/type_alias.rs b/crates/ide_completion/src/render/type_alias.rs index e47b4c745..e0234171a 100644 --- a/crates/ide_completion/src/render/type_alias.rs +++ b/crates/ide_completion/src/render/type_alias.rs | |||
@@ -16,7 +16,14 @@ pub(crate) fn render_type_alias<'a>( | |||
16 | ctx: RenderContext<'a>, | 16 | ctx: RenderContext<'a>, |
17 | type_alias: hir::TypeAlias, | 17 | type_alias: hir::TypeAlias, |
18 | ) -> Option<CompletionItem> { | 18 | ) -> Option<CompletionItem> { |
19 | TypeAliasRender::new(ctx, type_alias)?.render() | 19 | TypeAliasRender::new(ctx, type_alias)?.render(false) |
20 | } | ||
21 | |||
22 | pub(crate) fn render_type_alias_with_eq<'a>( | ||
23 | ctx: RenderContext<'a>, | ||
24 | type_alias: hir::TypeAlias, | ||
25 | ) -> Option<CompletionItem> { | ||
26 | TypeAliasRender::new(ctx, type_alias)?.render(true) | ||
20 | } | 27 | } |
21 | 28 | ||
22 | #[derive(Debug)] | 29 | #[derive(Debug)] |
@@ -32,8 +39,14 @@ impl<'a> TypeAliasRender<'a> { | |||
32 | Some(TypeAliasRender { ctx, type_alias, ast_node }) | 39 | Some(TypeAliasRender { ctx, type_alias, ast_node }) |
33 | } | 40 | } |
34 | 41 | ||
35 | fn render(self) -> Option<CompletionItem> { | 42 | fn render(self, with_eq: bool) -> Option<CompletionItem> { |
36 | let name = self.name()?; | 43 | let name = self.ast_node.name().map(|name| { |
44 | if with_eq { | ||
45 | format!("{} = ", name.text()) | ||
46 | } else { | ||
47 | name.text().to_string() | ||
48 | } | ||
49 | })?; | ||
37 | let detail = self.detail(); | 50 | let detail = self.detail(); |
38 | 51 | ||
39 | let mut item = | 52 | let mut item = |
@@ -49,10 +62,6 @@ impl<'a> TypeAliasRender<'a> { | |||
49 | Some(item.build()) | 62 | Some(item.build()) |
50 | } | 63 | } |
51 | 64 | ||
52 | fn name(&self) -> Option<String> { | ||
53 | self.ast_node.name().map(|name| name.text().to_string()) | ||
54 | } | ||
55 | |||
56 | fn detail(&self) -> String { | 65 | fn detail(&self) -> String { |
57 | type_label(&self.ast_node) | 66 | type_label(&self.ast_node) |
58 | } | 67 | } |
diff --git a/crates/ide_db/Cargo.toml b/crates/ide_db/Cargo.toml index 6229996ec..e219c577a 100644 --- a/crates/ide_db/Cargo.toml +++ b/crates/ide_db/Cargo.toml | |||
@@ -10,7 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = { version = "1.1", features = ["thread-local"] } | 13 | cov-mark = "2.0.0-pre.1" |
14 | log = "0.4.8" | 14 | log = "0.4.8" |
15 | rayon = "1.5.0" | 15 | rayon = "1.5.0" |
16 | fst = { version = "0.4", default-features = false } | 16 | fst = { version = "0.4", default-features = false } |
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs index bad277a95..4795e2565 100644 --- a/crates/ide_db/src/call_info.rs +++ b/crates/ide_db/src/call_info.rs | |||
@@ -162,7 +162,7 @@ impl ActiveParameter { | |||
162 | } | 162 | } |
163 | 163 | ||
164 | pub fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> { | 164 | pub fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> { |
165 | let (signature, active_parameter) = call_info_impl(&sema, token)?; | 165 | let (signature, active_parameter) = call_info_impl(sema, token)?; |
166 | 166 | ||
167 | let idx = active_parameter?; | 167 | let idx = active_parameter?; |
168 | let mut params = signature.params(sema.db); | 168 | let mut params = signature.params(sema.db); |
@@ -223,9 +223,8 @@ impl FnCallNode { | |||
223 | ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?, | 223 | ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?, |
224 | _ => return None, | 224 | _ => return None, |
225 | }), | 225 | }), |
226 | |||
227 | FnCallNode::MethodCallExpr(call_expr) => { | 226 | FnCallNode::MethodCallExpr(call_expr) => { |
228 | call_expr.syntax().children().filter_map(ast::NameRef::cast).next() | 227 | call_expr.syntax().children().find_map(ast::NameRef::cast) |
229 | } | 228 | } |
230 | } | 229 | } |
231 | } | 230 | } |
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs index 1dcccbb8b..a54f2c323 100644 --- a/crates/ide_db/src/defs.rs +++ b/crates/ide_db/src/defs.rs | |||
@@ -357,9 +357,9 @@ impl NameRefClass { | |||
357 | } | 357 | } |
358 | } | 358 | } |
359 | 359 | ||
360 | if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { | 360 | if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) { |
361 | if let Some(path) = macro_call.path() { | 361 | if path.qualifier().is_none() { |
362 | if path.qualifier().is_none() { | 362 | if let Some(macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) { |
363 | // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment | 363 | // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment |
364 | // paths are handled below (allowing `log$0::info!` to resolve to the log crate). | 364 | // paths are handled below (allowing `log$0::info!` to resolve to the log crate). |
365 | if let Some(macro_def) = sema.resolve_macro_call(¯o_call) { | 365 | if let Some(macro_def) = sema.resolve_macro_call(¯o_call) { |
@@ -367,11 +367,9 @@ impl NameRefClass { | |||
367 | } | 367 | } |
368 | } | 368 | } |
369 | } | 369 | } |
370 | } | ||
371 | 370 | ||
372 | if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) { | ||
373 | if let Some(resolved) = sema.resolve_path(&path) { | 371 | if let Some(resolved) = sema.resolve_path(&path) { |
374 | if path.syntax().parent().and_then(ast::Attr::cast).is_some() { | 372 | if path.syntax().ancestors().find_map(ast::Attr::cast).is_some() { |
375 | if let PathResolution::Def(ModuleDef::Function(func)) = resolved { | 373 | if let PathResolution::Def(ModuleDef::Function(func)) = resolved { |
376 | if func.attrs(sema.db).by_key("proc_macro_attribute").exists() { | 374 | if func.attrs(sema.db).by_key("proc_macro_attribute").exists() { |
377 | return Some(NameRefClass::Definition(resolved.into())); | 375 | return Some(NameRefClass::Definition(resolved.into())); |
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index 21b48237a..00900cdc2 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs | |||
@@ -3,6 +3,7 @@ pub mod import_assets; | |||
3 | pub mod insert_use; | 3 | pub mod insert_use; |
4 | pub mod merge_imports; | 4 | pub mod merge_imports; |
5 | pub mod rust_doc; | 5 | pub mod rust_doc; |
6 | pub mod generated_lints; | ||
6 | 7 | ||
7 | use std::collections::VecDeque; | 8 | use std::collections::VecDeque; |
8 | 9 | ||
diff --git a/crates/ide_completion/src/generated_lint_completions.rs b/crates/ide_db/src/helpers/generated_lints.rs index 0d405350d..55438749b 100644 --- a/crates/ide_completion/src/generated_lint_completions.rs +++ b/crates/ide_db/src/helpers/generated_lints.rs | |||
@@ -1,320 +1,487 @@ | |||
1 | //! Generated file, do not edit by hand, see `xtask/src/codegen` | 1 | //! Generated file, do not edit by hand, see `xtask/src/codegen` |
2 | 2 | ||
3 | use crate::completions::attribute::LintCompletion; | 3 | pub struct Lint { |
4 | pub(super) const FEATURES: &[LintCompletion] = &[ | 4 | pub label: &'static str, |
5 | LintCompletion { | 5 | pub description: &'static str, |
6 | label: "plugin_registrar", | ||
7 | description: r##"# `plugin_registrar` | ||
8 | |||
9 | The tracking issue for this feature is: [#29597] | ||
10 | |||
11 | [#29597]: https://github.com/rust-lang/rust/issues/29597 | ||
12 | |||
13 | This feature is part of "compiler plugins." It will often be used with the | ||
14 | [`plugin`] and `rustc_private` features as well. For more details, see | ||
15 | their docs. | ||
16 | |||
17 | [`plugin`]: plugin.md | ||
18 | |||
19 | ------------------------ | ||
20 | "##, | ||
21 | }, | ||
22 | LintCompletion { | ||
23 | label: "inline_const", | ||
24 | description: r##"# `inline_const` | ||
25 | |||
26 | The tracking issue for this feature is: [#76001] | ||
27 | |||
28 | ------ | ||
29 | |||
30 | This feature allows you to use inline constant expressions. For example, you can | ||
31 | turn this code: | ||
32 | |||
33 | ```rust | ||
34 | # fn add_one(x: i32) -> i32 { x + 1 } | ||
35 | const MY_COMPUTATION: i32 = 1 + 2 * 3 / 4; | ||
36 | |||
37 | fn main() { | ||
38 | let x = add_one(MY_COMPUTATION); | ||
39 | } | ||
40 | ``` | ||
41 | |||
42 | into this code: | ||
43 | |||
44 | ```rust | ||
45 | #![feature(inline_const)] | ||
46 | |||
47 | # fn add_one(x: i32) -> i32 { x + 1 } | ||
48 | fn main() { | ||
49 | let x = add_one(const { 1 + 2 * 3 / 4 }); | ||
50 | } | ||
51 | ``` | ||
52 | |||
53 | You can also use inline constant expressions in patterns: | ||
54 | |||
55 | ```rust | ||
56 | #![feature(inline_const)] | ||
57 | |||
58 | const fn one() -> i32 { 1 } | ||
59 | |||
60 | let some_int = 3; | ||
61 | match some_int { | ||
62 | const { 1 + 2 } => println!("Matched 1 + 2"), | ||
63 | const { one() } => println!("Matched const fn returning 1"), | ||
64 | _ => println!("Didn't match anything :("), | ||
65 | } | 6 | } |
66 | ``` | ||
67 | |||
68 | [#76001]: https://github.com/rust-lang/rust/issues/76001 | ||
69 | "##, | ||
70 | }, | ||
71 | LintCompletion { | ||
72 | label: "auto_traits", | ||
73 | description: r##"# `auto_traits` | ||
74 | |||
75 | The tracking issue for this feature is [#13231] | ||
76 | |||
77 | [#13231]: https://github.com/rust-lang/rust/issues/13231 | ||
78 | |||
79 | ---- | ||
80 | |||
81 | The `auto_traits` feature gate allows you to define auto traits. | ||
82 | |||
83 | Auto traits, like [`Send`] or [`Sync`] in the standard library, are marker traits | ||
84 | that are automatically implemented for every type, unless the type, or a type it contains, | ||
85 | has explicitly opted out via a negative impl. (Negative impls are separately controlled | ||
86 | by the `negative_impls` feature.) | ||
87 | |||
88 | [`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html | ||
89 | [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html | ||
90 | |||
91 | ```rust,ignore (partial-example) | ||
92 | impl !Trait for Type {} | ||
93 | ``` | ||
94 | |||
95 | Example: | ||
96 | |||
97 | ```rust | ||
98 | #![feature(negative_impls)] | ||
99 | #![feature(auto_traits)] | ||
100 | |||
101 | auto trait Valid {} | ||
102 | |||
103 | struct True; | ||
104 | struct False; | ||
105 | |||
106 | impl !Valid for False {} | ||
107 | |||
108 | struct MaybeValid<T>(T); | ||
109 | |||
110 | fn must_be_valid<T: Valid>(_t: T) { } | ||
111 | |||
112 | fn main() { | ||
113 | // works | ||
114 | must_be_valid( MaybeValid(True) ); | ||
115 | |||
116 | // compiler error - trait bound not satisfied | ||
117 | // must_be_valid( MaybeValid(False) ); | ||
118 | } | ||
119 | ``` | ||
120 | |||
121 | ## Automatic trait implementations | ||
122 | |||
123 | When a type is declared as an `auto trait`, we will automatically | ||
124 | create impls for every struct/enum/union, unless an explicit impl is | ||
125 | provided. These automatic impls contain a where clause for each field | ||
126 | of the form `T: AutoTrait`, where `T` is the type of the field and | ||
127 | `AutoTrait` is the auto trait in question. As an example, consider the | ||
128 | struct `List` and the auto trait `Send`: | ||
129 | |||
130 | ```rust | ||
131 | struct List<T> { | ||
132 | data: T, | ||
133 | next: Option<Box<List<T>>>, | ||
134 | } | ||
135 | ``` | ||
136 | |||
137 | Presuming that there is no explicit impl of `Send` for `List`, the | ||
138 | compiler will supply an automatic impl of the form: | ||
139 | |||
140 | ```rust | ||
141 | struct List<T> { | ||
142 | data: T, | ||
143 | next: Option<Box<List<T>>>, | ||
144 | } | ||
145 | |||
146 | unsafe impl<T> Send for List<T> | ||
147 | where | ||
148 | T: Send, // from the field `data` | ||
149 | Option<Box<List<T>>>: Send, // from the field `next` | ||
150 | { } | ||
151 | ``` | ||
152 | |||
153 | Explicit impls may be either positive or negative. They take the form: | ||
154 | |||
155 | ```rust,ignore (partial-example) | ||
156 | impl<...> AutoTrait for StructName<..> { } | ||
157 | impl<...> !AutoTrait for StructName<..> { } | ||
158 | ``` | ||
159 | |||
160 | ## Coinduction: Auto traits permit cyclic matching | ||
161 | |||
162 | Unlike ordinary trait matching, auto traits are **coinductive**. This | ||
163 | means, in short, that cycles which occur in trait matching are | ||
164 | considered ok. As an example, consider the recursive struct `List` | ||
165 | introduced in the previous section. In attempting to determine whether | ||
166 | `List: Send`, we would wind up in a cycle: to apply the impl, we must | ||
167 | show that `Option<Box<List>>: Send`, which will in turn require | ||
168 | `Box<List>: Send` and then finally `List: Send` again. Under ordinary | ||
169 | trait matching, this cycle would be an error, but for an auto trait it | ||
170 | is considered a successful match. | ||
171 | |||
172 | ## Items | ||
173 | |||
174 | Auto traits cannot have any trait items, such as methods or associated types. This ensures that we can generate default implementations. | ||
175 | |||
176 | ## Supertraits | ||
177 | |||
178 | Auto traits cannot have supertraits. This is for soundness reasons, as the interaction of coinduction with implied bounds is difficult to reconcile. | ||
179 | "##, | ||
180 | }, | ||
181 | LintCompletion { | ||
182 | label: "ffi_const", | ||
183 | description: r##"# `ffi_const` | ||
184 | |||
185 | The tracking issue for this feature is: [#58328] | ||
186 | |||
187 | ------ | ||
188 | |||
189 | The `#[ffi_const]` attribute applies clang's `const` attribute to foreign | ||
190 | functions declarations. | ||
191 | |||
192 | That is, `#[ffi_const]` functions shall have no effects except for its return | ||
193 | value, which can only depend on the values of the function parameters, and is | ||
194 | not affected by changes to the observable state of the program. | ||
195 | |||
196 | Applying the `#[ffi_const]` attribute to a function that violates these | ||
197 | requirements is undefined behaviour. | ||
198 | |||
199 | This attribute enables Rust to perform common optimizations, like sub-expression | ||
200 | elimination, and it can avoid emitting some calls in repeated invocations of the | ||
201 | function with the same argument values regardless of other operations being | ||
202 | performed in between these functions calls (as opposed to `#[ffi_pure]` | ||
203 | functions). | ||
204 | |||
205 | ## Pitfalls | ||
206 | |||
207 | A `#[ffi_const]` function can only read global memory that would not affect | ||
208 | its return value for the whole execution of the program (e.g. immutable global | ||
209 | memory). `#[ffi_const]` functions are referentially-transparent and therefore | ||
210 | more strict than `#[ffi_pure]` functions. | ||
211 | |||
212 | A common pitfall involves applying the `#[ffi_const]` attribute to a | ||
213 | function that reads memory through pointer arguments which do not necessarily | ||
214 | point to immutable global memory. | ||
215 | |||
216 | A `#[ffi_const]` function that returns unit has no effect on the abstract | ||
217 | machine's state, and a `#[ffi_const]` function cannot be `#[ffi_pure]`. | ||
218 | |||
219 | A `#[ffi_const]` function must not diverge, neither via a side effect (e.g. a | ||
220 | call to `abort`) nor by infinite loops. | ||
221 | |||
222 | When translating C headers to Rust FFI, it is worth verifying for which targets | ||
223 | the `const` attribute is enabled in those headers, and using the appropriate | ||
224 | `cfg` macros in the Rust side to match those definitions. While the semantics of | ||
225 | `const` are implemented identically by many C and C++ compilers, e.g., clang, | ||
226 | [GCC], [ARM C/C++ compiler], [IBM ILE C/C++], etc. they are not necessarily | ||
227 | implemented in this way on all of them. It is therefore also worth verifying | ||
228 | that the semantics of the C toolchain used to compile the binary being linked | ||
229 | against are compatible with those of the `#[ffi_const]`. | ||
230 | 7 | ||
231 | [#58328]: https://github.com/rust-lang/rust/issues/58328 | 8 | pub const DEFAULT_LINTS: &[Lint] = &[ |
232 | [ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacgigch.html | 9 | Lint { |
233 | [GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-const-function-attribute | 10 | label: "absolute_paths_not_starting_with_crate", |
234 | [IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_const.htm | 11 | description: r##"fully qualified paths that start with a module name instead of `crate`, `self`, or an extern crate name"##, |
235 | "##, | 12 | }, |
13 | Lint { label: "ambiguous_associated_items", description: r##"ambiguous associated items"## }, | ||
14 | Lint { label: "anonymous_parameters", description: r##"detects anonymous parameters"## }, | ||
15 | Lint { label: "arithmetic_overflow", description: r##"arithmetic operation overflows"## }, | ||
16 | Lint { label: "array_into_iter", description: r##"detects calling `into_iter` on arrays"## }, | ||
17 | Lint { | ||
18 | label: "asm_sub_register", | ||
19 | description: r##"using only a subset of a register for inline asm inputs"##, | ||
20 | }, | ||
21 | Lint { label: "bad_asm_style", description: r##"incorrect use of inline assembly"## }, | ||
22 | Lint { | ||
23 | label: "bare_trait_objects", | ||
24 | description: r##"suggest using `dyn Trait` for trait objects"##, | ||
25 | }, | ||
26 | Lint { | ||
27 | label: "bindings_with_variant_name", | ||
28 | description: r##"detects pattern bindings with the same name as one of the matched variants"##, | ||
29 | }, | ||
30 | Lint { label: "box_pointers", description: r##"use of owned (Box type) heap memory"## }, | ||
31 | Lint { | ||
32 | label: "cenum_impl_drop_cast", | ||
33 | description: r##"a C-like enum implementing Drop is cast"##, | ||
34 | }, | ||
35 | Lint { | ||
36 | label: "clashing_extern_declarations", | ||
37 | description: r##"detects when an extern fn has been declared with the same name but different types"##, | ||
38 | }, | ||
39 | Lint { | ||
40 | label: "coherence_leak_check", | ||
41 | description: r##"distinct impls distinguished only by the leak-check code"##, | ||
42 | }, | ||
43 | Lint { | ||
44 | label: "conflicting_repr_hints", | ||
45 | description: r##"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"##, | ||
46 | }, | ||
47 | Lint { | ||
48 | label: "confusable_idents", | ||
49 | description: r##"detects visually confusable pairs between identifiers"##, | ||
50 | }, | ||
51 | Lint { | ||
52 | label: "const_err", | ||
53 | description: r##"constant evaluation encountered erroneous expression"##, | ||
54 | }, | ||
55 | Lint { | ||
56 | label: "const_evaluatable_unchecked", | ||
57 | description: r##"detects a generic constant is used in a type without a emitting a warning"##, | ||
58 | }, | ||
59 | Lint { | ||
60 | label: "const_item_mutation", | ||
61 | description: r##"detects attempts to mutate a `const` item"##, | ||
62 | }, | ||
63 | Lint { label: "dead_code", description: r##"detect unused, unexported items"## }, | ||
64 | Lint { label: "deprecated", description: r##"detects use of deprecated items"## }, | ||
65 | Lint { | ||
66 | label: "deprecated_in_future", | ||
67 | description: r##"detects use of items that will be deprecated in a future version"##, | ||
68 | }, | ||
69 | Lint { | ||
70 | label: "deref_nullptr", | ||
71 | description: r##"detects when an null pointer is dereferenced"##, | ||
72 | }, | ||
73 | Lint { | ||
74 | label: "disjoint_capture_migration", | ||
75 | description: r##"Drop reorder and auto traits error because of `capture_disjoint_fields`"##, | ||
76 | }, | ||
77 | Lint { label: "drop_bounds", description: r##"bounds of the form `T: Drop` are useless"## }, | ||
78 | Lint { | ||
79 | label: "elided_lifetimes_in_paths", | ||
80 | description: r##"hidden lifetime parameters in types are deprecated"##, | ||
81 | }, | ||
82 | Lint { | ||
83 | label: "ellipsis_inclusive_range_patterns", | ||
84 | description: r##"`...` range patterns are deprecated"##, | ||
85 | }, | ||
86 | Lint { | ||
87 | label: "explicit_outlives_requirements", | ||
88 | description: r##"outlives requirements can be inferred"##, | ||
89 | }, | ||
90 | Lint { | ||
91 | label: "exported_private_dependencies", | ||
92 | description: r##"public interface leaks type from a private dependency"##, | ||
93 | }, | ||
94 | Lint { label: "forbidden_lint_groups", description: r##"applying forbid to lint-groups"## }, | ||
95 | Lint { | ||
96 | label: "function_item_references", | ||
97 | description: r##"suggest casting to a function pointer when attempting to take references to function items"##, | ||
98 | }, | ||
99 | Lint { | ||
100 | label: "future_incompatible", | ||
101 | description: r##"lint group for: keyword-idents, anonymous-parameters, ellipsis-inclusive-range-patterns, forbidden-lint-groups, illegal-floating-point-literal-pattern, private-in-public, pub-use-of-private-extern-crate, invalid-type-param-default, const-err, unaligned-references, patterns-in-fns-without-body, missing-fragment-specifier, late-bound-lifetime-arguments, order-dependent-trait-objects, coherence-leak-check, tyvar-behind-raw-pointer, bare-trait-objects, absolute-paths-not-starting-with-crate, unstable-name-collisions, where-clauses-object-safety, proc-macro-derive-resolution-fallback, macro-expanded-macro-exports-accessed-by-absolute-paths, ill-formed-attribute-input, conflicting-repr-hints, ambiguous-associated-items, mutable-borrow-reservation-conflict, indirect-structural-match, pointer-structural-match, nontrivial-structural-match, soft-unstable, cenum-impl-drop-cast, const-evaluatable-unchecked, uninhabited-static, unsupported-naked-functions, semicolon-in-expressions-from-macros, legacy-derive-helpers, proc-macro-back-compat, array-into-iter"##, | ||
102 | }, | ||
103 | Lint { | ||
104 | label: "ill_formed_attribute_input", | ||
105 | description: r##"ill-formed attribute inputs that were previously accepted and used in practice"##, | ||
106 | }, | ||
107 | Lint { | ||
108 | label: "illegal_floating_point_literal_pattern", | ||
109 | description: r##"floating-point literals cannot be used in patterns"##, | ||
110 | }, | ||
111 | Lint { | ||
112 | label: "improper_ctypes", | ||
113 | description: r##"proper use of libc types in foreign modules"##, | ||
114 | }, | ||
115 | Lint { | ||
116 | label: "improper_ctypes_definitions", | ||
117 | description: r##"proper use of libc types in foreign item definitions"##, | ||
118 | }, | ||
119 | Lint { | ||
120 | label: "incomplete_features", | ||
121 | description: r##"incomplete features that may function improperly in some or all cases"##, | ||
122 | }, | ||
123 | Lint { label: "incomplete_include", description: r##"trailing content in included file"## }, | ||
124 | Lint { | ||
125 | label: "indirect_structural_match", | ||
126 | description: r##"constant used in pattern contains value of non-structural-match type in a field or a variant"##, | ||
127 | }, | ||
128 | Lint { | ||
129 | label: "ineffective_unstable_trait_impl", | ||
130 | description: r##"detects `#[unstable]` on stable trait implementations for stable types"##, | ||
131 | }, | ||
132 | Lint { | ||
133 | label: "inline_no_sanitize", | ||
134 | description: r##"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"##, | ||
135 | }, | ||
136 | Lint { | ||
137 | label: "invalid_type_param_default", | ||
138 | description: r##"type parameter default erroneously allowed in invalid location"##, | ||
139 | }, | ||
140 | Lint { | ||
141 | label: "invalid_value", | ||
142 | description: r##"an invalid value is being created (such as a null reference)"##, | ||
143 | }, | ||
144 | Lint { | ||
145 | label: "irrefutable_let_patterns", | ||
146 | description: r##"detects irrefutable patterns in `if let` and `while let` statements"##, | ||
147 | }, | ||
148 | Lint { | ||
149 | label: "keyword_idents", | ||
150 | description: r##"detects edition keywords being used as an identifier"##, | ||
151 | }, | ||
152 | Lint { label: "large_assignments", description: r##"detects large moves or copies"## }, | ||
153 | Lint { | ||
154 | label: "late_bound_lifetime_arguments", | ||
155 | description: r##"detects generic lifetime arguments in path segments with late bound lifetime parameters"##, | ||
156 | }, | ||
157 | Lint { | ||
158 | label: "legacy_derive_helpers", | ||
159 | description: r##"detects derive helper attributes that are used before they are introduced"##, | ||
160 | }, | ||
161 | Lint { | ||
162 | label: "macro_expanded_macro_exports_accessed_by_absolute_paths", | ||
163 | description: r##"macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths"##, | ||
164 | }, | ||
165 | Lint { | ||
166 | label: "macro_use_extern_crate", | ||
167 | description: r##"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"##, | ||
168 | }, | ||
169 | Lint { | ||
170 | label: "meta_variable_misuse", | ||
171 | description: r##"possible meta-variable misuse at macro definition"##, | ||
172 | }, | ||
173 | Lint { label: "missing_abi", description: r##"No declared ABI for extern declaration"## }, | ||
174 | Lint { | ||
175 | label: "missing_copy_implementations", | ||
176 | description: r##"detects potentially-forgotten implementations of `Copy`"##, | ||
177 | }, | ||
178 | Lint { | ||
179 | label: "missing_debug_implementations", | ||
180 | description: r##"detects missing implementations of Debug"##, | ||
181 | }, | ||
182 | Lint { | ||
183 | label: "missing_docs", | ||
184 | description: r##"detects missing documentation for public members"##, | ||
185 | }, | ||
186 | Lint { | ||
187 | label: "missing_fragment_specifier", | ||
188 | description: r##"detects missing fragment specifiers in unused `macro_rules!` patterns"##, | ||
189 | }, | ||
190 | Lint { | ||
191 | label: "mixed_script_confusables", | ||
192 | description: r##"detects Unicode scripts whose mixed script confusables codepoints are solely used"##, | ||
193 | }, | ||
194 | Lint { | ||
195 | label: "mutable_borrow_reservation_conflict", | ||
196 | description: r##"reservation of a two-phased borrow conflicts with other shared borrows"##, | ||
197 | }, | ||
198 | Lint { | ||
199 | label: "mutable_transmutes", | ||
200 | description: r##"mutating transmuted &mut T from &T may cause undefined behavior"##, | ||
201 | }, | ||
202 | Lint { | ||
203 | label: "no_mangle_const_items", | ||
204 | description: r##"const items will not have their symbols exported"##, | ||
205 | }, | ||
206 | Lint { label: "no_mangle_generic_items", description: r##"generic items must be mangled"## }, | ||
207 | Lint { label: "non_ascii_idents", description: r##"detects non-ASCII identifiers"## }, | ||
208 | Lint { | ||
209 | label: "non_camel_case_types", | ||
210 | description: r##"types, variants, traits and type parameters should have camel case names"##, | ||
211 | }, | ||
212 | Lint { | ||
213 | label: "non_fmt_panic", | ||
214 | description: r##"detect single-argument panic!() invocations in which the argument is not a format string"##, | ||
215 | }, | ||
216 | Lint { | ||
217 | label: "non_shorthand_field_patterns", | ||
218 | description: r##"using `Struct { x: x }` instead of `Struct { x }` in a pattern"##, | ||
219 | }, | ||
220 | Lint { | ||
221 | label: "non_snake_case", | ||
222 | description: r##"variables, methods, functions, lifetime parameters and modules should have snake case names"##, | ||
223 | }, | ||
224 | Lint { | ||
225 | label: "non_upper_case_globals", | ||
226 | description: r##"static constants should have uppercase identifiers"##, | ||
227 | }, | ||
228 | Lint { | ||
229 | label: "nonstandard_style", | ||
230 | description: r##"lint group for: non-camel-case-types, non-snake-case, non-upper-case-globals"##, | ||
231 | }, | ||
232 | Lint { | ||
233 | label: "nontrivial_structural_match", | ||
234 | description: r##"constant used in pattern of non-structural-match type and the constant's initializer expression contains values of non-structural-match types"##, | ||
235 | }, | ||
236 | Lint { | ||
237 | label: "noop_method_call", | ||
238 | description: r##"detects the use of well-known noop methods"##, | ||
239 | }, | ||
240 | Lint { | ||
241 | label: "or_patterns_back_compat", | ||
242 | description: r##"detects usage of old versions of or-patterns"##, | ||
243 | }, | ||
244 | Lint { | ||
245 | label: "order_dependent_trait_objects", | ||
246 | description: r##"trait-object types were treated as different depending on marker-trait order"##, | ||
247 | }, | ||
248 | Lint { label: "overflowing_literals", description: r##"literal out of range for its type"## }, | ||
249 | Lint { | ||
250 | label: "overlapping_range_endpoints", | ||
251 | description: r##"detects range patterns with overlapping endpoints"##, | ||
252 | }, | ||
253 | Lint { label: "path_statements", description: r##"path statements with no effect"## }, | ||
254 | Lint { | ||
255 | label: "patterns_in_fns_without_body", | ||
256 | description: r##"patterns in functions without body were erroneously allowed"##, | ||
257 | }, | ||
258 | Lint { | ||
259 | label: "pointer_structural_match", | ||
260 | description: r##"pointers are not structural-match"##, | ||
261 | }, | ||
262 | Lint { | ||
263 | label: "private_in_public", | ||
264 | description: r##"detect private items in public interfaces not caught by the old implementation"##, | ||
265 | }, | ||
266 | Lint { | ||
267 | label: "proc_macro_back_compat", | ||
268 | description: r##"detects usage of old versions of certain proc-macro crates"##, | ||
269 | }, | ||
270 | Lint { | ||
271 | label: "proc_macro_derive_resolution_fallback", | ||
272 | description: r##"detects proc macro derives using inaccessible names from parent modules"##, | ||
273 | }, | ||
274 | Lint { | ||
275 | label: "pub_use_of_private_extern_crate", | ||
276 | description: r##"detect public re-exports of private extern crates"##, | ||
277 | }, | ||
278 | Lint { | ||
279 | label: "redundant_semicolons", | ||
280 | description: r##"detects unnecessary trailing semicolons"##, | ||
281 | }, | ||
282 | Lint { | ||
283 | label: "renamed_and_removed_lints", | ||
284 | description: r##"lints that have been renamed or removed"##, | ||
285 | }, | ||
286 | Lint { | ||
287 | label: "rust_2018_compatibility", | ||
288 | description: r##"lint group for: keyword-idents, anonymous-parameters, tyvar-behind-raw-pointer, absolute-paths-not-starting-with-crate"##, | ||
289 | }, | ||
290 | Lint { | ||
291 | label: "rust_2018_idioms", | ||
292 | description: r##"lint group for: bare-trait-objects, unused-extern-crates, ellipsis-inclusive-range-patterns, elided-lifetimes-in-paths, explicit-outlives-requirements"##, | ||
293 | }, | ||
294 | Lint { | ||
295 | label: "rust_2021_compatibility", | ||
296 | description: r##"lint group for: ellipsis-inclusive-range-patterns, bare-trait-objects"##, | ||
297 | }, | ||
298 | Lint { | ||
299 | label: "semicolon_in_expressions_from_macros", | ||
300 | description: r##"trailing semicolon in macro body used as expression"##, | ||
301 | }, | ||
302 | Lint { | ||
303 | label: "single_use_lifetimes", | ||
304 | description: r##"detects lifetime parameters that are only used once"##, | ||
305 | }, | ||
306 | Lint { | ||
307 | label: "soft_unstable", | ||
308 | description: r##"a feature gate that doesn't break dependent crates"##, | ||
309 | }, | ||
310 | Lint { | ||
311 | label: "stable_features", | ||
312 | description: r##"stable features found in `#[feature]` directive"##, | ||
313 | }, | ||
314 | Lint { | ||
315 | label: "temporary_cstring_as_ptr", | ||
316 | description: r##"detects getting the inner pointer of a temporary `CString`"##, | ||
317 | }, | ||
318 | Lint { | ||
319 | label: "trivial_bounds", | ||
320 | description: r##"these bounds don't depend on an type parameters"##, | ||
321 | }, | ||
322 | Lint { | ||
323 | label: "trivial_casts", | ||
324 | description: r##"detects trivial casts which could be removed"##, | ||
325 | }, | ||
326 | Lint { | ||
327 | label: "trivial_numeric_casts", | ||
328 | description: r##"detects trivial casts of numeric types which could be removed"##, | ||
329 | }, | ||
330 | Lint { | ||
331 | label: "type_alias_bounds", | ||
332 | description: r##"bounds in type aliases are not enforced"##, | ||
333 | }, | ||
334 | Lint { | ||
335 | label: "tyvar_behind_raw_pointer", | ||
336 | description: r##"raw pointer to an inference variable"##, | ||
337 | }, | ||
338 | Lint { | ||
339 | label: "unaligned_references", | ||
340 | description: r##"detects unaligned references to fields of packed structs"##, | ||
341 | }, | ||
342 | Lint { | ||
343 | label: "uncommon_codepoints", | ||
344 | description: r##"detects uncommon Unicode codepoints in identifiers"##, | ||
345 | }, | ||
346 | Lint { | ||
347 | label: "unconditional_panic", | ||
348 | description: r##"operation will cause a panic at runtime"##, | ||
349 | }, | ||
350 | Lint { | ||
351 | label: "unconditional_recursion", | ||
352 | description: r##"functions that cannot return without calling themselves"##, | ||
353 | }, | ||
354 | Lint { label: "uninhabited_static", description: r##"uninhabited static"## }, | ||
355 | Lint { | ||
356 | label: "unknown_crate_types", | ||
357 | description: r##"unknown crate type found in `#[crate_type]` directive"##, | ||
358 | }, | ||
359 | Lint { label: "unknown_lints", description: r##"unrecognized lint attribute"## }, | ||
360 | Lint { | ||
361 | label: "unnameable_test_items", | ||
362 | description: r##"detects an item that cannot be named being marked as `#[test_case]`"##, | ||
363 | }, | ||
364 | Lint { label: "unreachable_code", description: r##"detects unreachable code paths"## }, | ||
365 | Lint { label: "unreachable_patterns", description: r##"detects unreachable patterns"## }, | ||
366 | Lint { | ||
367 | label: "unreachable_pub", | ||
368 | description: r##"`pub` items not reachable from crate root"##, | ||
369 | }, | ||
370 | Lint { label: "unsafe_code", description: r##"usage of `unsafe` code"## }, | ||
371 | Lint { | ||
372 | label: "unsafe_op_in_unsafe_fn", | ||
373 | description: r##"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"##, | ||
374 | }, | ||
375 | Lint { | ||
376 | label: "unstable_features", | ||
377 | description: r##"enabling unstable features (deprecated. do not use)"##, | ||
378 | }, | ||
379 | Lint { | ||
380 | label: "unstable_name_collisions", | ||
381 | description: r##"detects name collision with an existing but unstable method"##, | ||
382 | }, | ||
383 | Lint { | ||
384 | label: "unsupported_naked_functions", | ||
385 | description: r##"unsupported naked function definitions"##, | ||
386 | }, | ||
387 | Lint { | ||
388 | label: "unused", | ||
389 | description: r##"lint group for: unused-imports, unused-variables, unused-assignments, dead-code, unused-mut, unreachable-code, unreachable-patterns, unused-must-use, unused-unsafe, path-statements, unused-attributes, unused-macros, unused-allocation, unused-doc-comments, unused-extern-crates, unused-features, unused-labels, unused-parens, unused-braces, redundant-semicolons"##, | ||
390 | }, | ||
391 | Lint { | ||
392 | label: "unused_allocation", | ||
393 | description: r##"detects unnecessary allocations that can be eliminated"##, | ||
394 | }, | ||
395 | Lint { | ||
396 | label: "unused_assignments", | ||
397 | description: r##"detect assignments that will never be read"##, | ||
398 | }, | ||
399 | Lint { | ||
400 | label: "unused_attributes", | ||
401 | description: r##"detects attributes that were not used by the compiler"##, | ||
402 | }, | ||
403 | Lint { label: "unused_braces", description: r##"unnecessary braces around an expression"## }, | ||
404 | Lint { | ||
405 | label: "unused_comparisons", | ||
406 | description: r##"comparisons made useless by limits of the types involved"##, | ||
407 | }, | ||
408 | Lint { | ||
409 | label: "unused_crate_dependencies", | ||
410 | description: r##"crate dependencies that are never used"##, | ||
411 | }, | ||
412 | Lint { | ||
413 | label: "unused_doc_comments", | ||
414 | description: r##"detects doc comments that aren't used by rustdoc"##, | ||
415 | }, | ||
416 | Lint { label: "unused_extern_crates", description: r##"extern crates that are never used"## }, | ||
417 | Lint { | ||
418 | label: "unused_features", | ||
419 | description: r##"unused features found in crate-level `#[feature]` directives"##, | ||
420 | }, | ||
421 | Lint { | ||
422 | label: "unused_import_braces", | ||
423 | description: r##"unnecessary braces around an imported item"##, | ||
424 | }, | ||
425 | Lint { label: "unused_imports", description: r##"imports that are never used"## }, | ||
426 | Lint { label: "unused_labels", description: r##"detects labels that are never used"## }, | ||
427 | Lint { | ||
428 | label: "unused_lifetimes", | ||
429 | description: r##"detects lifetime parameters that are never used"##, | ||
430 | }, | ||
431 | Lint { label: "unused_macros", description: r##"detects macros that were not used"## }, | ||
432 | Lint { | ||
433 | label: "unused_must_use", | ||
434 | description: r##"unused result of a type flagged as `#[must_use]`"##, | ||
435 | }, | ||
436 | Lint { | ||
437 | label: "unused_mut", | ||
438 | description: r##"detect mut variables which don't need to be mutable"##, | ||
439 | }, | ||
440 | Lint { | ||
441 | label: "unused_parens", | ||
442 | description: r##"`if`, `match`, `while` and `return` do not need parentheses"##, | ||
443 | }, | ||
444 | Lint { | ||
445 | label: "unused_qualifications", | ||
446 | description: r##"detects unnecessarily qualified names"##, | ||
447 | }, | ||
448 | Lint { | ||
449 | label: "unused_results", | ||
450 | description: r##"unused result of an expression in a statement"##, | ||
451 | }, | ||
452 | Lint { label: "unused_unsafe", description: r##"unnecessary use of an `unsafe` block"## }, | ||
453 | Lint { | ||
454 | label: "unused_variables", | ||
455 | description: r##"detect variables which are not used in any way"##, | ||
456 | }, | ||
457 | Lint { | ||
458 | label: "useless_deprecated", | ||
459 | description: r##"detects deprecation attributes with no effect"##, | ||
460 | }, | ||
461 | Lint { | ||
462 | label: "variant_size_differences", | ||
463 | description: r##"detects enums with widely varying variant sizes"##, | ||
464 | }, | ||
465 | Lint { | ||
466 | label: "warnings", | ||
467 | description: r##"mass-change the level for lints which produce warnings"##, | ||
468 | }, | ||
469 | Lint { | ||
470 | label: "warnings", | ||
471 | description: r##"lint group for: all lints that are set to issue warnings"##, | ||
472 | }, | ||
473 | Lint { | ||
474 | label: "where_clauses_object_safety", | ||
475 | description: r##"checks the object safety of where clauses"##, | ||
476 | }, | ||
477 | Lint { | ||
478 | label: "while_true", | ||
479 | description: r##"suggest using `loop { }` instead of `while true { }`"##, | ||
236 | }, | 480 | }, |
237 | LintCompletion { | 481 | ]; |
238 | label: "external_doc", | ||
239 | description: r##"# `external_doc` | ||
240 | |||
241 | The tracking issue for this feature is: [#44732] | ||
242 | |||
243 | The `external_doc` feature allows the use of the `include` parameter to the `#[doc]` attribute, to | ||
244 | include external files in documentation. Use the attribute in place of, or in addition to, regular | ||
245 | doc comments and `#[doc]` attributes, and `rustdoc` will load the given file when it renders | ||
246 | documentation for your crate. | ||
247 | |||
248 | With the following files in the same directory: | ||
249 | |||
250 | `external-doc.md`: | ||
251 | |||
252 | ```markdown | ||
253 | # My Awesome Type | ||
254 | |||
255 | This is the documentation for this spectacular type. | ||
256 | ``` | ||
257 | |||
258 | `lib.rs`: | ||
259 | |||
260 | ```no_run (needs-external-files) | ||
261 | #![feature(external_doc)] | ||
262 | |||
263 | #[doc(include = "external-doc.md")] | ||
264 | pub struct MyAwesomeType; | ||
265 | ``` | ||
266 | |||
267 | `rustdoc` will load the file `external-doc.md` and use it as the documentation for the `MyAwesomeType` | ||
268 | struct. | ||
269 | |||
270 | When locating files, `rustdoc` will base paths in the `src/` directory, as if they were alongside the | ||
271 | `lib.rs` for your crate. So if you want a `docs/` folder to live alongside the `src/` directory, | ||
272 | start your paths with `../docs/` for `rustdoc` to properly find the file. | ||
273 | |||
274 | This feature was proposed in [RFC #1990] and initially implemented in PR [#44781]. | ||
275 | |||
276 | [#44732]: https://github.com/rust-lang/rust/issues/44732 | ||
277 | [RFC #1990]: https://github.com/rust-lang/rfcs/pull/1990 | ||
278 | [#44781]: https://github.com/rust-lang/rust/pull/44781 | ||
279 | "##, | ||
280 | }, | ||
281 | LintCompletion { | ||
282 | label: "box_patterns", | ||
283 | description: r##"# `box_patterns` | ||
284 | |||
285 | The tracking issue for this feature is: [#29641] | ||
286 | |||
287 | [#29641]: https://github.com/rust-lang/rust/issues/29641 | ||
288 | |||
289 | See also [`box_syntax`](box-syntax.md) | ||
290 | |||
291 | ------------------------ | ||
292 | |||
293 | Box patterns let you match on `Box<T>`s: | ||
294 | |||
295 | |||
296 | ```rust | ||
297 | #![feature(box_patterns)] | ||
298 | 482 | ||
299 | fn main() { | 483 | pub const FEATURES: &[Lint] = &[ |
300 | let b = Some(Box::new(5)); | 484 | Lint { |
301 | match b { | ||
302 | Some(box n) if n < 0 => { | ||
303 | println!("Box contains negative number {}", n); | ||
304 | }, | ||
305 | Some(box n) if n >= 0 => { | ||
306 | println!("Box contains non-negative number {}", n); | ||
307 | }, | ||
308 | None => { | ||
309 | println!("No box"); | ||
310 | }, | ||
311 | _ => unreachable!() | ||
312 | } | ||
313 | } | ||
314 | ``` | ||
315 | "##, | ||
316 | }, | ||
317 | LintCompletion { | ||
318 | label: "abi_c_cmse_nonsecure_call", | 485 | label: "abi_c_cmse_nonsecure_call", |
319 | description: r##"# `abi_c_cmse_nonsecure_call` | 486 | description: r##"# `abi_c_cmse_nonsecure_call` |
320 | 487 | ||
@@ -406,1682 +573,7 @@ call_nonsecure_function: | |||
406 | ``` | 573 | ``` |
407 | "##, | 574 | "##, |
408 | }, | 575 | }, |
409 | LintCompletion { | 576 | Lint { |
410 | label: "member_constraints", | ||
411 | description: r##"# `member_constraints` | ||
412 | |||
413 | The tracking issue for this feature is: [#61997] | ||
414 | |||
415 | [#61997]: https://github.com/rust-lang/rust/issues/61997 | ||
416 | |||
417 | ------------------------ | ||
418 | |||
419 | The `member_constraints` feature gate lets you use `impl Trait` syntax with | ||
420 | multiple unrelated lifetime parameters. | ||
421 | |||
422 | A simple example is: | ||
423 | |||
424 | ```rust | ||
425 | #![feature(member_constraints)] | ||
426 | |||
427 | trait Trait<'a, 'b> { } | ||
428 | impl<T> Trait<'_, '_> for T {} | ||
429 | |||
430 | fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Trait<'a, 'b> { | ||
431 | (x, y) | ||
432 | } | ||
433 | |||
434 | fn main() { } | ||
435 | ``` | ||
436 | |||
437 | Without the `member_constraints` feature gate, the above example is an | ||
438 | error because both `'a` and `'b` appear in the impl Trait bounds, but | ||
439 | neither outlives the other. | ||
440 | "##, | ||
441 | }, | ||
442 | LintCompletion { | ||
443 | label: "allocator_internals", | ||
444 | description: r##"# `allocator_internals` | ||
445 | |||
446 | This feature does not have a tracking issue, it is an unstable implementation | ||
447 | detail of the `global_allocator` feature not intended for use outside the | ||
448 | compiler. | ||
449 | |||
450 | ------------------------ | ||
451 | "##, | ||
452 | }, | ||
453 | LintCompletion { | ||
454 | label: "cfg_sanitize", | ||
455 | description: r##"# `cfg_sanitize` | ||
456 | |||
457 | The tracking issue for this feature is: [#39699] | ||
458 | |||
459 | [#39699]: https://github.com/rust-lang/rust/issues/39699 | ||
460 | |||
461 | ------------------------ | ||
462 | |||
463 | The `cfg_sanitize` feature makes it possible to execute different code | ||
464 | depending on whether a particular sanitizer is enabled or not. | ||
465 | |||
466 | ## Examples | ||
467 | |||
468 | ```rust | ||
469 | #![feature(cfg_sanitize)] | ||
470 | |||
471 | #[cfg(sanitize = "thread")] | ||
472 | fn a() { | ||
473 | // ... | ||
474 | } | ||
475 | |||
476 | #[cfg(not(sanitize = "thread"))] | ||
477 | fn a() { | ||
478 | // ... | ||
479 | } | ||
480 | |||
481 | fn b() { | ||
482 | if cfg!(sanitize = "leak") { | ||
483 | // ... | ||
484 | } else { | ||
485 | // ... | ||
486 | } | ||
487 | } | ||
488 | ``` | ||
489 | "##, | ||
490 | }, | ||
491 | LintCompletion { | ||
492 | label: "cfg_panic", | ||
493 | description: r##"# `cfg_panic` | ||
494 | |||
495 | The tracking issue for this feature is: [#77443] | ||
496 | |||
497 | [#77443]: https://github.com/rust-lang/rust/issues/77443 | ||
498 | |||
499 | ------------------------ | ||
500 | |||
501 | The `cfg_panic` feature makes it possible to execute different code | ||
502 | depending on the panic strategy. | ||
503 | |||
504 | Possible values at the moment are `"unwind"` or `"abort"`, although | ||
505 | it is possible that new panic strategies may be added to Rust in the | ||
506 | future. | ||
507 | |||
508 | ## Examples | ||
509 | |||
510 | ```rust | ||
511 | #![feature(cfg_panic)] | ||
512 | |||
513 | #[cfg(panic = "unwind")] | ||
514 | fn a() { | ||
515 | // ... | ||
516 | } | ||
517 | |||
518 | #[cfg(not(panic = "unwind"))] | ||
519 | fn a() { | ||
520 | // ... | ||
521 | } | ||
522 | |||
523 | fn b() { | ||
524 | if cfg!(panic = "abort") { | ||
525 | // ... | ||
526 | } else { | ||
527 | // ... | ||
528 | } | ||
529 | } | ||
530 | ``` | ||
531 | "##, | ||
532 | }, | ||
533 | LintCompletion { | ||
534 | label: "ffi_pure", | ||
535 | description: r##"# `ffi_pure` | ||
536 | |||
537 | The tracking issue for this feature is: [#58329] | ||
538 | |||
539 | ------ | ||
540 | |||
541 | The `#[ffi_pure]` attribute applies clang's `pure` attribute to foreign | ||
542 | functions declarations. | ||
543 | |||
544 | That is, `#[ffi_pure]` functions shall have no effects except for its return | ||
545 | value, which shall not change across two consecutive function calls with | ||
546 | the same parameters. | ||
547 | |||
548 | Applying the `#[ffi_pure]` attribute to a function that violates these | ||
549 | requirements is undefined behavior. | ||
550 | |||
551 | This attribute enables Rust to perform common optimizations, like sub-expression | ||
552 | elimination and loop optimizations. Some common examples of pure functions are | ||
553 | `strlen` or `memcmp`. | ||
554 | |||
555 | These optimizations are only applicable when the compiler can prove that no | ||
556 | program state observable by the `#[ffi_pure]` function has changed between calls | ||
557 | of the function, which could alter the result. See also the `#[ffi_const]` | ||
558 | attribute, which provides stronger guarantees regarding the allowable behavior | ||
559 | of a function, enabling further optimization. | ||
560 | |||
561 | ## Pitfalls | ||
562 | |||
563 | A `#[ffi_pure]` function can read global memory through the function | ||
564 | parameters (e.g. pointers), globals, etc. `#[ffi_pure]` functions are not | ||
565 | referentially-transparent, and are therefore more relaxed than `#[ffi_const]` | ||
566 | functions. | ||
567 | |||
568 | However, accessing global memory through volatile or atomic reads can violate the | ||
569 | requirement that two consecutive function calls shall return the same value. | ||
570 | |||
571 | A `pure` function that returns unit has no effect on the abstract machine's | ||
572 | state. | ||
573 | |||
574 | A `#[ffi_pure]` function must not diverge, neither via a side effect (e.g. a | ||
575 | call to `abort`) nor by infinite loops. | ||
576 | |||
577 | When translating C headers to Rust FFI, it is worth verifying for which targets | ||
578 | the `pure` attribute is enabled in those headers, and using the appropriate | ||
579 | `cfg` macros in the Rust side to match those definitions. While the semantics of | ||
580 | `pure` are implemented identically by many C and C++ compilers, e.g., clang, | ||
581 | [GCC], [ARM C/C++ compiler], [IBM ILE C/C++], etc. they are not necessarily | ||
582 | implemented in this way on all of them. It is therefore also worth verifying | ||
583 | that the semantics of the C toolchain used to compile the binary being linked | ||
584 | against are compatible with those of the `#[ffi_pure]`. | ||
585 | |||
586 | |||
587 | [#58329]: https://github.com/rust-lang/rust/issues/58329 | ||
588 | [ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacigdac.html | ||
589 | [GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-pure-function-attribute | ||
590 | [IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_pure.htm | ||
591 | "##, | ||
592 | }, | ||
593 | LintCompletion { | ||
594 | label: "repr128", | ||
595 | description: r##"# `repr128` | ||
596 | |||
597 | The tracking issue for this feature is: [#56071] | ||
598 | |||
599 | [#56071]: https://github.com/rust-lang/rust/issues/56071 | ||
600 | |||
601 | ------------------------ | ||
602 | |||
603 | The `repr128` feature adds support for `#[repr(u128)]` on `enum`s. | ||
604 | |||
605 | ```rust | ||
606 | #![feature(repr128)] | ||
607 | |||
608 | #[repr(u128)] | ||
609 | enum Foo { | ||
610 | Bar(u64), | ||
611 | } | ||
612 | ``` | ||
613 | "##, | ||
614 | }, | ||
615 | LintCompletion { | ||
616 | label: "generators", | ||
617 | description: r##"# `generators` | ||
618 | |||
619 | The tracking issue for this feature is: [#43122] | ||
620 | |||
621 | [#43122]: https://github.com/rust-lang/rust/issues/43122 | ||
622 | |||
623 | ------------------------ | ||
624 | |||
625 | The `generators` feature gate in Rust allows you to define generator or | ||
626 | coroutine literals. A generator is a "resumable function" that syntactically | ||
627 | resembles a closure but compiles to much different semantics in the compiler | ||
628 | itself. The primary feature of a generator is that it can be suspended during | ||
629 | execution to be resumed at a later date. Generators use the `yield` keyword to | ||
630 | "return", and then the caller can `resume` a generator to resume execution just | ||
631 | after the `yield` keyword. | ||
632 | |||
633 | Generators are an extra-unstable feature in the compiler right now. Added in | ||
634 | [RFC 2033] they're mostly intended right now as a information/constraint | ||
635 | gathering phase. The intent is that experimentation can happen on the nightly | ||
636 | compiler before actual stabilization. A further RFC will be required to | ||
637 | stabilize generators/coroutines and will likely contain at least a few small | ||
638 | tweaks to the overall design. | ||
639 | |||
640 | [RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033 | ||
641 | |||
642 | A syntactical example of a generator is: | ||
643 | |||
644 | ```rust | ||
645 | #![feature(generators, generator_trait)] | ||
646 | |||
647 | use std::ops::{Generator, GeneratorState}; | ||
648 | use std::pin::Pin; | ||
649 | |||
650 | fn main() { | ||
651 | let mut generator = || { | ||
652 | yield 1; | ||
653 | return "foo" | ||
654 | }; | ||
655 | |||
656 | match Pin::new(&mut generator).resume(()) { | ||
657 | GeneratorState::Yielded(1) => {} | ||
658 | _ => panic!("unexpected value from resume"), | ||
659 | } | ||
660 | match Pin::new(&mut generator).resume(()) { | ||
661 | GeneratorState::Complete("foo") => {} | ||
662 | _ => panic!("unexpected value from resume"), | ||
663 | } | ||
664 | } | ||
665 | ``` | ||
666 | |||
667 | Generators are closure-like literals which can contain a `yield` statement. The | ||
668 | `yield` statement takes an optional expression of a value to yield out of the | ||
669 | generator. All generator literals implement the `Generator` trait in the | ||
670 | `std::ops` module. The `Generator` trait has one main method, `resume`, which | ||
671 | resumes execution of the generator at the previous suspension point. | ||
672 | |||
673 | An example of the control flow of generators is that the following example | ||
674 | prints all numbers in order: | ||
675 | |||
676 | ```rust | ||
677 | #![feature(generators, generator_trait)] | ||
678 | |||
679 | use std::ops::Generator; | ||
680 | use std::pin::Pin; | ||
681 | |||
682 | fn main() { | ||
683 | let mut generator = || { | ||
684 | println!("2"); | ||
685 | yield; | ||
686 | println!("4"); | ||
687 | }; | ||
688 | |||
689 | println!("1"); | ||
690 | Pin::new(&mut generator).resume(()); | ||
691 | println!("3"); | ||
692 | Pin::new(&mut generator).resume(()); | ||
693 | println!("5"); | ||
694 | } | ||
695 | ``` | ||
696 | |||
697 | At this time the main intended use case of generators is an implementation | ||
698 | primitive for async/await syntax, but generators will likely be extended to | ||
699 | ergonomic implementations of iterators and other primitives in the future. | ||
700 | Feedback on the design and usage is always appreciated! | ||
701 | |||
702 | ### The `Generator` trait | ||
703 | |||
704 | The `Generator` trait in `std::ops` currently looks like: | ||
705 | |||
706 | ```rust | ||
707 | # #![feature(arbitrary_self_types, generator_trait)] | ||
708 | # use std::ops::GeneratorState; | ||
709 | # use std::pin::Pin; | ||
710 | |||
711 | pub trait Generator<R = ()> { | ||
712 | type Yield; | ||
713 | type Return; | ||
714 | fn resume(self: Pin<&mut Self>, resume: R) -> GeneratorState<Self::Yield, Self::Return>; | ||
715 | } | ||
716 | ``` | ||
717 | |||
718 | The `Generator::Yield` type is the type of values that can be yielded with the | ||
719 | `yield` statement. The `Generator::Return` type is the returned type of the | ||
720 | generator. This is typically the last expression in a generator's definition or | ||
721 | any value passed to `return` in a generator. The `resume` function is the entry | ||
722 | point for executing the `Generator` itself. | ||
723 | |||
724 | The return value of `resume`, `GeneratorState`, looks like: | ||
725 | |||
726 | ```rust | ||
727 | pub enum GeneratorState<Y, R> { | ||
728 | Yielded(Y), | ||
729 | Complete(R), | ||
730 | } | ||
731 | ``` | ||
732 | |||
733 | The `Yielded` variant indicates that the generator can later be resumed. This | ||
734 | corresponds to a `yield` point in a generator. The `Complete` variant indicates | ||
735 | that the generator is complete and cannot be resumed again. Calling `resume` | ||
736 | after a generator has returned `Complete` will likely result in a panic of the | ||
737 | program. | ||
738 | |||
739 | ### Closure-like semantics | ||
740 | |||
741 | The closure-like syntax for generators alludes to the fact that they also have | ||
742 | closure-like semantics. Namely: | ||
743 | |||
744 | * When created, a generator executes no code. A closure literal does not | ||
745 | actually execute any of the closure's code on construction, and similarly a | ||
746 | generator literal does not execute any code inside the generator when | ||
747 | constructed. | ||
748 | |||
749 | * Generators can capture outer variables by reference or by move, and this can | ||
750 | be tweaked with the `move` keyword at the beginning of the closure. Like | ||
751 | closures all generators will have an implicit environment which is inferred by | ||
752 | the compiler. Outer variables can be moved into a generator for use as the | ||
753 | generator progresses. | ||
754 | |||
755 | * Generator literals produce a value with a unique type which implements the | ||
756 | `std::ops::Generator` trait. This allows actual execution of the generator | ||
757 | through the `Generator::resume` method as well as also naming it in return | ||
758 | types and such. | ||
759 | |||
760 | * Traits like `Send` and `Sync` are automatically implemented for a `Generator` | ||
761 | depending on the captured variables of the environment. Unlike closures, | ||
762 | generators also depend on variables live across suspension points. This means | ||
763 | that although the ambient environment may be `Send` or `Sync`, the generator | ||
764 | itself may not be due to internal variables live across `yield` points being | ||
765 | not-`Send` or not-`Sync`. Note that generators do | ||
766 | not implement traits like `Copy` or `Clone` automatically. | ||
767 | |||
768 | * Whenever a generator is dropped it will drop all captured environment | ||
769 | variables. | ||
770 | |||
771 | ### Generators as state machines | ||
772 | |||
773 | In the compiler, generators are currently compiled as state machines. Each | ||
774 | `yield` expression will correspond to a different state that stores all live | ||
775 | variables over that suspension point. Resumption of a generator will dispatch on | ||
776 | the current state and then execute internally until a `yield` is reached, at | ||
777 | which point all state is saved off in the generator and a value is returned. | ||
778 | |||
779 | Let's take a look at an example to see what's going on here: | ||
780 | |||
781 | ```rust | ||
782 | #![feature(generators, generator_trait)] | ||
783 | |||
784 | use std::ops::Generator; | ||
785 | use std::pin::Pin; | ||
786 | |||
787 | fn main() { | ||
788 | let ret = "foo"; | ||
789 | let mut generator = move || { | ||
790 | yield 1; | ||
791 | return ret | ||
792 | }; | ||
793 | |||
794 | Pin::new(&mut generator).resume(()); | ||
795 | Pin::new(&mut generator).resume(()); | ||
796 | } | ||
797 | ``` | ||
798 | |||
799 | This generator literal will compile down to something similar to: | ||
800 | |||
801 | ```rust | ||
802 | #![feature(arbitrary_self_types, generators, generator_trait)] | ||
803 | |||
804 | use std::ops::{Generator, GeneratorState}; | ||
805 | use std::pin::Pin; | ||
806 | |||
807 | fn main() { | ||
808 | let ret = "foo"; | ||
809 | let mut generator = { | ||
810 | enum __Generator { | ||
811 | Start(&'static str), | ||
812 | Yield1(&'static str), | ||
813 | Done, | ||
814 | } | ||
815 | |||
816 | impl Generator for __Generator { | ||
817 | type Yield = i32; | ||
818 | type Return = &'static str; | ||
819 | |||
820 | fn resume(mut self: Pin<&mut Self>, resume: ()) -> GeneratorState<i32, &'static str> { | ||
821 | use std::mem; | ||
822 | match mem::replace(&mut *self, __Generator::Done) { | ||
823 | __Generator::Start(s) => { | ||
824 | *self = __Generator::Yield1(s); | ||
825 | GeneratorState::Yielded(1) | ||
826 | } | ||
827 | |||
828 | __Generator::Yield1(s) => { | ||
829 | *self = __Generator::Done; | ||
830 | GeneratorState::Complete(s) | ||
831 | } | ||
832 | |||
833 | __Generator::Done => { | ||
834 | panic!("generator resumed after completion") | ||
835 | } | ||
836 | } | ||
837 | } | ||
838 | } | ||
839 | |||
840 | __Generator::Start(ret) | ||
841 | }; | ||
842 | |||
843 | Pin::new(&mut generator).resume(()); | ||
844 | Pin::new(&mut generator).resume(()); | ||
845 | } | ||
846 | ``` | ||
847 | |||
848 | Notably here we can see that the compiler is generating a fresh type, | ||
849 | `__Generator` in this case. This type has a number of states (represented here | ||
850 | as an `enum`) corresponding to each of the conceptual states of the generator. | ||
851 | At the beginning we're closing over our outer variable `foo` and then that | ||
852 | variable is also live over the `yield` point, so it's stored in both states. | ||
853 | |||
854 | When the generator starts it'll immediately yield 1, but it saves off its state | ||
855 | just before it does so indicating that it has reached the yield point. Upon | ||
856 | resuming again we'll execute the `return ret` which returns the `Complete` | ||
857 | state. | ||
858 | |||
859 | Here we can also note that the `Done` state, if resumed, panics immediately as | ||
860 | it's invalid to resume a completed generator. It's also worth noting that this | ||
861 | is just a rough desugaring, not a normative specification for what the compiler | ||
862 | does. | ||
863 | "##, | ||
864 | }, | ||
865 | LintCompletion { | ||
866 | label: "non_ascii_idents", | ||
867 | description: r##"# `non_ascii_idents` | ||
868 | |||
869 | The tracking issue for this feature is: [#55467] | ||
870 | |||
871 | [#55467]: https://github.com/rust-lang/rust/issues/55467 | ||
872 | |||
873 | ------------------------ | ||
874 | |||
875 | The `non_ascii_idents` feature adds support for non-ASCII identifiers. | ||
876 | |||
877 | ## Examples | ||
878 | |||
879 | ```rust | ||
880 | #![feature(non_ascii_idents)] | ||
881 | |||
882 | const ε: f64 = 0.00001f64; | ||
883 | const Π: f64 = 3.14f64; | ||
884 | ``` | ||
885 | |||
886 | ## Changes to the language reference | ||
887 | |||
888 | > **<sup>Lexer:<sup>**\ | ||
889 | > IDENTIFIER :\ | ||
890 | > XID_start XID_continue<sup>\*</sup>\ | ||
891 | > | `_` XID_continue<sup>+</sup> | ||
892 | |||
893 | An identifier is any nonempty Unicode string of the following form: | ||
894 | |||
895 | Either | ||
896 | |||
897 | * The first character has property [`XID_start`] | ||
898 | * The remaining characters have property [`XID_continue`] | ||
899 | |||
900 | Or | ||
901 | |||
902 | * The first character is `_` | ||
903 | * The identifier is more than one character, `_` alone is not an identifier | ||
904 | * The remaining characters have property [`XID_continue`] | ||
905 | |||
906 | that does _not_ occur in the set of [strict keywords]. | ||
907 | |||
908 | > **Note**: [`XID_start`] and [`XID_continue`] as character properties cover the | ||
909 | > character ranges used to form the more familiar C and Java language-family | ||
910 | > identifiers. | ||
911 | |||
912 | [`XID_start`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Start%3A%5D&abb=on&g=&i= | ||
913 | [`XID_continue`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Continue%3A%5D&abb=on&g=&i= | ||
914 | [strict keywords]: ../../reference/keywords.md#strict-keywords | ||
915 | "##, | ||
916 | }, | ||
917 | LintCompletion { | ||
918 | label: "compiler_builtins", | ||
919 | description: r##"# `compiler_builtins` | ||
920 | |||
921 | This feature is internal to the Rust compiler and is not intended for general use. | ||
922 | |||
923 | ------------------------ | ||
924 | "##, | ||
925 | }, | ||
926 | LintCompletion { | ||
927 | label: "or_patterns", | ||
928 | description: r##"# `or_patterns` | ||
929 | |||
930 | The tracking issue for this feature is: [#54883] | ||
931 | |||
932 | [#54883]: https://github.com/rust-lang/rust/issues/54883 | ||
933 | |||
934 | ------------------------ | ||
935 | |||
936 | The `or_pattern` language feature allows `|` to be arbitrarily nested within | ||
937 | a pattern, for example, `Some(A(0) | B(1 | 2))` becomes a valid pattern. | ||
938 | |||
939 | ## Examples | ||
940 | |||
941 | ```rust,no_run | ||
942 | #![feature(or_patterns)] | ||
943 | |||
944 | pub enum Foo { | ||
945 | Bar, | ||
946 | Baz, | ||
947 | Quux, | ||
948 | } | ||
949 | |||
950 | pub fn example(maybe_foo: Option<Foo>) { | ||
951 | match maybe_foo { | ||
952 | Some(Foo::Bar | Foo::Baz) => { | ||
953 | println!("The value contained `Bar` or `Baz`"); | ||
954 | } | ||
955 | Some(_) => { | ||
956 | println!("The value did not contain `Bar` or `Baz`"); | ||
957 | } | ||
958 | None => { | ||
959 | println!("The value was `None`"); | ||
960 | } | ||
961 | } | ||
962 | } | ||
963 | ``` | ||
964 | "##, | ||
965 | }, | ||
966 | LintCompletion { | ||
967 | label: "negative_impls", | ||
968 | description: r##"# `negative_impls` | ||
969 | |||
970 | The tracking issue for this feature is [#68318]. | ||
971 | |||
972 | [#68318]: https://github.com/rust-lang/rust/issues/68318 | ||
973 | |||
974 | ---- | ||
975 | |||
976 | With the feature gate `negative_impls`, you can write negative impls as well as positive ones: | ||
977 | |||
978 | ```rust | ||
979 | #![feature(negative_impls)] | ||
980 | trait DerefMut { } | ||
981 | impl<T: ?Sized> !DerefMut for &T { } | ||
982 | ``` | ||
983 | |||
984 | Negative impls indicate a semver guarantee that the given trait will not be implemented for the given types. Negative impls play an additional purpose for auto traits, described below. | ||
985 | |||
986 | Negative impls have the following characteristics: | ||
987 | |||
988 | * They do not have any items. | ||
989 | * They must obey the orphan rules as if they were a positive impl. | ||
990 | * They cannot "overlap" with any positive impls. | ||
991 | |||
992 | ## Semver interaction | ||
993 | |||
994 | It is a breaking change to remove a negative impl. Negative impls are a commitment not to implement the given trait for the named types. | ||
995 | |||
996 | ## Orphan and overlap rules | ||
997 | |||
998 | Negative impls must obey the same orphan rules as a positive impl. This implies you cannot add a negative impl for types defined in upstream crates and so forth. | ||
999 | |||
1000 | Similarly, negative impls cannot overlap with positive impls, again using the same "overlap" check that we ordinarily use to determine if two impls overlap. (Note that positive impls typically cannot overlap with one another either, except as permitted by specialization.) | ||
1001 | |||
1002 | ## Interaction with auto traits | ||
1003 | |||
1004 | Declaring a negative impl `impl !SomeAutoTrait for SomeType` for an | ||
1005 | auto-trait serves two purposes: | ||
1006 | |||
1007 | * as with any trait, it declares that `SomeType` will never implement `SomeAutoTrait`; | ||
1008 | * it disables the automatic `SomeType: SomeAutoTrait` impl that would otherwise have been generated. | ||
1009 | |||
1010 | Note that, at present, there is no way to indicate that a given type | ||
1011 | does not implement an auto trait *but that it may do so in the | ||
1012 | future*. For ordinary types, this is done by simply not declaring any | ||
1013 | impl at all, but that is not an option for auto traits. A workaround | ||
1014 | is that one could embed a marker type as one of the fields, where the | ||
1015 | marker type is `!AutoTrait`. | ||
1016 | |||
1017 | ## Immediate uses | ||
1018 | |||
1019 | Negative impls are used to declare that `&T: !DerefMut` and `&mut T: !Clone`, as required to fix the soundness of `Pin` described in [#66544](https://github.com/rust-lang/rust/issues/66544). | ||
1020 | |||
1021 | This serves two purposes: | ||
1022 | |||
1023 | * For proving the correctness of unsafe code, we can use that impl as evidence that no `DerefMut` or `Clone` impl exists. | ||
1024 | * It prevents downstream crates from creating such impls. | ||
1025 | "##, | ||
1026 | }, | ||
1027 | LintCompletion { | ||
1028 | label: "cmse_nonsecure_entry", | ||
1029 | description: r##"# `cmse_nonsecure_entry` | ||
1030 | |||
1031 | The tracking issue for this feature is: [#75835] | ||
1032 | |||
1033 | [#75835]: https://github.com/rust-lang/rust/issues/75835 | ||
1034 | |||
1035 | ------------------------ | ||
1036 | |||
1037 | The [TrustZone-M | ||
1038 | feature](https://developer.arm.com/documentation/100690/latest/) is available | ||
1039 | for targets with the Armv8-M architecture profile (`thumbv8m` in their target | ||
1040 | name). | ||
1041 | LLVM, the Rust compiler and the linker are providing | ||
1042 | [support](https://developer.arm.com/documentation/ecm0359818/latest/) for the | ||
1043 | TrustZone-M feature. | ||
1044 | |||
1045 | One of the things provided, with this unstable feature, is the | ||
1046 | `cmse_nonsecure_entry` attribute. This attribute marks a Secure function as an | ||
1047 | entry function (see [section | ||
1048 | 5.4](https://developer.arm.com/documentation/ecm0359818/latest/) for details). | ||
1049 | With this attribute, the compiler will do the following: | ||
1050 | * add a special symbol on the function which is the `__acle_se_` prefix and the | ||
1051 | standard function name | ||
1052 | * constrain the number of parameters to avoid using the Non-Secure stack | ||
1053 | * before returning from the function, clear registers that might contain Secure | ||
1054 | information | ||
1055 | * use the `BXNS` instruction to return | ||
1056 | |||
1057 | Because the stack can not be used to pass parameters, there will be compilation | ||
1058 | errors if: | ||
1059 | * the total size of all parameters is too big (for example more than four 32 | ||
1060 | bits integers) | ||
1061 | * the entry function is not using a C ABI | ||
1062 | |||
1063 | The special symbol `__acle_se_` will be used by the linker to generate a secure | ||
1064 | gateway veneer. | ||
1065 | |||
1066 | <!-- NOTE(ignore) this example is specific to thumbv8m targets --> | ||
1067 | |||
1068 | ``` rust,ignore | ||
1069 | #![feature(cmse_nonsecure_entry)] | ||
1070 | |||
1071 | #[no_mangle] | ||
1072 | #[cmse_nonsecure_entry] | ||
1073 | pub extern "C" fn entry_function(input: u32) -> u32 { | ||
1074 | input + 6 | ||
1075 | } | ||
1076 | ``` | ||
1077 | |||
1078 | ``` text | ||
1079 | $ rustc --emit obj --crate-type lib --target thumbv8m.main-none-eabi function.rs | ||
1080 | $ arm-none-eabi-objdump -D function.o | ||
1081 | |||
1082 | 00000000 <entry_function>: | ||
1083 | 0: b580 push {r7, lr} | ||
1084 | 2: 466f mov r7, sp | ||
1085 | 4: b082 sub sp, #8 | ||
1086 | 6: 9001 str r0, [sp, #4] | ||
1087 | 8: 1d81 adds r1, r0, #6 | ||
1088 | a: 460a mov r2, r1 | ||
1089 | c: 4281 cmp r1, r0 | ||
1090 | e: 9200 str r2, [sp, #0] | ||
1091 | 10: d30b bcc.n 2a <entry_function+0x2a> | ||
1092 | 12: e7ff b.n 14 <entry_function+0x14> | ||
1093 | 14: 9800 ldr r0, [sp, #0] | ||
1094 | 16: b002 add sp, #8 | ||
1095 | 18: e8bd 4080 ldmia.w sp!, {r7, lr} | ||
1096 | 1c: 4671 mov r1, lr | ||
1097 | 1e: 4672 mov r2, lr | ||
1098 | 20: 4673 mov r3, lr | ||
1099 | 22: 46f4 mov ip, lr | ||
1100 | 24: f38e 8800 msr CPSR_f, lr | ||
1101 | 28: 4774 bxns lr | ||
1102 | 2a: f240 0000 movw r0, #0 | ||
1103 | 2e: f2c0 0000 movt r0, #0 | ||
1104 | 32: f240 0200 movw r2, #0 | ||
1105 | 36: f2c0 0200 movt r2, #0 | ||
1106 | 3a: 211c movs r1, #28 | ||
1107 | 3c: f7ff fffe bl 0 <_ZN4core9panicking5panic17h5c028258ca2fb3f5E> | ||
1108 | 40: defe udf #254 ; 0xfe | ||
1109 | ``` | ||
1110 | "##, | ||
1111 | }, | ||
1112 | LintCompletion { | ||
1113 | label: "plugin", | ||
1114 | description: r##"# `plugin` | ||
1115 | |||
1116 | The tracking issue for this feature is: [#29597] | ||
1117 | |||
1118 | [#29597]: https://github.com/rust-lang/rust/issues/29597 | ||
1119 | |||
1120 | |||
1121 | This feature is part of "compiler plugins." It will often be used with the | ||
1122 | [`plugin_registrar`] and `rustc_private` features. | ||
1123 | |||
1124 | [`plugin_registrar`]: plugin-registrar.md | ||
1125 | |||
1126 | ------------------------ | ||
1127 | |||
1128 | `rustc` can load compiler plugins, which are user-provided libraries that | ||
1129 | extend the compiler's behavior with new lint checks, etc. | ||
1130 | |||
1131 | A plugin is a dynamic library crate with a designated *registrar* function that | ||
1132 | registers extensions with `rustc`. Other crates can load these extensions using | ||
1133 | the crate attribute `#![plugin(...)]`. See the | ||
1134 | `rustc_driver::plugin` documentation for more about the | ||
1135 | mechanics of defining and loading a plugin. | ||
1136 | |||
1137 | In the vast majority of cases, a plugin should *only* be used through | ||
1138 | `#![plugin]` and not through an `extern crate` item. Linking a plugin would | ||
1139 | pull in all of librustc_ast and librustc as dependencies of your crate. This is | ||
1140 | generally unwanted unless you are building another plugin. | ||
1141 | |||
1142 | The usual practice is to put compiler plugins in their own crate, separate from | ||
1143 | any `macro_rules!` macros or ordinary Rust code meant to be used by consumers | ||
1144 | of a library. | ||
1145 | |||
1146 | # Lint plugins | ||
1147 | |||
1148 | Plugins can extend [Rust's lint | ||
1149 | infrastructure](../../reference/attributes/diagnostics.md#lint-check-attributes) with | ||
1150 | additional checks for code style, safety, etc. Now let's write a plugin | ||
1151 | [`lint-plugin-test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/ui-fulldeps/auxiliary/lint-plugin-test.rs) | ||
1152 | that warns about any item named `lintme`. | ||
1153 | |||
1154 | ```rust,ignore (requires-stage-2) | ||
1155 | #![feature(plugin_registrar)] | ||
1156 | #![feature(box_syntax, rustc_private)] | ||
1157 | |||
1158 | extern crate rustc_ast; | ||
1159 | |||
1160 | // Load rustc as a plugin to get macros | ||
1161 | extern crate rustc_driver; | ||
1162 | #[macro_use] | ||
1163 | extern crate rustc_lint; | ||
1164 | #[macro_use] | ||
1165 | extern crate rustc_session; | ||
1166 | |||
1167 | use rustc_driver::plugin::Registry; | ||
1168 | use rustc_lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass}; | ||
1169 | use rustc_ast::ast; | ||
1170 | declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'"); | ||
1171 | |||
1172 | declare_lint_pass!(Pass => [TEST_LINT]); | ||
1173 | |||
1174 | impl EarlyLintPass for Pass { | ||
1175 | fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) { | ||
1176 | if it.ident.name.as_str() == "lintme" { | ||
1177 | cx.lint(TEST_LINT, |lint| { | ||
1178 | lint.build("item is named 'lintme'").set_span(it.span).emit() | ||
1179 | }); | ||
1180 | } | ||
1181 | } | ||
1182 | } | ||
1183 | |||
1184 | #[plugin_registrar] | ||
1185 | pub fn plugin_registrar(reg: &mut Registry) { | ||
1186 | reg.lint_store.register_lints(&[&TEST_LINT]); | ||
1187 | reg.lint_store.register_early_pass(|| box Pass); | ||
1188 | } | ||
1189 | ``` | ||
1190 | |||
1191 | Then code like | ||
1192 | |||
1193 | ```rust,ignore (requires-plugin) | ||
1194 | #![feature(plugin)] | ||
1195 | #![plugin(lint_plugin_test)] | ||
1196 | |||
1197 | fn lintme() { } | ||
1198 | ``` | ||
1199 | |||
1200 | will produce a compiler warning: | ||
1201 | |||
1202 | ```txt | ||
1203 | foo.rs:4:1: 4:16 warning: item is named 'lintme', #[warn(test_lint)] on by default | ||
1204 | foo.rs:4 fn lintme() { } | ||
1205 | ^~~~~~~~~~~~~~~ | ||
1206 | ``` | ||
1207 | |||
1208 | The components of a lint plugin are: | ||
1209 | |||
1210 | * one or more `declare_lint!` invocations, which define static `Lint` structs; | ||
1211 | |||
1212 | * a struct holding any state needed by the lint pass (here, none); | ||
1213 | |||
1214 | * a `LintPass` | ||
1215 | implementation defining how to check each syntax element. A single | ||
1216 | `LintPass` may call `span_lint` for several different `Lint`s, but should | ||
1217 | register them all through the `get_lints` method. | ||
1218 | |||
1219 | Lint passes are syntax traversals, but they run at a late stage of compilation | ||
1220 | where type information is available. `rustc`'s [built-in | ||
1221 | lints](https://github.com/rust-lang/rust/blob/master/src/librustc_session/lint/builtin.rs) | ||
1222 | mostly use the same infrastructure as lint plugins, and provide examples of how | ||
1223 | to access type information. | ||
1224 | |||
1225 | Lints defined by plugins are controlled by the usual [attributes and compiler | ||
1226 | flags](../../reference/attributes/diagnostics.md#lint-check-attributes), e.g. | ||
1227 | `#[allow(test_lint)]` or `-A test-lint`. These identifiers are derived from the | ||
1228 | first argument to `declare_lint!`, with appropriate case and punctuation | ||
1229 | conversion. | ||
1230 | |||
1231 | You can run `rustc -W help foo.rs` to see a list of lints known to `rustc`, | ||
1232 | including those provided by plugins loaded by `foo.rs`. | ||
1233 | "##, | ||
1234 | }, | ||
1235 | LintCompletion { | ||
1236 | label: "intrinsics", | ||
1237 | description: r##"# `intrinsics` | ||
1238 | |||
1239 | The tracking issue for this feature is: None. | ||
1240 | |||
1241 | Intrinsics are never intended to be stable directly, but intrinsics are often | ||
1242 | exported in some sort of stable manner. Prefer using the stable interfaces to | ||
1243 | the intrinsic directly when you can. | ||
1244 | |||
1245 | ------------------------ | ||
1246 | |||
1247 | |||
1248 | These are imported as if they were FFI functions, with the special | ||
1249 | `rust-intrinsic` ABI. For example, if one was in a freestanding | ||
1250 | context, but wished to be able to `transmute` between types, and | ||
1251 | perform efficient pointer arithmetic, one would import those functions | ||
1252 | via a declaration like | ||
1253 | |||
1254 | ```rust | ||
1255 | #![feature(intrinsics)] | ||
1256 | # fn main() {} | ||
1257 | |||
1258 | extern "rust-intrinsic" { | ||
1259 | fn transmute<T, U>(x: T) -> U; | ||
1260 | |||
1261 | fn offset<T>(dst: *const T, offset: isize) -> *const T; | ||
1262 | } | ||
1263 | ``` | ||
1264 | |||
1265 | As with any other FFI functions, these are always `unsafe` to call. | ||
1266 | "##, | ||
1267 | }, | ||
1268 | LintCompletion { | ||
1269 | label: "rustc_attrs", | ||
1270 | description: r##"# `rustc_attrs` | ||
1271 | |||
1272 | This feature has no tracking issue, and is therefore internal to | ||
1273 | the compiler, not being intended for general use. | ||
1274 | |||
1275 | Note: `rustc_attrs` enables many rustc-internal attributes and this page | ||
1276 | only discuss a few of them. | ||
1277 | |||
1278 | ------------------------ | ||
1279 | |||
1280 | The `rustc_attrs` feature allows debugging rustc type layouts by using | ||
1281 | `#[rustc_layout(...)]` to debug layout at compile time (it even works | ||
1282 | with `cargo check`) as an alternative to `rustc -Z print-type-sizes` | ||
1283 | that is way more verbose. | ||
1284 | |||
1285 | Options provided by `#[rustc_layout(...)]` are `debug`, `size`, `align`, | ||
1286 | `abi`. Note that it only works on sized types without generics. | ||
1287 | |||
1288 | ## Examples | ||
1289 | |||
1290 | ```rust,compile_fail | ||
1291 | #![feature(rustc_attrs)] | ||
1292 | |||
1293 | #[rustc_layout(abi, size)] | ||
1294 | pub enum X { | ||
1295 | Y(u8, u8, u8), | ||
1296 | Z(isize), | ||
1297 | } | ||
1298 | ``` | ||
1299 | |||
1300 | When that is compiled, the compiler will error with something like | ||
1301 | |||
1302 | ```text | ||
1303 | error: abi: Aggregate { sized: true } | ||
1304 | --> src/lib.rs:4:1 | ||
1305 | | | ||
1306 | 4 | / pub enum T { | ||
1307 | 5 | | Y(u8, u8, u8), | ||
1308 | 6 | | Z(isize), | ||
1309 | 7 | | } | ||
1310 | | |_^ | ||
1311 | |||
1312 | error: size: Size { raw: 16 } | ||
1313 | --> src/lib.rs:4:1 | ||
1314 | | | ||
1315 | 4 | / pub enum T { | ||
1316 | 5 | | Y(u8, u8, u8), | ||
1317 | 6 | | Z(isize), | ||
1318 | 7 | | } | ||
1319 | | |_^ | ||
1320 | |||
1321 | error: aborting due to 2 previous errors | ||
1322 | ``` | ||
1323 | "##, | ||
1324 | }, | ||
1325 | LintCompletion { | ||
1326 | label: "const_fn", | ||
1327 | description: r##"# `const_fn` | ||
1328 | |||
1329 | The tracking issue for this feature is: [#57563] | ||
1330 | |||
1331 | [#57563]: https://github.com/rust-lang/rust/issues/57563 | ||
1332 | |||
1333 | ------------------------ | ||
1334 | |||
1335 | The `const_fn` feature enables additional functionality not stabilized in the | ||
1336 | [minimal subset of `const_fn`](https://github.com/rust-lang/rust/issues/53555) | ||
1337 | "##, | ||
1338 | }, | ||
1339 | LintCompletion { | ||
1340 | label: "abi_thiscall", | ||
1341 | description: r##"# `abi_thiscall` | ||
1342 | |||
1343 | The tracking issue for this feature is: [#42202] | ||
1344 | |||
1345 | [#42202]: https://github.com/rust-lang/rust/issues/42202 | ||
1346 | |||
1347 | ------------------------ | ||
1348 | |||
1349 | The MSVC ABI on x86 Windows uses the `thiscall` calling convention for C++ | ||
1350 | instance methods by default; it is identical to the usual (C) calling | ||
1351 | convention on x86 Windows except that the first parameter of the method, | ||
1352 | the `this` pointer, is passed in the ECX register. | ||
1353 | "##, | ||
1354 | }, | ||
1355 | LintCompletion { | ||
1356 | label: "trait_alias", | ||
1357 | description: r##"# `trait_alias` | ||
1358 | |||
1359 | The tracking issue for this feature is: [#41517] | ||
1360 | |||
1361 | [#41517]: https://github.com/rust-lang/rust/issues/41517 | ||
1362 | |||
1363 | ------------------------ | ||
1364 | |||
1365 | The `trait_alias` feature adds support for trait aliases. These allow aliases | ||
1366 | to be created for one or more traits (currently just a single regular trait plus | ||
1367 | any number of auto-traits), and used wherever traits would normally be used as | ||
1368 | either bounds or trait objects. | ||
1369 | |||
1370 | ```rust | ||
1371 | #![feature(trait_alias)] | ||
1372 | |||
1373 | trait Foo = std::fmt::Debug + Send; | ||
1374 | trait Bar = Foo + Sync; | ||
1375 | |||
1376 | // Use trait alias as bound on type parameter. | ||
1377 | fn foo<T: Foo>(v: &T) { | ||
1378 | println!("{:?}", v); | ||
1379 | } | ||
1380 | |||
1381 | pub fn main() { | ||
1382 | foo(&1); | ||
1383 | |||
1384 | // Use trait alias for trait objects. | ||
1385 | let a: &Bar = &123; | ||
1386 | println!("{:?}", a); | ||
1387 | let b = Box::new(456) as Box<dyn Foo>; | ||
1388 | println!("{:?}", b); | ||
1389 | } | ||
1390 | ``` | ||
1391 | "##, | ||
1392 | }, | ||
1393 | LintCompletion { | ||
1394 | label: "lang_items", | ||
1395 | description: r##"# `lang_items` | ||
1396 | |||
1397 | The tracking issue for this feature is: None. | ||
1398 | |||
1399 | ------------------------ | ||
1400 | |||
1401 | The `rustc` compiler has certain pluggable operations, that is, | ||
1402 | functionality that isn't hard-coded into the language, but is | ||
1403 | implemented in libraries, with a special marker to tell the compiler | ||
1404 | it exists. The marker is the attribute `#[lang = "..."]` and there are | ||
1405 | various different values of `...`, i.e. various different 'lang | ||
1406 | items'. | ||
1407 | |||
1408 | For example, `Box` pointers require two lang items, one for allocation | ||
1409 | and one for deallocation. A freestanding program that uses the `Box` | ||
1410 | sugar for dynamic allocations via `malloc` and `free`: | ||
1411 | |||
1412 | ```rust,ignore (libc-is-finicky) | ||
1413 | #![feature(lang_items, box_syntax, start, libc, core_intrinsics, rustc_private)] | ||
1414 | #![no_std] | ||
1415 | use core::intrinsics; | ||
1416 | use core::panic::PanicInfo; | ||
1417 | |||
1418 | extern crate libc; | ||
1419 | |||
1420 | #[lang = "owned_box"] | ||
1421 | pub struct Box<T>(*mut T); | ||
1422 | |||
1423 | #[lang = "exchange_malloc"] | ||
1424 | unsafe fn allocate(size: usize, _align: usize) -> *mut u8 { | ||
1425 | let p = libc::malloc(size as libc::size_t) as *mut u8; | ||
1426 | |||
1427 | // Check if `malloc` failed: | ||
1428 | if p as usize == 0 { | ||
1429 | intrinsics::abort(); | ||
1430 | } | ||
1431 | |||
1432 | p | ||
1433 | } | ||
1434 | |||
1435 | #[lang = "box_free"] | ||
1436 | unsafe fn box_free<T: ?Sized>(ptr: *mut T) { | ||
1437 | libc::free(ptr as *mut libc::c_void) | ||
1438 | } | ||
1439 | |||
1440 | #[start] | ||
1441 | fn main(_argc: isize, _argv: *const *const u8) -> isize { | ||
1442 | let _x = box 1; | ||
1443 | |||
1444 | 0 | ||
1445 | } | ||
1446 | |||
1447 | #[lang = "eh_personality"] extern fn rust_eh_personality() {} | ||
1448 | #[lang = "panic_impl"] extern fn rust_begin_panic(info: &PanicInfo) -> ! { unsafe { intrinsics::abort() } } | ||
1449 | #[no_mangle] pub extern fn rust_eh_register_frames () {} | ||
1450 | #[no_mangle] pub extern fn rust_eh_unregister_frames () {} | ||
1451 | ``` | ||
1452 | |||
1453 | Note the use of `abort`: the `exchange_malloc` lang item is assumed to | ||
1454 | return a valid pointer, and so needs to do the check internally. | ||
1455 | |||
1456 | Other features provided by lang items include: | ||
1457 | |||
1458 | - overloadable operators via traits: the traits corresponding to the | ||
1459 | `==`, `<`, dereferencing (`*`) and `+` (etc.) operators are all | ||
1460 | marked with lang items; those specific four are `eq`, `ord`, | ||
1461 | `deref`, and `add` respectively. | ||
1462 | - stack unwinding and general failure; the `eh_personality`, | ||
1463 | `panic` and `panic_bounds_check` lang items. | ||
1464 | - the traits in `std::marker` used to indicate types of | ||
1465 | various kinds; lang items `send`, `sync` and `copy`. | ||
1466 | - the marker types and variance indicators found in | ||
1467 | `std::marker`; lang items `covariant_type`, | ||
1468 | `contravariant_lifetime`, etc. | ||
1469 | |||
1470 | Lang items are loaded lazily by the compiler; e.g. if one never uses | ||
1471 | `Box` then there is no need to define functions for `exchange_malloc` | ||
1472 | and `box_free`. `rustc` will emit an error when an item is needed | ||
1473 | but not found in the current crate or any that it depends on. | ||
1474 | |||
1475 | Most lang items are defined by `libcore`, but if you're trying to build | ||
1476 | an executable without the standard library, you'll run into the need | ||
1477 | for lang items. The rest of this page focuses on this use-case, even though | ||
1478 | lang items are a bit broader than that. | ||
1479 | |||
1480 | ### Using libc | ||
1481 | |||
1482 | In order to build a `#[no_std]` executable we will need libc as a dependency. | ||
1483 | We can specify this using our `Cargo.toml` file: | ||
1484 | |||
1485 | ```toml | ||
1486 | [dependencies] | ||
1487 | libc = { version = "0.2.14", default-features = false } | ||
1488 | ``` | ||
1489 | |||
1490 | Note that the default features have been disabled. This is a critical step - | ||
1491 | **the default features of libc include the standard library and so must be | ||
1492 | disabled.** | ||
1493 | |||
1494 | ### Writing an executable without stdlib | ||
1495 | |||
1496 | Controlling the entry point is possible in two ways: the `#[start]` attribute, | ||
1497 | or overriding the default shim for the C `main` function with your own. | ||
1498 | |||
1499 | The function marked `#[start]` is passed the command line parameters | ||
1500 | in the same format as C: | ||
1501 | |||
1502 | ```rust,ignore (libc-is-finicky) | ||
1503 | #![feature(lang_items, core_intrinsics, rustc_private)] | ||
1504 | #![feature(start)] | ||
1505 | #![no_std] | ||
1506 | use core::intrinsics; | ||
1507 | use core::panic::PanicInfo; | ||
1508 | |||
1509 | // Pull in the system libc library for what crt0.o likely requires. | ||
1510 | extern crate libc; | ||
1511 | |||
1512 | // Entry point for this program. | ||
1513 | #[start] | ||
1514 | fn start(_argc: isize, _argv: *const *const u8) -> isize { | ||
1515 | 0 | ||
1516 | } | ||
1517 | |||
1518 | // These functions are used by the compiler, but not | ||
1519 | // for a bare-bones hello world. These are normally | ||
1520 | // provided by libstd. | ||
1521 | #[lang = "eh_personality"] | ||
1522 | #[no_mangle] | ||
1523 | pub extern fn rust_eh_personality() { | ||
1524 | } | ||
1525 | |||
1526 | #[lang = "panic_impl"] | ||
1527 | #[no_mangle] | ||
1528 | pub extern fn rust_begin_panic(info: &PanicInfo) -> ! { | ||
1529 | unsafe { intrinsics::abort() } | ||
1530 | } | ||
1531 | ``` | ||
1532 | |||
1533 | To override the compiler-inserted `main` shim, one has to disable it | ||
1534 | with `#![no_main]` and then create the appropriate symbol with the | ||
1535 | correct ABI and the correct name, which requires overriding the | ||
1536 | compiler's name mangling too: | ||
1537 | |||
1538 | ```rust,ignore (libc-is-finicky) | ||
1539 | #![feature(lang_items, core_intrinsics, rustc_private)] | ||
1540 | #![feature(start)] | ||
1541 | #![no_std] | ||
1542 | #![no_main] | ||
1543 | use core::intrinsics; | ||
1544 | use core::panic::PanicInfo; | ||
1545 | |||
1546 | // Pull in the system libc library for what crt0.o likely requires. | ||
1547 | extern crate libc; | ||
1548 | |||
1549 | // Entry point for this program. | ||
1550 | #[no_mangle] // ensure that this symbol is called `main` in the output | ||
1551 | pub extern fn main(_argc: i32, _argv: *const *const u8) -> i32 { | ||
1552 | 0 | ||
1553 | } | ||
1554 | |||
1555 | // These functions are used by the compiler, but not | ||
1556 | // for a bare-bones hello world. These are normally | ||
1557 | // provided by libstd. | ||
1558 | #[lang = "eh_personality"] | ||
1559 | #[no_mangle] | ||
1560 | pub extern fn rust_eh_personality() { | ||
1561 | } | ||
1562 | |||
1563 | #[lang = "panic_impl"] | ||
1564 | #[no_mangle] | ||
1565 | pub extern fn rust_begin_panic(info: &PanicInfo) -> ! { | ||
1566 | unsafe { intrinsics::abort() } | ||
1567 | } | ||
1568 | ``` | ||
1569 | |||
1570 | In many cases, you may need to manually link to the `compiler_builtins` crate | ||
1571 | when building a `no_std` binary. You may observe this via linker error messages | ||
1572 | such as "```undefined reference to `__rust_probestack'```". | ||
1573 | |||
1574 | ## More about the language items | ||
1575 | |||
1576 | The compiler currently makes a few assumptions about symbols which are | ||
1577 | available in the executable to call. Normally these functions are provided by | ||
1578 | the standard library, but without it you must define your own. These symbols | ||
1579 | are called "language items", and they each have an internal name, and then a | ||
1580 | signature that an implementation must conform to. | ||
1581 | |||
1582 | The first of these functions, `rust_eh_personality`, is used by the failure | ||
1583 | mechanisms of the compiler. This is often mapped to GCC's personality function | ||
1584 | (see the [libstd implementation][unwind] for more information), but crates | ||
1585 | which do not trigger a panic can be assured that this function is never | ||
1586 | called. The language item's name is `eh_personality`. | ||
1587 | |||
1588 | [unwind]: https://github.com/rust-lang/rust/blob/master/src/libpanic_unwind/gcc.rs | ||
1589 | |||
1590 | The second function, `rust_begin_panic`, is also used by the failure mechanisms of the | ||
1591 | compiler. When a panic happens, this controls the message that's displayed on | ||
1592 | the screen. While the language item's name is `panic_impl`, the symbol name is | ||
1593 | `rust_begin_panic`. | ||
1594 | |||
1595 | Finally, a `eh_catch_typeinfo` static is needed for certain targets which | ||
1596 | implement Rust panics on top of C++ exceptions. | ||
1597 | |||
1598 | ## List of all language items | ||
1599 | |||
1600 | This is a list of all language items in Rust along with where they are located in | ||
1601 | the source code. | ||
1602 | |||
1603 | - Primitives | ||
1604 | - `i8`: `libcore/num/mod.rs` | ||
1605 | - `i16`: `libcore/num/mod.rs` | ||
1606 | - `i32`: `libcore/num/mod.rs` | ||
1607 | - `i64`: `libcore/num/mod.rs` | ||
1608 | - `i128`: `libcore/num/mod.rs` | ||
1609 | - `isize`: `libcore/num/mod.rs` | ||
1610 | - `u8`: `libcore/num/mod.rs` | ||
1611 | - `u16`: `libcore/num/mod.rs` | ||
1612 | - `u32`: `libcore/num/mod.rs` | ||
1613 | - `u64`: `libcore/num/mod.rs` | ||
1614 | - `u128`: `libcore/num/mod.rs` | ||
1615 | - `usize`: `libcore/num/mod.rs` | ||
1616 | - `f32`: `libstd/f32.rs` | ||
1617 | - `f64`: `libstd/f64.rs` | ||
1618 | - `char`: `libcore/char.rs` | ||
1619 | - `slice`: `liballoc/slice.rs` | ||
1620 | - `str`: `liballoc/str.rs` | ||
1621 | - `const_ptr`: `libcore/ptr.rs` | ||
1622 | - `mut_ptr`: `libcore/ptr.rs` | ||
1623 | - `unsafe_cell`: `libcore/cell.rs` | ||
1624 | - Runtime | ||
1625 | - `start`: `libstd/rt.rs` | ||
1626 | - `eh_personality`: `libpanic_unwind/emcc.rs` (EMCC) | ||
1627 | - `eh_personality`: `libpanic_unwind/gcc.rs` (GNU) | ||
1628 | - `eh_personality`: `libpanic_unwind/seh.rs` (SEH) | ||
1629 | - `eh_catch_typeinfo`: `libpanic_unwind/emcc.rs` (EMCC) | ||
1630 | - `panic`: `libcore/panicking.rs` | ||
1631 | - `panic_bounds_check`: `libcore/panicking.rs` | ||
1632 | - `panic_impl`: `libcore/panicking.rs` | ||
1633 | - `panic_impl`: `libstd/panicking.rs` | ||
1634 | - Allocations | ||
1635 | - `owned_box`: `liballoc/boxed.rs` | ||
1636 | - `exchange_malloc`: `liballoc/heap.rs` | ||
1637 | - `box_free`: `liballoc/heap.rs` | ||
1638 | - Operands | ||
1639 | - `not`: `libcore/ops/bit.rs` | ||
1640 | - `bitand`: `libcore/ops/bit.rs` | ||
1641 | - `bitor`: `libcore/ops/bit.rs` | ||
1642 | - `bitxor`: `libcore/ops/bit.rs` | ||
1643 | - `shl`: `libcore/ops/bit.rs` | ||
1644 | - `shr`: `libcore/ops/bit.rs` | ||
1645 | - `bitand_assign`: `libcore/ops/bit.rs` | ||
1646 | - `bitor_assign`: `libcore/ops/bit.rs` | ||
1647 | - `bitxor_assign`: `libcore/ops/bit.rs` | ||
1648 | - `shl_assign`: `libcore/ops/bit.rs` | ||
1649 | - `shr_assign`: `libcore/ops/bit.rs` | ||
1650 | - `deref`: `libcore/ops/deref.rs` | ||
1651 | - `deref_mut`: `libcore/ops/deref.rs` | ||
1652 | - `index`: `libcore/ops/index.rs` | ||
1653 | - `index_mut`: `libcore/ops/index.rs` | ||
1654 | - `add`: `libcore/ops/arith.rs` | ||
1655 | - `sub`: `libcore/ops/arith.rs` | ||
1656 | - `mul`: `libcore/ops/arith.rs` | ||
1657 | - `div`: `libcore/ops/arith.rs` | ||
1658 | - `rem`: `libcore/ops/arith.rs` | ||
1659 | - `neg`: `libcore/ops/arith.rs` | ||
1660 | - `add_assign`: `libcore/ops/arith.rs` | ||
1661 | - `sub_assign`: `libcore/ops/arith.rs` | ||
1662 | - `mul_assign`: `libcore/ops/arith.rs` | ||
1663 | - `div_assign`: `libcore/ops/arith.rs` | ||
1664 | - `rem_assign`: `libcore/ops/arith.rs` | ||
1665 | - `eq`: `libcore/cmp.rs` | ||
1666 | - `ord`: `libcore/cmp.rs` | ||
1667 | - Functions | ||
1668 | - `fn`: `libcore/ops/function.rs` | ||
1669 | - `fn_mut`: `libcore/ops/function.rs` | ||
1670 | - `fn_once`: `libcore/ops/function.rs` | ||
1671 | - `generator_state`: `libcore/ops/generator.rs` | ||
1672 | - `generator`: `libcore/ops/generator.rs` | ||
1673 | - Other | ||
1674 | - `coerce_unsized`: `libcore/ops/unsize.rs` | ||
1675 | - `drop`: `libcore/ops/drop.rs` | ||
1676 | - `drop_in_place`: `libcore/ptr.rs` | ||
1677 | - `clone`: `libcore/clone.rs` | ||
1678 | - `copy`: `libcore/marker.rs` | ||
1679 | - `send`: `libcore/marker.rs` | ||
1680 | - `sized`: `libcore/marker.rs` | ||
1681 | - `unsize`: `libcore/marker.rs` | ||
1682 | - `sync`: `libcore/marker.rs` | ||
1683 | - `phantom_data`: `libcore/marker.rs` | ||
1684 | - `discriminant_kind`: `libcore/marker.rs` | ||
1685 | - `freeze`: `libcore/marker.rs` | ||
1686 | - `debug_trait`: `libcore/fmt/mod.rs` | ||
1687 | - `non_zero`: `libcore/nonzero.rs` | ||
1688 | - `arc`: `liballoc/sync.rs` | ||
1689 | - `rc`: `liballoc/rc.rs` | ||
1690 | "##, | ||
1691 | }, | ||
1692 | LintCompletion { | ||
1693 | label: "doc_spotlight", | ||
1694 | description: r##"# `doc_spotlight` | ||
1695 | |||
1696 | The tracking issue for this feature is: [#45040] | ||
1697 | |||
1698 | The `doc_spotlight` feature allows the use of the `spotlight` parameter to the `#[doc]` attribute, | ||
1699 | to "spotlight" a specific trait on the return values of functions. Adding a `#[doc(spotlight)]` | ||
1700 | attribute to a trait definition will make rustdoc print extra information for functions which return | ||
1701 | a type that implements that trait. For example, this attribute is applied to the `Iterator`, | ||
1702 | `io::Read`, `io::Write`, and `Future` traits in the standard library. | ||
1703 | |||
1704 | You can do this on your own traits, like this: | ||
1705 | |||
1706 | ``` | ||
1707 | #![feature(doc_spotlight)] | ||
1708 | |||
1709 | #[doc(spotlight)] | ||
1710 | pub trait MyTrait {} | ||
1711 | |||
1712 | pub struct MyStruct; | ||
1713 | impl MyTrait for MyStruct {} | ||
1714 | |||
1715 | /// The docs for this function will have an extra line about `MyStruct` implementing `MyTrait`, | ||
1716 | /// without having to write that yourself! | ||
1717 | pub fn my_fn() -> MyStruct { MyStruct } | ||
1718 | ``` | ||
1719 | |||
1720 | This feature was originally implemented in PR [#45039]. | ||
1721 | |||
1722 | [#45040]: https://github.com/rust-lang/rust/issues/45040 | ||
1723 | [#45039]: https://github.com/rust-lang/rust/pull/45039 | ||
1724 | "##, | ||
1725 | }, | ||
1726 | LintCompletion { | ||
1727 | label: "c_variadic", | ||
1728 | description: r##"# `c_variadic` | ||
1729 | |||
1730 | The tracking issue for this feature is: [#44930] | ||
1731 | |||
1732 | [#44930]: https://github.com/rust-lang/rust/issues/44930 | ||
1733 | |||
1734 | ------------------------ | ||
1735 | |||
1736 | The `c_variadic` language feature enables C-variadic functions to be | ||
1737 | defined in Rust. The may be called both from within Rust and via FFI. | ||
1738 | |||
1739 | ## Examples | ||
1740 | |||
1741 | ```rust | ||
1742 | #![feature(c_variadic)] | ||
1743 | |||
1744 | pub unsafe extern "C" fn add(n: usize, mut args: ...) -> usize { | ||
1745 | let mut sum = 0; | ||
1746 | for _ in 0..n { | ||
1747 | sum += args.arg::<usize>(); | ||
1748 | } | ||
1749 | sum | ||
1750 | } | ||
1751 | ``` | ||
1752 | "##, | ||
1753 | }, | ||
1754 | LintCompletion { | ||
1755 | label: "intra_doc_pointers", | ||
1756 | description: r##"# `intra-doc-pointers` | ||
1757 | |||
1758 | The tracking issue for this feature is: [#80896] | ||
1759 | |||
1760 | [#80896]: https://github.com/rust-lang/rust/issues/80896 | ||
1761 | |||
1762 | ------------------------ | ||
1763 | |||
1764 | Rustdoc does not currently allow disambiguating between `*const` and `*mut`, and | ||
1765 | raw pointers in intra-doc links are unstable until it does. | ||
1766 | |||
1767 | ```rust | ||
1768 | #![feature(intra_doc_pointers)] | ||
1769 | //! [pointer::add] | ||
1770 | ``` | ||
1771 | "##, | ||
1772 | }, | ||
1773 | LintCompletion { | ||
1774 | label: "box_syntax", | ||
1775 | description: r##"# `box_syntax` | ||
1776 | |||
1777 | The tracking issue for this feature is: [#49733] | ||
1778 | |||
1779 | [#49733]: https://github.com/rust-lang/rust/issues/49733 | ||
1780 | |||
1781 | See also [`box_patterns`](box-patterns.md) | ||
1782 | |||
1783 | ------------------------ | ||
1784 | |||
1785 | Currently the only stable way to create a `Box` is via the `Box::new` method. | ||
1786 | Also it is not possible in stable Rust to destructure a `Box` in a match | ||
1787 | pattern. The unstable `box` keyword can be used to create a `Box`. An example | ||
1788 | usage would be: | ||
1789 | |||
1790 | ```rust | ||
1791 | #![feature(box_syntax)] | ||
1792 | |||
1793 | fn main() { | ||
1794 | let b = box 5; | ||
1795 | } | ||
1796 | ``` | ||
1797 | "##, | ||
1798 | }, | ||
1799 | LintCompletion { | ||
1800 | label: "unsized_locals", | ||
1801 | description: r##"# `unsized_locals` | ||
1802 | |||
1803 | The tracking issue for this feature is: [#48055] | ||
1804 | |||
1805 | [#48055]: https://github.com/rust-lang/rust/issues/48055 | ||
1806 | |||
1807 | ------------------------ | ||
1808 | |||
1809 | This implements [RFC1909]. When turned on, you can have unsized arguments and locals: | ||
1810 | |||
1811 | [RFC1909]: https://github.com/rust-lang/rfcs/blob/master/text/1909-unsized-rvalues.md | ||
1812 | |||
1813 | ```rust | ||
1814 | #![allow(incomplete_features)] | ||
1815 | #![feature(unsized_locals, unsized_fn_params)] | ||
1816 | |||
1817 | use std::any::Any; | ||
1818 | |||
1819 | fn main() { | ||
1820 | let x: Box<dyn Any> = Box::new(42); | ||
1821 | let x: dyn Any = *x; | ||
1822 | // ^ unsized local variable | ||
1823 | // ^^ unsized temporary | ||
1824 | foo(x); | ||
1825 | } | ||
1826 | |||
1827 | fn foo(_: dyn Any) {} | ||
1828 | // ^^^^^^ unsized argument | ||
1829 | ``` | ||
1830 | |||
1831 | The RFC still forbids the following unsized expressions: | ||
1832 | |||
1833 | ```rust,compile_fail | ||
1834 | #![feature(unsized_locals)] | ||
1835 | |||
1836 | use std::any::Any; | ||
1837 | |||
1838 | struct MyStruct<T: ?Sized> { | ||
1839 | content: T, | ||
1840 | } | ||
1841 | |||
1842 | struct MyTupleStruct<T: ?Sized>(T); | ||
1843 | |||
1844 | fn answer() -> Box<dyn Any> { | ||
1845 | Box::new(42) | ||
1846 | } | ||
1847 | |||
1848 | fn main() { | ||
1849 | // You CANNOT have unsized statics. | ||
1850 | static X: dyn Any = *answer(); // ERROR | ||
1851 | const Y: dyn Any = *answer(); // ERROR | ||
1852 | |||
1853 | // You CANNOT have struct initialized unsized. | ||
1854 | MyStruct { content: *answer() }; // ERROR | ||
1855 | MyTupleStruct(*answer()); // ERROR | ||
1856 | (42, *answer()); // ERROR | ||
1857 | |||
1858 | // You CANNOT have unsized return types. | ||
1859 | fn my_function() -> dyn Any { *answer() } // ERROR | ||
1860 | |||
1861 | // You CAN have unsized local variables... | ||
1862 | let mut x: dyn Any = *answer(); // OK | ||
1863 | // ...but you CANNOT reassign to them. | ||
1864 | x = *answer(); // ERROR | ||
1865 | |||
1866 | // You CANNOT even initialize them separately. | ||
1867 | let y: dyn Any; // OK | ||
1868 | y = *answer(); // ERROR | ||
1869 | |||
1870 | // Not mentioned in the RFC, but by-move captured variables are also Sized. | ||
1871 | let x: dyn Any = *answer(); | ||
1872 | (move || { // ERROR | ||
1873 | let y = x; | ||
1874 | })(); | ||
1875 | |||
1876 | // You CAN create a closure with unsized arguments, | ||
1877 | // but you CANNOT call it. | ||
1878 | // This is an implementation detail and may be changed in the future. | ||
1879 | let f = |x: dyn Any| {}; | ||
1880 | f(*answer()); // ERROR | ||
1881 | } | ||
1882 | ``` | ||
1883 | |||
1884 | ## By-value trait objects | ||
1885 | |||
1886 | With this feature, you can have by-value `self` arguments without `Self: Sized` bounds. | ||
1887 | |||
1888 | ```rust | ||
1889 | #![feature(unsized_fn_params)] | ||
1890 | |||
1891 | trait Foo { | ||
1892 | fn foo(self) {} | ||
1893 | } | ||
1894 | |||
1895 | impl<T: ?Sized> Foo for T {} | ||
1896 | |||
1897 | fn main() { | ||
1898 | let slice: Box<[i32]> = Box::new([1, 2, 3]); | ||
1899 | <[i32] as Foo>::foo(*slice); | ||
1900 | } | ||
1901 | ``` | ||
1902 | |||
1903 | And `Foo` will also be object-safe. | ||
1904 | |||
1905 | ```rust | ||
1906 | #![feature(unsized_fn_params)] | ||
1907 | |||
1908 | trait Foo { | ||
1909 | fn foo(self) {} | ||
1910 | } | ||
1911 | |||
1912 | impl<T: ?Sized> Foo for T {} | ||
1913 | |||
1914 | fn main () { | ||
1915 | let slice: Box<dyn Foo> = Box::new([1, 2, 3]); | ||
1916 | // doesn't compile yet | ||
1917 | <dyn Foo as Foo>::foo(*slice); | ||
1918 | } | ||
1919 | ``` | ||
1920 | |||
1921 | One of the objectives of this feature is to allow `Box<dyn FnOnce>`. | ||
1922 | |||
1923 | ## Variable length arrays | ||
1924 | |||
1925 | The RFC also describes an extension to the array literal syntax: `[e; dyn n]`. In the syntax, `n` isn't necessarily a constant expression. The array is dynamically allocated on the stack and has the type of `[T]`, instead of `[T; n]`. | ||
1926 | |||
1927 | ```rust,ignore (not-yet-implemented) | ||
1928 | #![feature(unsized_locals)] | ||
1929 | |||
1930 | fn mergesort<T: Ord>(a: &mut [T]) { | ||
1931 | let mut tmp = [T; dyn a.len()]; | ||
1932 | // ... | ||
1933 | } | ||
1934 | |||
1935 | fn main() { | ||
1936 | let mut a = [3, 1, 5, 6]; | ||
1937 | mergesort(&mut a); | ||
1938 | assert_eq!(a, [1, 3, 5, 6]); | ||
1939 | } | ||
1940 | ``` | ||
1941 | |||
1942 | VLAs are not implemented yet. The syntax isn't final, either. We may need an alternative syntax for Rust 2015 because, in Rust 2015, expressions like `[e; dyn(1)]` would be ambiguous. One possible alternative proposed in the RFC is `[e; n]`: if `n` captures one or more local variables, then it is considered as `[e; dyn n]`. | ||
1943 | |||
1944 | ## Advisory on stack usage | ||
1945 | |||
1946 | It's advised not to casually use the `#![feature(unsized_locals)]` feature. Typical use-cases are: | ||
1947 | |||
1948 | - When you need a by-value trait objects. | ||
1949 | - When you really need a fast allocation of small temporary arrays. | ||
1950 | |||
1951 | Another pitfall is repetitive allocation and temporaries. Currently the compiler simply extends the stack frame every time it encounters an unsized assignment. So for example, the code | ||
1952 | |||
1953 | ```rust | ||
1954 | #![feature(unsized_locals)] | ||
1955 | |||
1956 | fn main() { | ||
1957 | let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]); | ||
1958 | let _x = {{{{{{{{{{*x}}}}}}}}}}; | ||
1959 | } | ||
1960 | ``` | ||
1961 | |||
1962 | and the code | ||
1963 | |||
1964 | ```rust | ||
1965 | #![feature(unsized_locals)] | ||
1966 | |||
1967 | fn main() { | ||
1968 | for _ in 0..10 { | ||
1969 | let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]); | ||
1970 | let _x = *x; | ||
1971 | } | ||
1972 | } | ||
1973 | ``` | ||
1974 | |||
1975 | will unnecessarily extend the stack frame. | ||
1976 | "##, | ||
1977 | }, | ||
1978 | LintCompletion { | ||
1979 | label: "arbitrary_enum_discriminant", | ||
1980 | description: r##"# `arbitrary_enum_discriminant` | ||
1981 | |||
1982 | The tracking issue for this feature is: [#60553] | ||
1983 | |||
1984 | [#60553]: https://github.com/rust-lang/rust/issues/60553 | ||
1985 | |||
1986 | ------------------------ | ||
1987 | |||
1988 | The `arbitrary_enum_discriminant` feature permits tuple-like and | ||
1989 | struct-like enum variants with `#[repr(<int-type>)]` to have explicit discriminants. | ||
1990 | |||
1991 | ## Examples | ||
1992 | |||
1993 | ```rust | ||
1994 | #![feature(arbitrary_enum_discriminant)] | ||
1995 | |||
1996 | #[allow(dead_code)] | ||
1997 | #[repr(u8)] | ||
1998 | enum Enum { | ||
1999 | Unit = 3, | ||
2000 | Tuple(u16) = 2, | ||
2001 | Struct { | ||
2002 | a: u8, | ||
2003 | b: u16, | ||
2004 | } = 1, | ||
2005 | } | ||
2006 | |||
2007 | impl Enum { | ||
2008 | fn tag(&self) -> u8 { | ||
2009 | unsafe { *(self as *const Self as *const u8) } | ||
2010 | } | ||
2011 | } | ||
2012 | |||
2013 | assert_eq!(3, Enum::Unit.tag()); | ||
2014 | assert_eq!(2, Enum::Tuple(5).tag()); | ||
2015 | assert_eq!(1, Enum::Struct{a: 7, b: 11}.tag()); | ||
2016 | ``` | ||
2017 | "##, | ||
2018 | }, | ||
2019 | LintCompletion { | ||
2020 | label: "unboxed_closures", | ||
2021 | description: r##"# `unboxed_closures` | ||
2022 | |||
2023 | The tracking issue for this feature is [#29625] | ||
2024 | |||
2025 | See Also: [`fn_traits`](../library-features/fn-traits.md) | ||
2026 | |||
2027 | [#29625]: https://github.com/rust-lang/rust/issues/29625 | ||
2028 | |||
2029 | ---- | ||
2030 | |||
2031 | The `unboxed_closures` feature allows you to write functions using the `"rust-call"` ABI, | ||
2032 | required for implementing the [`Fn*`] family of traits. `"rust-call"` functions must have | ||
2033 | exactly one (non self) argument, a tuple representing the argument list. | ||
2034 | |||
2035 | [`Fn*`]: https://doc.rust-lang.org/std/ops/trait.Fn.html | ||
2036 | |||
2037 | ```rust | ||
2038 | #![feature(unboxed_closures)] | ||
2039 | |||
2040 | extern "rust-call" fn add_args(args: (u32, u32)) -> u32 { | ||
2041 | args.0 + args.1 | ||
2042 | } | ||
2043 | |||
2044 | fn main() {} | ||
2045 | ``` | ||
2046 | "##, | ||
2047 | }, | ||
2048 | LintCompletion { | ||
2049 | label: "custom_test_frameworks", | ||
2050 | description: r##"# `custom_test_frameworks` | ||
2051 | |||
2052 | The tracking issue for this feature is: [#50297] | ||
2053 | |||
2054 | [#50297]: https://github.com/rust-lang/rust/issues/50297 | ||
2055 | |||
2056 | ------------------------ | ||
2057 | |||
2058 | The `custom_test_frameworks` feature allows the use of `#[test_case]` and `#![test_runner]`. | ||
2059 | Any function, const, or static can be annotated with `#[test_case]` causing it to be aggregated (like `#[test]`) | ||
2060 | and be passed to the test runner determined by the `#![test_runner]` crate attribute. | ||
2061 | |||
2062 | ```rust | ||
2063 | #![feature(custom_test_frameworks)] | ||
2064 | #![test_runner(my_runner)] | ||
2065 | |||
2066 | fn my_runner(tests: &[&i32]) { | ||
2067 | for t in tests { | ||
2068 | if **t == 0 { | ||
2069 | println!("PASSED"); | ||
2070 | } else { | ||
2071 | println!("FAILED"); | ||
2072 | } | ||
2073 | } | ||
2074 | } | ||
2075 | |||
2076 | #[test_case] | ||
2077 | const WILL_PASS: i32 = 0; | ||
2078 | |||
2079 | #[test_case] | ||
2080 | const WILL_FAIL: i32 = 4; | ||
2081 | ``` | ||
2082 | "##, | ||
2083 | }, | ||
2084 | LintCompletion { | ||
2085 | label: "abi_msp430_interrupt", | 577 | label: "abi_msp430_interrupt", |
2086 | description: r##"# `abi_msp430_interrupt` | 578 | description: r##"# `abi_msp430_interrupt` |
2087 | 579 | ||
@@ -2127,201 +619,7 @@ Disassembly of section .text: | |||
2127 | ``` | 619 | ``` |
2128 | "##, | 620 | "##, |
2129 | }, | 621 | }, |
2130 | LintCompletion { | 622 | Lint { |
2131 | label: "impl_trait_in_bindings", | ||
2132 | description: r##"# `impl_trait_in_bindings` | ||
2133 | |||
2134 | The tracking issue for this feature is: [#63065] | ||
2135 | |||
2136 | [#63065]: https://github.com/rust-lang/rust/issues/63065 | ||
2137 | |||
2138 | ------------------------ | ||
2139 | |||
2140 | The `impl_trait_in_bindings` feature gate lets you use `impl Trait` syntax in | ||
2141 | `let`, `static`, and `const` bindings. | ||
2142 | |||
2143 | A simple example is: | ||
2144 | |||
2145 | ```rust | ||
2146 | #![feature(impl_trait_in_bindings)] | ||
2147 | |||
2148 | use std::fmt::Debug; | ||
2149 | |||
2150 | fn main() { | ||
2151 | let a: impl Debug + Clone = 42; | ||
2152 | let b = a.clone(); | ||
2153 | println!("{:?}", b); // prints `42` | ||
2154 | } | ||
2155 | ``` | ||
2156 | |||
2157 | Note however that because the types of `a` and `b` are opaque in the above | ||
2158 | example, calling inherent methods or methods outside of the specified traits | ||
2159 | (e.g., `a.abs()` or `b.abs()`) is not allowed, and yields an error. | ||
2160 | "##, | ||
2161 | }, | ||
2162 | LintCompletion { | ||
2163 | label: "cfg_version", | ||
2164 | description: r##"# `cfg_version` | ||
2165 | |||
2166 | The tracking issue for this feature is: [#64796] | ||
2167 | |||
2168 | [#64796]: https://github.com/rust-lang/rust/issues/64796 | ||
2169 | |||
2170 | ------------------------ | ||
2171 | |||
2172 | The `cfg_version` feature makes it possible to execute different code | ||
2173 | depending on the compiler version. | ||
2174 | |||
2175 | ## Examples | ||
2176 | |||
2177 | ```rust | ||
2178 | #![feature(cfg_version)] | ||
2179 | |||
2180 | #[cfg(version("1.42"))] | ||
2181 | fn a() { | ||
2182 | // ... | ||
2183 | } | ||
2184 | |||
2185 | #[cfg(not(version("1.42")))] | ||
2186 | fn a() { | ||
2187 | // ... | ||
2188 | } | ||
2189 | |||
2190 | fn b() { | ||
2191 | if cfg!(version("1.42")) { | ||
2192 | // ... | ||
2193 | } else { | ||
2194 | // ... | ||
2195 | } | ||
2196 | } | ||
2197 | ``` | ||
2198 | "##, | ||
2199 | }, | ||
2200 | LintCompletion { | ||
2201 | label: "link_cfg", | ||
2202 | description: r##"# `link_cfg` | ||
2203 | |||
2204 | This feature is internal to the Rust compiler and is not intended for general use. | ||
2205 | |||
2206 | ------------------------ | ||
2207 | "##, | ||
2208 | }, | ||
2209 | LintCompletion { | ||
2210 | label: "infer_static_outlives_requirements", | ||
2211 | description: r##"# `infer_static_outlives_requirements` | ||
2212 | |||
2213 | The tracking issue for this feature is: [#54185] | ||
2214 | |||
2215 | [#54185]: https://github.com/rust-lang/rust/issues/54185 | ||
2216 | |||
2217 | ------------------------ | ||
2218 | The `infer_static_outlives_requirements` feature indicates that certain | ||
2219 | `'static` outlives requirements can be inferred by the compiler rather than | ||
2220 | stating them explicitly. | ||
2221 | |||
2222 | Note: It is an accompanying feature to `infer_outlives_requirements`, | ||
2223 | which must be enabled to infer outlives requirements. | ||
2224 | |||
2225 | For example, currently generic struct definitions that contain | ||
2226 | references, require where-clauses of the form T: 'static. By using | ||
2227 | this feature the outlives predicates will be inferred, although | ||
2228 | they may still be written explicitly. | ||
2229 | |||
2230 | ```rust,ignore (pseudo-Rust) | ||
2231 | struct Foo<U> where U: 'static { // <-- currently required | ||
2232 | bar: Bar<U> | ||
2233 | } | ||
2234 | struct Bar<T: 'static> { | ||
2235 | x: T, | ||
2236 | } | ||
2237 | ``` | ||
2238 | |||
2239 | |||
2240 | ## Examples: | ||
2241 | |||
2242 | ```rust,ignore (pseudo-Rust) | ||
2243 | #![feature(infer_outlives_requirements)] | ||
2244 | #![feature(infer_static_outlives_requirements)] | ||
2245 | |||
2246 | #[rustc_outlives] | ||
2247 | // Implicitly infer U: 'static | ||
2248 | struct Foo<U> { | ||
2249 | bar: Bar<U> | ||
2250 | } | ||
2251 | struct Bar<T: 'static> { | ||
2252 | x: T, | ||
2253 | } | ||
2254 | ``` | ||
2255 | "##, | ||
2256 | }, | ||
2257 | LintCompletion { | ||
2258 | label: "marker_trait_attr", | ||
2259 | description: r##"# `marker_trait_attr` | ||
2260 | |||
2261 | The tracking issue for this feature is: [#29864] | ||
2262 | |||
2263 | [#29864]: https://github.com/rust-lang/rust/issues/29864 | ||
2264 | |||
2265 | ------------------------ | ||
2266 | |||
2267 | Normally, Rust keeps you from adding trait implementations that could | ||
2268 | overlap with each other, as it would be ambiguous which to use. This | ||
2269 | feature, however, carves out an exception to that rule: a trait can | ||
2270 | opt-in to having overlapping implementations, at the cost that those | ||
2271 | implementations are not allowed to override anything (and thus the | ||
2272 | trait itself cannot have any associated items, as they're pointless | ||
2273 | when they'd need to do the same thing for every type anyway). | ||
2274 | |||
2275 | ```rust | ||
2276 | #![feature(marker_trait_attr)] | ||
2277 | |||
2278 | #[marker] trait CheapToClone: Clone {} | ||
2279 | |||
2280 | impl<T: Copy> CheapToClone for T {} | ||
2281 | |||
2282 | // These could potentially overlap with the blanket implementation above, | ||
2283 | // so are only allowed because CheapToClone is a marker trait. | ||
2284 | impl<T: CheapToClone, U: CheapToClone> CheapToClone for (T, U) {} | ||
2285 | impl<T: CheapToClone> CheapToClone for std::ops::Range<T> {} | ||
2286 | |||
2287 | fn cheap_clone<T: CheapToClone>(t: T) -> T { | ||
2288 | t.clone() | ||
2289 | } | ||
2290 | ``` | ||
2291 | |||
2292 | This is expected to replace the unstable `overlapping_marker_traits` | ||
2293 | feature, which applied to all empty traits (without needing an opt-in). | ||
2294 | "##, | ||
2295 | }, | ||
2296 | LintCompletion { | ||
2297 | label: "doc_masked", | ||
2298 | description: r##"# `doc_masked` | ||
2299 | |||
2300 | The tracking issue for this feature is: [#44027] | ||
2301 | |||
2302 | ----- | ||
2303 | |||
2304 | The `doc_masked` feature allows a crate to exclude types from a given crate from appearing in lists | ||
2305 | of trait implementations. The specifics of the feature are as follows: | ||
2306 | |||
2307 | 1. When rustdoc encounters an `extern crate` statement annotated with a `#[doc(masked)]` attribute, | ||
2308 | it marks the crate as being masked. | ||
2309 | |||
2310 | 2. When listing traits a given type implements, rustdoc ensures that traits from masked crates are | ||
2311 | not emitted into the documentation. | ||
2312 | |||
2313 | 3. When listing types that implement a given trait, rustdoc ensures that types from masked crates | ||
2314 | are not emitted into the documentation. | ||
2315 | |||
2316 | This feature was introduced in PR [#44026] to ensure that compiler-internal and | ||
2317 | implementation-specific types and traits were not included in the standard library's documentation. | ||
2318 | Such types would introduce broken links into the documentation. | ||
2319 | |||
2320 | [#44026]: https://github.com/rust-lang/rust/pull/44026 | ||
2321 | [#44027]: https://github.com/rust-lang/rust/pull/44027 | ||
2322 | "##, | ||
2323 | }, | ||
2324 | LintCompletion { | ||
2325 | label: "abi_ptx", | 623 | label: "abi_ptx", |
2326 | description: r##"# `abi_ptx` | 624 | description: r##"# `abi_ptx` |
2327 | 625 | ||
@@ -2385,340 +683,94 @@ $ cat $(find -name '*.s') | |||
2385 | ``` | 683 | ``` |
2386 | "##, | 684 | "##, |
2387 | }, | 685 | }, |
2388 | LintCompletion { | 686 | Lint { |
2389 | label: "profiler_runtime", | 687 | label: "abi_thiscall", |
2390 | description: r##"# `profiler_runtime` | 688 | description: r##"# `abi_thiscall` |
2391 | |||
2392 | The tracking issue for this feature is: [#42524](https://github.com/rust-lang/rust/issues/42524). | ||
2393 | |||
2394 | ------------------------ | ||
2395 | "##, | ||
2396 | }, | ||
2397 | LintCompletion { | ||
2398 | label: "crate_visibility_modifier", | ||
2399 | description: r##"# `crate_visibility_modifier` | ||
2400 | |||
2401 | The tracking issue for this feature is: [#53120] | ||
2402 | |||
2403 | [#53120]: https://github.com/rust-lang/rust/issues/53120 | ||
2404 | |||
2405 | ----- | ||
2406 | |||
2407 | The `crate_visibility_modifier` feature allows the `crate` keyword to be used | ||
2408 | as a visibility modifier synonymous to `pub(crate)`, indicating that a type | ||
2409 | (function, _&c._) is to be visible to the entire enclosing crate, but not to | ||
2410 | other crates. | ||
2411 | |||
2412 | ```rust | ||
2413 | #![feature(crate_visibility_modifier)] | ||
2414 | |||
2415 | crate struct Foo { | ||
2416 | bar: usize, | ||
2417 | } | ||
2418 | ``` | ||
2419 | "##, | ||
2420 | }, | ||
2421 | LintCompletion { | ||
2422 | label: "doc_cfg", | ||
2423 | description: r##"# `doc_cfg` | ||
2424 | |||
2425 | The tracking issue for this feature is: [#43781] | ||
2426 | |||
2427 | ------ | ||
2428 | |||
2429 | The `doc_cfg` feature allows an API be documented as only available in some specific platforms. | ||
2430 | This attribute has two effects: | ||
2431 | |||
2432 | 1. In the annotated item's documentation, there will be a message saying "This is supported on | ||
2433 | (platform) only". | ||
2434 | |||
2435 | 2. The item's doc-tests will only run on the specific platform. | ||
2436 | |||
2437 | In addition to allowing the use of the `#[doc(cfg)]` attribute, this feature enables the use of a | ||
2438 | special conditional compilation flag, `#[cfg(doc)]`, set whenever building documentation on your | ||
2439 | crate. | ||
2440 | |||
2441 | This feature was introduced as part of PR [#43348] to allow the platform-specific parts of the | ||
2442 | standard library be documented. | ||
2443 | |||
2444 | ```rust | ||
2445 | #![feature(doc_cfg)] | ||
2446 | |||
2447 | #[cfg(any(windows, doc))] | ||
2448 | #[doc(cfg(windows))] | ||
2449 | /// The application's icon in the notification area (a.k.a. system tray). | ||
2450 | /// | ||
2451 | /// # Examples | ||
2452 | /// | ||
2453 | /// ```no_run | ||
2454 | /// extern crate my_awesome_ui_library; | ||
2455 | /// use my_awesome_ui_library::current_app; | ||
2456 | /// use my_awesome_ui_library::windows::notification; | ||
2457 | /// | ||
2458 | /// let icon = current_app().get::<notification::Icon>(); | ||
2459 | /// icon.show(); | ||
2460 | /// icon.show_message("Hello"); | ||
2461 | /// ``` | ||
2462 | pub struct Icon { | ||
2463 | // ... | ||
2464 | } | ||
2465 | ``` | ||
2466 | |||
2467 | [#43781]: https://github.com/rust-lang/rust/issues/43781 | ||
2468 | [#43348]: https://github.com/rust-lang/rust/issues/43348 | ||
2469 | "##, | ||
2470 | }, | ||
2471 | LintCompletion { | ||
2472 | label: "unsized_tuple_coercion", | ||
2473 | description: r##"# `unsized_tuple_coercion` | ||
2474 | 689 | ||
2475 | The tracking issue for this feature is: [#42877] | 690 | The tracking issue for this feature is: [#42202] |
2476 | 691 | ||
2477 | [#42877]: https://github.com/rust-lang/rust/issues/42877 | 692 | [#42202]: https://github.com/rust-lang/rust/issues/42202 |
2478 | 693 | ||
2479 | ------------------------ | 694 | ------------------------ |
2480 | 695 | ||
2481 | This is a part of [RFC0401]. According to the RFC, there should be an implementation like this: | 696 | The MSVC ABI on x86 Windows uses the `thiscall` calling convention for C++ |
2482 | 697 | instance methods by default; it is identical to the usual (C) calling | |
2483 | ```rust,ignore (partial-example) | 698 | convention on x86 Windows except that the first parameter of the method, |
2484 | impl<..., T, U: ?Sized> Unsized<(..., U)> for (..., T) where T: Unsized<U> {} | 699 | the `this` pointer, is passed in the ECX register. |
2485 | ``` | ||
2486 | |||
2487 | This implementation is currently gated behind `#[feature(unsized_tuple_coercion)]` to avoid insta-stability. Therefore you can use it like this: | ||
2488 | |||
2489 | ```rust | ||
2490 | #![feature(unsized_tuple_coercion)] | ||
2491 | |||
2492 | fn main() { | ||
2493 | let x : ([i32; 3], [i32; 3]) = ([1, 2, 3], [4, 5, 6]); | ||
2494 | let y : &([i32; 3], [i32]) = &x; | ||
2495 | assert_eq!(y.1[0], 4); | ||
2496 | } | ||
2497 | ``` | ||
2498 | |||
2499 | [RFC0401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md | ||
2500 | "##, | 700 | "##, |
2501 | }, | 701 | }, |
2502 | LintCompletion { | 702 | Lint { |
2503 | label: "no_sanitize", | 703 | label: "allocator_api", |
2504 | description: r##"# `no_sanitize` | 704 | description: r##"# `allocator_api` |
2505 | 705 | ||
2506 | The tracking issue for this feature is: [#39699] | 706 | The tracking issue for this feature is [#32838] |
2507 | 707 | ||
2508 | [#39699]: https://github.com/rust-lang/rust/issues/39699 | 708 | [#32838]: https://github.com/rust-lang/rust/issues/32838 |
2509 | 709 | ||
2510 | ------------------------ | 710 | ------------------------ |
2511 | 711 | ||
2512 | The `no_sanitize` attribute can be used to selectively disable sanitizer | 712 | Sometimes you want the memory for one collection to use a different |
2513 | instrumentation in an annotated function. This might be useful to: avoid | 713 | allocator than the memory for another collection. In this case, |
2514 | instrumentation overhead in a performance critical function, or avoid | 714 | replacing the global allocator is not a workable option. Instead, |
2515 | instrumenting code that contains constructs unsupported by given sanitizer. | 715 | you need to pass in an instance of an `AllocRef` to each collection |
2516 | 716 | for which you want a custom allocator. | |
2517 | The precise effect of this annotation depends on particular sanitizer in use. | ||
2518 | For example, with `no_sanitize(thread)`, the thread sanitizer will no longer | ||
2519 | instrument non-atomic store / load operations, but it will instrument atomic | ||
2520 | operations to avoid reporting false positives and provide meaning full stack | ||
2521 | traces. | ||
2522 | |||
2523 | ## Examples | ||
2524 | |||
2525 | ``` rust | ||
2526 | #![feature(no_sanitize)] | ||
2527 | 717 | ||
2528 | #[no_sanitize(address)] | 718 | TBD |
2529 | fn foo() { | ||
2530 | // ... | ||
2531 | } | ||
2532 | ``` | ||
2533 | "##, | 719 | "##, |
2534 | }, | 720 | }, |
2535 | LintCompletion { | 721 | Lint { |
2536 | label: "try_blocks", | 722 | label: "allocator_internals", |
2537 | description: r##"# `try_blocks` | 723 | description: r##"# `allocator_internals` |
2538 | |||
2539 | The tracking issue for this feature is: [#31436] | ||
2540 | 724 | ||
2541 | [#31436]: https://github.com/rust-lang/rust/issues/31436 | 725 | This feature does not have a tracking issue, it is an unstable implementation |
726 | detail of the `global_allocator` feature not intended for use outside the | ||
727 | compiler. | ||
2542 | 728 | ||
2543 | ------------------------ | 729 | ------------------------ |
2544 | |||
2545 | The `try_blocks` feature adds support for `try` blocks. A `try` | ||
2546 | block creates a new scope one can use the `?` operator in. | ||
2547 | |||
2548 | ```rust,edition2018 | ||
2549 | #![feature(try_blocks)] | ||
2550 | |||
2551 | use std::num::ParseIntError; | ||
2552 | |||
2553 | let result: Result<i32, ParseIntError> = try { | ||
2554 | "1".parse::<i32>()? | ||
2555 | + "2".parse::<i32>()? | ||
2556 | + "3".parse::<i32>()? | ||
2557 | }; | ||
2558 | assert_eq!(result, Ok(6)); | ||
2559 | |||
2560 | let result: Result<i32, ParseIntError> = try { | ||
2561 | "1".parse::<i32>()? | ||
2562 | + "foo".parse::<i32>()? | ||
2563 | + "3".parse::<i32>()? | ||
2564 | }; | ||
2565 | assert!(result.is_err()); | ||
2566 | ``` | ||
2567 | "##, | 730 | "##, |
2568 | }, | 731 | }, |
2569 | LintCompletion { | 732 | Lint { |
2570 | label: "transparent_unions", | 733 | label: "arbitrary_enum_discriminant", |
2571 | description: r##"# `transparent_unions` | 734 | description: r##"# `arbitrary_enum_discriminant` |
2572 | |||
2573 | The tracking issue for this feature is [#60405] | ||
2574 | |||
2575 | [#60405]: https://github.com/rust-lang/rust/issues/60405 | ||
2576 | |||
2577 | ---- | ||
2578 | 735 | ||
2579 | The `transparent_unions` feature allows you mark `union`s as | 736 | The tracking issue for this feature is: [#60553] |
2580 | `#[repr(transparent)]`. A `union` may be `#[repr(transparent)]` in exactly the | ||
2581 | same conditions in which a `struct` may be `#[repr(transparent)]` (generally, | ||
2582 | this means the `union` must have exactly one non-zero-sized field). Some | ||
2583 | concrete illustrations follow. | ||
2584 | 737 | ||
2585 | ```rust | 738 | [#60553]: https://github.com/rust-lang/rust/issues/60553 |
2586 | #![feature(transparent_unions)] | ||
2587 | 739 | ||
2588 | // This union has the same representation as `f32`. | 740 | ------------------------ |
2589 | #[repr(transparent)] | ||
2590 | union SingleFieldUnion { | ||
2591 | field: f32, | ||
2592 | } | ||
2593 | 741 | ||
2594 | // This union has the same representation as `usize`. | 742 | The `arbitrary_enum_discriminant` feature permits tuple-like and |
2595 | #[repr(transparent)] | 743 | struct-like enum variants with `#[repr(<int-type>)]` to have explicit discriminants. |
2596 | union MultiFieldUnion { | ||
2597 | field: usize, | ||
2598 | nothing: (), | ||
2599 | } | ||
2600 | ``` | ||
2601 | 744 | ||
2602 | For consistency with transparent `struct`s, `union`s must have exactly one | 745 | ## Examples |
2603 | non-zero-sized field. If all fields are zero-sized, the `union` must not be | ||
2604 | `#[repr(transparent)]`: | ||
2605 | 746 | ||
2606 | ```rust | 747 | ```rust |
2607 | #![feature(transparent_unions)] | 748 | #![feature(arbitrary_enum_discriminant)] |
2608 | 749 | ||
2609 | // This (non-transparent) union is already valid in stable Rust: | 750 | #[allow(dead_code)] |
2610 | pub union GoodUnion { | 751 | #[repr(u8)] |
2611 | pub nothing: (), | 752 | enum Enum { |
753 | Unit = 3, | ||
754 | Tuple(u16) = 2, | ||
755 | Struct { | ||
756 | a: u8, | ||
757 | b: u16, | ||
758 | } = 1, | ||
2612 | } | 759 | } |
2613 | 760 | ||
2614 | // Error: transparent union needs exactly one non-zero-sized field, but has 0 | 761 | impl Enum { |
2615 | // #[repr(transparent)] | 762 | fn tag(&self) -> u8 { |
2616 | // pub union BadUnion { | 763 | unsafe { *(self as *const Self as *const u8) } |
2617 | // pub nothing: (), | 764 | } |
2618 | // } | ||
2619 | ``` | ||
2620 | |||
2621 | The one exception is if the `union` is generic over `T` and has a field of type | ||
2622 | `T`, it may be `#[repr(transparent)]` even if `T` is a zero-sized type: | ||
2623 | |||
2624 | ```rust | ||
2625 | #![feature(transparent_unions)] | ||
2626 | |||
2627 | // This union has the same representation as `T`. | ||
2628 | #[repr(transparent)] | ||
2629 | pub union GenericUnion<T: Copy> { // Unions with non-`Copy` fields are unstable. | ||
2630 | pub field: T, | ||
2631 | pub nothing: (), | ||
2632 | } | 765 | } |
2633 | 766 | ||
2634 | // This is okay even though `()` is a zero-sized type. | 767 | assert_eq!(3, Enum::Unit.tag()); |
2635 | pub const THIS_IS_OKAY: GenericUnion<()> = GenericUnion { field: () }; | 768 | assert_eq!(2, Enum::Tuple(5).tag()); |
2636 | ``` | 769 | assert_eq!(1, Enum::Struct{a: 7, b: 11}.tag()); |
2637 | |||
2638 | Like transarent `struct`s, a transparent `union` of type `U` has the same | ||
2639 | layout, size, and ABI as its single non-ZST field. If it is generic over a type | ||
2640 | `T`, and all its fields are ZSTs except for exactly one field of type `T`, then | ||
2641 | it has the same layout and ABI as `T` (even if `T` is a ZST when monomorphized). | ||
2642 | |||
2643 | Like transparent `struct`s, transparent `union`s are FFI-safe if and only if | ||
2644 | their underlying representation type is also FFI-safe. | ||
2645 | |||
2646 | A `union` may not be eligible for the same nonnull-style optimizations that a | ||
2647 | `struct` or `enum` (with the same fields) are eligible for. Adding | ||
2648 | `#[repr(transparent)]` to `union` does not change this. To give a more concrete | ||
2649 | example, it is unspecified whether `size_of::<T>()` is equal to | ||
2650 | `size_of::<Option<T>>()`, where `T` is a `union` (regardless of whether or not | ||
2651 | it is transparent). The Rust compiler is free to perform this optimization if | ||
2652 | possible, but is not required to, and different compiler versions may differ in | ||
2653 | their application of these optimizations. | ||
2654 | "##, | ||
2655 | }, | ||
2656 | LintCompletion { | ||
2657 | label: "const_eval_limit", | ||
2658 | description: r##"# `const_eval_limit` | ||
2659 | |||
2660 | The tracking issue for this feature is: [#67217] | ||
2661 | |||
2662 | [#67217]: https://github.com/rust-lang/rust/issues/67217 | ||
2663 | |||
2664 | The `const_eval_limit` allows someone to limit the evaluation steps the CTFE undertakes to evaluate a `const fn`. | ||
2665 | "##, | ||
2666 | }, | ||
2667 | LintCompletion { | ||
2668 | label: "link_args", | ||
2669 | description: r##"# `link_args` | ||
2670 | |||
2671 | The tracking issue for this feature is: [#29596] | ||
2672 | |||
2673 | [#29596]: https://github.com/rust-lang/rust/issues/29596 | ||
2674 | |||
2675 | ------------------------ | ||
2676 | |||
2677 | You can tell `rustc` how to customize linking, and that is via the `link_args` | ||
2678 | attribute. This attribute is applied to `extern` blocks and specifies raw flags | ||
2679 | which need to get passed to the linker when producing an artifact. An example | ||
2680 | usage would be: | ||
2681 | |||
2682 | ```rust,no_run | ||
2683 | #![feature(link_args)] | ||
2684 | |||
2685 | #[link_args = "-foo -bar -baz"] | ||
2686 | extern "C" {} | ||
2687 | # fn main() {} | ||
2688 | ``` | 770 | ``` |
2689 | |||
2690 | Note that this feature is currently hidden behind the `feature(link_args)` gate | ||
2691 | because this is not a sanctioned way of performing linking. Right now `rustc` | ||
2692 | shells out to the system linker (`gcc` on most systems, `link.exe` on MSVC), so | ||
2693 | it makes sense to provide extra command line arguments, but this will not | ||
2694 | always be the case. In the future `rustc` may use LLVM directly to link native | ||
2695 | libraries, in which case `link_args` will have no meaning. You can achieve the | ||
2696 | same effect as the `link_args` attribute with the `-C link-args` argument to | ||
2697 | `rustc`. | ||
2698 | |||
2699 | It is highly recommended to *not* use this attribute, and rather use the more | ||
2700 | formal `#[link(...)]` attribute on `extern` blocks instead. | ||
2701 | "##, | ||
2702 | }, | ||
2703 | LintCompletion { | ||
2704 | label: "internal_output_capture", | ||
2705 | description: r##"# `internal_output_capture` | ||
2706 | |||
2707 | This feature is internal to the Rust compiler and is not intended for general use. | ||
2708 | |||
2709 | ------------------------ | ||
2710 | "##, | ||
2711 | }, | ||
2712 | LintCompletion { | ||
2713 | label: "windows_handle", | ||
2714 | description: r##"# `windows_handle` | ||
2715 | |||
2716 | This feature is internal to the Rust compiler and is not intended for general use. | ||
2717 | |||
2718 | ------------------------ | ||
2719 | "##, | 771 | "##, |
2720 | }, | 772 | }, |
2721 | LintCompletion { | 773 | Lint { |
2722 | label: "asm", | 774 | label: "asm", |
2723 | description: r##"# `asm` | 775 | description: r##"# `asm` |
2724 | 776 | ||
@@ -2748,6 +800,7 @@ Inline assembly is currently supported on the following architectures: | |||
2748 | - AArch64 | 800 | - AArch64 |
2749 | - RISC-V | 801 | - RISC-V |
2750 | - NVPTX | 802 | - NVPTX |
803 | - PowerPC | ||
2751 | - Hexagon | 804 | - Hexagon |
2752 | - MIPS32r2 and MIPS64r2 | 805 | - MIPS32r2 and MIPS64r2 |
2753 | - wasm32 | 806 | - wasm32 |
@@ -2757,7 +810,7 @@ Inline assembly is currently supported on the following architectures: | |||
2757 | Let us start with the simplest possible example: | 810 | Let us start with the simplest possible example: |
2758 | 811 | ||
2759 | ```rust,allow_fail | 812 | ```rust,allow_fail |
2760 | # #![feature(asm)] | 813 | #![feature(asm)] |
2761 | unsafe { | 814 | unsafe { |
2762 | asm!("nop"); | 815 | asm!("nop"); |
2763 | } | 816 | } |
@@ -2774,7 +827,7 @@ Now inserting an instruction that does nothing is rather boring. Let us do somet | |||
2774 | actually acts on data: | 827 | actually acts on data: |
2775 | 828 | ||
2776 | ```rust,allow_fail | 829 | ```rust,allow_fail |
2777 | # #![feature(asm)] | 830 | #![feature(asm)] |
2778 | let x: u64; | 831 | let x: u64; |
2779 | unsafe { | 832 | unsafe { |
2780 | asm!("mov {}, 5", out(reg) x); | 833 | asm!("mov {}, 5", out(reg) x); |
@@ -2796,7 +849,7 @@ the template and will read the variable from there after the inline assembly fin | |||
2796 | Let us see another example that also uses an input: | 849 | Let us see another example that also uses an input: |
2797 | 850 | ||
2798 | ```rust,allow_fail | 851 | ```rust,allow_fail |
2799 | # #![feature(asm)] | 852 | #![feature(asm)] |
2800 | let i: u64 = 3; | 853 | let i: u64 = 3; |
2801 | let o: u64; | 854 | let o: u64; |
2802 | unsafe { | 855 | unsafe { |
@@ -2836,7 +889,7 @@ readability, and allows reordering instructions without changing the argument or | |||
2836 | We can further refine the above example to avoid the `mov` instruction: | 889 | We can further refine the above example to avoid the `mov` instruction: |
2837 | 890 | ||
2838 | ```rust,allow_fail | 891 | ```rust,allow_fail |
2839 | # #![feature(asm)] | 892 | #![feature(asm)] |
2840 | let mut x: u64 = 3; | 893 | let mut x: u64 = 3; |
2841 | unsafe { | 894 | unsafe { |
2842 | asm!("add {0}, {number}", inout(reg) x, number = const 5); | 895 | asm!("add {0}, {number}", inout(reg) x, number = const 5); |
@@ -2850,7 +903,7 @@ This is different from specifying an input and output separately in that it is g | |||
2850 | It is also possible to specify different variables for the input and output parts of an `inout` operand: | 903 | It is also possible to specify different variables for the input and output parts of an `inout` operand: |
2851 | 904 | ||
2852 | ```rust,allow_fail | 905 | ```rust,allow_fail |
2853 | # #![feature(asm)] | 906 | #![feature(asm)] |
2854 | let x: u64 = 3; | 907 | let x: u64 = 3; |
2855 | let y: u64; | 908 | let y: u64; |
2856 | unsafe { | 909 | unsafe { |
@@ -2872,7 +925,7 @@ There is also a `inlateout` variant of this specifier. | |||
2872 | Here is an example where `inlateout` *cannot* be used: | 925 | Here is an example where `inlateout` *cannot* be used: |
2873 | 926 | ||
2874 | ```rust,allow_fail | 927 | ```rust,allow_fail |
2875 | # #![feature(asm)] | 928 | #![feature(asm)] |
2876 | let mut a: u64 = 4; | 929 | let mut a: u64 = 4; |
2877 | let b: u64 = 4; | 930 | let b: u64 = 4; |
2878 | let c: u64 = 4; | 931 | let c: u64 = 4; |
@@ -2893,7 +946,7 @@ Here the compiler is free to allocate the same register for inputs `b` and `c` s | |||
2893 | However the following example can use `inlateout` since the output is only modified after all input registers have been read: | 946 | However the following example can use `inlateout` since the output is only modified after all input registers have been read: |
2894 | 947 | ||
2895 | ```rust,allow_fail | 948 | ```rust,allow_fail |
2896 | # #![feature(asm)] | 949 | #![feature(asm)] |
2897 | let mut a: u64 = 4; | 950 | let mut a: u64 = 4; |
2898 | let b: u64 = 4; | 951 | let b: u64 = 4; |
2899 | unsafe { | 952 | unsafe { |
@@ -2912,7 +965,7 @@ While `reg` is generally available on any architecture, these are highly archite | |||
2912 | among others can be addressed by their name. | 965 | among others can be addressed by their name. |
2913 | 966 | ||
2914 | ```rust,allow_fail,no_run | 967 | ```rust,allow_fail,no_run |
2915 | # #![feature(asm)] | 968 | #![feature(asm)] |
2916 | let cmd = 0xd1; | 969 | let cmd = 0xd1; |
2917 | unsafe { | 970 | unsafe { |
2918 | asm!("out 0x64, eax", in("eax") cmd); | 971 | asm!("out 0x64, eax", in("eax") cmd); |
@@ -2928,7 +981,7 @@ Note that unlike other operand types, explicit register operands cannot be used | |||
2928 | Consider this example which uses the x86 `mul` instruction: | 981 | Consider this example which uses the x86 `mul` instruction: |
2929 | 982 | ||
2930 | ```rust,allow_fail | 983 | ```rust,allow_fail |
2931 | # #![feature(asm)] | 984 | #![feature(asm)] |
2932 | fn mul(a: u64, b: u64) -> u128 { | 985 | fn mul(a: u64, b: u64) -> u128 { |
2933 | let lo: u64; | 986 | let lo: u64; |
2934 | let hi: u64; | 987 | let hi: u64; |
@@ -2964,7 +1017,7 @@ We need to tell the compiler about this since it may need to save and restore th | |||
2964 | around the inline assembly block. | 1017 | around the inline assembly block. |
2965 | 1018 | ||
2966 | ```rust,allow_fail | 1019 | ```rust,allow_fail |
2967 | # #![feature(asm)] | 1020 | #![feature(asm)] |
2968 | let ebx: u32; | 1021 | let ebx: u32; |
2969 | let ecx: u32; | 1022 | let ecx: u32; |
2970 | 1023 | ||
@@ -2994,7 +1047,7 @@ However we still need to tell the compiler that `eax` and `edx` have been modifi | |||
2994 | This can also be used with a general register class (e.g. `reg`) to obtain a scratch register for use inside the asm code: | 1047 | This can also be used with a general register class (e.g. `reg`) to obtain a scratch register for use inside the asm code: |
2995 | 1048 | ||
2996 | ```rust,allow_fail | 1049 | ```rust,allow_fail |
2997 | # #![feature(asm)] | 1050 | #![feature(asm)] |
2998 | // Multiply x by 6 using shifts and adds | 1051 | // Multiply x by 6 using shifts and adds |
2999 | let mut x: u64 = 4; | 1052 | let mut x: u64 = 4; |
3000 | unsafe { | 1053 | unsafe { |
@@ -3016,7 +1069,7 @@ A special operand type, `sym`, allows you to use the symbol name of a `fn` or `s | |||
3016 | This allows you to call a function or access a global variable without needing to keep its address in a register. | 1069 | This allows you to call a function or access a global variable without needing to keep its address in a register. |
3017 | 1070 | ||
3018 | ```rust,allow_fail | 1071 | ```rust,allow_fail |
3019 | # #![feature(asm)] | 1072 | #![feature(asm)] |
3020 | extern "C" fn foo(arg: i32) { | 1073 | extern "C" fn foo(arg: i32) { |
3021 | println!("arg = {}", arg); | 1074 | println!("arg = {}", arg); |
3022 | } | 1075 | } |
@@ -3028,13 +1081,19 @@ fn call_foo(arg: i32) { | |||
3028 | sym foo, | 1081 | sym foo, |
3029 | // 1st argument in rdi, which is caller-saved | 1082 | // 1st argument in rdi, which is caller-saved |
3030 | inout("rdi") arg => _, | 1083 | inout("rdi") arg => _, |
3031 | // All caller-saved registers must be marked as clobberred | 1084 | // All caller-saved registers must be marked as clobbered |
3032 | out("rax") _, out("rcx") _, out("rdx") _, out("rsi") _, | 1085 | out("rax") _, out("rcx") _, out("rdx") _, out("rsi") _, |
3033 | out("r8") _, out("r9") _, out("r10") _, out("r11") _, | 1086 | out("r8") _, out("r9") _, out("r10") _, out("r11") _, |
3034 | out("xmm0") _, out("xmm1") _, out("xmm2") _, out("xmm3") _, | 1087 | out("xmm0") _, out("xmm1") _, out("xmm2") _, out("xmm3") _, |
3035 | out("xmm4") _, out("xmm5") _, out("xmm6") _, out("xmm7") _, | 1088 | out("xmm4") _, out("xmm5") _, out("xmm6") _, out("xmm7") _, |
3036 | out("xmm8") _, out("xmm9") _, out("xmm10") _, out("xmm11") _, | 1089 | out("xmm8") _, out("xmm9") _, out("xmm10") _, out("xmm11") _, |
3037 | out("xmm12") _, out("xmm13") _, out("xmm14") _, out("xmm15") _, | 1090 | out("xmm12") _, out("xmm13") _, out("xmm14") _, out("xmm15") _, |
1091 | // Also mark AVX-512 registers as clobbered. This is accepted by the | ||
1092 | // compiler even if AVX-512 is not enabled on the current target. | ||
1093 | out("xmm16") _, out("xmm17") _, out("xmm18") _, out("xmm19") _, | ||
1094 | out("xmm20") _, out("xmm21") _, out("xmm22") _, out("xmm23") _, | ||
1095 | out("xmm24") _, out("xmm25") _, out("xmm26") _, out("xmm27") _, | ||
1096 | out("xmm28") _, out("xmm29") _, out("xmm30") _, out("xmm31") _, | ||
3038 | ) | 1097 | ) |
3039 | } | 1098 | } |
3040 | } | 1099 | } |
@@ -3052,7 +1111,7 @@ By default the compiler will always choose the name that refers to the full regi | |||
3052 | This default can be overriden by using modifiers on the template string operands, just like you would with format strings: | 1111 | This default can be overriden by using modifiers on the template string operands, just like you would with format strings: |
3053 | 1112 | ||
3054 | ```rust,allow_fail | 1113 | ```rust,allow_fail |
3055 | # #![feature(asm)] | 1114 | #![feature(asm)] |
3056 | let mut x: u16 = 0xab; | 1115 | let mut x: u16 = 0xab; |
3057 | 1116 | ||
3058 | unsafe { | 1117 | unsafe { |
@@ -3077,7 +1136,7 @@ For example, in x86/x86_64 and intel assembly syntax, you should wrap inputs/out | |||
3077 | to indicate they are memory operands: | 1136 | to indicate they are memory operands: |
3078 | 1137 | ||
3079 | ```rust,allow_fail | 1138 | ```rust,allow_fail |
3080 | # #![feature(asm, llvm_asm)] | 1139 | #![feature(asm, llvm_asm)] |
3081 | # fn load_fpu_control_word(control: u16) { | 1140 | # fn load_fpu_control_word(control: u16) { |
3082 | unsafe { | 1141 | unsafe { |
3083 | asm!("fldcw [{}]", in(reg) &control, options(nostack)); | 1142 | asm!("fldcw [{}]", in(reg) &control, options(nostack)); |
@@ -3088,6 +1147,43 @@ unsafe { | |||
3088 | # } | 1147 | # } |
3089 | ``` | 1148 | ``` |
3090 | 1149 | ||
1150 | ## Labels | ||
1151 | |||
1152 | The compiler is allowed to instantiate multiple copies an `asm!` block, for example when the function containing it is inlined in multiple places. As a consequence, you should only use GNU assembler [local labels] inside inline assembly code. Defining symbols in assembly code may lead to assembler and/or linker errors due to duplicate symbol definitions. | ||
1153 | |||
1154 | Moreover, due to [an llvm bug], you shouldn't use labels exclusively made of `0` and `1` digits, e.g. `0`, `11` or `101010`, as they may end up being interpreted as binary values. | ||
1155 | |||
1156 | ```rust,allow_fail | ||
1157 | #![feature(asm)] | ||
1158 | |||
1159 | let mut a = 0; | ||
1160 | unsafe { | ||
1161 | asm!( | ||
1162 | "mov {0}, 10", | ||
1163 | "2:", | ||
1164 | "sub {0}, 1", | ||
1165 | "cmp {0}, 3", | ||
1166 | "jle 2f", | ||
1167 | "jmp 2b", | ||
1168 | "2:", | ||
1169 | "add {0}, 2", | ||
1170 | out(reg) a | ||
1171 | ); | ||
1172 | } | ||
1173 | assert_eq!(a, 5); | ||
1174 | ``` | ||
1175 | |||
1176 | This will decrement the `{0}` register value from 10 to 3, then add 2 and store it in `a`. | ||
1177 | |||
1178 | This example show a few thing: | ||
1179 | |||
1180 | First that the same number can be used as a label multiple times in the same inline block. | ||
1181 | |||
1182 | Second, that when a numeric label is used as a reference (as an instruction operand, for example), the suffixes b (“backward”) or f (“forward”) should be added to the numeric label. It will then refer to the nearest label defined by this number in this direction. | ||
1183 | |||
1184 | [local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels | ||
1185 | [an llvm bug]: https://bugs.llvm.org/show_bug.cgi?id=36144 | ||
1186 | |||
3091 | ## Options | 1187 | ## Options |
3092 | 1188 | ||
3093 | By default, an inline assembly block is treated the same way as an external FFI function call with a custom calling convention: it may read/write memory, have observable side effects, etc. However in many cases, it is desirable to give the compiler more information about what the assembly code is actually doing so that it can optimize better. | 1189 | By default, an inline assembly block is treated the same way as an external FFI function call with a custom calling convention: it may read/write memory, have observable side effects, etc. However in many cases, it is desirable to give the compiler more information about what the assembly code is actually doing so that it can optimize better. |
@@ -3095,7 +1191,7 @@ By default, an inline assembly block is treated the same way as an external FFI | |||
3095 | Let's take our previous example of an `add` instruction: | 1191 | Let's take our previous example of an `add` instruction: |
3096 | 1192 | ||
3097 | ```rust,allow_fail | 1193 | ```rust,allow_fail |
3098 | # #![feature(asm)] | 1194 | #![feature(asm)] |
3099 | let mut a: u64 = 4; | 1195 | let mut a: u64 = 4; |
3100 | let b: u64 = 4; | 1196 | let b: u64 = 4; |
3101 | unsafe { | 1197 | unsafe { |
@@ -3138,7 +1234,7 @@ options := "options(" option *["," option] [","] ")" | |||
3138 | asm := "asm!(" format_string *("," format_string) *("," [ident "="] operand) ["," options] [","] ")" | 1234 | asm := "asm!(" format_string *("," format_string) *("," [ident "="] operand) ["," options] [","] ")" |
3139 | ``` | 1235 | ``` |
3140 | 1236 | ||
3141 | The macro will initially be supported only on ARM, AArch64, Hexagon, x86, x86-64 and RISC-V targets. Support for more targets may be added in the future. The compiler will emit an error if `asm!` is used on an unsupported target. | 1237 | The macro will initially be supported only on ARM, AArch64, Hexagon, PowerPC, x86, x86-64 and RISC-V targets. Support for more targets may be added in the future. The compiler will emit an error if `asm!` is used on an unsupported target. |
3142 | 1238 | ||
3143 | [format-syntax]: https://doc.rust-lang.org/std/fmt/#syntax | 1239 | [format-syntax]: https://doc.rust-lang.org/std/fmt/#syntax |
3144 | 1240 | ||
@@ -3187,7 +1283,7 @@ Several types of operands are supported: | |||
3187 | - Identical to `inout` except that the register allocator can reuse a register allocated to an `in` (this can happen if the compiler knows the `in` has the same initial value as the `inlateout`). | 1283 | - Identical to `inout` except that the register allocator can reuse a register allocated to an `in` (this can happen if the compiler knows the `in` has the same initial value as the `inlateout`). |
3188 | - You should only write to the register after all inputs are read, otherwise you may clobber an input. | 1284 | - You should only write to the register after all inputs are read, otherwise you may clobber an input. |
3189 | * `const <expr>` | 1285 | * `const <expr>` |
3190 | - `<expr>` must be an integer or floating-point constant expression. | 1286 | - `<expr>` must be an integer constant expression. |
3191 | - The value of the expression is formatted as a string and substituted directly into the asm template string. | 1287 | - The value of the expression is formatted as a string and substituted directly into the asm template string. |
3192 | * `sym <path>` | 1288 | * `sym <path>` |
3193 | - `<path>` must refer to a `fn` or `static`. | 1289 | - `<path>` must refer to a `fn` or `static`. |
@@ -3214,20 +1310,20 @@ Here is the list of currently supported register classes: | |||
3214 | 1310 | ||
3215 | | Architecture | Register class | Registers | LLVM constraint code | | 1311 | | Architecture | Register class | Registers | LLVM constraint code | |
3216 | | ------------ | -------------- | --------- | -------------------- | | 1312 | | ------------ | -------------- | --------- | -------------------- | |
3217 | | x86 | `reg` | `ax`, `bx`, `cx`, `dx`, `si`, `di`, `r[8-15]` (x86-64 only) | `r` | | 1313 | | x86 | `reg` | `ax`, `bx`, `cx`, `dx`, `si`, `di`, `bp`, `r[8-15]` (x86-64 only) | `r` | |
3218 | | x86 | `reg_abcd` | `ax`, `bx`, `cx`, `dx` | `Q` | | 1314 | | x86 | `reg_abcd` | `ax`, `bx`, `cx`, `dx` | `Q` | |
3219 | | x86-32 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `ah`, `bh`, `ch`, `dh` | `q` | | 1315 | | x86-32 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `ah`, `bh`, `ch`, `dh` | `q` | |
3220 | | x86-64 | `reg_byte` | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `r[8-15]b`, `ah`\*, `bh`\*, `ch`\*, `dh`\* | `q` | | 1316 | | x86-64 | `reg_byte`\* | `al`, `bl`, `cl`, `dl`, `sil`, `dil`, `bpl`, `r[8-15]b` | `q` | |
3221 | | x86 | `xmm_reg` | `xmm[0-7]` (x86) `xmm[0-15]` (x86-64) | `x` | | 1317 | | x86 | `xmm_reg` | `xmm[0-7]` (x86) `xmm[0-15]` (x86-64) | `x` | |
3222 | | x86 | `ymm_reg` | `ymm[0-7]` (x86) `ymm[0-15]` (x86-64) | `x` | | 1318 | | x86 | `ymm_reg` | `ymm[0-7]` (x86) `ymm[0-15]` (x86-64) | `x` | |
3223 | | x86 | `zmm_reg` | `zmm[0-7]` (x86) `zmm[0-31]` (x86-64) | `v` | | 1319 | | x86 | `zmm_reg` | `zmm[0-7]` (x86) `zmm[0-31]` (x86-64) | `v` | |
3224 | | x86 | `kreg` | `k[1-7]` | `Yk` | | 1320 | | x86 | `kreg` | `k[1-7]` | `Yk` | |
3225 | | AArch64 | `reg` | `x[0-28]`, `x30` | `r` | | 1321 | | AArch64 | `reg` | `x[0-30]` | `r` | |
3226 | | AArch64 | `vreg` | `v[0-31]` | `w` | | 1322 | | AArch64 | `vreg` | `v[0-31]` | `w` | |
3227 | | AArch64 | `vreg_low16` | `v[0-15]` | `x` | | 1323 | | AArch64 | `vreg_low16` | `v[0-15]` | `x` | |
3228 | | ARM | `reg` | `r[0-5]` `r7`\*, `r[8-10]`, `r11`\*, `r12`, `r14` | `r` | | 1324 | | ARM | `reg` | `r[0-12]`, `r14` | `r` | |
3229 | | ARM (Thumb) | `reg_thumb` | `r[0-r7]` | `l` | | 1325 | | ARM (Thumb) | `reg_thumb` | `r[0-r7]` | `l` | |
3230 | | ARM (ARM) | `reg_thumb` | `r[0-r10]`, `r12`, `r14` | `l` | | 1326 | | ARM (ARM) | `reg_thumb` | `r[0-r12]`, `r14` | `l` | |
3231 | | ARM | `sreg` | `s[0-31]` | `t` | | 1327 | | ARM | `sreg` | `s[0-31]` | `t` | |
3232 | | ARM | `sreg_low16` | `s[0-15]` | `x` | | 1328 | | ARM | `sreg_low16` | `s[0-15]` | `x` | |
3233 | | ARM | `dreg` | `d[0-31]` | `w` | | 1329 | | ARM | `dreg` | `d[0-31]` | `w` | |
@@ -3244,17 +1340,18 @@ Here is the list of currently supported register classes: | |||
3244 | | RISC-V | `reg` | `x1`, `x[5-7]`, `x[9-15]`, `x[16-31]` (non-RV32E) | `r` | | 1340 | | RISC-V | `reg` | `x1`, `x[5-7]`, `x[9-15]`, `x[16-31]` (non-RV32E) | `r` | |
3245 | | RISC-V | `freg` | `f[0-31]` | `f` | | 1341 | | RISC-V | `freg` | `f[0-31]` | `f` | |
3246 | | Hexagon | `reg` | `r[0-28]` | `r` | | 1342 | | Hexagon | `reg` | `r[0-28]` | `r` | |
1343 | | PowerPC | `reg` | `r[0-31]` | `r` | | ||
1344 | | PowerPC | `reg_nonzero` | | `r[1-31]` | `b` | | ||
1345 | | PowerPC | `freg` | `f[0-31]` | `f` | | ||
3247 | | wasm32 | `local` | None\* | `r` | | 1346 | | wasm32 | `local` | None\* | `r` | |
3248 | 1347 | ||
3249 | > **Note**: On x86 we treat `reg_byte` differently from `reg` because the compiler can allocate `al` and `ah` separately whereas `reg` reserves the whole register. | 1348 | > **Note**: On x86 we treat `reg_byte` differently from `reg` because the compiler can allocate `al` and `ah` separately whereas `reg` reserves the whole register. |
3250 | > | 1349 | > |
3251 | > Note #2: On x86-64 the high byte registers (e.g. `ah`) are only available when used as an explicit register. Specifying the `reg_byte` register class for an operand will always allocate a low byte register. | 1350 | > Note #2: On x86-64 the high byte registers (e.g. `ah`) are not available in the `reg_byte` register class. |
3252 | > | 1351 | > |
3253 | > Note #3: NVPTX doesn't have a fixed register set, so named registers are not supported. | 1352 | > Note #3: NVPTX doesn't have a fixed register set, so named registers are not supported. |
3254 | > | 1353 | > |
3255 | > Note #4: On ARM the frame pointer is either `r7` or `r11` depending on the platform. | 1354 | > Note #4: WebAssembly doesn't have registers, so named registers are not supported. |
3256 | > | ||
3257 | > Note #5: WebAssembly doesn't have registers, so named registers are not supported. | ||
3258 | 1355 | ||
3259 | Additional register classes may be added in the future based on demand (e.g. MMX, x87, etc). | 1356 | Additional register classes may be added in the future based on demand (e.g. MMX, x87, etc). |
3260 | 1357 | ||
@@ -3288,6 +1385,9 @@ Each register class has constraints on which value types they can be used with. | |||
3288 | | RISC-V | `freg` | `f` | `f32` | | 1385 | | RISC-V | `freg` | `f` | `f32` | |
3289 | | RISC-V | `freg` | `d` | `f64` | | 1386 | | RISC-V | `freg` | `d` | `f64` | |
3290 | | Hexagon | `reg` | None | `i8`, `i16`, `i32`, `f32` | | 1387 | | Hexagon | `reg` | None | `i8`, `i16`, `i32`, `f32` | |
1388 | | PowerPC | `reg` | None | `i8`, `i16`, `i32` | | ||
1389 | | PowerPC | `reg_nonzero` | None | `i8`, `i16`, `i32` | | ||
1390 | | PowerPC | `freg` | None | `f32`, `f64` | | ||
3291 | | wasm32 | `local` | None | `i8` `i16` `i32` `i64` `f32` `f64` | | 1391 | | wasm32 | `local` | None | `i8` `i16` `i32` `i64` `f32` `f64` | |
3292 | 1392 | ||
3293 | > **Note**: For the purposes of the above table pointers, function pointers and `isize`/`usize` are treated as the equivalent integer type (`i16`/`i32`/`i64` depending on the target). | 1393 | > **Note**: For the purposes of the above table pointers, function pointers and `isize`/`usize` are treated as the equivalent integer type (`i16`/`i32`/`i64` depending on the target). |
@@ -3356,13 +1456,14 @@ Some registers cannot be used for input or output operands: | |||
3356 | | All | `sp` | The stack pointer must be restored to its original value at the end of an asm code block. | | 1456 | | All | `sp` | The stack pointer must be restored to its original value at the end of an asm code block. | |
3357 | | All | `bp` (x86), `x29` (AArch64), `x8` (RISC-V), `fr` (Hexagon), `$fp` (MIPS) | The frame pointer cannot be used as an input or output. | | 1457 | | All | `bp` (x86), `x29` (AArch64), `x8` (RISC-V), `fr` (Hexagon), `$fp` (MIPS) | The frame pointer cannot be used as an input or output. | |
3358 | | ARM | `r7` or `r11` | On ARM the frame pointer can be either `r7` or `r11` depending on the target. The frame pointer cannot be used as an input or output. | | 1458 | | ARM | `r7` or `r11` | On ARM the frame pointer can be either `r7` or `r11` depending on the target. The frame pointer cannot be used as an input or output. | |
3359 | | ARM | `r6` | `r6` is used internally by LLVM as a base pointer and therefore cannot be used as an input or output. | | 1459 | | All | `si` (x86-32), `bx` (x86-64), `r6` (ARM), `x19` (AArch64), `r19` (Hexagon), `x9` (RISC-V) | This is used internally by LLVM as a "base pointer" for functions with complex stack frames. | |
3360 | | x86 | `k0` | This is a constant zero register which can't be modified. | | 1460 | | x86 | `k0` | This is a constant zero register which can't be modified. | |
3361 | | x86 | `ip` | This is the program counter, not a real register. | | 1461 | | x86 | `ip` | This is the program counter, not a real register. | |
3362 | | x86 | `mm[0-7]` | MMX registers are not currently supported (but may be in the future). | | 1462 | | x86 | `mm[0-7]` | MMX registers are not currently supported (but may be in the future). | |
3363 | | x86 | `st([0-7])` | x87 registers are not currently supported (but may be in the future). | | 1463 | | x86 | `st([0-7])` | x87 registers are not currently supported (but may be in the future). | |
3364 | | AArch64 | `xzr` | This is a constant zero register which can't be modified. | | 1464 | | AArch64 | `xzr` | This is a constant zero register which can't be modified. | |
3365 | | ARM | `pc` | This is the program counter, not a real register. | | 1465 | | ARM | `pc` | This is the program counter, not a real register. | |
1466 | | ARM | `r9` | This is a reserved register on some ARM targets. | | ||
3366 | | MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. | | 1467 | | MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. | |
3367 | | MIPS | `$1` or `$at` | Reserved for assembler. | | 1468 | | MIPS | `$1` or `$at` | Reserved for assembler. | |
3368 | | MIPS | `$26`/`$k0`, `$27`/`$k1` | OS-reserved registers. | | 1469 | | MIPS | `$26`/`$k0`, `$27`/`$k1` | OS-reserved registers. | |
@@ -3372,9 +1473,10 @@ Some registers cannot be used for input or output operands: | |||
3372 | | RISC-V | `gp`, `tp` | These registers are reserved and cannot be used as inputs or outputs. | | 1473 | | RISC-V | `gp`, `tp` | These registers are reserved and cannot be used as inputs or outputs. | |
3373 | | Hexagon | `lr` | This is the link register which cannot be used as an input or output. | | 1474 | | Hexagon | `lr` | This is the link register which cannot be used as an input or output. | |
3374 | 1475 | ||
3375 | In some cases LLVM will allocate a "reserved register" for `reg` operands even though this register cannot be explicitly specified. Assembly code making use of reserved registers should be careful since `reg` operands may alias with those registers. Reserved registers are: | 1476 | In some cases LLVM will allocate a "reserved register" for `reg` operands even though this register cannot be explicitly specified. Assembly code making use of reserved registers should be careful since `reg` operands may alias with those registers. Reserved registers are the frame pointer and base pointer |
3376 | - The frame pointer on all architectures. | 1477 | - The frame pointer and LLVM base pointer on all architectures. |
3377 | - `r6` on ARM. | 1478 | - `r9` on ARM. |
1479 | - `x18` on AArch64. | ||
3378 | 1480 | ||
3379 | ## Template modifiers | 1481 | ## Template modifiers |
3380 | 1482 | ||
@@ -3423,6 +1525,9 @@ The supported modifiers are a subset of LLVM's (and GCC's) [asm template argumen | |||
3423 | | RISC-V | `reg` | None | `x1` | None | | 1525 | | RISC-V | `reg` | None | `x1` | None | |
3424 | | RISC-V | `freg` | None | `f0` | None | | 1526 | | RISC-V | `freg` | None | `f0` | None | |
3425 | | Hexagon | `reg` | None | `r0` | None | | 1527 | | Hexagon | `reg` | None | `r0` | None | |
1528 | | PowerPC | `reg` | None | `0` | None | | ||
1529 | | PowerPC | `reg_nonzero` | None | `3` | `b` | | ||
1530 | | PowerPC | `freg` | None | `0` | None | | ||
3426 | 1531 | ||
3427 | > Notes: | 1532 | > Notes: |
3428 | > - on ARM `e` / `f`: this prints the low or high doubleword register name of a NEON quad (128-bit) register. | 1533 | > - on ARM `e` / `f`: this prints the low or high doubleword register name of a NEON quad (128-bit) register. |
@@ -3503,116 +1608,633 @@ The compiler performs some additional checks on options: | |||
3503 | - You are responsible for switching any target-specific state (e.g. thread-local storage, stack bounds). | 1608 | - You are responsible for switching any target-specific state (e.g. thread-local storage, stack bounds). |
3504 | - The set of memory locations that you may access is the intersection of those allowed by the `asm!` blocks you entered and exited. | 1609 | - The set of memory locations that you may access is the intersection of those allowed by the `asm!` blocks you entered and exited. |
3505 | - You cannot assume that an `asm!` block will appear exactly once in the output binary. The compiler is allowed to instantiate multiple copies of the `asm!` block, for example when the function containing it is inlined in multiple places. | 1610 | - You cannot assume that an `asm!` block will appear exactly once in the output binary. The compiler is allowed to instantiate multiple copies of the `asm!` block, for example when the function containing it is inlined in multiple places. |
3506 | - As a consequence, you should only use [local labels] inside inline assembly code. Defining symbols in assembly code may lead to assembler and/or linker errors due to duplicate symbol definitions. | ||
3507 | 1611 | ||
3508 | > **Note**: As a general rule, the flags covered by `preserves_flags` are those which are *not* preserved when performing a function call. | 1612 | > **Note**: As a general rule, the flags covered by `preserves_flags` are those which are *not* preserved when performing a function call. |
1613 | "##, | ||
1614 | }, | ||
1615 | Lint { | ||
1616 | label: "auto_traits", | ||
1617 | description: r##"# `auto_traits` | ||
3509 | 1618 | ||
3510 | [local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels | 1619 | The tracking issue for this feature is [#13231] |
1620 | |||
1621 | [#13231]: https://github.com/rust-lang/rust/issues/13231 | ||
1622 | |||
1623 | ---- | ||
1624 | |||
1625 | The `auto_traits` feature gate allows you to define auto traits. | ||
1626 | |||
1627 | Auto traits, like [`Send`] or [`Sync`] in the standard library, are marker traits | ||
1628 | that are automatically implemented for every type, unless the type, or a type it contains, | ||
1629 | has explicitly opted out via a negative impl. (Negative impls are separately controlled | ||
1630 | by the `negative_impls` feature.) | ||
1631 | |||
1632 | [`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html | ||
1633 | [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html | ||
1634 | |||
1635 | ```rust,ignore (partial-example) | ||
1636 | impl !Trait for Type {} | ||
1637 | ``` | ||
1638 | |||
1639 | Example: | ||
1640 | |||
1641 | ```rust | ||
1642 | #![feature(negative_impls)] | ||
1643 | #![feature(auto_traits)] | ||
1644 | |||
1645 | auto trait Valid {} | ||
1646 | |||
1647 | struct True; | ||
1648 | struct False; | ||
1649 | |||
1650 | impl !Valid for False {} | ||
1651 | |||
1652 | struct MaybeValid<T>(T); | ||
1653 | |||
1654 | fn must_be_valid<T: Valid>(_t: T) { } | ||
1655 | |||
1656 | fn main() { | ||
1657 | // works | ||
1658 | must_be_valid( MaybeValid(True) ); | ||
1659 | |||
1660 | // compiler error - trait bound not satisfied | ||
1661 | // must_be_valid( MaybeValid(False) ); | ||
1662 | } | ||
1663 | ``` | ||
1664 | |||
1665 | ## Automatic trait implementations | ||
1666 | |||
1667 | When a type is declared as an `auto trait`, we will automatically | ||
1668 | create impls for every struct/enum/union, unless an explicit impl is | ||
1669 | provided. These automatic impls contain a where clause for each field | ||
1670 | of the form `T: AutoTrait`, where `T` is the type of the field and | ||
1671 | `AutoTrait` is the auto trait in question. As an example, consider the | ||
1672 | struct `List` and the auto trait `Send`: | ||
1673 | |||
1674 | ```rust | ||
1675 | struct List<T> { | ||
1676 | data: T, | ||
1677 | next: Option<Box<List<T>>>, | ||
1678 | } | ||
1679 | ``` | ||
1680 | |||
1681 | Presuming that there is no explicit impl of `Send` for `List`, the | ||
1682 | compiler will supply an automatic impl of the form: | ||
1683 | |||
1684 | ```rust | ||
1685 | struct List<T> { | ||
1686 | data: T, | ||
1687 | next: Option<Box<List<T>>>, | ||
1688 | } | ||
1689 | |||
1690 | unsafe impl<T> Send for List<T> | ||
1691 | where | ||
1692 | T: Send, // from the field `data` | ||
1693 | Option<Box<List<T>>>: Send, // from the field `next` | ||
1694 | { } | ||
1695 | ``` | ||
1696 | |||
1697 | Explicit impls may be either positive or negative. They take the form: | ||
1698 | |||
1699 | ```rust,ignore (partial-example) | ||
1700 | impl<...> AutoTrait for StructName<..> { } | ||
1701 | impl<...> !AutoTrait for StructName<..> { } | ||
1702 | ``` | ||
1703 | |||
1704 | ## Coinduction: Auto traits permit cyclic matching | ||
1705 | |||
1706 | Unlike ordinary trait matching, auto traits are **coinductive**. This | ||
1707 | means, in short, that cycles which occur in trait matching are | ||
1708 | considered ok. As an example, consider the recursive struct `List` | ||
1709 | introduced in the previous section. In attempting to determine whether | ||
1710 | `List: Send`, we would wind up in a cycle: to apply the impl, we must | ||
1711 | show that `Option<Box<List>>: Send`, which will in turn require | ||
1712 | `Box<List>: Send` and then finally `List: Send` again. Under ordinary | ||
1713 | trait matching, this cycle would be an error, but for an auto trait it | ||
1714 | is considered a successful match. | ||
1715 | |||
1716 | ## Items | ||
1717 | |||
1718 | Auto traits cannot have any trait items, such as methods or associated types. This ensures that we can generate default implementations. | ||
1719 | |||
1720 | ## Supertraits | ||
1721 | |||
1722 | Auto traits cannot have supertraits. This is for soundness reasons, as the interaction of coinduction with implied bounds is difficult to reconcile. | ||
3511 | "##, | 1723 | "##, |
3512 | }, | 1724 | }, |
3513 | LintCompletion { | 1725 | Lint { |
3514 | label: "flt2dec", | 1726 | label: "box_patterns", |
3515 | description: r##"# `flt2dec` | 1727 | description: r##"# `box_patterns` |
1728 | |||
1729 | The tracking issue for this feature is: [#29641] | ||
1730 | |||
1731 | [#29641]: https://github.com/rust-lang/rust/issues/29641 | ||
1732 | |||
1733 | See also [`box_syntax`](box-syntax.md) | ||
1734 | |||
1735 | ------------------------ | ||
1736 | |||
1737 | Box patterns let you match on `Box<T>`s: | ||
1738 | |||
1739 | |||
1740 | ```rust | ||
1741 | #![feature(box_patterns)] | ||
1742 | |||
1743 | fn main() { | ||
1744 | let b = Some(Box::new(5)); | ||
1745 | match b { | ||
1746 | Some(box n) if n < 0 => { | ||
1747 | println!("Box contains negative number {}", n); | ||
1748 | }, | ||
1749 | Some(box n) if n >= 0 => { | ||
1750 | println!("Box contains non-negative number {}", n); | ||
1751 | }, | ||
1752 | None => { | ||
1753 | println!("No box"); | ||
1754 | }, | ||
1755 | _ => unreachable!() | ||
1756 | } | ||
1757 | } | ||
1758 | ``` | ||
1759 | "##, | ||
1760 | }, | ||
1761 | Lint { | ||
1762 | label: "box_syntax", | ||
1763 | description: r##"# `box_syntax` | ||
1764 | |||
1765 | The tracking issue for this feature is: [#49733] | ||
1766 | |||
1767 | [#49733]: https://github.com/rust-lang/rust/issues/49733 | ||
1768 | |||
1769 | See also [`box_patterns`](box-patterns.md) | ||
1770 | |||
1771 | ------------------------ | ||
1772 | |||
1773 | Currently the only stable way to create a `Box` is via the `Box::new` method. | ||
1774 | Also it is not possible in stable Rust to destructure a `Box` in a match | ||
1775 | pattern. The unstable `box` keyword can be used to create a `Box`. An example | ||
1776 | usage would be: | ||
1777 | |||
1778 | ```rust | ||
1779 | #![feature(box_syntax)] | ||
1780 | |||
1781 | fn main() { | ||
1782 | let b = box 5; | ||
1783 | } | ||
1784 | ``` | ||
1785 | "##, | ||
1786 | }, | ||
1787 | Lint { | ||
1788 | label: "c_unwind", | ||
1789 | description: r##"# `c_unwind` | ||
1790 | |||
1791 | The tracking issue for this feature is: [#74990] | ||
1792 | |||
1793 | [#74990]: https://github.com/rust-lang/rust/issues/74990 | ||
1794 | |||
1795 | ------------------------ | ||
1796 | |||
1797 | Introduces four new ABI strings: "C-unwind", "stdcall-unwind", | ||
1798 | "thiscall-unwind", and "system-unwind". These enable unwinding from other | ||
1799 | languages (such as C++) into Rust frames and from Rust into other languages. | ||
1800 | |||
1801 | See [RFC 2945] for more information. | ||
1802 | |||
1803 | [RFC 2945]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md | ||
1804 | "##, | ||
1805 | }, | ||
1806 | Lint { | ||
1807 | label: "c_variadic", | ||
1808 | description: r##"# `c_variadic` | ||
1809 | |||
1810 | The tracking issue for this feature is: [#44930] | ||
1811 | |||
1812 | [#44930]: https://github.com/rust-lang/rust/issues/44930 | ||
1813 | |||
1814 | ------------------------ | ||
1815 | |||
1816 | The `c_variadic` language feature enables C-variadic functions to be | ||
1817 | defined in Rust. The may be called both from within Rust and via FFI. | ||
1818 | |||
1819 | ## Examples | ||
1820 | |||
1821 | ```rust | ||
1822 | #![feature(c_variadic)] | ||
1823 | |||
1824 | pub unsafe extern "C" fn add(n: usize, mut args: ...) -> usize { | ||
1825 | let mut sum = 0; | ||
1826 | for _ in 0..n { | ||
1827 | sum += args.arg::<usize>(); | ||
1828 | } | ||
1829 | sum | ||
1830 | } | ||
1831 | ``` | ||
1832 | "##, | ||
1833 | }, | ||
1834 | Lint { | ||
1835 | label: "c_variadic", | ||
1836 | description: r##"# `c_variadic` | ||
1837 | |||
1838 | The tracking issue for this feature is: [#44930] | ||
1839 | |||
1840 | [#44930]: https://github.com/rust-lang/rust/issues/44930 | ||
1841 | |||
1842 | ------------------------ | ||
1843 | |||
1844 | The `c_variadic` library feature exposes the `VaList` structure, | ||
1845 | Rust's analogue of C's `va_list` type. | ||
1846 | |||
1847 | ## Examples | ||
1848 | |||
1849 | ```rust | ||
1850 | #![feature(c_variadic)] | ||
1851 | |||
1852 | use std::ffi::VaList; | ||
1853 | |||
1854 | pub unsafe extern "C" fn vadd(n: usize, mut args: VaList) -> usize { | ||
1855 | let mut sum = 0; | ||
1856 | for _ in 0..n { | ||
1857 | sum += args.arg::<usize>(); | ||
1858 | } | ||
1859 | sum | ||
1860 | } | ||
1861 | ``` | ||
1862 | "##, | ||
1863 | }, | ||
1864 | Lint { | ||
1865 | label: "c_void_variant", | ||
1866 | description: r##"# `c_void_variant` | ||
3516 | 1867 | ||
3517 | This feature is internal to the Rust compiler and is not intended for general use. | 1868 | This feature is internal to the Rust compiler and is not intended for general use. |
3518 | 1869 | ||
3519 | ------------------------ | 1870 | ------------------------ |
3520 | "##, | 1871 | "##, |
3521 | }, | 1872 | }, |
3522 | LintCompletion { | 1873 | Lint { |
3523 | label: "global_asm", | 1874 | label: "cfg_panic", |
3524 | description: r##"# `global_asm` | 1875 | description: r##"# `cfg_panic` |
3525 | 1876 | ||
3526 | The tracking issue for this feature is: [#35119] | 1877 | The tracking issue for this feature is: [#77443] |
3527 | 1878 | ||
3528 | [#35119]: https://github.com/rust-lang/rust/issues/35119 | 1879 | [#77443]: https://github.com/rust-lang/rust/issues/77443 |
3529 | 1880 | ||
3530 | ------------------------ | 1881 | ------------------------ |
3531 | 1882 | ||
3532 | The `global_asm!` macro allows the programmer to write arbitrary | 1883 | The `cfg_panic` feature makes it possible to execute different code |
3533 | assembly outside the scope of a function body, passing it through | 1884 | depending on the panic strategy. |
3534 | `rustc` and `llvm` to the assembler. The macro is a no-frills | ||
3535 | interface to LLVM's concept of [module-level inline assembly]. That is, | ||
3536 | all caveats applicable to LLVM's module-level inline assembly apply | ||
3537 | to `global_asm!`. | ||
3538 | 1885 | ||
3539 | [module-level inline assembly]: http://llvm.org/docs/LangRef.html#module-level-inline-assembly | 1886 | Possible values at the moment are `"unwind"` or `"abort"`, although |
1887 | it is possible that new panic strategies may be added to Rust in the | ||
1888 | future. | ||
3540 | 1889 | ||
3541 | `global_asm!` fills a role not currently satisfied by either `asm!` | 1890 | ## Examples |
3542 | or `#[naked]` functions. The programmer has _all_ features of the | ||
3543 | assembler at their disposal. The linker will expect to resolve any | ||
3544 | symbols defined in the inline assembly, modulo any symbols marked as | ||
3545 | external. It also means syntax for directives and assembly follow the | ||
3546 | conventions of the assembler in your toolchain. | ||
3547 | 1891 | ||
3548 | A simple usage looks like this: | 1892 | ```rust |
1893 | #![feature(cfg_panic)] | ||
3549 | 1894 | ||
3550 | ```rust,ignore (requires-external-file) | 1895 | #[cfg(panic = "unwind")] |
3551 | #![feature(global_asm)] | 1896 | fn a() { |
3552 | # // you also need relevant target_arch cfgs | 1897 | // ... |
3553 | global_asm!(include_str!("something_neato.s")); | 1898 | } |
1899 | |||
1900 | #[cfg(not(panic = "unwind"))] | ||
1901 | fn a() { | ||
1902 | // ... | ||
1903 | } | ||
1904 | |||
1905 | fn b() { | ||
1906 | if cfg!(panic = "abort") { | ||
1907 | // ... | ||
1908 | } else { | ||
1909 | // ... | ||
1910 | } | ||
1911 | } | ||
3554 | ``` | 1912 | ``` |
1913 | "##, | ||
1914 | }, | ||
1915 | Lint { | ||
1916 | label: "cfg_sanitize", | ||
1917 | description: r##"# `cfg_sanitize` | ||
3555 | 1918 | ||
3556 | And a more complicated usage looks like this: | 1919 | The tracking issue for this feature is: [#39699] |
3557 | 1920 | ||
3558 | ```rust,no_run | 1921 | [#39699]: https://github.com/rust-lang/rust/issues/39699 |
3559 | #![feature(global_asm)] | ||
3560 | # #[cfg(any(target_arch="x86", target_arch="x86_64"))] | ||
3561 | # mod x86 { | ||
3562 | 1922 | ||
3563 | pub mod sally { | 1923 | ------------------------ |
3564 | global_asm!(r#" | ||
3565 | .global foo | ||
3566 | foo: | ||
3567 | jmp baz | ||
3568 | "#); | ||
3569 | 1924 | ||
3570 | #[no_mangle] | 1925 | The `cfg_sanitize` feature makes it possible to execute different code |
3571 | pub unsafe extern "C" fn baz() {} | 1926 | depending on whether a particular sanitizer is enabled or not. |
1927 | |||
1928 | ## Examples | ||
1929 | |||
1930 | ```rust | ||
1931 | #![feature(cfg_sanitize)] | ||
1932 | |||
1933 | #[cfg(sanitize = "thread")] | ||
1934 | fn a() { | ||
1935 | // ... | ||
3572 | } | 1936 | } |
3573 | 1937 | ||
3574 | // the symbols `foo` and `bar` are global, no matter where | 1938 | #[cfg(not(sanitize = "thread"))] |
3575 | // `global_asm!` was used. | 1939 | fn a() { |
3576 | extern "C" { | 1940 | // ... |
3577 | fn foo(); | ||
3578 | fn bar(); | ||
3579 | } | 1941 | } |
3580 | 1942 | ||
3581 | pub mod harry { | 1943 | fn b() { |
3582 | global_asm!(r#" | 1944 | if cfg!(sanitize = "leak") { |
3583 | .global bar | 1945 | // ... |
3584 | bar: | 1946 | } else { |
3585 | jmp quux | 1947 | // ... |
3586 | "#); | 1948 | } |
1949 | } | ||
1950 | ``` | ||
1951 | "##, | ||
1952 | }, | ||
1953 | Lint { | ||
1954 | label: "cfg_version", | ||
1955 | description: r##"# `cfg_version` | ||
3587 | 1956 | ||
3588 | #[no_mangle] | 1957 | The tracking issue for this feature is: [#64796] |
3589 | pub unsafe extern "C" fn quux() {} | 1958 | |
1959 | [#64796]: https://github.com/rust-lang/rust/issues/64796 | ||
1960 | |||
1961 | ------------------------ | ||
1962 | |||
1963 | The `cfg_version` feature makes it possible to execute different code | ||
1964 | depending on the compiler version. It will return true if the compiler | ||
1965 | version is greater than or equal to the specified version. | ||
1966 | |||
1967 | ## Examples | ||
1968 | |||
1969 | ```rust | ||
1970 | #![feature(cfg_version)] | ||
1971 | |||
1972 | #[cfg(version("1.42"))] // 1.42 and above | ||
1973 | fn a() { | ||
1974 | // ... | ||
1975 | } | ||
1976 | |||
1977 | #[cfg(not(version("1.42")))] // 1.41 and below | ||
1978 | fn a() { | ||
1979 | // ... | ||
1980 | } | ||
1981 | |||
1982 | fn b() { | ||
1983 | if cfg!(version("1.42")) { | ||
1984 | // ... | ||
1985 | } else { | ||
1986 | // ... | ||
1987 | } | ||
3590 | } | 1988 | } |
3591 | # } | ||
3592 | ``` | 1989 | ``` |
1990 | "##, | ||
1991 | }, | ||
1992 | Lint { | ||
1993 | label: "char_error_internals", | ||
1994 | description: r##"# `char_error_internals` | ||
3593 | 1995 | ||
3594 | You may use `global_asm!` multiple times, anywhere in your crate, in | 1996 | This feature is internal to the Rust compiler and is not intended for general use. |
3595 | whatever way suits you. The effect is as if you concatenated all | ||
3596 | usages and placed the larger, single usage in the crate root. | ||
3597 | 1997 | ||
3598 | ------------------------ | 1998 | ------------------------ |
1999 | "##, | ||
2000 | }, | ||
2001 | Lint { | ||
2002 | label: "cmse_nonsecure_entry", | ||
2003 | description: r##"# `cmse_nonsecure_entry` | ||
3599 | 2004 | ||
3600 | If you don't need quite as much power and flexibility as | 2005 | The tracking issue for this feature is: [#75835] |
3601 | `global_asm!` provides, and you don't mind restricting your inline | 2006 | |
3602 | assembly to `fn` bodies only, you might try the | 2007 | [#75835]: https://github.com/rust-lang/rust/issues/75835 |
3603 | [asm](asm.md) feature instead. | 2008 | |
2009 | ------------------------ | ||
2010 | |||
2011 | The [TrustZone-M | ||
2012 | feature](https://developer.arm.com/documentation/100690/latest/) is available | ||
2013 | for targets with the Armv8-M architecture profile (`thumbv8m` in their target | ||
2014 | name). | ||
2015 | LLVM, the Rust compiler and the linker are providing | ||
2016 | [support](https://developer.arm.com/documentation/ecm0359818/latest/) for the | ||
2017 | TrustZone-M feature. | ||
2018 | |||
2019 | One of the things provided, with this unstable feature, is the | ||
2020 | `cmse_nonsecure_entry` attribute. This attribute marks a Secure function as an | ||
2021 | entry function (see [section | ||
2022 | 5.4](https://developer.arm.com/documentation/ecm0359818/latest/) for details). | ||
2023 | With this attribute, the compiler will do the following: | ||
2024 | * add a special symbol on the function which is the `__acle_se_` prefix and the | ||
2025 | standard function name | ||
2026 | * constrain the number of parameters to avoid using the Non-Secure stack | ||
2027 | * before returning from the function, clear registers that might contain Secure | ||
2028 | information | ||
2029 | * use the `BXNS` instruction to return | ||
2030 | |||
2031 | Because the stack can not be used to pass parameters, there will be compilation | ||
2032 | errors if: | ||
2033 | * the total size of all parameters is too big (for example more than four 32 | ||
2034 | bits integers) | ||
2035 | * the entry function is not using a C ABI | ||
2036 | |||
2037 | The special symbol `__acle_se_` will be used by the linker to generate a secure | ||
2038 | gateway veneer. | ||
2039 | |||
2040 | <!-- NOTE(ignore) this example is specific to thumbv8m targets --> | ||
2041 | |||
2042 | ``` rust,ignore | ||
2043 | #![feature(cmse_nonsecure_entry)] | ||
2044 | |||
2045 | #[no_mangle] | ||
2046 | #[cmse_nonsecure_entry] | ||
2047 | pub extern "C" fn entry_function(input: u32) -> u32 { | ||
2048 | input + 6 | ||
2049 | } | ||
2050 | ``` | ||
2051 | |||
2052 | ``` text | ||
2053 | $ rustc --emit obj --crate-type lib --target thumbv8m.main-none-eabi function.rs | ||
2054 | $ arm-none-eabi-objdump -D function.o | ||
2055 | |||
2056 | 00000000 <entry_function>: | ||
2057 | 0: b580 push {r7, lr} | ||
2058 | 2: 466f mov r7, sp | ||
2059 | 4: b082 sub sp, #8 | ||
2060 | 6: 9001 str r0, [sp, #4] | ||
2061 | 8: 1d81 adds r1, r0, #6 | ||
2062 | a: 460a mov r2, r1 | ||
2063 | c: 4281 cmp r1, r0 | ||
2064 | e: 9200 str r2, [sp, #0] | ||
2065 | 10: d30b bcc.n 2a <entry_function+0x2a> | ||
2066 | 12: e7ff b.n 14 <entry_function+0x14> | ||
2067 | 14: 9800 ldr r0, [sp, #0] | ||
2068 | 16: b002 add sp, #8 | ||
2069 | 18: e8bd 4080 ldmia.w sp!, {r7, lr} | ||
2070 | 1c: 4671 mov r1, lr | ||
2071 | 1e: 4672 mov r2, lr | ||
2072 | 20: 4673 mov r3, lr | ||
2073 | 22: 46f4 mov ip, lr | ||
2074 | 24: f38e 8800 msr CPSR_f, lr | ||
2075 | 28: 4774 bxns lr | ||
2076 | 2a: f240 0000 movw r0, #0 | ||
2077 | 2e: f2c0 0000 movt r0, #0 | ||
2078 | 32: f240 0200 movw r2, #0 | ||
2079 | 36: f2c0 0200 movt r2, #0 | ||
2080 | 3a: 211c movs r1, #28 | ||
2081 | 3c: f7ff fffe bl 0 <_ZN4core9panicking5panic17h5c028258ca2fb3f5E> | ||
2082 | 40: defe udf #254 ; 0xfe | ||
2083 | ``` | ||
3604 | "##, | 2084 | "##, |
3605 | }, | 2085 | }, |
3606 | LintCompletion { | 2086 | Lint { |
3607 | label: "derive_eq", | 2087 | label: "compiler_builtins", |
3608 | description: r##"# `derive_eq` | 2088 | description: r##"# `compiler_builtins` |
2089 | |||
2090 | This feature is internal to the Rust compiler and is not intended for general use. | ||
2091 | |||
2092 | ------------------------ | ||
2093 | "##, | ||
2094 | }, | ||
2095 | Lint { | ||
2096 | label: "concat_idents", | ||
2097 | description: r##"# `concat_idents` | ||
2098 | |||
2099 | The tracking issue for this feature is: [#29599] | ||
2100 | |||
2101 | [#29599]: https://github.com/rust-lang/rust/issues/29599 | ||
2102 | |||
2103 | ------------------------ | ||
2104 | |||
2105 | The `concat_idents` feature adds a macro for concatenating multiple identifiers | ||
2106 | into one identifier. | ||
2107 | |||
2108 | ## Examples | ||
2109 | |||
2110 | ```rust | ||
2111 | #![feature(concat_idents)] | ||
2112 | |||
2113 | fn main() { | ||
2114 | fn foobar() -> u32 { 23 } | ||
2115 | let f = concat_idents!(foo, bar); | ||
2116 | assert_eq!(f(), 23); | ||
2117 | } | ||
2118 | ``` | ||
2119 | "##, | ||
2120 | }, | ||
2121 | Lint { | ||
2122 | label: "const_eval_limit", | ||
2123 | description: r##"# `const_eval_limit` | ||
2124 | |||
2125 | The tracking issue for this feature is: [#67217] | ||
2126 | |||
2127 | [#67217]: https://github.com/rust-lang/rust/issues/67217 | ||
2128 | |||
2129 | The `const_eval_limit` allows someone to limit the evaluation steps the CTFE undertakes to evaluate a `const fn`. | ||
2130 | "##, | ||
2131 | }, | ||
2132 | Lint { | ||
2133 | label: "core_intrinsics", | ||
2134 | description: r##"# `core_intrinsics` | ||
2135 | |||
2136 | This feature is internal to the Rust compiler and is not intended for general use. | ||
2137 | |||
2138 | ------------------------ | ||
2139 | "##, | ||
2140 | }, | ||
2141 | Lint { | ||
2142 | label: "core_panic", | ||
2143 | description: r##"# `core_panic` | ||
2144 | |||
2145 | This feature is internal to the Rust compiler and is not intended for general use. | ||
2146 | |||
2147 | ------------------------ | ||
2148 | "##, | ||
2149 | }, | ||
2150 | Lint { | ||
2151 | label: "core_private_bignum", | ||
2152 | description: r##"# `core_private_bignum` | ||
3609 | 2153 | ||
3610 | This feature is internal to the Rust compiler and is not intended for general use. | 2154 | This feature is internal to the Rust compiler and is not intended for general use. |
3611 | 2155 | ||
3612 | ------------------------ | 2156 | ------------------------ |
3613 | "##, | 2157 | "##, |
3614 | }, | 2158 | }, |
3615 | LintCompletion { | 2159 | Lint { |
2160 | label: "core_private_diy_float", | ||
2161 | description: r##"# `core_private_diy_float` | ||
2162 | |||
2163 | This feature is internal to the Rust compiler and is not intended for general use. | ||
2164 | |||
2165 | ------------------------ | ||
2166 | "##, | ||
2167 | }, | ||
2168 | Lint { | ||
2169 | label: "crate_visibility_modifier", | ||
2170 | description: r##"# `crate_visibility_modifier` | ||
2171 | |||
2172 | The tracking issue for this feature is: [#53120] | ||
2173 | |||
2174 | [#53120]: https://github.com/rust-lang/rust/issues/53120 | ||
2175 | |||
2176 | ----- | ||
2177 | |||
2178 | The `crate_visibility_modifier` feature allows the `crate` keyword to be used | ||
2179 | as a visibility modifier synonymous to `pub(crate)`, indicating that a type | ||
2180 | (function, _&c._) is to be visible to the entire enclosing crate, but not to | ||
2181 | other crates. | ||
2182 | |||
2183 | ```rust | ||
2184 | #![feature(crate_visibility_modifier)] | ||
2185 | |||
2186 | crate struct Foo { | ||
2187 | bar: usize, | ||
2188 | } | ||
2189 | ``` | ||
2190 | "##, | ||
2191 | }, | ||
2192 | Lint { | ||
2193 | label: "custom_test_frameworks", | ||
2194 | description: r##"# `custom_test_frameworks` | ||
2195 | |||
2196 | The tracking issue for this feature is: [#50297] | ||
2197 | |||
2198 | [#50297]: https://github.com/rust-lang/rust/issues/50297 | ||
2199 | |||
2200 | ------------------------ | ||
2201 | |||
2202 | The `custom_test_frameworks` feature allows the use of `#[test_case]` and `#![test_runner]`. | ||
2203 | Any function, const, or static can be annotated with `#[test_case]` causing it to be aggregated (like `#[test]`) | ||
2204 | and be passed to the test runner determined by the `#![test_runner]` crate attribute. | ||
2205 | |||
2206 | ```rust | ||
2207 | #![feature(custom_test_frameworks)] | ||
2208 | #![test_runner(my_runner)] | ||
2209 | |||
2210 | fn my_runner(tests: &[&i32]) { | ||
2211 | for t in tests { | ||
2212 | if **t == 0 { | ||
2213 | println!("PASSED"); | ||
2214 | } else { | ||
2215 | println!("FAILED"); | ||
2216 | } | ||
2217 | } | ||
2218 | } | ||
2219 | |||
2220 | #[test_case] | ||
2221 | const WILL_PASS: i32 = 0; | ||
2222 | |||
2223 | #[test_case] | ||
2224 | const WILL_FAIL: i32 = 4; | ||
2225 | ``` | ||
2226 | "##, | ||
2227 | }, | ||
2228 | Lint { | ||
2229 | label: "dec2flt", | ||
2230 | description: r##"# `dec2flt` | ||
2231 | |||
2232 | This feature is internal to the Rust compiler and is not intended for general use. | ||
2233 | |||
2234 | ------------------------ | ||
2235 | "##, | ||
2236 | }, | ||
2237 | Lint { | ||
3616 | label: "default_free_fn", | 2238 | label: "default_free_fn", |
3617 | description: r##"# `default_free_fn` | 2239 | description: r##"# `default_free_fn` |
3618 | 2240 | ||
@@ -3663,75 +2285,331 @@ fn main() { | |||
3663 | ``` | 2285 | ``` |
3664 | "##, | 2286 | "##, |
3665 | }, | 2287 | }, |
3666 | LintCompletion { | 2288 | Lint { |
3667 | label: "char_error_internals", | 2289 | label: "derive_clone_copy", |
3668 | description: r##"# `char_error_internals` | 2290 | description: r##"# `derive_clone_copy` |
3669 | 2291 | ||
3670 | This feature is internal to the Rust compiler and is not intended for general use. | 2292 | This feature is internal to the Rust compiler and is not intended for general use. |
3671 | 2293 | ||
3672 | ------------------------ | 2294 | ------------------------ |
3673 | "##, | 2295 | "##, |
3674 | }, | 2296 | }, |
3675 | LintCompletion { | 2297 | Lint { |
3676 | label: "libstd_sys_internals", | 2298 | label: "derive_eq", |
3677 | description: r##"# `libstd_sys_internals` | 2299 | description: r##"# `derive_eq` |
3678 | 2300 | ||
3679 | This feature is internal to the Rust compiler and is not intended for general use. | 2301 | This feature is internal to the Rust compiler and is not intended for general use. |
3680 | 2302 | ||
3681 | ------------------------ | 2303 | ------------------------ |
3682 | "##, | 2304 | "##, |
3683 | }, | 2305 | }, |
3684 | LintCompletion { | 2306 | Lint { |
3685 | label: "is_sorted", | 2307 | label: "doc_cfg", |
3686 | description: r##"# `is_sorted` | 2308 | description: r##"# `doc_cfg` |
3687 | 2309 | ||
3688 | The tracking issue for this feature is: [#53485] | 2310 | The tracking issue for this feature is: [#43781] |
3689 | 2311 | ||
3690 | [#53485]: https://github.com/rust-lang/rust/issues/53485 | 2312 | ------ |
3691 | 2313 | ||
3692 | ------------------------ | 2314 | The `doc_cfg` feature allows an API be documented as only available in some specific platforms. |
2315 | This attribute has two effects: | ||
3693 | 2316 | ||
3694 | Add the methods `is_sorted`, `is_sorted_by` and `is_sorted_by_key` to `[T]`; | 2317 | 1. In the annotated item's documentation, there will be a message saying "This is supported on |
3695 | add the methods `is_sorted`, `is_sorted_by` and `is_sorted_by_key` to | 2318 | (platform) only". |
3696 | `Iterator`. | 2319 | |
2320 | 2. The item's doc-tests will only run on the specific platform. | ||
2321 | |||
2322 | In addition to allowing the use of the `#[doc(cfg)]` attribute, this feature enables the use of a | ||
2323 | special conditional compilation flag, `#[cfg(doc)]`, set whenever building documentation on your | ||
2324 | crate. | ||
2325 | |||
2326 | This feature was introduced as part of PR [#43348] to allow the platform-specific parts of the | ||
2327 | standard library be documented. | ||
2328 | |||
2329 | ```rust | ||
2330 | #![feature(doc_cfg)] | ||
2331 | |||
2332 | #[cfg(any(windows, doc))] | ||
2333 | #[doc(cfg(windows))] | ||
2334 | /// The application's icon in the notification area (a.k.a. system tray). | ||
2335 | /// | ||
2336 | /// # Examples | ||
2337 | /// | ||
2338 | /// ```no_run | ||
2339 | /// extern crate my_awesome_ui_library; | ||
2340 | /// use my_awesome_ui_library::current_app; | ||
2341 | /// use my_awesome_ui_library::windows::notification; | ||
2342 | /// | ||
2343 | /// let icon = current_app().get::<notification::Icon>(); | ||
2344 | /// icon.show(); | ||
2345 | /// icon.show_message("Hello"); | ||
2346 | /// ``` | ||
2347 | pub struct Icon { | ||
2348 | // ... | ||
2349 | } | ||
2350 | ``` | ||
2351 | |||
2352 | [#43781]: https://github.com/rust-lang/rust/issues/43781 | ||
2353 | [#43348]: https://github.com/rust-lang/rust/issues/43348 | ||
3697 | "##, | 2354 | "##, |
3698 | }, | 2355 | }, |
3699 | LintCompletion { | 2356 | Lint { |
3700 | label: "c_void_variant", | 2357 | label: "doc_masked", |
3701 | description: r##"# `c_void_variant` | 2358 | description: r##"# `doc_masked` |
2359 | |||
2360 | The tracking issue for this feature is: [#44027] | ||
2361 | |||
2362 | ----- | ||
2363 | |||
2364 | The `doc_masked` feature allows a crate to exclude types from a given crate from appearing in lists | ||
2365 | of trait implementations. The specifics of the feature are as follows: | ||
2366 | |||
2367 | 1. When rustdoc encounters an `extern crate` statement annotated with a `#[doc(masked)]` attribute, | ||
2368 | it marks the crate as being masked. | ||
2369 | |||
2370 | 2. When listing traits a given type implements, rustdoc ensures that traits from masked crates are | ||
2371 | not emitted into the documentation. | ||
2372 | |||
2373 | 3. When listing types that implement a given trait, rustdoc ensures that types from masked crates | ||
2374 | are not emitted into the documentation. | ||
2375 | |||
2376 | This feature was introduced in PR [#44026] to ensure that compiler-internal and | ||
2377 | implementation-specific types and traits were not included in the standard library's documentation. | ||
2378 | Such types would introduce broken links into the documentation. | ||
2379 | |||
2380 | [#44026]: https://github.com/rust-lang/rust/pull/44026 | ||
2381 | [#44027]: https://github.com/rust-lang/rust/pull/44027 | ||
2382 | "##, | ||
2383 | }, | ||
2384 | Lint { | ||
2385 | label: "doc_notable_trait", | ||
2386 | description: r##"# `doc_notable_trait` | ||
2387 | |||
2388 | The tracking issue for this feature is: [#45040] | ||
2389 | |||
2390 | The `doc_notable_trait` feature allows the use of the `#[doc(notable_trait)]` | ||
2391 | attribute, which will display the trait in a "Notable traits" dialog for | ||
2392 | functions returning types that implement the trait. For example, this attribute | ||
2393 | is applied to the `Iterator`, `Future`, `io::Read`, and `io::Write` traits in | ||
2394 | the standard library. | ||
2395 | |||
2396 | You can do this on your own traits like so: | ||
2397 | |||
2398 | ``` | ||
2399 | #![feature(doc_notable_trait)] | ||
2400 | |||
2401 | #[doc(notable_trait)] | ||
2402 | pub trait MyTrait {} | ||
2403 | |||
2404 | pub struct MyStruct; | ||
2405 | impl MyTrait for MyStruct {} | ||
2406 | |||
2407 | /// The docs for this function will have a button that displays a dialog about | ||
2408 | /// `MyStruct` implementing `MyTrait`. | ||
2409 | pub fn my_fn() -> MyStruct { MyStruct } | ||
2410 | ``` | ||
2411 | |||
2412 | This feature was originally implemented in PR [#45039]. | ||
2413 | |||
2414 | See also its documentation in [the rustdoc book][rustdoc-book-notable_trait]. | ||
2415 | |||
2416 | [#45040]: https://github.com/rust-lang/rust/issues/45040 | ||
2417 | [#45039]: https://github.com/rust-lang/rust/pull/45039 | ||
2418 | [rustdoc-book-notable_trait]: ../../rustdoc/unstable-features.html#adding-your-trait-to-the-notable-traits-dialog | ||
2419 | "##, | ||
2420 | }, | ||
2421 | Lint { | ||
2422 | label: "fd", | ||
2423 | description: r##"# `fd` | ||
3702 | 2424 | ||
3703 | This feature is internal to the Rust compiler and is not intended for general use. | 2425 | This feature is internal to the Rust compiler and is not intended for general use. |
3704 | 2426 | ||
3705 | ------------------------ | 2427 | ------------------------ |
3706 | "##, | 2428 | "##, |
3707 | }, | 2429 | }, |
3708 | LintCompletion { | 2430 | Lint { |
3709 | label: "concat_idents", | 2431 | label: "fd_read", |
3710 | description: r##"# `concat_idents` | 2432 | description: r##"# `fd_read` |
3711 | 2433 | ||
3712 | The tracking issue for this feature is: [#29599] | 2434 | This feature is internal to the Rust compiler and is not intended for general use. |
3713 | 2435 | ||
3714 | [#29599]: https://github.com/rust-lang/rust/issues/29599 | 2436 | ------------------------ |
2437 | "##, | ||
2438 | }, | ||
2439 | Lint { | ||
2440 | label: "ffi_const", | ||
2441 | description: r##"# `ffi_const` | ||
2442 | |||
2443 | The tracking issue for this feature is: [#58328] | ||
2444 | |||
2445 | ------ | ||
2446 | |||
2447 | The `#[ffi_const]` attribute applies clang's `const` attribute to foreign | ||
2448 | functions declarations. | ||
2449 | |||
2450 | That is, `#[ffi_const]` functions shall have no effects except for its return | ||
2451 | value, which can only depend on the values of the function parameters, and is | ||
2452 | not affected by changes to the observable state of the program. | ||
2453 | |||
2454 | Applying the `#[ffi_const]` attribute to a function that violates these | ||
2455 | requirements is undefined behaviour. | ||
2456 | |||
2457 | This attribute enables Rust to perform common optimizations, like sub-expression | ||
2458 | elimination, and it can avoid emitting some calls in repeated invocations of the | ||
2459 | function with the same argument values regardless of other operations being | ||
2460 | performed in between these functions calls (as opposed to `#[ffi_pure]` | ||
2461 | functions). | ||
2462 | |||
2463 | ## Pitfalls | ||
2464 | |||
2465 | A `#[ffi_const]` function can only read global memory that would not affect | ||
2466 | its return value for the whole execution of the program (e.g. immutable global | ||
2467 | memory). `#[ffi_const]` functions are referentially-transparent and therefore | ||
2468 | more strict than `#[ffi_pure]` functions. | ||
2469 | |||
2470 | A common pitfall involves applying the `#[ffi_const]` attribute to a | ||
2471 | function that reads memory through pointer arguments which do not necessarily | ||
2472 | point to immutable global memory. | ||
2473 | |||
2474 | A `#[ffi_const]` function that returns unit has no effect on the abstract | ||
2475 | machine's state, and a `#[ffi_const]` function cannot be `#[ffi_pure]`. | ||
2476 | |||
2477 | A `#[ffi_const]` function must not diverge, neither via a side effect (e.g. a | ||
2478 | call to `abort`) nor by infinite loops. | ||
2479 | |||
2480 | When translating C headers to Rust FFI, it is worth verifying for which targets | ||
2481 | the `const` attribute is enabled in those headers, and using the appropriate | ||
2482 | `cfg` macros in the Rust side to match those definitions. While the semantics of | ||
2483 | `const` are implemented identically by many C and C++ compilers, e.g., clang, | ||
2484 | [GCC], [ARM C/C++ compiler], [IBM ILE C/C++], etc. they are not necessarily | ||
2485 | implemented in this way on all of them. It is therefore also worth verifying | ||
2486 | that the semantics of the C toolchain used to compile the binary being linked | ||
2487 | against are compatible with those of the `#[ffi_const]`. | ||
2488 | |||
2489 | [#58328]: https://github.com/rust-lang/rust/issues/58328 | ||
2490 | [ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacgigch.html | ||
2491 | [GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-const-function-attribute | ||
2492 | [IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_const.htm | ||
2493 | "##, | ||
2494 | }, | ||
2495 | Lint { | ||
2496 | label: "ffi_pure", | ||
2497 | description: r##"# `ffi_pure` | ||
2498 | |||
2499 | The tracking issue for this feature is: [#58329] | ||
2500 | |||
2501 | ------ | ||
2502 | |||
2503 | The `#[ffi_pure]` attribute applies clang's `pure` attribute to foreign | ||
2504 | functions declarations. | ||
2505 | |||
2506 | That is, `#[ffi_pure]` functions shall have no effects except for its return | ||
2507 | value, which shall not change across two consecutive function calls with | ||
2508 | the same parameters. | ||
2509 | |||
2510 | Applying the `#[ffi_pure]` attribute to a function that violates these | ||
2511 | requirements is undefined behavior. | ||
2512 | |||
2513 | This attribute enables Rust to perform common optimizations, like sub-expression | ||
2514 | elimination and loop optimizations. Some common examples of pure functions are | ||
2515 | `strlen` or `memcmp`. | ||
2516 | |||
2517 | These optimizations are only applicable when the compiler can prove that no | ||
2518 | program state observable by the `#[ffi_pure]` function has changed between calls | ||
2519 | of the function, which could alter the result. See also the `#[ffi_const]` | ||
2520 | attribute, which provides stronger guarantees regarding the allowable behavior | ||
2521 | of a function, enabling further optimization. | ||
2522 | |||
2523 | ## Pitfalls | ||
2524 | |||
2525 | A `#[ffi_pure]` function can read global memory through the function | ||
2526 | parameters (e.g. pointers), globals, etc. `#[ffi_pure]` functions are not | ||
2527 | referentially-transparent, and are therefore more relaxed than `#[ffi_const]` | ||
2528 | functions. | ||
2529 | |||
2530 | However, accessing global memory through volatile or atomic reads can violate the | ||
2531 | requirement that two consecutive function calls shall return the same value. | ||
2532 | |||
2533 | A `pure` function that returns unit has no effect on the abstract machine's | ||
2534 | state. | ||
2535 | |||
2536 | A `#[ffi_pure]` function must not diverge, neither via a side effect (e.g. a | ||
2537 | call to `abort`) nor by infinite loops. | ||
2538 | |||
2539 | When translating C headers to Rust FFI, it is worth verifying for which targets | ||
2540 | the `pure` attribute is enabled in those headers, and using the appropriate | ||
2541 | `cfg` macros in the Rust side to match those definitions. While the semantics of | ||
2542 | `pure` are implemented identically by many C and C++ compilers, e.g., clang, | ||
2543 | [GCC], [ARM C/C++ compiler], [IBM ILE C/C++], etc. they are not necessarily | ||
2544 | implemented in this way on all of them. It is therefore also worth verifying | ||
2545 | that the semantics of the C toolchain used to compile the binary being linked | ||
2546 | against are compatible with those of the `#[ffi_pure]`. | ||
2547 | |||
2548 | |||
2549 | [#58329]: https://github.com/rust-lang/rust/issues/58329 | ||
2550 | [ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacigdac.html | ||
2551 | [GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-pure-function-attribute | ||
2552 | [IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_pure.htm | ||
2553 | "##, | ||
2554 | }, | ||
2555 | Lint { | ||
2556 | label: "flt2dec", | ||
2557 | description: r##"# `flt2dec` | ||
2558 | |||
2559 | This feature is internal to the Rust compiler and is not intended for general use. | ||
2560 | |||
2561 | ------------------------ | ||
2562 | "##, | ||
2563 | }, | ||
2564 | Lint { | ||
2565 | label: "fmt_internals", | ||
2566 | description: r##"# `fmt_internals` | ||
2567 | |||
2568 | This feature is internal to the Rust compiler and is not intended for general use. | ||
3715 | 2569 | ||
3716 | ------------------------ | 2570 | ------------------------ |
2571 | "##, | ||
2572 | }, | ||
2573 | Lint { | ||
2574 | label: "fn_traits", | ||
2575 | description: r##"# `fn_traits` | ||
3717 | 2576 | ||
3718 | The `concat_idents` feature adds a macro for concatenating multiple identifiers | 2577 | The tracking issue for this feature is [#29625] |
3719 | into one identifier. | ||
3720 | 2578 | ||
3721 | ## Examples | 2579 | See Also: [`unboxed_closures`](../language-features/unboxed-closures.md) |
2580 | |||
2581 | [#29625]: https://github.com/rust-lang/rust/issues/29625 | ||
2582 | |||
2583 | ---- | ||
2584 | |||
2585 | The `fn_traits` feature allows for implementation of the [`Fn*`] traits | ||
2586 | for creating custom closure-like types. | ||
2587 | |||
2588 | [`Fn*`]: https://doc.rust-lang.org/std/ops/trait.Fn.html | ||
3722 | 2589 | ||
3723 | ```rust | 2590 | ```rust |
3724 | #![feature(concat_idents)] | 2591 | #![feature(unboxed_closures)] |
2592 | #![feature(fn_traits)] | ||
2593 | |||
2594 | struct Adder { | ||
2595 | a: u32 | ||
2596 | } | ||
2597 | |||
2598 | impl FnOnce<(u32, )> for Adder { | ||
2599 | type Output = u32; | ||
2600 | extern "rust-call" fn call_once(self, b: (u32, )) -> Self::Output { | ||
2601 | self.a + b.0 | ||
2602 | } | ||
2603 | } | ||
3725 | 2604 | ||
3726 | fn main() { | 2605 | fn main() { |
3727 | fn foobar() -> u32 { 23 } | 2606 | let adder = Adder { a: 3 }; |
3728 | let f = concat_idents!(foo, bar); | 2607 | assert_eq!(adder(2), 5); |
3729 | assert_eq!(f(), 23); | ||
3730 | } | 2608 | } |
3731 | ``` | 2609 | ``` |
3732 | "##, | 2610 | "##, |
3733 | }, | 2611 | }, |
3734 | LintCompletion { | 2612 | Lint { |
3735 | label: "format_args_capture", | 2613 | label: "format_args_capture", |
3736 | description: r##"# `format_args_capture` | 2614 | description: r##"# `format_args_capture` |
3737 | 2615 | ||
@@ -3782,16 +2660,914 @@ A non-exhaustive list of macros which benefit from this functionality include: | |||
3782 | - macros in many thirdparty crates, such as `log` | 2660 | - macros in many thirdparty crates, such as `log` |
3783 | "##, | 2661 | "##, |
3784 | }, | 2662 | }, |
3785 | LintCompletion { | 2663 | Lint { |
3786 | label: "print_internals", | 2664 | label: "generators", |
3787 | description: r##"# `print_internals` | 2665 | description: r##"# `generators` |
2666 | |||
2667 | The tracking issue for this feature is: [#43122] | ||
2668 | |||
2669 | [#43122]: https://github.com/rust-lang/rust/issues/43122 | ||
2670 | |||
2671 | ------------------------ | ||
2672 | |||
2673 | The `generators` feature gate in Rust allows you to define generator or | ||
2674 | coroutine literals. A generator is a "resumable function" that syntactically | ||
2675 | resembles a closure but compiles to much different semantics in the compiler | ||
2676 | itself. The primary feature of a generator is that it can be suspended during | ||
2677 | execution to be resumed at a later date. Generators use the `yield` keyword to | ||
2678 | "return", and then the caller can `resume` a generator to resume execution just | ||
2679 | after the `yield` keyword. | ||
2680 | |||
2681 | Generators are an extra-unstable feature in the compiler right now. Added in | ||
2682 | [RFC 2033] they're mostly intended right now as a information/constraint | ||
2683 | gathering phase. The intent is that experimentation can happen on the nightly | ||
2684 | compiler before actual stabilization. A further RFC will be required to | ||
2685 | stabilize generators/coroutines and will likely contain at least a few small | ||
2686 | tweaks to the overall design. | ||
2687 | |||
2688 | [RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033 | ||
2689 | |||
2690 | A syntactical example of a generator is: | ||
2691 | |||
2692 | ```rust | ||
2693 | #![feature(generators, generator_trait)] | ||
2694 | |||
2695 | use std::ops::{Generator, GeneratorState}; | ||
2696 | use std::pin::Pin; | ||
2697 | |||
2698 | fn main() { | ||
2699 | let mut generator = || { | ||
2700 | yield 1; | ||
2701 | return "foo" | ||
2702 | }; | ||
2703 | |||
2704 | match Pin::new(&mut generator).resume(()) { | ||
2705 | GeneratorState::Yielded(1) => {} | ||
2706 | _ => panic!("unexpected value from resume"), | ||
2707 | } | ||
2708 | match Pin::new(&mut generator).resume(()) { | ||
2709 | GeneratorState::Complete("foo") => {} | ||
2710 | _ => panic!("unexpected value from resume"), | ||
2711 | } | ||
2712 | } | ||
2713 | ``` | ||
2714 | |||
2715 | Generators are closure-like literals which can contain a `yield` statement. The | ||
2716 | `yield` statement takes an optional expression of a value to yield out of the | ||
2717 | generator. All generator literals implement the `Generator` trait in the | ||
2718 | `std::ops` module. The `Generator` trait has one main method, `resume`, which | ||
2719 | resumes execution of the generator at the previous suspension point. | ||
2720 | |||
2721 | An example of the control flow of generators is that the following example | ||
2722 | prints all numbers in order: | ||
2723 | |||
2724 | ```rust | ||
2725 | #![feature(generators, generator_trait)] | ||
2726 | |||
2727 | use std::ops::Generator; | ||
2728 | use std::pin::Pin; | ||
2729 | |||
2730 | fn main() { | ||
2731 | let mut generator = || { | ||
2732 | println!("2"); | ||
2733 | yield; | ||
2734 | println!("4"); | ||
2735 | }; | ||
2736 | |||
2737 | println!("1"); | ||
2738 | Pin::new(&mut generator).resume(()); | ||
2739 | println!("3"); | ||
2740 | Pin::new(&mut generator).resume(()); | ||
2741 | println!("5"); | ||
2742 | } | ||
2743 | ``` | ||
2744 | |||
2745 | At this time the main intended use case of generators is an implementation | ||
2746 | primitive for async/await syntax, but generators will likely be extended to | ||
2747 | ergonomic implementations of iterators and other primitives in the future. | ||
2748 | Feedback on the design and usage is always appreciated! | ||
2749 | |||
2750 | ### The `Generator` trait | ||
2751 | |||
2752 | The `Generator` trait in `std::ops` currently looks like: | ||
2753 | |||
2754 | ```rust | ||
2755 | # #![feature(arbitrary_self_types, generator_trait)] | ||
2756 | # use std::ops::GeneratorState; | ||
2757 | # use std::pin::Pin; | ||
2758 | |||
2759 | pub trait Generator<R = ()> { | ||
2760 | type Yield; | ||
2761 | type Return; | ||
2762 | fn resume(self: Pin<&mut Self>, resume: R) -> GeneratorState<Self::Yield, Self::Return>; | ||
2763 | } | ||
2764 | ``` | ||
2765 | |||
2766 | The `Generator::Yield` type is the type of values that can be yielded with the | ||
2767 | `yield` statement. The `Generator::Return` type is the returned type of the | ||
2768 | generator. This is typically the last expression in a generator's definition or | ||
2769 | any value passed to `return` in a generator. The `resume` function is the entry | ||
2770 | point for executing the `Generator` itself. | ||
2771 | |||
2772 | The return value of `resume`, `GeneratorState`, looks like: | ||
2773 | |||
2774 | ```rust | ||
2775 | pub enum GeneratorState<Y, R> { | ||
2776 | Yielded(Y), | ||
2777 | Complete(R), | ||
2778 | } | ||
2779 | ``` | ||
2780 | |||
2781 | The `Yielded` variant indicates that the generator can later be resumed. This | ||
2782 | corresponds to a `yield` point in a generator. The `Complete` variant indicates | ||
2783 | that the generator is complete and cannot be resumed again. Calling `resume` | ||
2784 | after a generator has returned `Complete` will likely result in a panic of the | ||
2785 | program. | ||
2786 | |||
2787 | ### Closure-like semantics | ||
2788 | |||
2789 | The closure-like syntax for generators alludes to the fact that they also have | ||
2790 | closure-like semantics. Namely: | ||
2791 | |||
2792 | * When created, a generator executes no code. A closure literal does not | ||
2793 | actually execute any of the closure's code on construction, and similarly a | ||
2794 | generator literal does not execute any code inside the generator when | ||
2795 | constructed. | ||
2796 | |||
2797 | * Generators can capture outer variables by reference or by move, and this can | ||
2798 | be tweaked with the `move` keyword at the beginning of the closure. Like | ||
2799 | closures all generators will have an implicit environment which is inferred by | ||
2800 | the compiler. Outer variables can be moved into a generator for use as the | ||
2801 | generator progresses. | ||
2802 | |||
2803 | * Generator literals produce a value with a unique type which implements the | ||
2804 | `std::ops::Generator` trait. This allows actual execution of the generator | ||
2805 | through the `Generator::resume` method as well as also naming it in return | ||
2806 | types and such. | ||
2807 | |||
2808 | * Traits like `Send` and `Sync` are automatically implemented for a `Generator` | ||
2809 | depending on the captured variables of the environment. Unlike closures, | ||
2810 | generators also depend on variables live across suspension points. This means | ||
2811 | that although the ambient environment may be `Send` or `Sync`, the generator | ||
2812 | itself may not be due to internal variables live across `yield` points being | ||
2813 | not-`Send` or not-`Sync`. Note that generators do | ||
2814 | not implement traits like `Copy` or `Clone` automatically. | ||
2815 | |||
2816 | * Whenever a generator is dropped it will drop all captured environment | ||
2817 | variables. | ||
2818 | |||
2819 | ### Generators as state machines | ||
2820 | |||
2821 | In the compiler, generators are currently compiled as state machines. Each | ||
2822 | `yield` expression will correspond to a different state that stores all live | ||
2823 | variables over that suspension point. Resumption of a generator will dispatch on | ||
2824 | the current state and then execute internally until a `yield` is reached, at | ||
2825 | which point all state is saved off in the generator and a value is returned. | ||
2826 | |||
2827 | Let's take a look at an example to see what's going on here: | ||
2828 | |||
2829 | ```rust | ||
2830 | #![feature(generators, generator_trait)] | ||
2831 | |||
2832 | use std::ops::Generator; | ||
2833 | use std::pin::Pin; | ||
2834 | |||
2835 | fn main() { | ||
2836 | let ret = "foo"; | ||
2837 | let mut generator = move || { | ||
2838 | yield 1; | ||
2839 | return ret | ||
2840 | }; | ||
2841 | |||
2842 | Pin::new(&mut generator).resume(()); | ||
2843 | Pin::new(&mut generator).resume(()); | ||
2844 | } | ||
2845 | ``` | ||
2846 | |||
2847 | This generator literal will compile down to something similar to: | ||
2848 | |||
2849 | ```rust | ||
2850 | #![feature(arbitrary_self_types, generators, generator_trait)] | ||
2851 | |||
2852 | use std::ops::{Generator, GeneratorState}; | ||
2853 | use std::pin::Pin; | ||
2854 | |||
2855 | fn main() { | ||
2856 | let ret = "foo"; | ||
2857 | let mut generator = { | ||
2858 | enum __Generator { | ||
2859 | Start(&'static str), | ||
2860 | Yield1(&'static str), | ||
2861 | Done, | ||
2862 | } | ||
2863 | |||
2864 | impl Generator for __Generator { | ||
2865 | type Yield = i32; | ||
2866 | type Return = &'static str; | ||
2867 | |||
2868 | fn resume(mut self: Pin<&mut Self>, resume: ()) -> GeneratorState<i32, &'static str> { | ||
2869 | use std::mem; | ||
2870 | match mem::replace(&mut *self, __Generator::Done) { | ||
2871 | __Generator::Start(s) => { | ||
2872 | *self = __Generator::Yield1(s); | ||
2873 | GeneratorState::Yielded(1) | ||
2874 | } | ||
2875 | |||
2876 | __Generator::Yield1(s) => { | ||
2877 | *self = __Generator::Done; | ||
2878 | GeneratorState::Complete(s) | ||
2879 | } | ||
2880 | |||
2881 | __Generator::Done => { | ||
2882 | panic!("generator resumed after completion") | ||
2883 | } | ||
2884 | } | ||
2885 | } | ||
2886 | } | ||
2887 | |||
2888 | __Generator::Start(ret) | ||
2889 | }; | ||
2890 | |||
2891 | Pin::new(&mut generator).resume(()); | ||
2892 | Pin::new(&mut generator).resume(()); | ||
2893 | } | ||
2894 | ``` | ||
2895 | |||
2896 | Notably here we can see that the compiler is generating a fresh type, | ||
2897 | `__Generator` in this case. This type has a number of states (represented here | ||
2898 | as an `enum`) corresponding to each of the conceptual states of the generator. | ||
2899 | At the beginning we're closing over our outer variable `foo` and then that | ||
2900 | variable is also live over the `yield` point, so it's stored in both states. | ||
2901 | |||
2902 | When the generator starts it'll immediately yield 1, but it saves off its state | ||
2903 | just before it does so indicating that it has reached the yield point. Upon | ||
2904 | resuming again we'll execute the `return ret` which returns the `Complete` | ||
2905 | state. | ||
2906 | |||
2907 | Here we can also note that the `Done` state, if resumed, panics immediately as | ||
2908 | it's invalid to resume a completed generator. It's also worth noting that this | ||
2909 | is just a rough desugaring, not a normative specification for what the compiler | ||
2910 | does. | ||
2911 | "##, | ||
2912 | }, | ||
2913 | Lint { | ||
2914 | label: "global_asm", | ||
2915 | description: r##"# `global_asm` | ||
2916 | |||
2917 | The tracking issue for this feature is: [#35119] | ||
2918 | |||
2919 | [#35119]: https://github.com/rust-lang/rust/issues/35119 | ||
2920 | |||
2921 | ------------------------ | ||
2922 | |||
2923 | The `global_asm!` macro allows the programmer to write arbitrary | ||
2924 | assembly outside the scope of a function body, passing it through | ||
2925 | `rustc` and `llvm` to the assembler. That is to say, `global_asm!` is | ||
2926 | equivalent to assembling the asm with an external assembler and then | ||
2927 | linking the resulting object file with the current crate. | ||
2928 | |||
2929 | `global_asm!` fills a role not currently satisfied by either `asm!` | ||
2930 | or `#[naked]` functions. The programmer has _all_ features of the | ||
2931 | assembler at their disposal. The linker will expect to resolve any | ||
2932 | symbols defined in the inline assembly, modulo any symbols marked as | ||
2933 | external. It also means syntax for directives and assembly follow the | ||
2934 | conventions of the assembler in your toolchain. | ||
2935 | |||
2936 | A simple usage looks like this: | ||
2937 | |||
2938 | ```rust,ignore (requires-external-file) | ||
2939 | #![feature(global_asm)] | ||
2940 | # // you also need relevant target_arch cfgs | ||
2941 | global_asm!(include_str!("something_neato.s")); | ||
2942 | ``` | ||
2943 | |||
2944 | And a more complicated usage looks like this: | ||
2945 | |||
2946 | ```rust,no_run | ||
2947 | #![feature(global_asm)] | ||
2948 | # #[cfg(any(target_arch="x86", target_arch="x86_64"))] | ||
2949 | # mod x86 { | ||
2950 | |||
2951 | pub mod sally { | ||
2952 | global_asm!( | ||
2953 | ".global foo", | ||
2954 | "foo:", | ||
2955 | "jmp baz", | ||
2956 | ); | ||
2957 | |||
2958 | #[no_mangle] | ||
2959 | pub unsafe extern "C" fn baz() {} | ||
2960 | } | ||
2961 | |||
2962 | // the symbols `foo` and `bar` are global, no matter where | ||
2963 | // `global_asm!` was used. | ||
2964 | extern "C" { | ||
2965 | fn foo(); | ||
2966 | fn bar(); | ||
2967 | } | ||
2968 | |||
2969 | pub mod harry { | ||
2970 | global_asm!( | ||
2971 | ".global bar", | ||
2972 | "bar:", | ||
2973 | "jmp quux", | ||
2974 | ); | ||
2975 | |||
2976 | #[no_mangle] | ||
2977 | pub unsafe extern "C" fn quux() {} | ||
2978 | } | ||
2979 | # } | ||
2980 | ``` | ||
2981 | |||
2982 | You may use `global_asm!` multiple times, anywhere in your crate, in | ||
2983 | whatever way suits you. However, you should not rely on assembler state | ||
2984 | (e.g. assembler macros) defined in one `global_asm!` to be available in | ||
2985 | another one. It is implementation-defined whether the multiple usages | ||
2986 | are concatenated into one or assembled separately. | ||
2987 | |||
2988 | `global_asm!` also supports `const` operands like `asm!`, which allows | ||
2989 | constants defined in Rust to be used in assembly code: | ||
2990 | |||
2991 | ```rust,no_run | ||
2992 | #![feature(global_asm)] | ||
2993 | # #[cfg(any(target_arch="x86", target_arch="x86_64"))] | ||
2994 | # mod x86 { | ||
2995 | const C: i32 = 1234; | ||
2996 | global_asm!( | ||
2997 | ".global bar", | ||
2998 | "bar: .word {c}", | ||
2999 | c = const C, | ||
3000 | ); | ||
3001 | # } | ||
3002 | ``` | ||
3003 | |||
3004 | The syntax for passing operands is the same as `asm!` except that only | ||
3005 | `const` operands are allowed. Refer to the [asm](asm.md) documentation | ||
3006 | for more details. | ||
3007 | |||
3008 | On x86, the assembly code will use intel syntax by default. You can | ||
3009 | override this by adding `options(att_syntax)` at the end of the macro | ||
3010 | arguments list: | ||
3011 | |||
3012 | ```rust,no_run | ||
3013 | #![feature(global_asm)] | ||
3014 | # #[cfg(any(target_arch="x86", target_arch="x86_64"))] | ||
3015 | # mod x86 { | ||
3016 | global_asm!("movl ${}, %ecx", const 5, options(att_syntax)); | ||
3017 | // is equivalent to | ||
3018 | global_asm!("mov ecx, {}", const 5); | ||
3019 | # } | ||
3020 | ``` | ||
3021 | |||
3022 | ------------------------ | ||
3023 | |||
3024 | If you don't need quite as much power and flexibility as | ||
3025 | `global_asm!` provides, and you don't mind restricting your inline | ||
3026 | assembly to `fn` bodies only, you might try the | ||
3027 | [asm](asm.md) feature instead. | ||
3028 | "##, | ||
3029 | }, | ||
3030 | Lint { | ||
3031 | label: "impl_trait_in_bindings", | ||
3032 | description: r##"# `impl_trait_in_bindings` | ||
3033 | |||
3034 | The tracking issue for this feature is: [#63065] | ||
3035 | |||
3036 | [#63065]: https://github.com/rust-lang/rust/issues/63065 | ||
3037 | |||
3038 | ------------------------ | ||
3039 | |||
3040 | The `impl_trait_in_bindings` feature gate lets you use `impl Trait` syntax in | ||
3041 | `let`, `static`, and `const` bindings. | ||
3042 | |||
3043 | A simple example is: | ||
3044 | |||
3045 | ```rust | ||
3046 | #![feature(impl_trait_in_bindings)] | ||
3047 | |||
3048 | use std::fmt::Debug; | ||
3049 | |||
3050 | fn main() { | ||
3051 | let a: impl Debug + Clone = 42; | ||
3052 | let b = a.clone(); | ||
3053 | println!("{:?}", b); // prints `42` | ||
3054 | } | ||
3055 | ``` | ||
3056 | |||
3057 | Note however that because the types of `a` and `b` are opaque in the above | ||
3058 | example, calling inherent methods or methods outside of the specified traits | ||
3059 | (e.g., `a.abs()` or `b.abs()`) is not allowed, and yields an error. | ||
3060 | "##, | ||
3061 | }, | ||
3062 | Lint { | ||
3063 | label: "infer_static_outlives_requirements", | ||
3064 | description: r##"# `infer_static_outlives_requirements` | ||
3065 | |||
3066 | The tracking issue for this feature is: [#54185] | ||
3067 | |||
3068 | [#54185]: https://github.com/rust-lang/rust/issues/54185 | ||
3069 | |||
3070 | ------------------------ | ||
3071 | The `infer_static_outlives_requirements` feature indicates that certain | ||
3072 | `'static` outlives requirements can be inferred by the compiler rather than | ||
3073 | stating them explicitly. | ||
3074 | |||
3075 | Note: It is an accompanying feature to `infer_outlives_requirements`, | ||
3076 | which must be enabled to infer outlives requirements. | ||
3077 | |||
3078 | For example, currently generic struct definitions that contain | ||
3079 | references, require where-clauses of the form T: 'static. By using | ||
3080 | this feature the outlives predicates will be inferred, although | ||
3081 | they may still be written explicitly. | ||
3082 | |||
3083 | ```rust,ignore (pseudo-Rust) | ||
3084 | struct Foo<U> where U: 'static { // <-- currently required | ||
3085 | bar: Bar<U> | ||
3086 | } | ||
3087 | struct Bar<T: 'static> { | ||
3088 | x: T, | ||
3089 | } | ||
3090 | ``` | ||
3091 | |||
3092 | |||
3093 | ## Examples: | ||
3094 | |||
3095 | ```rust,ignore (pseudo-Rust) | ||
3096 | #![feature(infer_outlives_requirements)] | ||
3097 | #![feature(infer_static_outlives_requirements)] | ||
3098 | |||
3099 | #[rustc_outlives] | ||
3100 | // Implicitly infer U: 'static | ||
3101 | struct Foo<U> { | ||
3102 | bar: Bar<U> | ||
3103 | } | ||
3104 | struct Bar<T: 'static> { | ||
3105 | x: T, | ||
3106 | } | ||
3107 | ``` | ||
3108 | "##, | ||
3109 | }, | ||
3110 | Lint { | ||
3111 | label: "inline_const", | ||
3112 | description: r##"# `inline_const` | ||
3113 | |||
3114 | The tracking issue for this feature is: [#76001] | ||
3115 | |||
3116 | ------ | ||
3117 | |||
3118 | This feature allows you to use inline constant expressions. For example, you can | ||
3119 | turn this code: | ||
3120 | |||
3121 | ```rust | ||
3122 | # fn add_one(x: i32) -> i32 { x + 1 } | ||
3123 | const MY_COMPUTATION: i32 = 1 + 2 * 3 / 4; | ||
3124 | |||
3125 | fn main() { | ||
3126 | let x = add_one(MY_COMPUTATION); | ||
3127 | } | ||
3128 | ``` | ||
3129 | |||
3130 | into this code: | ||
3131 | |||
3132 | ```rust | ||
3133 | #![feature(inline_const)] | ||
3134 | |||
3135 | # fn add_one(x: i32) -> i32 { x + 1 } | ||
3136 | fn main() { | ||
3137 | let x = add_one(const { 1 + 2 * 3 / 4 }); | ||
3138 | } | ||
3139 | ``` | ||
3140 | |||
3141 | You can also use inline constant expressions in patterns: | ||
3142 | |||
3143 | ```rust | ||
3144 | #![feature(inline_const)] | ||
3145 | |||
3146 | const fn one() -> i32 { 1 } | ||
3147 | |||
3148 | let some_int = 3; | ||
3149 | match some_int { | ||
3150 | const { 1 + 2 } => println!("Matched 1 + 2"), | ||
3151 | const { one() } => println!("Matched const fn returning 1"), | ||
3152 | _ => println!("Didn't match anything :("), | ||
3153 | } | ||
3154 | ``` | ||
3155 | |||
3156 | [#76001]: https://github.com/rust-lang/rust/issues/76001 | ||
3157 | "##, | ||
3158 | }, | ||
3159 | Lint { | ||
3160 | label: "int_error_internals", | ||
3161 | description: r##"# `int_error_internals` | ||
3162 | |||
3163 | This feature is internal to the Rust compiler and is not intended for general use. | ||
3164 | |||
3165 | ------------------------ | ||
3166 | "##, | ||
3167 | }, | ||
3168 | Lint { | ||
3169 | label: "internal_output_capture", | ||
3170 | description: r##"# `internal_output_capture` | ||
3788 | 3171 | ||
3789 | This feature is internal to the Rust compiler and is not intended for general use. | 3172 | This feature is internal to the Rust compiler and is not intended for general use. |
3790 | 3173 | ||
3791 | ------------------------ | 3174 | ------------------------ |
3792 | "##, | 3175 | "##, |
3793 | }, | 3176 | }, |
3794 | LintCompletion { | 3177 | Lint { |
3178 | label: "intra_doc_pointers", | ||
3179 | description: r##"# `intra-doc-pointers` | ||
3180 | |||
3181 | The tracking issue for this feature is: [#80896] | ||
3182 | |||
3183 | [#80896]: https://github.com/rust-lang/rust/issues/80896 | ||
3184 | |||
3185 | ------------------------ | ||
3186 | |||
3187 | Rustdoc does not currently allow disambiguating between `*const` and `*mut`, and | ||
3188 | raw pointers in intra-doc links are unstable until it does. | ||
3189 | |||
3190 | ```rust | ||
3191 | #![feature(intra_doc_pointers)] | ||
3192 | //! [pointer::add] | ||
3193 | ``` | ||
3194 | "##, | ||
3195 | }, | ||
3196 | Lint { | ||
3197 | label: "intrinsics", | ||
3198 | description: r##"# `intrinsics` | ||
3199 | |||
3200 | The tracking issue for this feature is: None. | ||
3201 | |||
3202 | Intrinsics are never intended to be stable directly, but intrinsics are often | ||
3203 | exported in some sort of stable manner. Prefer using the stable interfaces to | ||
3204 | the intrinsic directly when you can. | ||
3205 | |||
3206 | ------------------------ | ||
3207 | |||
3208 | |||
3209 | These are imported as if they were FFI functions, with the special | ||
3210 | `rust-intrinsic` ABI. For example, if one was in a freestanding | ||
3211 | context, but wished to be able to `transmute` between types, and | ||
3212 | perform efficient pointer arithmetic, one would import those functions | ||
3213 | via a declaration like | ||
3214 | |||
3215 | ```rust | ||
3216 | #![feature(intrinsics)] | ||
3217 | # fn main() {} | ||
3218 | |||
3219 | extern "rust-intrinsic" { | ||
3220 | fn transmute<T, U>(x: T) -> U; | ||
3221 | |||
3222 | fn offset<T>(dst: *const T, offset: isize) -> *const T; | ||
3223 | } | ||
3224 | ``` | ||
3225 | |||
3226 | As with any other FFI functions, these are always `unsafe` to call. | ||
3227 | "##, | ||
3228 | }, | ||
3229 | Lint { | ||
3230 | label: "is_sorted", | ||
3231 | description: r##"# `is_sorted` | ||
3232 | |||
3233 | The tracking issue for this feature is: [#53485] | ||
3234 | |||
3235 | [#53485]: https://github.com/rust-lang/rust/issues/53485 | ||
3236 | |||
3237 | ------------------------ | ||
3238 | |||
3239 | Add the methods `is_sorted`, `is_sorted_by` and `is_sorted_by_key` to `[T]`; | ||
3240 | add the methods `is_sorted`, `is_sorted_by` and `is_sorted_by_key` to | ||
3241 | `Iterator`. | ||
3242 | "##, | ||
3243 | }, | ||
3244 | Lint { | ||
3245 | label: "lang_items", | ||
3246 | description: r##"# `lang_items` | ||
3247 | |||
3248 | The tracking issue for this feature is: None. | ||
3249 | |||
3250 | ------------------------ | ||
3251 | |||
3252 | The `rustc` compiler has certain pluggable operations, that is, | ||
3253 | functionality that isn't hard-coded into the language, but is | ||
3254 | implemented in libraries, with a special marker to tell the compiler | ||
3255 | it exists. The marker is the attribute `#[lang = "..."]` and there are | ||
3256 | various different values of `...`, i.e. various different 'lang | ||
3257 | items'. | ||
3258 | |||
3259 | For example, `Box` pointers require two lang items, one for allocation | ||
3260 | and one for deallocation. A freestanding program that uses the `Box` | ||
3261 | sugar for dynamic allocations via `malloc` and `free`: | ||
3262 | |||
3263 | ```rust,ignore (libc-is-finicky) | ||
3264 | #![feature(lang_items, box_syntax, start, libc, core_intrinsics, rustc_private)] | ||
3265 | #![no_std] | ||
3266 | use core::intrinsics; | ||
3267 | use core::panic::PanicInfo; | ||
3268 | |||
3269 | extern crate libc; | ||
3270 | |||
3271 | #[lang = "owned_box"] | ||
3272 | pub struct Box<T>(*mut T); | ||
3273 | |||
3274 | #[lang = "exchange_malloc"] | ||
3275 | unsafe fn allocate(size: usize, _align: usize) -> *mut u8 { | ||
3276 | let p = libc::malloc(size as libc::size_t) as *mut u8; | ||
3277 | |||
3278 | // Check if `malloc` failed: | ||
3279 | if p as usize == 0 { | ||
3280 | intrinsics::abort(); | ||
3281 | } | ||
3282 | |||
3283 | p | ||
3284 | } | ||
3285 | |||
3286 | #[lang = "box_free"] | ||
3287 | unsafe fn box_free<T: ?Sized>(ptr: *mut T) { | ||
3288 | libc::free(ptr as *mut libc::c_void) | ||
3289 | } | ||
3290 | |||
3291 | #[start] | ||
3292 | fn main(_argc: isize, _argv: *const *const u8) -> isize { | ||
3293 | let _x = box 1; | ||
3294 | |||
3295 | 0 | ||
3296 | } | ||
3297 | |||
3298 | #[lang = "eh_personality"] extern fn rust_eh_personality() {} | ||
3299 | #[lang = "panic_impl"] extern fn rust_begin_panic(info: &PanicInfo) -> ! { unsafe { intrinsics::abort() } } | ||
3300 | #[no_mangle] pub extern fn rust_eh_register_frames () {} | ||
3301 | #[no_mangle] pub extern fn rust_eh_unregister_frames () {} | ||
3302 | ``` | ||
3303 | |||
3304 | Note the use of `abort`: the `exchange_malloc` lang item is assumed to | ||
3305 | return a valid pointer, and so needs to do the check internally. | ||
3306 | |||
3307 | Other features provided by lang items include: | ||
3308 | |||
3309 | - overloadable operators via traits: the traits corresponding to the | ||
3310 | `==`, `<`, dereferencing (`*`) and `+` (etc.) operators are all | ||
3311 | marked with lang items; those specific four are `eq`, `ord`, | ||
3312 | `deref`, and `add` respectively. | ||
3313 | - stack unwinding and general failure; the `eh_personality`, | ||
3314 | `panic` and `panic_bounds_check` lang items. | ||
3315 | - the traits in `std::marker` used to indicate types of | ||
3316 | various kinds; lang items `send`, `sync` and `copy`. | ||
3317 | - the marker types and variance indicators found in | ||
3318 | `std::marker`; lang items `covariant_type`, | ||
3319 | `contravariant_lifetime`, etc. | ||
3320 | |||
3321 | Lang items are loaded lazily by the compiler; e.g. if one never uses | ||
3322 | `Box` then there is no need to define functions for `exchange_malloc` | ||
3323 | and `box_free`. `rustc` will emit an error when an item is needed | ||
3324 | but not found in the current crate or any that it depends on. | ||
3325 | |||
3326 | Most lang items are defined by `libcore`, but if you're trying to build | ||
3327 | an executable without the standard library, you'll run into the need | ||
3328 | for lang items. The rest of this page focuses on this use-case, even though | ||
3329 | lang items are a bit broader than that. | ||
3330 | |||
3331 | ### Using libc | ||
3332 | |||
3333 | In order to build a `#[no_std]` executable we will need libc as a dependency. | ||
3334 | We can specify this using our `Cargo.toml` file: | ||
3335 | |||
3336 | ```toml | ||
3337 | [dependencies] | ||
3338 | libc = { version = "0.2.14", default-features = false } | ||
3339 | ``` | ||
3340 | |||
3341 | Note that the default features have been disabled. This is a critical step - | ||
3342 | **the default features of libc include the standard library and so must be | ||
3343 | disabled.** | ||
3344 | |||
3345 | ### Writing an executable without stdlib | ||
3346 | |||
3347 | Controlling the entry point is possible in two ways: the `#[start]` attribute, | ||
3348 | or overriding the default shim for the C `main` function with your own. | ||
3349 | |||
3350 | The function marked `#[start]` is passed the command line parameters | ||
3351 | in the same format as C: | ||
3352 | |||
3353 | ```rust,ignore (libc-is-finicky) | ||
3354 | #![feature(lang_items, core_intrinsics, rustc_private)] | ||
3355 | #![feature(start)] | ||
3356 | #![no_std] | ||
3357 | use core::intrinsics; | ||
3358 | use core::panic::PanicInfo; | ||
3359 | |||
3360 | // Pull in the system libc library for what crt0.o likely requires. | ||
3361 | extern crate libc; | ||
3362 | |||
3363 | // Entry point for this program. | ||
3364 | #[start] | ||
3365 | fn start(_argc: isize, _argv: *const *const u8) -> isize { | ||
3366 | 0 | ||
3367 | } | ||
3368 | |||
3369 | // These functions are used by the compiler, but not | ||
3370 | // for a bare-bones hello world. These are normally | ||
3371 | // provided by libstd. | ||
3372 | #[lang = "eh_personality"] | ||
3373 | #[no_mangle] | ||
3374 | pub extern fn rust_eh_personality() { | ||
3375 | } | ||
3376 | |||
3377 | #[lang = "panic_impl"] | ||
3378 | #[no_mangle] | ||
3379 | pub extern fn rust_begin_panic(info: &PanicInfo) -> ! { | ||
3380 | unsafe { intrinsics::abort() } | ||
3381 | } | ||
3382 | ``` | ||
3383 | |||
3384 | To override the compiler-inserted `main` shim, one has to disable it | ||
3385 | with `#![no_main]` and then create the appropriate symbol with the | ||
3386 | correct ABI and the correct name, which requires overriding the | ||
3387 | compiler's name mangling too: | ||
3388 | |||
3389 | ```rust,ignore (libc-is-finicky) | ||
3390 | #![feature(lang_items, core_intrinsics, rustc_private)] | ||
3391 | #![feature(start)] | ||
3392 | #![no_std] | ||
3393 | #![no_main] | ||
3394 | use core::intrinsics; | ||
3395 | use core::panic::PanicInfo; | ||
3396 | |||
3397 | // Pull in the system libc library for what crt0.o likely requires. | ||
3398 | extern crate libc; | ||
3399 | |||
3400 | // Entry point for this program. | ||
3401 | #[no_mangle] // ensure that this symbol is called `main` in the output | ||
3402 | pub extern fn main(_argc: i32, _argv: *const *const u8) -> i32 { | ||
3403 | 0 | ||
3404 | } | ||
3405 | |||
3406 | // These functions are used by the compiler, but not | ||
3407 | // for a bare-bones hello world. These are normally | ||
3408 | // provided by libstd. | ||
3409 | #[lang = "eh_personality"] | ||
3410 | #[no_mangle] | ||
3411 | pub extern fn rust_eh_personality() { | ||
3412 | } | ||
3413 | |||
3414 | #[lang = "panic_impl"] | ||
3415 | #[no_mangle] | ||
3416 | pub extern fn rust_begin_panic(info: &PanicInfo) -> ! { | ||
3417 | unsafe { intrinsics::abort() } | ||
3418 | } | ||
3419 | ``` | ||
3420 | |||
3421 | In many cases, you may need to manually link to the `compiler_builtins` crate | ||
3422 | when building a `no_std` binary. You may observe this via linker error messages | ||
3423 | such as "```undefined reference to `__rust_probestack'```". | ||
3424 | |||
3425 | ## More about the language items | ||
3426 | |||
3427 | The compiler currently makes a few assumptions about symbols which are | ||
3428 | available in the executable to call. Normally these functions are provided by | ||
3429 | the standard library, but without it you must define your own. These symbols | ||
3430 | are called "language items", and they each have an internal name, and then a | ||
3431 | signature that an implementation must conform to. | ||
3432 | |||
3433 | The first of these functions, `rust_eh_personality`, is used by the failure | ||
3434 | mechanisms of the compiler. This is often mapped to GCC's personality function | ||
3435 | (see the [libstd implementation][unwind] for more information), but crates | ||
3436 | which do not trigger a panic can be assured that this function is never | ||
3437 | called. The language item's name is `eh_personality`. | ||
3438 | |||
3439 | [unwind]: https://github.com/rust-lang/rust/blob/master/library/panic_unwind/src/gcc.rs | ||
3440 | |||
3441 | The second function, `rust_begin_panic`, is also used by the failure mechanisms of the | ||
3442 | compiler. When a panic happens, this controls the message that's displayed on | ||
3443 | the screen. While the language item's name is `panic_impl`, the symbol name is | ||
3444 | `rust_begin_panic`. | ||
3445 | |||
3446 | Finally, a `eh_catch_typeinfo` static is needed for certain targets which | ||
3447 | implement Rust panics on top of C++ exceptions. | ||
3448 | |||
3449 | ## List of all language items | ||
3450 | |||
3451 | This is a list of all language items in Rust along with where they are located in | ||
3452 | the source code. | ||
3453 | |||
3454 | - Primitives | ||
3455 | - `i8`: `libcore/num/mod.rs` | ||
3456 | - `i16`: `libcore/num/mod.rs` | ||
3457 | - `i32`: `libcore/num/mod.rs` | ||
3458 | - `i64`: `libcore/num/mod.rs` | ||
3459 | - `i128`: `libcore/num/mod.rs` | ||
3460 | - `isize`: `libcore/num/mod.rs` | ||
3461 | - `u8`: `libcore/num/mod.rs` | ||
3462 | - `u16`: `libcore/num/mod.rs` | ||
3463 | - `u32`: `libcore/num/mod.rs` | ||
3464 | - `u64`: `libcore/num/mod.rs` | ||
3465 | - `u128`: `libcore/num/mod.rs` | ||
3466 | - `usize`: `libcore/num/mod.rs` | ||
3467 | - `f32`: `libstd/f32.rs` | ||
3468 | - `f64`: `libstd/f64.rs` | ||
3469 | - `char`: `libcore/char.rs` | ||
3470 | - `slice`: `liballoc/slice.rs` | ||
3471 | - `str`: `liballoc/str.rs` | ||
3472 | - `const_ptr`: `libcore/ptr.rs` | ||
3473 | - `mut_ptr`: `libcore/ptr.rs` | ||
3474 | - `unsafe_cell`: `libcore/cell.rs` | ||
3475 | - Runtime | ||
3476 | - `start`: `libstd/rt.rs` | ||
3477 | - `eh_personality`: `libpanic_unwind/emcc.rs` (EMCC) | ||
3478 | - `eh_personality`: `libpanic_unwind/gcc.rs` (GNU) | ||
3479 | - `eh_personality`: `libpanic_unwind/seh.rs` (SEH) | ||
3480 | - `eh_catch_typeinfo`: `libpanic_unwind/emcc.rs` (EMCC) | ||
3481 | - `panic`: `libcore/panicking.rs` | ||
3482 | - `panic_bounds_check`: `libcore/panicking.rs` | ||
3483 | - `panic_impl`: `libcore/panicking.rs` | ||
3484 | - `panic_impl`: `libstd/panicking.rs` | ||
3485 | - Allocations | ||
3486 | - `owned_box`: `liballoc/boxed.rs` | ||
3487 | - `exchange_malloc`: `liballoc/heap.rs` | ||
3488 | - `box_free`: `liballoc/heap.rs` | ||
3489 | - Operands | ||
3490 | - `not`: `libcore/ops/bit.rs` | ||
3491 | - `bitand`: `libcore/ops/bit.rs` | ||
3492 | - `bitor`: `libcore/ops/bit.rs` | ||
3493 | - `bitxor`: `libcore/ops/bit.rs` | ||
3494 | - `shl`: `libcore/ops/bit.rs` | ||
3495 | - `shr`: `libcore/ops/bit.rs` | ||
3496 | - `bitand_assign`: `libcore/ops/bit.rs` | ||
3497 | - `bitor_assign`: `libcore/ops/bit.rs` | ||
3498 | - `bitxor_assign`: `libcore/ops/bit.rs` | ||
3499 | - `shl_assign`: `libcore/ops/bit.rs` | ||
3500 | - `shr_assign`: `libcore/ops/bit.rs` | ||
3501 | - `deref`: `libcore/ops/deref.rs` | ||
3502 | - `deref_mut`: `libcore/ops/deref.rs` | ||
3503 | - `index`: `libcore/ops/index.rs` | ||
3504 | - `index_mut`: `libcore/ops/index.rs` | ||
3505 | - `add`: `libcore/ops/arith.rs` | ||
3506 | - `sub`: `libcore/ops/arith.rs` | ||
3507 | - `mul`: `libcore/ops/arith.rs` | ||
3508 | - `div`: `libcore/ops/arith.rs` | ||
3509 | - `rem`: `libcore/ops/arith.rs` | ||
3510 | - `neg`: `libcore/ops/arith.rs` | ||
3511 | - `add_assign`: `libcore/ops/arith.rs` | ||
3512 | - `sub_assign`: `libcore/ops/arith.rs` | ||
3513 | - `mul_assign`: `libcore/ops/arith.rs` | ||
3514 | - `div_assign`: `libcore/ops/arith.rs` | ||
3515 | - `rem_assign`: `libcore/ops/arith.rs` | ||
3516 | - `eq`: `libcore/cmp.rs` | ||
3517 | - `ord`: `libcore/cmp.rs` | ||
3518 | - Functions | ||
3519 | - `fn`: `libcore/ops/function.rs` | ||
3520 | - `fn_mut`: `libcore/ops/function.rs` | ||
3521 | - `fn_once`: `libcore/ops/function.rs` | ||
3522 | - `generator_state`: `libcore/ops/generator.rs` | ||
3523 | - `generator`: `libcore/ops/generator.rs` | ||
3524 | - Other | ||
3525 | - `coerce_unsized`: `libcore/ops/unsize.rs` | ||
3526 | - `drop`: `libcore/ops/drop.rs` | ||
3527 | - `drop_in_place`: `libcore/ptr.rs` | ||
3528 | - `clone`: `libcore/clone.rs` | ||
3529 | - `copy`: `libcore/marker.rs` | ||
3530 | - `send`: `libcore/marker.rs` | ||
3531 | - `sized`: `libcore/marker.rs` | ||
3532 | - `unsize`: `libcore/marker.rs` | ||
3533 | - `sync`: `libcore/marker.rs` | ||
3534 | - `phantom_data`: `libcore/marker.rs` | ||
3535 | - `discriminant_kind`: `libcore/marker.rs` | ||
3536 | - `freeze`: `libcore/marker.rs` | ||
3537 | - `debug_trait`: `libcore/fmt/mod.rs` | ||
3538 | - `non_zero`: `libcore/nonzero.rs` | ||
3539 | - `arc`: `liballoc/sync.rs` | ||
3540 | - `rc`: `liballoc/rc.rs` | ||
3541 | "##, | ||
3542 | }, | ||
3543 | Lint { | ||
3544 | label: "libstd_sys_internals", | ||
3545 | description: r##"# `libstd_sys_internals` | ||
3546 | |||
3547 | This feature is internal to the Rust compiler and is not intended for general use. | ||
3548 | |||
3549 | ------------------------ | ||
3550 | "##, | ||
3551 | }, | ||
3552 | Lint { | ||
3553 | label: "libstd_thread_internals", | ||
3554 | description: r##"# `libstd_thread_internals` | ||
3555 | |||
3556 | This feature is internal to the Rust compiler and is not intended for general use. | ||
3557 | |||
3558 | ------------------------ | ||
3559 | "##, | ||
3560 | }, | ||
3561 | Lint { | ||
3562 | label: "link_cfg", | ||
3563 | description: r##"# `link_cfg` | ||
3564 | |||
3565 | This feature is internal to the Rust compiler and is not intended for general use. | ||
3566 | |||
3567 | ------------------------ | ||
3568 | "##, | ||
3569 | }, | ||
3570 | Lint { | ||
3795 | label: "llvm_asm", | 3571 | label: "llvm_asm", |
3796 | description: r##"# `llvm_asm` | 3572 | description: r##"# `llvm_asm` |
3797 | 3573 | ||
@@ -3988,188 +3764,544 @@ If you need more power and don't mind losing some of the niceties of | |||
3988 | `llvm_asm!`, check out [global_asm](global-asm.md). | 3764 | `llvm_asm!`, check out [global_asm](global-asm.md). |
3989 | "##, | 3765 | "##, |
3990 | }, | 3766 | }, |
3991 | LintCompletion { | 3767 | Lint { |
3992 | label: "core_intrinsics", | 3768 | label: "marker_trait_attr", |
3993 | description: r##"# `core_intrinsics` | 3769 | description: r##"# `marker_trait_attr` |
3994 | 3770 | ||
3995 | This feature is internal to the Rust compiler and is not intended for general use. | 3771 | The tracking issue for this feature is: [#29864] |
3772 | |||
3773 | [#29864]: https://github.com/rust-lang/rust/issues/29864 | ||
3996 | 3774 | ||
3997 | ------------------------ | 3775 | ------------------------ |
3776 | |||
3777 | Normally, Rust keeps you from adding trait implementations that could | ||
3778 | overlap with each other, as it would be ambiguous which to use. This | ||
3779 | feature, however, carves out an exception to that rule: a trait can | ||
3780 | opt-in to having overlapping implementations, at the cost that those | ||
3781 | implementations are not allowed to override anything (and thus the | ||
3782 | trait itself cannot have any associated items, as they're pointless | ||
3783 | when they'd need to do the same thing for every type anyway). | ||
3784 | |||
3785 | ```rust | ||
3786 | #![feature(marker_trait_attr)] | ||
3787 | |||
3788 | #[marker] trait CheapToClone: Clone {} | ||
3789 | |||
3790 | impl<T: Copy> CheapToClone for T {} | ||
3791 | |||
3792 | // These could potentially overlap with the blanket implementation above, | ||
3793 | // so are only allowed because CheapToClone is a marker trait. | ||
3794 | impl<T: CheapToClone, U: CheapToClone> CheapToClone for (T, U) {} | ||
3795 | impl<T: CheapToClone> CheapToClone for std::ops::Range<T> {} | ||
3796 | |||
3797 | fn cheap_clone<T: CheapToClone>(t: T) -> T { | ||
3798 | t.clone() | ||
3799 | } | ||
3800 | ``` | ||
3801 | |||
3802 | This is expected to replace the unstable `overlapping_marker_traits` | ||
3803 | feature, which applied to all empty traits (without needing an opt-in). | ||
3998 | "##, | 3804 | "##, |
3999 | }, | 3805 | }, |
4000 | LintCompletion { | 3806 | Lint { |
4001 | label: "trace_macros", | 3807 | label: "native_link_modifiers", |
4002 | description: r##"# `trace_macros` | 3808 | description: r##"# `native_link_modifiers` |
4003 | 3809 | ||
4004 | The tracking issue for this feature is [#29598]. | 3810 | The tracking issue for this feature is: [#81490] |
4005 | 3811 | ||
4006 | [#29598]: https://github.com/rust-lang/rust/issues/29598 | 3812 | [#81490]: https://github.com/rust-lang/rust/issues/81490 |
4007 | 3813 | ||
4008 | ------------------------ | 3814 | ------------------------ |
4009 | 3815 | ||
4010 | With `trace_macros` you can trace the expansion of macros in your code. | 3816 | The `native_link_modifiers` feature allows you to use the `modifiers` syntax with the `#[link(..)]` attribute. |
4011 | 3817 | ||
4012 | ## Examples | 3818 | Modifiers are specified as a comma-delimited string with each modifier prefixed with either a `+` or `-` to indicate that the modifier is enabled or disabled, respectively. The last boolean value specified for a given modifier wins. |
3819 | "##, | ||
3820 | }, | ||
3821 | Lint { | ||
3822 | label: "native_link_modifiers_as_needed", | ||
3823 | description: r##"# `native_link_modifiers_as_needed` | ||
4013 | 3824 | ||
4014 | ```rust | 3825 | The tracking issue for this feature is: [#81490] |
4015 | #![feature(trace_macros)] | ||
4016 | 3826 | ||
4017 | fn main() { | 3827 | [#81490]: https://github.com/rust-lang/rust/issues/81490 |
4018 | trace_macros!(true); | ||
4019 | println!("Hello, Rust!"); | ||
4020 | trace_macros!(false); | ||
4021 | } | ||
4022 | ``` | ||
4023 | 3828 | ||
4024 | The `cargo build` output: | 3829 | ------------------------ |
4025 | 3830 | ||
4026 | ```txt | 3831 | The `native_link_modifiers_as_needed` feature allows you to use the `as-needed` modifier. |
4027 | note: trace_macro | ||
4028 | --> src/main.rs:5:5 | ||
4029 | | | ||
4030 | 5 | println!("Hello, Rust!"); | ||
4031 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
4032 | | | ||
4033 | = note: expanding `println! { "Hello, Rust!" }` | ||
4034 | = note: to `print ! ( concat ! ( "Hello, Rust!" , "\n" ) )` | ||
4035 | = note: expanding `print! { concat ! ( "Hello, Rust!" , "\n" ) }` | ||
4036 | = note: to `$crate :: io :: _print ( format_args ! ( concat ! ( "Hello, Rust!" , "\n" ) ) | ||
4037 | )` | ||
4038 | 3832 | ||
4039 | Finished dev [unoptimized + debuginfo] target(s) in 0.60 secs | 3833 | `as-needed` is only compatible with the `dynamic` and `framework` linking kinds. Using any other kind will result in a compiler error. |
4040 | ``` | 3834 | |
3835 | `+as-needed` means that the library will be actually linked only if it satisfies some undefined symbols at the point at which it is specified on the command line, making it similar to static libraries in this regard. | ||
3836 | |||
3837 | This modifier translates to `--as-needed` for ld-like linkers, and to `-dead_strip_dylibs` / `-needed_library` / `-needed_framework` for ld64. | ||
3838 | The modifier does nothing for linkers that don't support it (e.g. `link.exe`). | ||
3839 | |||
3840 | The default for this modifier is unclear, some targets currently specify it as `+as-needed`, some do not. We may want to try making `+as-needed` a default for all targets. | ||
4041 | "##, | 3841 | "##, |
4042 | }, | 3842 | }, |
4043 | LintCompletion { | 3843 | Lint { |
4044 | label: "update_panic_count", | 3844 | label: "native_link_modifiers_bundle", |
4045 | description: r##"# `update_panic_count` | 3845 | description: r##"# `native_link_modifiers_bundle` |
4046 | 3846 | ||
4047 | This feature is internal to the Rust compiler and is not intended for general use. | 3847 | The tracking issue for this feature is: [#81490] |
3848 | |||
3849 | [#81490]: https://github.com/rust-lang/rust/issues/81490 | ||
4048 | 3850 | ||
4049 | ------------------------ | 3851 | ------------------------ |
3852 | |||
3853 | The `native_link_modifiers_bundle` feature allows you to use the `bundle` modifier. | ||
3854 | |||
3855 | Only compatible with the `static` linking kind. Using any other kind will result in a compiler error. | ||
3856 | |||
3857 | `+bundle` means objects from the static library are bundled into the produced crate (a rlib, for example) and are used from this crate later during linking of the final binary. | ||
3858 | |||
3859 | `-bundle` means the static library is included into the produced rlib "by name" and object files from it are included only during linking of the final binary, the file search by that name is also performed during final linking. | ||
3860 | |||
3861 | This modifier is supposed to supersede the `static-nobundle` linking kind defined by [RFC 1717](https://github.com/rust-lang/rfcs/pull/1717). | ||
3862 | |||
3863 | The default for this modifier is currently `+bundle`, but it could be changed later on some future edition boundary. | ||
4050 | "##, | 3864 | "##, |
4051 | }, | 3865 | }, |
4052 | LintCompletion { | 3866 | Lint { |
4053 | label: "core_private_bignum", | 3867 | label: "native_link_modifiers_verbatim", |
4054 | description: r##"# `core_private_bignum` | 3868 | description: r##"# `native_link_modifiers_verbatim` |
4055 | 3869 | ||
4056 | This feature is internal to the Rust compiler and is not intended for general use. | 3870 | The tracking issue for this feature is: [#81490] |
3871 | |||
3872 | [#81490]: https://github.com/rust-lang/rust/issues/81490 | ||
4057 | 3873 | ||
4058 | ------------------------ | 3874 | ------------------------ |
3875 | |||
3876 | The `native_link_modifiers_verbatim` feature allows you to use the `verbatim` modifier. | ||
3877 | |||
3878 | `+verbatim` means that rustc itself won't add any target-specified library prefixes or suffixes (like `lib` or `.a`) to the library name, and will try its best to ask for the same thing from the linker. | ||
3879 | |||
3880 | For `ld`-like linkers rustc will use the `-l:filename` syntax (note the colon) when passing the library, so the linker won't add any prefixes or suffixes as well. | ||
3881 | See [`-l namespec`](https://sourceware.org/binutils/docs/ld/Options.html) in ld documentation for more details. | ||
3882 | For linkers not supporting any verbatim modifiers (e.g. `link.exe` or `ld64`) the library name will be passed as is. | ||
3883 | |||
3884 | The default for this modifier is `-verbatim`. | ||
3885 | |||
3886 | This RFC changes the behavior of `raw-dylib` linking kind specified by [RFC 2627](https://github.com/rust-lang/rfcs/pull/2627). The `.dll` suffix (or other target-specified suffixes for other targets) is now added automatically. | ||
3887 | If your DLL doesn't have the `.dll` suffix, it can be specified with `+verbatim`. | ||
4059 | "##, | 3888 | "##, |
4060 | }, | 3889 | }, |
4061 | LintCompletion { | 3890 | Lint { |
4062 | label: "sort_internals", | 3891 | label: "native_link_modifiers_whole_archive", |
4063 | description: r##"# `sort_internals` | 3892 | description: r##"# `native_link_modifiers_whole_archive` |
4064 | 3893 | ||
4065 | This feature is internal to the Rust compiler and is not intended for general use. | 3894 | The tracking issue for this feature is: [#81490] |
3895 | |||
3896 | [#81490]: https://github.com/rust-lang/rust/issues/81490 | ||
4066 | 3897 | ||
4067 | ------------------------ | 3898 | ------------------------ |
3899 | |||
3900 | The `native_link_modifiers_whole_archive` feature allows you to use the `whole-archive` modifier. | ||
3901 | |||
3902 | Only compatible with the `static` linking kind. Using any other kind will result in a compiler error. | ||
3903 | |||
3904 | `+whole-archive` means that the static library is linked as a whole archive without throwing any object files away. | ||
3905 | |||
3906 | This modifier translates to `--whole-archive` for `ld`-like linkers, to `/WHOLEARCHIVE` for `link.exe`, and to `-force_load` for `ld64`. | ||
3907 | The modifier does nothing for linkers that don't support it. | ||
3908 | |||
3909 | The default for this modifier is `-whole-archive`. | ||
4068 | "##, | 3910 | "##, |
4069 | }, | 3911 | }, |
4070 | LintCompletion { | 3912 | Lint { |
4071 | label: "windows_net", | 3913 | label: "negative_impls", |
4072 | description: r##"# `windows_net` | 3914 | description: r##"# `negative_impls` |
4073 | 3915 | ||
4074 | This feature is internal to the Rust compiler and is not intended for general use. | 3916 | The tracking issue for this feature is [#68318]. |
4075 | 3917 | ||
4076 | ------------------------ | 3918 | [#68318]: https://github.com/rust-lang/rust/issues/68318 |
3919 | |||
3920 | ---- | ||
3921 | |||
3922 | With the feature gate `negative_impls`, you can write negative impls as well as positive ones: | ||
3923 | |||
3924 | ```rust | ||
3925 | #![feature(negative_impls)] | ||
3926 | trait DerefMut { } | ||
3927 | impl<T: ?Sized> !DerefMut for &T { } | ||
3928 | ``` | ||
3929 | |||
3930 | Negative impls indicate a semver guarantee that the given trait will not be implemented for the given types. Negative impls play an additional purpose for auto traits, described below. | ||
3931 | |||
3932 | Negative impls have the following characteristics: | ||
3933 | |||
3934 | * They do not have any items. | ||
3935 | * They must obey the orphan rules as if they were a positive impl. | ||
3936 | * They cannot "overlap" with any positive impls. | ||
3937 | |||
3938 | ## Semver interaction | ||
3939 | |||
3940 | It is a breaking change to remove a negative impl. Negative impls are a commitment not to implement the given trait for the named types. | ||
3941 | |||
3942 | ## Orphan and overlap rules | ||
3943 | |||
3944 | Negative impls must obey the same orphan rules as a positive impl. This implies you cannot add a negative impl for types defined in upstream crates and so forth. | ||
3945 | |||
3946 | Similarly, negative impls cannot overlap with positive impls, again using the same "overlap" check that we ordinarily use to determine if two impls overlap. (Note that positive impls typically cannot overlap with one another either, except as permitted by specialization.) | ||
3947 | |||
3948 | ## Interaction with auto traits | ||
3949 | |||
3950 | Declaring a negative impl `impl !SomeAutoTrait for SomeType` for an | ||
3951 | auto-trait serves two purposes: | ||
3952 | |||
3953 | * as with any trait, it declares that `SomeType` will never implement `SomeAutoTrait`; | ||
3954 | * it disables the automatic `SomeType: SomeAutoTrait` impl that would otherwise have been generated. | ||
3955 | |||
3956 | Note that, at present, there is no way to indicate that a given type | ||
3957 | does not implement an auto trait *but that it may do so in the | ||
3958 | future*. For ordinary types, this is done by simply not declaring any | ||
3959 | impl at all, but that is not an option for auto traits. A workaround | ||
3960 | is that one could embed a marker type as one of the fields, where the | ||
3961 | marker type is `!AutoTrait`. | ||
3962 | |||
3963 | ## Immediate uses | ||
3964 | |||
3965 | Negative impls are used to declare that `&T: !DerefMut` and `&mut T: !Clone`, as required to fix the soundness of `Pin` described in [#66544](https://github.com/rust-lang/rust/issues/66544). | ||
3966 | |||
3967 | This serves two purposes: | ||
3968 | |||
3969 | * For proving the correctness of unsafe code, we can use that impl as evidence that no `DerefMut` or `Clone` impl exists. | ||
3970 | * It prevents downstream crates from creating such impls. | ||
4077 | "##, | 3971 | "##, |
4078 | }, | 3972 | }, |
4079 | LintCompletion { | 3973 | Lint { |
4080 | label: "c_variadic", | 3974 | label: "no_coverage", |
4081 | description: r##"# `c_variadic` | 3975 | description: r##"# `no_coverage` |
4082 | 3976 | ||
4083 | The tracking issue for this feature is: [#44930] | 3977 | The tracking issue for this feature is: [#84605] |
4084 | 3978 | ||
4085 | [#44930]: https://github.com/rust-lang/rust/issues/44930 | 3979 | [#84605]: https://github.com/rust-lang/rust/issues/84605 |
3980 | |||
3981 | --- | ||
3982 | |||
3983 | The `no_coverage` attribute can be used to selectively disable coverage | ||
3984 | instrumentation in an annotated function. This might be useful to: | ||
3985 | |||
3986 | - Avoid instrumentation overhead in a performance critical function | ||
3987 | - Avoid generating coverage for a function that is not meant to be executed, | ||
3988 | but still target 100% coverage for the rest of the program. | ||
3989 | |||
3990 | ## Example | ||
3991 | |||
3992 | ```rust | ||
3993 | #![feature(no_coverage)] | ||
3994 | |||
3995 | // `foo()` will get coverage instrumentation (by default) | ||
3996 | fn foo() { | ||
3997 | // ... | ||
3998 | } | ||
3999 | |||
4000 | #[no_coverage] | ||
4001 | fn bar() { | ||
4002 | // ... | ||
4003 | } | ||
4004 | ``` | ||
4005 | "##, | ||
4006 | }, | ||
4007 | Lint { | ||
4008 | label: "no_sanitize", | ||
4009 | description: r##"# `no_sanitize` | ||
4010 | |||
4011 | The tracking issue for this feature is: [#39699] | ||
4012 | |||
4013 | [#39699]: https://github.com/rust-lang/rust/issues/39699 | ||
4086 | 4014 | ||
4087 | ------------------------ | 4015 | ------------------------ |
4088 | 4016 | ||
4089 | The `c_variadic` library feature exposes the `VaList` structure, | 4017 | The `no_sanitize` attribute can be used to selectively disable sanitizer |
4090 | Rust's analogue of C's `va_list` type. | 4018 | instrumentation in an annotated function. This might be useful to: avoid |
4019 | instrumentation overhead in a performance critical function, or avoid | ||
4020 | instrumenting code that contains constructs unsupported by given sanitizer. | ||
4021 | |||
4022 | The precise effect of this annotation depends on particular sanitizer in use. | ||
4023 | For example, with `no_sanitize(thread)`, the thread sanitizer will no longer | ||
4024 | instrument non-atomic store / load operations, but it will instrument atomic | ||
4025 | operations to avoid reporting false positives and provide meaning full stack | ||
4026 | traces. | ||
4091 | 4027 | ||
4092 | ## Examples | 4028 | ## Examples |
4093 | 4029 | ||
4094 | ```rust | 4030 | ``` rust |
4095 | #![feature(c_variadic)] | 4031 | #![feature(no_sanitize)] |
4096 | 4032 | ||
4097 | use std::ffi::VaList; | 4033 | #[no_sanitize(address)] |
4034 | fn foo() { | ||
4035 | // ... | ||
4036 | } | ||
4037 | ``` | ||
4038 | "##, | ||
4039 | }, | ||
4040 | Lint { | ||
4041 | label: "plugin", | ||
4042 | description: r##"# `plugin` | ||
4098 | 4043 | ||
4099 | pub unsafe extern "C" fn vadd(n: usize, mut args: VaList) -> usize { | 4044 | The tracking issue for this feature is: [#29597] |
4100 | let mut sum = 0; | 4045 | |
4101 | for _ in 0..n { | 4046 | [#29597]: https://github.com/rust-lang/rust/issues/29597 |
4102 | sum += args.arg::<usize>(); | 4047 | |
4048 | |||
4049 | This feature is part of "compiler plugins." It will often be used with the | ||
4050 | [`plugin_registrar`] and `rustc_private` features. | ||
4051 | |||
4052 | [`plugin_registrar`]: plugin-registrar.md | ||
4053 | |||
4054 | ------------------------ | ||
4055 | |||
4056 | `rustc` can load compiler plugins, which are user-provided libraries that | ||
4057 | extend the compiler's behavior with new lint checks, etc. | ||
4058 | |||
4059 | A plugin is a dynamic library crate with a designated *registrar* function that | ||
4060 | registers extensions with `rustc`. Other crates can load these extensions using | ||
4061 | the crate attribute `#![plugin(...)]`. See the | ||
4062 | `rustc_driver::plugin` documentation for more about the | ||
4063 | mechanics of defining and loading a plugin. | ||
4064 | |||
4065 | In the vast majority of cases, a plugin should *only* be used through | ||
4066 | `#![plugin]` and not through an `extern crate` item. Linking a plugin would | ||
4067 | pull in all of librustc_ast and librustc as dependencies of your crate. This is | ||
4068 | generally unwanted unless you are building another plugin. | ||
4069 | |||
4070 | The usual practice is to put compiler plugins in their own crate, separate from | ||
4071 | any `macro_rules!` macros or ordinary Rust code meant to be used by consumers | ||
4072 | of a library. | ||
4073 | |||
4074 | # Lint plugins | ||
4075 | |||
4076 | Plugins can extend [Rust's lint | ||
4077 | infrastructure](../../reference/attributes/diagnostics.md#lint-check-attributes) with | ||
4078 | additional checks for code style, safety, etc. Now let's write a plugin | ||
4079 | [`lint-plugin-test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/ui-fulldeps/auxiliary/lint-plugin-test.rs) | ||
4080 | that warns about any item named `lintme`. | ||
4081 | |||
4082 | ```rust,ignore (requires-stage-2) | ||
4083 | #![feature(plugin_registrar)] | ||
4084 | #![feature(box_syntax, rustc_private)] | ||
4085 | |||
4086 | extern crate rustc_ast; | ||
4087 | |||
4088 | // Load rustc as a plugin to get macros | ||
4089 | extern crate rustc_driver; | ||
4090 | #[macro_use] | ||
4091 | extern crate rustc_lint; | ||
4092 | #[macro_use] | ||
4093 | extern crate rustc_session; | ||
4094 | |||
4095 | use rustc_driver::plugin::Registry; | ||
4096 | use rustc_lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass}; | ||
4097 | use rustc_ast::ast; | ||
4098 | declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'"); | ||
4099 | |||
4100 | declare_lint_pass!(Pass => [TEST_LINT]); | ||
4101 | |||
4102 | impl EarlyLintPass for Pass { | ||
4103 | fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) { | ||
4104 | if it.ident.name.as_str() == "lintme" { | ||
4105 | cx.lint(TEST_LINT, |lint| { | ||
4106 | lint.build("item is named 'lintme'").set_span(it.span).emit() | ||
4107 | }); | ||
4108 | } | ||
4103 | } | 4109 | } |
4104 | sum | ||
4105 | } | 4110 | } |
4111 | |||
4112 | #[plugin_registrar] | ||
4113 | pub fn plugin_registrar(reg: &mut Registry) { | ||
4114 | reg.lint_store.register_lints(&[&TEST_LINT]); | ||
4115 | reg.lint_store.register_early_pass(|| box Pass); | ||
4116 | } | ||
4117 | ``` | ||
4118 | |||
4119 | Then code like | ||
4120 | |||
4121 | ```rust,ignore (requires-plugin) | ||
4122 | #![feature(plugin)] | ||
4123 | #![plugin(lint_plugin_test)] | ||
4124 | |||
4125 | fn lintme() { } | ||
4126 | ``` | ||
4127 | |||
4128 | will produce a compiler warning: | ||
4129 | |||
4130 | ```txt | ||
4131 | foo.rs:4:1: 4:16 warning: item is named 'lintme', #[warn(test_lint)] on by default | ||
4132 | foo.rs:4 fn lintme() { } | ||
4133 | ^~~~~~~~~~~~~~~ | ||
4106 | ``` | 4134 | ``` |
4135 | |||
4136 | The components of a lint plugin are: | ||
4137 | |||
4138 | * one or more `declare_lint!` invocations, which define static `Lint` structs; | ||
4139 | |||
4140 | * a struct holding any state needed by the lint pass (here, none); | ||
4141 | |||
4142 | * a `LintPass` | ||
4143 | implementation defining how to check each syntax element. A single | ||
4144 | `LintPass` may call `span_lint` for several different `Lint`s, but should | ||
4145 | register them all through the `get_lints` method. | ||
4146 | |||
4147 | Lint passes are syntax traversals, but they run at a late stage of compilation | ||
4148 | where type information is available. `rustc`'s [built-in | ||
4149 | lints](https://github.com/rust-lang/rust/blob/master/src/librustc_session/lint/builtin.rs) | ||
4150 | mostly use the same infrastructure as lint plugins, and provide examples of how | ||
4151 | to access type information. | ||
4152 | |||
4153 | Lints defined by plugins are controlled by the usual [attributes and compiler | ||
4154 | flags](../../reference/attributes/diagnostics.md#lint-check-attributes), e.g. | ||
4155 | `#[allow(test_lint)]` or `-A test-lint`. These identifiers are derived from the | ||
4156 | first argument to `declare_lint!`, with appropriate case and punctuation | ||
4157 | conversion. | ||
4158 | |||
4159 | You can run `rustc -W help foo.rs` to see a list of lints known to `rustc`, | ||
4160 | including those provided by plugins loaded by `foo.rs`. | ||
4107 | "##, | 4161 | "##, |
4108 | }, | 4162 | }, |
4109 | LintCompletion { | 4163 | Lint { |
4110 | label: "core_private_diy_float", | 4164 | label: "plugin_registrar", |
4111 | description: r##"# `core_private_diy_float` | 4165 | description: r##"# `plugin_registrar` |
4112 | 4166 | ||
4113 | This feature is internal to the Rust compiler and is not intended for general use. | 4167 | The tracking issue for this feature is: [#29597] |
4168 | |||
4169 | [#29597]: https://github.com/rust-lang/rust/issues/29597 | ||
4170 | |||
4171 | This feature is part of "compiler plugins." It will often be used with the | ||
4172 | [`plugin`] and `rustc_private` features as well. For more details, see | ||
4173 | their docs. | ||
4174 | |||
4175 | [`plugin`]: plugin.md | ||
4114 | 4176 | ||
4115 | ------------------------ | 4177 | ------------------------ |
4116 | "##, | 4178 | "##, |
4117 | }, | 4179 | }, |
4118 | LintCompletion { | 4180 | Lint { |
4119 | label: "profiler_runtime_lib", | 4181 | label: "print_internals", |
4120 | description: r##"# `profiler_runtime_lib` | 4182 | description: r##"# `print_internals` |
4121 | 4183 | ||
4122 | This feature is internal to the Rust compiler and is not intended for general use. | 4184 | This feature is internal to the Rust compiler and is not intended for general use. |
4123 | 4185 | ||
4124 | ------------------------ | 4186 | ------------------------ |
4125 | "##, | 4187 | "##, |
4126 | }, | 4188 | }, |
4127 | LintCompletion { | 4189 | Lint { |
4128 | label: "thread_local_internals", | 4190 | label: "profiler_runtime", |
4129 | description: r##"# `thread_local_internals` | 4191 | description: r##"# `profiler_runtime` |
4130 | 4192 | ||
4131 | This feature is internal to the Rust compiler and is not intended for general use. | 4193 | The tracking issue for this feature is: [#42524](https://github.com/rust-lang/rust/issues/42524). |
4132 | 4194 | ||
4133 | ------------------------ | 4195 | ------------------------ |
4134 | "##, | 4196 | "##, |
4135 | }, | 4197 | }, |
4136 | LintCompletion { | 4198 | Lint { |
4137 | label: "int_error_internals", | 4199 | label: "profiler_runtime_lib", |
4138 | description: r##"# `int_error_internals` | 4200 | description: r##"# `profiler_runtime_lib` |
4139 | 4201 | ||
4140 | This feature is internal to the Rust compiler and is not intended for general use. | 4202 | This feature is internal to the Rust compiler and is not intended for general use. |
4141 | 4203 | ||
4142 | ------------------------ | 4204 | ------------------------ |
4143 | "##, | 4205 | "##, |
4144 | }, | 4206 | }, |
4145 | LintCompletion { | 4207 | Lint { |
4146 | label: "windows_stdio", | 4208 | label: "repr128", |
4147 | description: r##"# `windows_stdio` | 4209 | description: r##"# `repr128` |
4148 | 4210 | ||
4149 | This feature is internal to the Rust compiler and is not intended for general use. | 4211 | The tracking issue for this feature is: [#56071] |
4212 | |||
4213 | [#56071]: https://github.com/rust-lang/rust/issues/56071 | ||
4150 | 4214 | ||
4151 | ------------------------ | 4215 | ------------------------ |
4216 | |||
4217 | The `repr128` feature adds support for `#[repr(u128)]` on `enum`s. | ||
4218 | |||
4219 | ```rust | ||
4220 | #![feature(repr128)] | ||
4221 | |||
4222 | #[repr(u128)] | ||
4223 | enum Foo { | ||
4224 | Bar(u64), | ||
4225 | } | ||
4226 | ``` | ||
4152 | "##, | 4227 | "##, |
4153 | }, | 4228 | }, |
4154 | LintCompletion { | 4229 | Lint { |
4155 | label: "fmt_internals", | 4230 | label: "rt", |
4156 | description: r##"# `fmt_internals` | 4231 | description: r##"# `rt` |
4157 | 4232 | ||
4158 | This feature is internal to the Rust compiler and is not intended for general use. | 4233 | This feature is internal to the Rust compiler and is not intended for general use. |
4159 | 4234 | ||
4160 | ------------------------ | 4235 | ------------------------ |
4161 | "##, | 4236 | "##, |
4162 | }, | 4237 | }, |
4163 | LintCompletion { | 4238 | Lint { |
4164 | label: "fd_read", | 4239 | label: "rustc_attrs", |
4165 | description: r##"# `fd_read` | 4240 | description: r##"# `rustc_attrs` |
4241 | |||
4242 | This feature has no tracking issue, and is therefore internal to | ||
4243 | the compiler, not being intended for general use. | ||
4244 | |||
4245 | Note: `rustc_attrs` enables many rustc-internal attributes and this page | ||
4246 | only discuss a few of them. | ||
4247 | |||
4248 | ------------------------ | ||
4249 | |||
4250 | The `rustc_attrs` feature allows debugging rustc type layouts by using | ||
4251 | `#[rustc_layout(...)]` to debug layout at compile time (it even works | ||
4252 | with `cargo check`) as an alternative to `rustc -Z print-type-sizes` | ||
4253 | that is way more verbose. | ||
4254 | |||
4255 | Options provided by `#[rustc_layout(...)]` are `debug`, `size`, `align`, | ||
4256 | `abi`. Note that it only works on sized types without generics. | ||
4257 | |||
4258 | ## Examples | ||
4259 | |||
4260 | ```rust,compile_fail | ||
4261 | #![feature(rustc_attrs)] | ||
4262 | |||
4263 | #[rustc_layout(abi, size)] | ||
4264 | pub enum X { | ||
4265 | Y(u8, u8, u8), | ||
4266 | Z(isize), | ||
4267 | } | ||
4268 | ``` | ||
4269 | |||
4270 | When that is compiled, the compiler will error with something like | ||
4271 | |||
4272 | ```text | ||
4273 | error: abi: Aggregate { sized: true } | ||
4274 | --> src/lib.rs:4:1 | ||
4275 | | | ||
4276 | 4 | / pub enum T { | ||
4277 | 5 | | Y(u8, u8, u8), | ||
4278 | 6 | | Z(isize), | ||
4279 | 7 | | } | ||
4280 | | |_^ | ||
4281 | |||
4282 | error: size: Size { raw: 16 } | ||
4283 | --> src/lib.rs:4:1 | ||
4284 | | | ||
4285 | 4 | / pub enum T { | ||
4286 | 5 | | Y(u8, u8, u8), | ||
4287 | 6 | | Z(isize), | ||
4288 | 7 | | } | ||
4289 | | |_^ | ||
4290 | |||
4291 | error: aborting due to 2 previous errors | ||
4292 | ``` | ||
4293 | "##, | ||
4294 | }, | ||
4295 | Lint { | ||
4296 | label: "sort_internals", | ||
4297 | description: r##"# `sort_internals` | ||
4166 | 4298 | ||
4167 | This feature is internal to the Rust compiler and is not intended for general use. | 4299 | This feature is internal to the Rust compiler and is not intended for general use. |
4168 | 4300 | ||
4169 | ------------------------ | 4301 | ------------------------ |
4170 | "##, | 4302 | "##, |
4171 | }, | 4303 | }, |
4172 | LintCompletion { | 4304 | Lint { |
4173 | label: "str_internals", | 4305 | label: "str_internals", |
4174 | description: r##"# `str_internals` | 4306 | description: r##"# `str_internals` |
4175 | 4307 | ||
@@ -4178,7 +4310,7 @@ This feature is internal to the Rust compiler and is not intended for general us | |||
4178 | ------------------------ | 4310 | ------------------------ |
4179 | "##, | 4311 | "##, |
4180 | }, | 4312 | }, |
4181 | LintCompletion { | 4313 | Lint { |
4182 | label: "test", | 4314 | label: "test", |
4183 | description: r##"# `test` | 4315 | description: r##"# `test` |
4184 | 4316 | ||
@@ -4340,101 +4472,218 @@ However, the optimizer can still modify a testcase in an undesirable manner | |||
4340 | even when using either of the above. | 4472 | even when using either of the above. |
4341 | "##, | 4473 | "##, |
4342 | }, | 4474 | }, |
4343 | LintCompletion { | 4475 | Lint { |
4344 | label: "windows_c", | 4476 | label: "thread_local_internals", |
4345 | description: r##"# `windows_c` | 4477 | description: r##"# `thread_local_internals` |
4346 | 4478 | ||
4347 | This feature is internal to the Rust compiler and is not intended for general use. | 4479 | This feature is internal to the Rust compiler and is not intended for general use. |
4348 | 4480 | ||
4349 | ------------------------ | 4481 | ------------------------ |
4350 | "##, | 4482 | "##, |
4351 | }, | 4483 | }, |
4352 | LintCompletion { | 4484 | Lint { |
4353 | label: "dec2flt", | 4485 | label: "trace_macros", |
4354 | description: r##"# `dec2flt` | 4486 | description: r##"# `trace_macros` |
4355 | 4487 | ||
4356 | This feature is internal to the Rust compiler and is not intended for general use. | 4488 | The tracking issue for this feature is [#29598]. |
4489 | |||
4490 | [#29598]: https://github.com/rust-lang/rust/issues/29598 | ||
4357 | 4491 | ||
4358 | ------------------------ | 4492 | ------------------------ |
4359 | "##, | ||
4360 | }, | ||
4361 | LintCompletion { | ||
4362 | label: "derive_clone_copy", | ||
4363 | description: r##"# `derive_clone_copy` | ||
4364 | 4493 | ||
4365 | This feature is internal to the Rust compiler and is not intended for general use. | 4494 | With `trace_macros` you can trace the expansion of macros in your code. |
4366 | 4495 | ||
4367 | ------------------------ | 4496 | ## Examples |
4368 | "##, | ||
4369 | }, | ||
4370 | LintCompletion { | ||
4371 | label: "allocator_api", | ||
4372 | description: r##"# `allocator_api` | ||
4373 | 4497 | ||
4374 | The tracking issue for this feature is [#32838] | 4498 | ```rust |
4499 | #![feature(trace_macros)] | ||
4375 | 4500 | ||
4376 | [#32838]: https://github.com/rust-lang/rust/issues/32838 | 4501 | fn main() { |
4502 | trace_macros!(true); | ||
4503 | println!("Hello, Rust!"); | ||
4504 | trace_macros!(false); | ||
4505 | } | ||
4506 | ``` | ||
4377 | 4507 | ||
4378 | ------------------------ | 4508 | The `cargo build` output: |
4379 | 4509 | ||
4380 | Sometimes you want the memory for one collection to use a different | 4510 | ```txt |
4381 | allocator than the memory for another collection. In this case, | 4511 | note: trace_macro |
4382 | replacing the global allocator is not a workable option. Instead, | 4512 | --> src/main.rs:5:5 |
4383 | you need to pass in an instance of an `AllocRef` to each collection | 4513 | | |
4384 | for which you want a custom allocator. | 4514 | 5 | println!("Hello, Rust!"); |
4515 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
4516 | | | ||
4517 | = note: expanding `println! { "Hello, Rust!" }` | ||
4518 | = note: to `print ! ( concat ! ( "Hello, Rust!" , "\n" ) )` | ||
4519 | = note: expanding `print! { concat ! ( "Hello, Rust!" , "\n" ) }` | ||
4520 | = note: to `$crate :: io :: _print ( format_args ! ( concat ! ( "Hello, Rust!" , "\n" ) ) | ||
4521 | )` | ||
4385 | 4522 | ||
4386 | TBD | 4523 | Finished dev [unoptimized + debuginfo] target(s) in 0.60 secs |
4524 | ``` | ||
4387 | "##, | 4525 | "##, |
4388 | }, | 4526 | }, |
4389 | LintCompletion { | 4527 | Lint { |
4390 | label: "core_panic", | 4528 | label: "trait_alias", |
4391 | description: r##"# `core_panic` | 4529 | description: r##"# `trait_alias` |
4392 | 4530 | ||
4393 | This feature is internal to the Rust compiler and is not intended for general use. | 4531 | The tracking issue for this feature is: [#41517] |
4532 | |||
4533 | [#41517]: https://github.com/rust-lang/rust/issues/41517 | ||
4394 | 4534 | ||
4395 | ------------------------ | 4535 | ------------------------ |
4536 | |||
4537 | The `trait_alias` feature adds support for trait aliases. These allow aliases | ||
4538 | to be created for one or more traits (currently just a single regular trait plus | ||
4539 | any number of auto-traits), and used wherever traits would normally be used as | ||
4540 | either bounds or trait objects. | ||
4541 | |||
4542 | ```rust | ||
4543 | #![feature(trait_alias)] | ||
4544 | |||
4545 | trait Foo = std::fmt::Debug + Send; | ||
4546 | trait Bar = Foo + Sync; | ||
4547 | |||
4548 | // Use trait alias as bound on type parameter. | ||
4549 | fn foo<T: Foo>(v: &T) { | ||
4550 | println!("{:?}", v); | ||
4551 | } | ||
4552 | |||
4553 | pub fn main() { | ||
4554 | foo(&1); | ||
4555 | |||
4556 | // Use trait alias for trait objects. | ||
4557 | let a: &Bar = &123; | ||
4558 | println!("{:?}", a); | ||
4559 | let b = Box::new(456) as Box<dyn Foo>; | ||
4560 | println!("{:?}", b); | ||
4561 | } | ||
4562 | ``` | ||
4396 | "##, | 4563 | "##, |
4397 | }, | 4564 | }, |
4398 | LintCompletion { | 4565 | Lint { |
4399 | label: "fn_traits", | 4566 | label: "transparent_unions", |
4400 | description: r##"# `fn_traits` | 4567 | description: r##"# `transparent_unions` |
4401 | |||
4402 | The tracking issue for this feature is [#29625] | ||
4403 | 4568 | ||
4404 | See Also: [`unboxed_closures`](../language-features/unboxed-closures.md) | 4569 | The tracking issue for this feature is [#60405] |
4405 | 4570 | ||
4406 | [#29625]: https://github.com/rust-lang/rust/issues/29625 | 4571 | [#60405]: https://github.com/rust-lang/rust/issues/60405 |
4407 | 4572 | ||
4408 | ---- | 4573 | ---- |
4409 | 4574 | ||
4410 | The `fn_traits` feature allows for implementation of the [`Fn*`] traits | 4575 | The `transparent_unions` feature allows you mark `union`s as |
4411 | for creating custom closure-like types. | 4576 | `#[repr(transparent)]`. A `union` may be `#[repr(transparent)]` in exactly the |
4412 | 4577 | same conditions in which a `struct` may be `#[repr(transparent)]` (generally, | |
4413 | [`Fn*`]: https://doc.rust-lang.org/std/ops/trait.Fn.html | 4578 | this means the `union` must have exactly one non-zero-sized field). Some |
4579 | concrete illustrations follow. | ||
4414 | 4580 | ||
4415 | ```rust | 4581 | ```rust |
4416 | #![feature(unboxed_closures)] | 4582 | #![feature(transparent_unions)] |
4417 | #![feature(fn_traits)] | ||
4418 | 4583 | ||
4419 | struct Adder { | 4584 | // This union has the same representation as `f32`. |
4420 | a: u32 | 4585 | #[repr(transparent)] |
4586 | union SingleFieldUnion { | ||
4587 | field: f32, | ||
4421 | } | 4588 | } |
4422 | 4589 | ||
4423 | impl FnOnce<(u32, )> for Adder { | 4590 | // This union has the same representation as `usize`. |
4424 | type Output = u32; | 4591 | #[repr(transparent)] |
4425 | extern "rust-call" fn call_once(self, b: (u32, )) -> Self::Output { | 4592 | union MultiFieldUnion { |
4426 | self.a + b.0 | 4593 | field: usize, |
4427 | } | 4594 | nothing: (), |
4428 | } | 4595 | } |
4596 | ``` | ||
4429 | 4597 | ||
4430 | fn main() { | 4598 | For consistency with transparent `struct`s, `union`s must have exactly one |
4431 | let adder = Adder { a: 3 }; | 4599 | non-zero-sized field. If all fields are zero-sized, the `union` must not be |
4432 | assert_eq!(adder(2), 5); | 4600 | `#[repr(transparent)]`: |
4601 | |||
4602 | ```rust | ||
4603 | #![feature(transparent_unions)] | ||
4604 | |||
4605 | // This (non-transparent) union is already valid in stable Rust: | ||
4606 | pub union GoodUnion { | ||
4607 | pub nothing: (), | ||
4433 | } | 4608 | } |
4609 | |||
4610 | // Error: transparent union needs exactly one non-zero-sized field, but has 0 | ||
4611 | // #[repr(transparent)] | ||
4612 | // pub union BadUnion { | ||
4613 | // pub nothing: (), | ||
4614 | // } | ||
4434 | ``` | 4615 | ``` |
4616 | |||
4617 | The one exception is if the `union` is generic over `T` and has a field of type | ||
4618 | `T`, it may be `#[repr(transparent)]` even if `T` is a zero-sized type: | ||
4619 | |||
4620 | ```rust | ||
4621 | #![feature(transparent_unions)] | ||
4622 | |||
4623 | // This union has the same representation as `T`. | ||
4624 | #[repr(transparent)] | ||
4625 | pub union GenericUnion<T: Copy> { // Unions with non-`Copy` fields are unstable. | ||
4626 | pub field: T, | ||
4627 | pub nothing: (), | ||
4628 | } | ||
4629 | |||
4630 | // This is okay even though `()` is a zero-sized type. | ||
4631 | pub const THIS_IS_OKAY: GenericUnion<()> = GenericUnion { field: () }; | ||
4632 | ``` | ||
4633 | |||
4634 | Like transarent `struct`s, a transparent `union` of type `U` has the same | ||
4635 | layout, size, and ABI as its single non-ZST field. If it is generic over a type | ||
4636 | `T`, and all its fields are ZSTs except for exactly one field of type `T`, then | ||
4637 | it has the same layout and ABI as `T` (even if `T` is a ZST when monomorphized). | ||
4638 | |||
4639 | Like transparent `struct`s, transparent `union`s are FFI-safe if and only if | ||
4640 | their underlying representation type is also FFI-safe. | ||
4641 | |||
4642 | A `union` may not be eligible for the same nonnull-style optimizations that a | ||
4643 | `struct` or `enum` (with the same fields) are eligible for. Adding | ||
4644 | `#[repr(transparent)]` to `union` does not change this. To give a more concrete | ||
4645 | example, it is unspecified whether `size_of::<T>()` is equal to | ||
4646 | `size_of::<Option<T>>()`, where `T` is a `union` (regardless of whether or not | ||
4647 | it is transparent). The Rust compiler is free to perform this optimization if | ||
4648 | possible, but is not required to, and different compiler versions may differ in | ||
4649 | their application of these optimizations. | ||
4435 | "##, | 4650 | "##, |
4436 | }, | 4651 | }, |
4437 | LintCompletion { | 4652 | Lint { |
4653 | label: "try_blocks", | ||
4654 | description: r##"# `try_blocks` | ||
4655 | |||
4656 | The tracking issue for this feature is: [#31436] | ||
4657 | |||
4658 | [#31436]: https://github.com/rust-lang/rust/issues/31436 | ||
4659 | |||
4660 | ------------------------ | ||
4661 | |||
4662 | The `try_blocks` feature adds support for `try` blocks. A `try` | ||
4663 | block creates a new scope one can use the `?` operator in. | ||
4664 | |||
4665 | ```rust,edition2018 | ||
4666 | #![feature(try_blocks)] | ||
4667 | |||
4668 | use std::num::ParseIntError; | ||
4669 | |||
4670 | let result: Result<i32, ParseIntError> = try { | ||
4671 | "1".parse::<i32>()? | ||
4672 | + "2".parse::<i32>()? | ||
4673 | + "3".parse::<i32>()? | ||
4674 | }; | ||
4675 | assert_eq!(result, Ok(6)); | ||
4676 | |||
4677 | let result: Result<i32, ParseIntError> = try { | ||
4678 | "1".parse::<i32>()? | ||
4679 | + "foo".parse::<i32>()? | ||
4680 | + "3".parse::<i32>()? | ||
4681 | }; | ||
4682 | assert!(result.is_err()); | ||
4683 | ``` | ||
4684 | "##, | ||
4685 | }, | ||
4686 | Lint { | ||
4438 | label: "try_trait", | 4687 | label: "try_trait", |
4439 | description: r##"# `try_trait` | 4688 | description: r##"# `try_trait` |
4440 | 4689 | ||
@@ -4488,27 +4737,284 @@ function (or catch block). Having a distinct error type (as opposed to | |||
4488 | just `()`, or similar) restricts this to where it's semantically meaningful. | 4737 | just `()`, or similar) restricts this to where it's semantically meaningful. |
4489 | "##, | 4738 | "##, |
4490 | }, | 4739 | }, |
4491 | LintCompletion { | 4740 | Lint { |
4492 | label: "rt", | 4741 | label: "unboxed_closures", |
4493 | description: r##"# `rt` | 4742 | description: r##"# `unboxed_closures` |
4743 | |||
4744 | The tracking issue for this feature is [#29625] | ||
4745 | |||
4746 | See Also: [`fn_traits`](../library-features/fn-traits.md) | ||
4747 | |||
4748 | [#29625]: https://github.com/rust-lang/rust/issues/29625 | ||
4749 | |||
4750 | ---- | ||
4751 | |||
4752 | The `unboxed_closures` feature allows you to write functions using the `"rust-call"` ABI, | ||
4753 | required for implementing the [`Fn*`] family of traits. `"rust-call"` functions must have | ||
4754 | exactly one (non self) argument, a tuple representing the argument list. | ||
4755 | |||
4756 | [`Fn*`]: https://doc.rust-lang.org/std/ops/trait.Fn.html | ||
4757 | |||
4758 | ```rust | ||
4759 | #![feature(unboxed_closures)] | ||
4760 | |||
4761 | extern "rust-call" fn add_args(args: (u32, u32)) -> u32 { | ||
4762 | args.0 + args.1 | ||
4763 | } | ||
4764 | |||
4765 | fn main() {} | ||
4766 | ``` | ||
4767 | "##, | ||
4768 | }, | ||
4769 | Lint { | ||
4770 | label: "unsized_locals", | ||
4771 | description: r##"# `unsized_locals` | ||
4772 | |||
4773 | The tracking issue for this feature is: [#48055] | ||
4774 | |||
4775 | [#48055]: https://github.com/rust-lang/rust/issues/48055 | ||
4776 | |||
4777 | ------------------------ | ||
4778 | |||
4779 | This implements [RFC1909]. When turned on, you can have unsized arguments and locals: | ||
4780 | |||
4781 | [RFC1909]: https://github.com/rust-lang/rfcs/blob/master/text/1909-unsized-rvalues.md | ||
4782 | |||
4783 | ```rust | ||
4784 | #![allow(incomplete_features)] | ||
4785 | #![feature(unsized_locals, unsized_fn_params)] | ||
4786 | |||
4787 | use std::any::Any; | ||
4788 | |||
4789 | fn main() { | ||
4790 | let x: Box<dyn Any> = Box::new(42); | ||
4791 | let x: dyn Any = *x; | ||
4792 | // ^ unsized local variable | ||
4793 | // ^^ unsized temporary | ||
4794 | foo(x); | ||
4795 | } | ||
4796 | |||
4797 | fn foo(_: dyn Any) {} | ||
4798 | // ^^^^^^ unsized argument | ||
4799 | ``` | ||
4800 | |||
4801 | The RFC still forbids the following unsized expressions: | ||
4802 | |||
4803 | ```rust,compile_fail | ||
4804 | #![feature(unsized_locals)] | ||
4805 | |||
4806 | use std::any::Any; | ||
4807 | |||
4808 | struct MyStruct<T: ?Sized> { | ||
4809 | content: T, | ||
4810 | } | ||
4811 | |||
4812 | struct MyTupleStruct<T: ?Sized>(T); | ||
4813 | |||
4814 | fn answer() -> Box<dyn Any> { | ||
4815 | Box::new(42) | ||
4816 | } | ||
4817 | |||
4818 | fn main() { | ||
4819 | // You CANNOT have unsized statics. | ||
4820 | static X: dyn Any = *answer(); // ERROR | ||
4821 | const Y: dyn Any = *answer(); // ERROR | ||
4822 | |||
4823 | // You CANNOT have struct initialized unsized. | ||
4824 | MyStruct { content: *answer() }; // ERROR | ||
4825 | MyTupleStruct(*answer()); // ERROR | ||
4826 | (42, *answer()); // ERROR | ||
4827 | |||
4828 | // You CANNOT have unsized return types. | ||
4829 | fn my_function() -> dyn Any { *answer() } // ERROR | ||
4830 | |||
4831 | // You CAN have unsized local variables... | ||
4832 | let mut x: dyn Any = *answer(); // OK | ||
4833 | // ...but you CANNOT reassign to them. | ||
4834 | x = *answer(); // ERROR | ||
4835 | |||
4836 | // You CANNOT even initialize them separately. | ||
4837 | let y: dyn Any; // OK | ||
4838 | y = *answer(); // ERROR | ||
4839 | |||
4840 | // Not mentioned in the RFC, but by-move captured variables are also Sized. | ||
4841 | let x: dyn Any = *answer(); | ||
4842 | (move || { // ERROR | ||
4843 | let y = x; | ||
4844 | })(); | ||
4845 | |||
4846 | // You CAN create a closure with unsized arguments, | ||
4847 | // but you CANNOT call it. | ||
4848 | // This is an implementation detail and may be changed in the future. | ||
4849 | let f = |x: dyn Any| {}; | ||
4850 | f(*answer()); // ERROR | ||
4851 | } | ||
4852 | ``` | ||
4853 | |||
4854 | ## By-value trait objects | ||
4855 | |||
4856 | With this feature, you can have by-value `self` arguments without `Self: Sized` bounds. | ||
4857 | |||
4858 | ```rust | ||
4859 | #![feature(unsized_fn_params)] | ||
4860 | |||
4861 | trait Foo { | ||
4862 | fn foo(self) {} | ||
4863 | } | ||
4864 | |||
4865 | impl<T: ?Sized> Foo for T {} | ||
4866 | |||
4867 | fn main() { | ||
4868 | let slice: Box<[i32]> = Box::new([1, 2, 3]); | ||
4869 | <[i32] as Foo>::foo(*slice); | ||
4870 | } | ||
4871 | ``` | ||
4872 | |||
4873 | And `Foo` will also be object-safe. | ||
4874 | |||
4875 | ```rust | ||
4876 | #![feature(unsized_fn_params)] | ||
4877 | |||
4878 | trait Foo { | ||
4879 | fn foo(self) {} | ||
4880 | } | ||
4881 | |||
4882 | impl<T: ?Sized> Foo for T {} | ||
4883 | |||
4884 | fn main () { | ||
4885 | let slice: Box<dyn Foo> = Box::new([1, 2, 3]); | ||
4886 | // doesn't compile yet | ||
4887 | <dyn Foo as Foo>::foo(*slice); | ||
4888 | } | ||
4889 | ``` | ||
4890 | |||
4891 | One of the objectives of this feature is to allow `Box<dyn FnOnce>`. | ||
4892 | |||
4893 | ## Variable length arrays | ||
4894 | |||
4895 | The RFC also describes an extension to the array literal syntax: `[e; dyn n]`. In the syntax, `n` isn't necessarily a constant expression. The array is dynamically allocated on the stack and has the type of `[T]`, instead of `[T; n]`. | ||
4896 | |||
4897 | ```rust,ignore (not-yet-implemented) | ||
4898 | #![feature(unsized_locals)] | ||
4899 | |||
4900 | fn mergesort<T: Ord>(a: &mut [T]) { | ||
4901 | let mut tmp = [T; dyn a.len()]; | ||
4902 | // ... | ||
4903 | } | ||
4904 | |||
4905 | fn main() { | ||
4906 | let mut a = [3, 1, 5, 6]; | ||
4907 | mergesort(&mut a); | ||
4908 | assert_eq!(a, [1, 3, 5, 6]); | ||
4909 | } | ||
4910 | ``` | ||
4911 | |||
4912 | VLAs are not implemented yet. The syntax isn't final, either. We may need an alternative syntax for Rust 2015 because, in Rust 2015, expressions like `[e; dyn(1)]` would be ambiguous. One possible alternative proposed in the RFC is `[e; n]`: if `n` captures one or more local variables, then it is considered as `[e; dyn n]`. | ||
4913 | |||
4914 | ## Advisory on stack usage | ||
4915 | |||
4916 | It's advised not to casually use the `#![feature(unsized_locals)]` feature. Typical use-cases are: | ||
4917 | |||
4918 | - When you need a by-value trait objects. | ||
4919 | - When you really need a fast allocation of small temporary arrays. | ||
4920 | |||
4921 | Another pitfall is repetitive allocation and temporaries. Currently the compiler simply extends the stack frame every time it encounters an unsized assignment. So for example, the code | ||
4922 | |||
4923 | ```rust | ||
4924 | #![feature(unsized_locals)] | ||
4925 | |||
4926 | fn main() { | ||
4927 | let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]); | ||
4928 | let _x = {{{{{{{{{{*x}}}}}}}}}}; | ||
4929 | } | ||
4930 | ``` | ||
4931 | |||
4932 | and the code | ||
4933 | |||
4934 | ```rust | ||
4935 | #![feature(unsized_locals)] | ||
4936 | |||
4937 | fn main() { | ||
4938 | for _ in 0..10 { | ||
4939 | let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]); | ||
4940 | let _x = *x; | ||
4941 | } | ||
4942 | } | ||
4943 | ``` | ||
4944 | |||
4945 | will unnecessarily extend the stack frame. | ||
4946 | "##, | ||
4947 | }, | ||
4948 | Lint { | ||
4949 | label: "unsized_tuple_coercion", | ||
4950 | description: r##"# `unsized_tuple_coercion` | ||
4951 | |||
4952 | The tracking issue for this feature is: [#42877] | ||
4953 | |||
4954 | [#42877]: https://github.com/rust-lang/rust/issues/42877 | ||
4955 | |||
4956 | ------------------------ | ||
4957 | |||
4958 | This is a part of [RFC0401]. According to the RFC, there should be an implementation like this: | ||
4959 | |||
4960 | ```rust,ignore (partial-example) | ||
4961 | impl<..., T, U: ?Sized> Unsized<(..., U)> for (..., T) where T: Unsized<U> {} | ||
4962 | ``` | ||
4963 | |||
4964 | This implementation is currently gated behind `#[feature(unsized_tuple_coercion)]` to avoid insta-stability. Therefore you can use it like this: | ||
4965 | |||
4966 | ```rust | ||
4967 | #![feature(unsized_tuple_coercion)] | ||
4968 | |||
4969 | fn main() { | ||
4970 | let x : ([i32; 3], [i32; 3]) = ([1, 2, 3], [4, 5, 6]); | ||
4971 | let y : &([i32; 3], [i32]) = &x; | ||
4972 | assert_eq!(y.1[0], 4); | ||
4973 | } | ||
4974 | ``` | ||
4975 | |||
4976 | [RFC0401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md | ||
4977 | "##, | ||
4978 | }, | ||
4979 | Lint { | ||
4980 | label: "update_panic_count", | ||
4981 | description: r##"# `update_panic_count` | ||
4494 | 4982 | ||
4495 | This feature is internal to the Rust compiler and is not intended for general use. | 4983 | This feature is internal to the Rust compiler and is not intended for general use. |
4496 | 4984 | ||
4497 | ------------------------ | 4985 | ------------------------ |
4498 | "##, | 4986 | "##, |
4499 | }, | 4987 | }, |
4500 | LintCompletion { | 4988 | Lint { |
4501 | label: "fd", | 4989 | label: "windows_c", |
4502 | description: r##"# `fd` | 4990 | description: r##"# `windows_c` |
4503 | 4991 | ||
4504 | This feature is internal to the Rust compiler and is not intended for general use. | 4992 | This feature is internal to the Rust compiler and is not intended for general use. |
4505 | 4993 | ||
4506 | ------------------------ | 4994 | ------------------------ |
4507 | "##, | 4995 | "##, |
4508 | }, | 4996 | }, |
4509 | LintCompletion { | 4997 | Lint { |
4510 | label: "libstd_thread_internals", | 4998 | label: "windows_handle", |
4511 | description: r##"# `libstd_thread_internals` | 4999 | description: r##"# `windows_handle` |
5000 | |||
5001 | This feature is internal to the Rust compiler and is not intended for general use. | ||
5002 | |||
5003 | ------------------------ | ||
5004 | "##, | ||
5005 | }, | ||
5006 | Lint { | ||
5007 | label: "windows_net", | ||
5008 | description: r##"# `windows_net` | ||
5009 | |||
5010 | This feature is internal to the Rust compiler and is not intended for general use. | ||
5011 | |||
5012 | ------------------------ | ||
5013 | "##, | ||
5014 | }, | ||
5015 | Lint { | ||
5016 | label: "windows_stdio", | ||
5017 | description: r##"# `windows_stdio` | ||
4512 | 5018 | ||
4513 | This feature is internal to the Rust compiler and is not intended for general use. | 5019 | This feature is internal to the Rust compiler and is not intended for general use. |
4514 | 5020 | ||
@@ -4517,1864 +5023,2308 @@ This feature is internal to the Rust compiler and is not intended for general us | |||
4517 | }, | 5023 | }, |
4518 | ]; | 5024 | ]; |
4519 | 5025 | ||
4520 | pub(super) const CLIPPY_LINTS: &[LintCompletion] = &[ | 5026 | pub const CLIPPY_LINTS: &[Lint] = &[ |
4521 | LintCompletion { | 5027 | Lint { |
4522 | label: "clippy::absurd_extreme_comparisons", | 5028 | label: "clippy::absurd_extreme_comparisons", |
4523 | description: r##"Checks for comparisons where one side of the relation is\neither the minimum or maximum value for its type and warns if it involves a\ncase that is always true or always false. Only integer and boolean types are\nchecked."##, | 5029 | description: r##"Checks for comparisons where one side of the relation is |
5030 | either the minimum or maximum value for its type and warns if it involves a | ||
5031 | case that is always true or always false. Only integer and boolean types are | ||
5032 | checked."##, | ||
4524 | }, | 5033 | }, |
4525 | LintCompletion { | 5034 | Lint { |
4526 | label: "clippy::almost_swapped", | 5035 | label: "clippy::almost_swapped", |
4527 | description: r##"Checks for `foo = bar; bar = foo` sequences."##, | 5036 | description: r##"Checks for `foo = bar; bar = foo` sequences."##, |
4528 | }, | 5037 | }, |
4529 | LintCompletion { | 5038 | Lint { |
4530 | label: "clippy::approx_constant", | 5039 | label: "clippy::approx_constant", |
4531 | description: r##"Checks for floating point literals that approximate\nconstants which are defined in\n[`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)\nor\n[`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),\nrespectively, suggesting to use the predefined constant."##, | 5040 | description: r##"Checks for floating point literals that approximate |
4532 | }, | 5041 | constants which are defined in |
4533 | LintCompletion { | 5042 | [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants) |
5043 | or | ||
5044 | [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants), | ||
5045 | respectively, suggesting to use the predefined constant."##, | ||
5046 | }, | ||
5047 | Lint { | ||
4534 | label: "clippy::as_conversions", | 5048 | label: "clippy::as_conversions", |
4535 | description: r##"Checks for usage of `as` conversions.\n\nNote that this lint is specialized in linting *every single* use of `as`\nregardless of whether good alternatives exist or not.\nIf you want more precise lints for `as`, please consider using these separate lints:\n`unnecessary_cast`, `cast_lossless/possible_truncation/possible_wrap/precision_loss/sign_loss`,\n`fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`.\nThere is a good explanation the reason why this lint should work in this way and how it is useful\n[in this issue](https://github.com/rust-lang/rust-clippy/issues/5122)."##, | 5049 | description: r##"Checks for usage of `as` conversions. |
5050 | |||
5051 | Note that this lint is specialized in linting *every single* use of `as` | ||
5052 | regardless of whether good alternatives exist or not. | ||
5053 | If you want more precise lints for `as`, please consider using these separate lints: | ||
5054 | `unnecessary_cast`, `cast_lossless/possible_truncation/possible_wrap/precision_loss/sign_loss`, | ||
5055 | `fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`. | ||
5056 | There is a good explanation the reason why this lint should work in this way and how it is useful | ||
5057 | [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122)."##, | ||
4536 | }, | 5058 | }, |
4537 | LintCompletion { | 5059 | Lint { |
4538 | label: "clippy::assertions_on_constants", | 5060 | label: "clippy::assertions_on_constants", |
4539 | description: r##"Checks for `assert!(true)` and `assert!(false)` calls."##, | 5061 | description: r##"Checks for `assert!(true)` and `assert!(false)` calls."##, |
4540 | }, | 5062 | }, |
4541 | LintCompletion { | 5063 | Lint { |
4542 | label: "clippy::assign_op_pattern", | 5064 | label: "clippy::assign_op_pattern", |
4543 | description: r##"Checks for `a = a op b` or `a = b commutative_op a`\npatterns."##, | 5065 | description: r##"Checks for `a = a op b` or `a = b commutative_op a` |
5066 | patterns."##, | ||
4544 | }, | 5067 | }, |
4545 | LintCompletion { | 5068 | Lint { |
4546 | label: "clippy::assign_ops", | 5069 | label: "clippy::assign_ops", |
4547 | description: r##"Nothing. This lint has been deprecated."##, | 5070 | description: r##"Nothing. This lint has been deprecated."##, |
4548 | }, | 5071 | }, |
4549 | LintCompletion { | 5072 | Lint { |
4550 | label: "clippy::async_yields_async", | 5073 | label: "clippy::async_yields_async", |
4551 | description: r##"Checks for async blocks that yield values of types\nthat can themselves be awaited."##, | 5074 | description: r##"Checks for async blocks that yield values of types |
5075 | that can themselves be awaited."##, | ||
4552 | }, | 5076 | }, |
4553 | LintCompletion { | 5077 | Lint { |
4554 | label: "clippy::await_holding_lock", | 5078 | label: "clippy::await_holding_lock", |
4555 | description: r##"Checks for calls to await while holding a\nnon-async-aware MutexGuard."##, | 5079 | description: r##"Checks for calls to await while holding a |
5080 | non-async-aware MutexGuard."##, | ||
4556 | }, | 5081 | }, |
4557 | LintCompletion { | 5082 | Lint { |
4558 | label: "clippy::await_holding_refcell_ref", | 5083 | label: "clippy::await_holding_refcell_ref", |
4559 | description: r##"Checks for calls to await while holding a\n`RefCell` `Ref` or `RefMut`."##, | 5084 | description: r##"Checks for calls to await while holding a |
5085 | `RefCell` `Ref` or `RefMut`."##, | ||
4560 | }, | 5086 | }, |
4561 | LintCompletion { | 5087 | Lint { |
4562 | label: "clippy::bad_bit_mask", | 5088 | label: "clippy::bad_bit_mask", |
4563 | description: r##"Checks for incompatible bit masks in comparisons.\n\nThe formula for detecting if an expression of the type `_ <bit_op> m\n<cmp_op> c` (where `<bit_op>` is one of {`&`, `|`} and `<cmp_op>` is one of\n{`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following\ntable:\n\n|Comparison |Bit Op|Example |is always|Formula |\n|------------|------|------------|---------|----------------------|\n|`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` |\n|`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` |\n|`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` |\n|`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` |\n|`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` |\n|`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` |"##, | 5089 | description: r##"Checks for incompatible bit masks in comparisons. |
4564 | }, | 5090 | |
4565 | LintCompletion { | 5091 | The formula for detecting if an expression of the type `_ <bit_op> m |
5092 | <cmp_op> c` (where `<bit_op>` is one of {`&`, `|`} and `<cmp_op>` is one of | ||
5093 | {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following | ||
5094 | table: | ||
5095 | |||
5096 | |Comparison |Bit Op|Example |is always|Formula | | ||
5097 | |------------|------|------------|---------|----------------------| | ||
5098 | |`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` | | ||
5099 | |`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` | | ||
5100 | |`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` | | ||
5101 | |`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` | | ||
5102 | |`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` | | ||
5103 | |`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` |"##, | ||
5104 | }, | ||
5105 | Lint { | ||
4566 | label: "clippy::bind_instead_of_map", | 5106 | label: "clippy::bind_instead_of_map", |
4567 | description: r##"Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or\n`_.or_else(|x| Err(y))`."##, | 5107 | description: r##"Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or |
5108 | `_.or_else(|x| Err(y))`."##, | ||
4568 | }, | 5109 | }, |
4569 | LintCompletion { | 5110 | Lint { |
4570 | label: "clippy::blacklisted_name", | 5111 | label: "clippy::blacklisted_name", |
4571 | description: r##"Checks for usage of blacklisted names for variables, such\nas `foo`."##, | 5112 | description: r##"Checks for usage of blacklisted names for variables, such |
5113 | as `foo`."##, | ||
4572 | }, | 5114 | }, |
4573 | LintCompletion { | 5115 | Lint { |
4574 | label: "clippy::blanket_clippy_restriction_lints", | 5116 | label: "clippy::blanket_clippy_restriction_lints", |
4575 | description: r##"Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category."##, | 5117 | description: r##"Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category."##, |
4576 | }, | 5118 | }, |
4577 | LintCompletion { | 5119 | Lint { |
4578 | label: "clippy::blocks_in_if_conditions", | 5120 | label: "clippy::blocks_in_if_conditions", |
4579 | description: r##"Checks for `if` conditions that use blocks containing an\nexpression, statements or conditions that use closures with blocks."##, | 5121 | description: r##"Checks for `if` conditions that use blocks containing an |
5122 | expression, statements or conditions that use closures with blocks."##, | ||
5123 | }, | ||
5124 | Lint { | ||
5125 | label: "clippy::bool_assert_comparison", | ||
5126 | description: r##"This lint warns about boolean comparisons in assert-like macros."##, | ||
4580 | }, | 5127 | }, |
4581 | LintCompletion { | 5128 | Lint { |
4582 | label: "clippy::bool_comparison", | 5129 | label: "clippy::bool_comparison", |
4583 | description: r##"Checks for expressions of the form `x == true`,\n`x != true` and order comparisons such as `x < true` (or vice versa) and\nsuggest using the variable directly."##, | 5130 | description: r##"Checks for expressions of the form `x == true`, |
5131 | `x != true` and order comparisons such as `x < true` (or vice versa) and | ||
5132 | suggest using the variable directly."##, | ||
4584 | }, | 5133 | }, |
4585 | LintCompletion { | 5134 | Lint { |
4586 | label: "clippy::borrow_interior_mutable_const", | 5135 | label: "clippy::borrow_interior_mutable_const", |
4587 | description: r##"Checks if `const` items which is interior mutable (e.g.,\ncontains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly."##, | 5136 | description: r##"Checks if `const` items which is interior mutable (e.g., |
5137 | contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly."##, | ||
4588 | }, | 5138 | }, |
4589 | LintCompletion { | 5139 | Lint { |
4590 | label: "clippy::borrowed_box", | 5140 | label: "clippy::borrowed_box", |
4591 | description: r##"Checks for use of `&Box<T>` anywhere in the code.\nCheck the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information."##, | 5141 | description: r##"Checks for use of `&Box<T>` anywhere in the code. |
5142 | Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information."##, | ||
4592 | }, | 5143 | }, |
4593 | LintCompletion { | 5144 | Lint { |
4594 | label: "clippy::box_vec", | 5145 | label: "clippy::box_vec", |
4595 | description: r##"Checks for use of `Box<Vec<_>>` anywhere in the code.\nCheck the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information."##, | 5146 | description: r##"Checks for use of `Box<Vec<_>>` anywhere in the code. |
5147 | Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information."##, | ||
4596 | }, | 5148 | }, |
4597 | LintCompletion { | 5149 | Lint { |
4598 | label: "clippy::boxed_local", | 5150 | label: "clippy::boxed_local", |
4599 | description: r##"Checks for usage of `Box<T>` where an unboxed `T` would\nwork fine."##, | 5151 | description: r##"Checks for usage of `Box<T>` where an unboxed `T` would |
5152 | work fine."##, | ||
5153 | }, | ||
5154 | Lint { | ||
5155 | label: "clippy::branches_sharing_code", | ||
5156 | description: r##"Checks if the `if` and `else` block contain shared code that can be | ||
5157 | moved out of the blocks."##, | ||
4600 | }, | 5158 | }, |
4601 | LintCompletion { | 5159 | Lint { |
4602 | label: "clippy::builtin_type_shadow", | 5160 | label: "clippy::builtin_type_shadow", |
4603 | description: r##"Warns if a generic shadows a built-in type."##, | 5161 | description: r##"Warns if a generic shadows a built-in type."##, |
4604 | }, | 5162 | }, |
4605 | LintCompletion { | 5163 | Lint { |
4606 | label: "clippy::bytes_nth", | 5164 | label: "clippy::bytes_nth", |
4607 | description: r##"Checks for the use of `.bytes().nth()`."##, | 5165 | description: r##"Checks for the use of `.bytes().nth()`."##, |
4608 | }, | 5166 | }, |
4609 | LintCompletion { | 5167 | Lint { |
4610 | label: "clippy::cargo_common_metadata", | 5168 | label: "clippy::cargo_common_metadata", |
4611 | description: r##"Checks to see if all common metadata is defined in\n`Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata"##, | 5169 | description: r##"Checks to see if all common metadata is defined in |
5170 | `Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata"##, | ||
4612 | }, | 5171 | }, |
4613 | LintCompletion { | 5172 | Lint { |
4614 | label: "clippy::case_sensitive_file_extension_comparisons", | 5173 | label: "clippy::case_sensitive_file_extension_comparisons", |
4615 | description: r##"Checks for calls to `ends_with` with possible file extensions\nand suggests to use a case-insensitive approach instead."##, | 5174 | description: r##"Checks for calls to `ends_with` with possible file extensions |
5175 | and suggests to use a case-insensitive approach instead."##, | ||
4616 | }, | 5176 | }, |
4617 | LintCompletion { | 5177 | Lint { |
4618 | label: "clippy::cast_lossless", | 5178 | label: "clippy::cast_lossless", |
4619 | description: r##"Checks for casts between numerical types that may\nbe replaced by safe conversion functions."##, | 5179 | description: r##"Checks for casts between numerical types that may |
5180 | be replaced by safe conversion functions."##, | ||
4620 | }, | 5181 | }, |
4621 | LintCompletion { | 5182 | Lint { |
4622 | label: "clippy::cast_possible_truncation", | 5183 | label: "clippy::cast_possible_truncation", |
4623 | description: r##"Checks for casts between numerical types that may\ntruncate large values. This is expected behavior, so the cast is `Allow` by\ndefault."##, | 5184 | description: r##"Checks for casts between numerical types that may |
5185 | truncate large values. This is expected behavior, so the cast is `Allow` by | ||
5186 | default."##, | ||
4624 | }, | 5187 | }, |
4625 | LintCompletion { | 5188 | Lint { |
4626 | label: "clippy::cast_possible_wrap", | 5189 | label: "clippy::cast_possible_wrap", |
4627 | description: r##"Checks for casts from an unsigned type to a signed type of\nthe same size. Performing such a cast is a 'no-op' for the compiler,\ni.e., nothing is changed at the bit level, and the binary representation of\nthe value is reinterpreted. This can cause wrapping if the value is too big\nfor the target signed type. However, the cast works as defined, so this lint\nis `Allow` by default."##, | 5190 | description: r##"Checks for casts from an unsigned type to a signed type of |
4628 | }, | 5191 | the same size. Performing such a cast is a 'no-op' for the compiler, |
4629 | LintCompletion { | 5192 | i.e., nothing is changed at the bit level, and the binary representation of |
5193 | the value is reinterpreted. This can cause wrapping if the value is too big | ||
5194 | for the target signed type. However, the cast works as defined, so this lint | ||
5195 | is `Allow` by default."##, | ||
5196 | }, | ||
5197 | Lint { | ||
4630 | label: "clippy::cast_precision_loss", | 5198 | label: "clippy::cast_precision_loss", |
4631 | description: r##"Checks for casts from any numerical to a float type where\nthe receiving type cannot store all values from the original type without\nrounding errors. This possible rounding is to be expected, so this lint is\n`Allow` by default.\n\nBasically, this warns on casting any integer with 32 or more bits to `f32`\nor any 64-bit integer to `f64`."##, | 5199 | description: r##"Checks for casts from any numerical to a float type where |
5200 | the receiving type cannot store all values from the original type without | ||
5201 | rounding errors. This possible rounding is to be expected, so this lint is | ||
5202 | `Allow` by default. | ||
5203 | |||
5204 | Basically, this warns on casting any integer with 32 or more bits to `f32` | ||
5205 | or any 64-bit integer to `f64`."##, | ||
4632 | }, | 5206 | }, |
4633 | LintCompletion { | 5207 | Lint { |
4634 | label: "clippy::cast_ptr_alignment", | 5208 | label: "clippy::cast_ptr_alignment", |
4635 | description: r##"Checks for casts, using `as` or `pointer::cast`,\nfrom a less-strictly-aligned pointer to a more-strictly-aligned pointer"##, | 5209 | description: r##"Checks for casts, using `as` or `pointer::cast`, |
5210 | from a less-strictly-aligned pointer to a more-strictly-aligned pointer"##, | ||
4636 | }, | 5211 | }, |
4637 | LintCompletion { | 5212 | Lint { |
4638 | label: "clippy::cast_ref_to_mut", | 5213 | label: "clippy::cast_ref_to_mut", |
4639 | description: r##"Checks for casts of `&T` to `&mut T` anywhere in the code."##, | 5214 | description: r##"Checks for casts of `&T` to `&mut T` anywhere in the code."##, |
4640 | }, | 5215 | }, |
4641 | LintCompletion { | 5216 | Lint { |
4642 | label: "clippy::cast_sign_loss", | 5217 | label: "clippy::cast_sign_loss", |
4643 | description: r##"Checks for casts from a signed to an unsigned numerical\ntype. In this case, negative values wrap around to large positive values,\nwhich can be quite surprising in practice. However, as the cast works as\ndefined, this lint is `Allow` by default."##, | 5218 | description: r##"Checks for casts from a signed to an unsigned numerical |
5219 | type. In this case, negative values wrap around to large positive values, | ||
5220 | which can be quite surprising in practice. However, as the cast works as | ||
5221 | defined, this lint is `Allow` by default."##, | ||
4644 | }, | 5222 | }, |
4645 | LintCompletion { | 5223 | Lint { |
4646 | label: "clippy::char_lit_as_u8", | 5224 | label: "clippy::char_lit_as_u8", |
4647 | description: r##"Checks for expressions where a character literal is cast\nto `u8` and suggests using a byte literal instead."##, | 5225 | description: r##"Checks for expressions where a character literal is cast |
5226 | to `u8` and suggests using a byte literal instead."##, | ||
4648 | }, | 5227 | }, |
4649 | LintCompletion { | 5228 | Lint { |
4650 | label: "clippy::chars_last_cmp", | 5229 | label: "clippy::chars_last_cmp", |
4651 | description: r##"Checks for usage of `_.chars().last()` or\n`_.chars().next_back()` on a `str` to check if it ends with a given char."##, | 5230 | description: r##"Checks for usage of `_.chars().last()` or |
5231 | `_.chars().next_back()` on a `str` to check if it ends with a given char."##, | ||
4652 | }, | 5232 | }, |
4653 | LintCompletion { | 5233 | Lint { |
4654 | label: "clippy::chars_next_cmp", | 5234 | label: "clippy::chars_next_cmp", |
4655 | description: r##"Checks for usage of `.chars().next()` on a `str` to check\nif it starts with a given char."##, | 5235 | description: r##"Checks for usage of `.chars().next()` on a `str` to check |
5236 | if it starts with a given char."##, | ||
4656 | }, | 5237 | }, |
4657 | LintCompletion { | 5238 | Lint { |
4658 | label: "clippy::checked_conversions", | 5239 | label: "clippy::checked_conversions", |
4659 | description: r##"Checks for explicit bounds checking when casting."##, | 5240 | description: r##"Checks for explicit bounds checking when casting."##, |
4660 | }, | 5241 | }, |
4661 | LintCompletion { | 5242 | Lint { |
4662 | label: "clippy::clone_double_ref", | 5243 | label: "clippy::clone_double_ref", |
4663 | description: r##"Checks for usage of `.clone()` on an `&&T`."##, | 5244 | description: r##"Checks for usage of `.clone()` on an `&&T`."##, |
4664 | }, | 5245 | }, |
4665 | LintCompletion { | 5246 | Lint { |
4666 | label: "clippy::clone_on_copy", | 5247 | label: "clippy::clone_on_copy", |
4667 | description: r##"Checks for usage of `.clone()` on a `Copy` type."##, | 5248 | description: r##"Checks for usage of `.clone()` on a `Copy` type."##, |
4668 | }, | 5249 | }, |
4669 | LintCompletion { | 5250 | Lint { |
4670 | label: "clippy::clone_on_ref_ptr", | 5251 | label: "clippy::clone_on_ref_ptr", |
4671 | description: r##"Checks for usage of `.clone()` on a ref-counted pointer,\n(`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified\nfunction syntax instead (e.g., `Rc::clone(foo)`)."##, | 5252 | description: r##"Checks for usage of `.clone()` on a ref-counted pointer, |
5253 | (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified | ||
5254 | function syntax instead (e.g., `Rc::clone(foo)`)."##, | ||
4672 | }, | 5255 | }, |
4673 | LintCompletion { label: "clippy::cmp_nan", description: r##"Checks for comparisons to NaN."## }, | 5256 | Lint { |
4674 | LintCompletion { | 5257 | label: "clippy::cloned_instead_of_copied", |
5258 | description: r##"Checks for usages of `cloned()` on an `Iterator` or `Option` where | ||
5259 | `copied()` could be used instead."##, | ||
5260 | }, | ||
5261 | Lint { label: "clippy::cmp_nan", description: r##"Checks for comparisons to NaN."## }, | ||
5262 | Lint { | ||
4675 | label: "clippy::cmp_null", | 5263 | label: "clippy::cmp_null", |
4676 | description: r##"This lint checks for equality comparisons with `ptr::null`"##, | 5264 | description: r##"This lint checks for equality comparisons with `ptr::null`"##, |
4677 | }, | 5265 | }, |
4678 | LintCompletion { | 5266 | Lint { |
4679 | label: "clippy::cmp_owned", | 5267 | label: "clippy::cmp_owned", |
4680 | description: r##"Checks for conversions to owned values just for the sake\nof a comparison."##, | 5268 | description: r##"Checks for conversions to owned values just for the sake |
5269 | of a comparison."##, | ||
4681 | }, | 5270 | }, |
4682 | LintCompletion { | 5271 | Lint { |
4683 | label: "clippy::cognitive_complexity", | 5272 | label: "clippy::cognitive_complexity", |
4684 | description: r##"Checks for methods with high cognitive complexity."##, | 5273 | description: r##"Checks for methods with high cognitive complexity."##, |
4685 | }, | 5274 | }, |
4686 | LintCompletion { | 5275 | Lint { |
4687 | label: "clippy::collapsible_else_if", | 5276 | label: "clippy::collapsible_else_if", |
4688 | description: r##"Checks for collapsible `else { if ... }` expressions\nthat can be collapsed to `else if ...`."##, | 5277 | description: r##"Checks for collapsible `else { if ... }` expressions |
5278 | that can be collapsed to `else if ...`."##, | ||
4689 | }, | 5279 | }, |
4690 | LintCompletion { | 5280 | Lint { |
4691 | label: "clippy::collapsible_if", | 5281 | label: "clippy::collapsible_if", |
4692 | description: r##"Checks for nested `if` statements which can be collapsed\nby `&&`-combining their conditions."##, | 5282 | description: r##"Checks for nested `if` statements which can be collapsed |
5283 | by `&&`-combining their conditions."##, | ||
4693 | }, | 5284 | }, |
4694 | LintCompletion { | 5285 | Lint { |
4695 | label: "clippy::collapsible_match", | 5286 | label: "clippy::collapsible_match", |
4696 | description: r##"Finds nested `match` or `if let` expressions where the patterns may be \"collapsed\" together\nwithout adding any branches.\n\nNote that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only\ncases where merging would most likely make the code more readable."##, | 5287 | description: r##"Finds nested `match` or `if let` expressions where the patterns may be collapsed together |
5288 | without adding any branches. | ||
5289 | |||
5290 | Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only | ||
5291 | cases where merging would most likely make the code more readable."##, | ||
4697 | }, | 5292 | }, |
4698 | LintCompletion { | 5293 | Lint { |
4699 | label: "clippy::comparison_chain", | 5294 | label: "clippy::comparison_chain", |
4700 | description: r##"Checks comparison chains written with `if` that can be\nrewritten with `match` and `cmp`."##, | 5295 | description: r##"Checks comparison chains written with `if` that can be |
5296 | rewritten with `match` and `cmp`."##, | ||
4701 | }, | 5297 | }, |
4702 | LintCompletion { | 5298 | Lint { |
4703 | label: "clippy::comparison_to_empty", | 5299 | label: "clippy::comparison_to_empty", |
4704 | description: r##"Checks for comparing to an empty slice such as `\"\"` or `[]`,\nand suggests using `.is_empty()` where applicable."##, | 5300 | description: r##"Checks for comparing to an empty slice such as `` or `[]`, |
5301 | and suggests using `.is_empty()` where applicable."##, | ||
4705 | }, | 5302 | }, |
4706 | LintCompletion { | 5303 | Lint { |
4707 | label: "clippy::copy_iterator", | 5304 | label: "clippy::copy_iterator", |
4708 | description: r##"Checks for types that implement `Copy` as well as\n`Iterator`."##, | 5305 | description: r##"Checks for types that implement `Copy` as well as |
5306 | `Iterator`."##, | ||
4709 | }, | 5307 | }, |
4710 | LintCompletion { | 5308 | Lint { |
4711 | label: "clippy::create_dir", | 5309 | label: "clippy::create_dir", |
4712 | description: r##"Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead."##, | 5310 | description: r##"Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead."##, |
4713 | }, | 5311 | }, |
4714 | LintCompletion { | 5312 | Lint { |
4715 | label: "clippy::crosspointer_transmute", | 5313 | label: "clippy::crosspointer_transmute", |
4716 | description: r##"Checks for transmutes between a type `T` and `*T`."##, | 5314 | description: r##"Checks for transmutes between a type `T` and `*T`."##, |
4717 | }, | 5315 | }, |
4718 | LintCompletion { | 5316 | Lint { label: "clippy::dbg_macro", description: r##"Checks for usage of dbg!() macro."## }, |
4719 | label: "clippy::dbg_macro", | 5317 | Lint { |
4720 | description: r##"Checks for usage of dbg!() macro."##, | ||
4721 | }, | ||
4722 | LintCompletion { | ||
4723 | label: "clippy::debug_assert_with_mut_call", | 5318 | label: "clippy::debug_assert_with_mut_call", |
4724 | description: r##"Checks for function/method calls with a mutable\nparameter in `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!` macros."##, | 5319 | description: r##"Checks for function/method calls with a mutable |
5320 | parameter in `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!` macros."##, | ||
4725 | }, | 5321 | }, |
4726 | LintCompletion { | 5322 | Lint { |
4727 | label: "clippy::decimal_literal_representation", | 5323 | label: "clippy::decimal_literal_representation", |
4728 | description: r##"Warns if there is a better representation for a numeric literal."##, | 5324 | description: r##"Warns if there is a better representation for a numeric literal."##, |
4729 | }, | 5325 | }, |
4730 | LintCompletion { | 5326 | Lint { |
4731 | label: "clippy::declare_interior_mutable_const", | 5327 | label: "clippy::declare_interior_mutable_const", |
4732 | description: r##"Checks for declaration of `const` items which is interior\nmutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.)."##, | 5328 | description: r##"Checks for declaration of `const` items which is interior |
5329 | mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.)."##, | ||
4733 | }, | 5330 | }, |
4734 | LintCompletion { | 5331 | Lint { |
4735 | label: "clippy::default_numeric_fallback", | 5332 | label: "clippy::default_numeric_fallback", |
4736 | description: r##"Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type\ninference.\n\nDefault numeric fallback means that if numeric types have not yet been bound to concrete\ntypes at the end of type inference, then integer type is bound to `i32`, and similarly\nfloating type is bound to `f64`.\n\nSee [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback."##, | 5333 | description: r##"Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type |
5334 | inference. | ||
5335 | |||
5336 | Default numeric fallback means that if numeric types have not yet been bound to concrete | ||
5337 | types at the end of type inference, then integer type is bound to `i32`, and similarly | ||
5338 | floating type is bound to `f64`. | ||
5339 | |||
5340 | See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback."##, | ||
4737 | }, | 5341 | }, |
4738 | LintCompletion { | 5342 | Lint { |
4739 | label: "clippy::default_trait_access", | 5343 | label: "clippy::default_trait_access", |
4740 | description: r##"Checks for literal calls to `Default::default()`."##, | 5344 | description: r##"Checks for literal calls to `Default::default()`."##, |
4741 | }, | 5345 | }, |
4742 | LintCompletion { | 5346 | Lint { |
4743 | label: "clippy::deprecated_cfg_attr", | 5347 | label: "clippy::deprecated_cfg_attr", |
4744 | description: r##"Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it\nwith `#[rustfmt::skip]`."##, | 5348 | description: r##"Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it |
5349 | with `#[rustfmt::skip]`."##, | ||
4745 | }, | 5350 | }, |
4746 | LintCompletion { | 5351 | Lint { |
4747 | label: "clippy::deprecated_semver", | 5352 | label: "clippy::deprecated_semver", |
4748 | description: r##"Checks for `#[deprecated]` annotations with a `since`\nfield that is not a valid semantic version."##, | 5353 | description: r##"Checks for `#[deprecated]` annotations with a `since` |
5354 | field that is not a valid semantic version."##, | ||
4749 | }, | 5355 | }, |
4750 | LintCompletion { | 5356 | Lint { |
4751 | label: "clippy::deref_addrof", | 5357 | label: "clippy::deref_addrof", |
4752 | description: r##"Checks for usage of `*&` and `*&mut` in expressions."##, | 5358 | description: r##"Checks for usage of `*&` and `*&mut` in expressions."##, |
4753 | }, | 5359 | }, |
4754 | LintCompletion { | 5360 | Lint { |
4755 | label: "clippy::derive_hash_xor_eq", | 5361 | label: "clippy::derive_hash_xor_eq", |
4756 | description: r##"Checks for deriving `Hash` but implementing `PartialEq`\nexplicitly or vice versa."##, | 5362 | description: r##"Checks for deriving `Hash` but implementing `PartialEq` |
5363 | explicitly or vice versa."##, | ||
4757 | }, | 5364 | }, |
4758 | LintCompletion { | 5365 | Lint { |
4759 | label: "clippy::derive_ord_xor_partial_ord", | 5366 | label: "clippy::derive_ord_xor_partial_ord", |
4760 | description: r##"Checks for deriving `Ord` but implementing `PartialOrd`\nexplicitly or vice versa."##, | 5367 | description: r##"Checks for deriving `Ord` but implementing `PartialOrd` |
5368 | explicitly or vice versa."##, | ||
4761 | }, | 5369 | }, |
4762 | LintCompletion { | 5370 | Lint { |
4763 | label: "clippy::disallowed_method", | 5371 | label: "clippy::disallowed_method", |
4764 | description: r##"Denies the configured methods and functions in clippy.toml"##, | 5372 | description: r##"Denies the configured methods and functions in clippy.toml"##, |
4765 | }, | 5373 | }, |
4766 | LintCompletion { | 5374 | Lint { |
4767 | label: "clippy::diverging_sub_expression", | 5375 | label: "clippy::diverging_sub_expression", |
4768 | description: r##"Checks for diverging calls that are not match arms or\nstatements."##, | 5376 | description: r##"Checks for diverging calls that are not match arms or |
5377 | statements."##, | ||
4769 | }, | 5378 | }, |
4770 | LintCompletion { | 5379 | Lint { |
4771 | label: "clippy::doc_markdown", | 5380 | label: "clippy::doc_markdown", |
4772 | description: r##"Checks for the presence of `_`, `::` or camel-case words\noutside ticks in documentation."##, | 5381 | description: r##"Checks for the presence of `_`, `::` or camel-case words |
5382 | outside ticks in documentation."##, | ||
4773 | }, | 5383 | }, |
4774 | LintCompletion { | 5384 | Lint { |
4775 | label: "clippy::double_comparisons", | 5385 | label: "clippy::double_comparisons", |
4776 | description: r##"Checks for double comparisons that could be simplified to a single expression."##, | 5386 | description: r##"Checks for double comparisons that could be simplified to a single expression."##, |
4777 | }, | 5387 | }, |
4778 | LintCompletion { | 5388 | Lint { |
4779 | label: "clippy::double_must_use", | 5389 | label: "clippy::double_must_use", |
4780 | description: r##"Checks for a [`#[must_use]`] attribute without\nfurther information on functions and methods that return a type already\nmarked as `#[must_use]`.\n\n[`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"##, | 5390 | description: r##"Checks for a [`#[must_use]`] attribute without |
5391 | further information on functions and methods that return a type already | ||
5392 | marked as `#[must_use]`. | ||
5393 | |||
5394 | [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"##, | ||
4781 | }, | 5395 | }, |
4782 | LintCompletion { | 5396 | Lint { |
4783 | label: "clippy::double_neg", | 5397 | label: "clippy::double_neg", |
4784 | description: r##"Detects expressions of the form `--x`."##, | 5398 | description: r##"Detects expressions of the form `--x`."##, |
4785 | }, | 5399 | }, |
4786 | LintCompletion { | 5400 | Lint { |
4787 | label: "clippy::double_parens", | 5401 | label: "clippy::double_parens", |
4788 | description: r##"Checks for unnecessary double parentheses."##, | 5402 | description: r##"Checks for unnecessary double parentheses."##, |
4789 | }, | 5403 | }, |
4790 | LintCompletion { | 5404 | Lint { |
4791 | label: "clippy::drop_bounds", | ||
4792 | description: r##"Nothing. This lint has been deprecated."##, | ||
4793 | }, | ||
4794 | LintCompletion { | ||
4795 | label: "clippy::drop_copy", | 5405 | label: "clippy::drop_copy", |
4796 | description: r##"Checks for calls to `std::mem::drop` with a value\nthat derives the Copy trait"##, | 5406 | description: r##"Checks for calls to `std::mem::drop` with a value |
5407 | that derives the Copy trait"##, | ||
4797 | }, | 5408 | }, |
4798 | LintCompletion { | 5409 | Lint { |
4799 | label: "clippy::drop_ref", | 5410 | label: "clippy::drop_ref", |
4800 | description: r##"Checks for calls to `std::mem::drop` with a reference\ninstead of an owned value."##, | 5411 | description: r##"Checks for calls to `std::mem::drop` with a reference |
5412 | instead of an owned value."##, | ||
4801 | }, | 5413 | }, |
4802 | LintCompletion { | 5414 | Lint { |
4803 | label: "clippy::duplicate_underscore_argument", | 5415 | label: "clippy::duplicate_underscore_argument", |
4804 | description: r##"Checks for function arguments having the similar names\ndiffering by an underscore."##, | 5416 | description: r##"Checks for function arguments having the similar names |
5417 | differing by an underscore."##, | ||
4805 | }, | 5418 | }, |
4806 | LintCompletion { | 5419 | Lint { |
4807 | label: "clippy::duration_subsec", | 5420 | label: "clippy::duration_subsec", |
4808 | description: r##"Checks for calculation of subsecond microseconds or milliseconds\nfrom other `Duration` methods."##, | 5421 | description: r##"Checks for calculation of subsecond microseconds or milliseconds |
5422 | from other `Duration` methods."##, | ||
4809 | }, | 5423 | }, |
4810 | LintCompletion { | 5424 | Lint { |
4811 | label: "clippy::else_if_without_else", | 5425 | label: "clippy::else_if_without_else", |
4812 | description: r##"Checks for usage of if expressions with an `else if` branch,\nbut without a final `else` branch."##, | 5426 | description: r##"Checks for usage of if expressions with an `else if` branch, |
5427 | but without a final `else` branch."##, | ||
4813 | }, | 5428 | }, |
4814 | LintCompletion { | 5429 | Lint { |
4815 | label: "clippy::empty_enum", | 5430 | label: "clippy::empty_enum", |
4816 | description: r##"Checks for `enum`s with no variants.\n\nAs of this writing, the `never_type` is still a\nnightly-only experimental API. Therefore, this lint is only triggered\nif the `never_type` is enabled."##, | 5431 | description: r##"Checks for `enum`s with no variants. |
5432 | |||
5433 | As of this writing, the `never_type` is still a | ||
5434 | nightly-only experimental API. Therefore, this lint is only triggered | ||
5435 | if the `never_type` is enabled."##, | ||
4817 | }, | 5436 | }, |
4818 | LintCompletion { | 5437 | Lint { |
4819 | label: "clippy::empty_line_after_outer_attr", | 5438 | label: "clippy::empty_line_after_outer_attr", |
4820 | description: r##"Checks for empty lines after outer attributes"##, | 5439 | description: r##"Checks for empty lines after outer attributes"##, |
4821 | }, | 5440 | }, |
4822 | LintCompletion { | 5441 | Lint { label: "clippy::empty_loop", description: r##"Checks for empty `loop` expressions."## }, |
4823 | label: "clippy::empty_loop", | 5442 | Lint { |
4824 | description: r##"Checks for empty `loop` expressions."##, | ||
4825 | }, | ||
4826 | LintCompletion { | ||
4827 | label: "clippy::enum_clike_unportable_variant", | 5443 | label: "clippy::enum_clike_unportable_variant", |
4828 | description: r##"Checks for C-like enumerations that are\n`repr(isize/usize)` and have values that don't fit into an `i32`."##, | 5444 | description: r##"Checks for C-like enumerations that are |
5445 | `repr(isize/usize)` and have values that don't fit into an `i32`."##, | ||
4829 | }, | 5446 | }, |
4830 | LintCompletion { | 5447 | Lint { label: "clippy::enum_glob_use", description: r##"Checks for `use Enum::*`."## }, |
4831 | label: "clippy::enum_glob_use", | 5448 | Lint { |
4832 | description: r##"Checks for `use Enum::*`."##, | ||
4833 | }, | ||
4834 | LintCompletion { | ||
4835 | label: "clippy::enum_variant_names", | 5449 | label: "clippy::enum_variant_names", |
4836 | description: r##"Detects enumeration variants that are prefixed or suffixed\nby the same characters."##, | 5450 | description: r##"Detects enumeration variants that are prefixed or suffixed |
5451 | by the same characters."##, | ||
4837 | }, | 5452 | }, |
4838 | LintCompletion { | 5453 | Lint { |
4839 | label: "clippy::eq_op", | 5454 | label: "clippy::eq_op", |
4840 | description: r##"Checks for equal operands to comparison, logical and\nbitwise, difference and division binary operators (`==`, `>`, etc., `&&`,\n`||`, `&`, `|`, `^`, `-` and `/`)."##, | 5455 | description: r##"Checks for equal operands to comparison, logical and |
5456 | bitwise, difference and division binary operators (`==`, `>`, etc., `&&`, | ||
5457 | `||`, `&`, `|`, `^`, `-` and `/`)."##, | ||
4841 | }, | 5458 | }, |
4842 | LintCompletion { | 5459 | Lint { |
4843 | label: "clippy::erasing_op", | 5460 | label: "clippy::erasing_op", |
4844 | description: r##"Checks for erasing operations, e.g., `x * 0`."##, | 5461 | description: r##"Checks for erasing operations, e.g., `x * 0`."##, |
4845 | }, | 5462 | }, |
4846 | LintCompletion { | 5463 | Lint { |
4847 | label: "clippy::eval_order_dependence", | 5464 | label: "clippy::eval_order_dependence", |
4848 | description: r##"Checks for a read and a write to the same variable where\nwhether the read occurs before or after the write depends on the evaluation\norder of sub-expressions."##, | 5465 | description: r##"Checks for a read and a write to the same variable where |
5466 | whether the read occurs before or after the write depends on the evaluation | ||
5467 | order of sub-expressions."##, | ||
4849 | }, | 5468 | }, |
4850 | LintCompletion { | 5469 | Lint { |
4851 | label: "clippy::excessive_precision", | 5470 | label: "clippy::excessive_precision", |
4852 | description: r##"Checks for float literals with a precision greater\nthan that supported by the underlying type."##, | 5471 | description: r##"Checks for float literals with a precision greater |
5472 | than that supported by the underlying type."##, | ||
4853 | }, | 5473 | }, |
4854 | LintCompletion { | 5474 | Lint { |
4855 | label: "clippy::exhaustive_enums", | 5475 | label: "clippy::exhaustive_enums", |
4856 | description: r##"Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`"##, | 5476 | description: r##"Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`"##, |
4857 | }, | 5477 | }, |
4858 | LintCompletion { | 5478 | Lint { |
4859 | label: "clippy::exhaustive_structs", | 5479 | label: "clippy::exhaustive_structs", |
4860 | description: r##"Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`"##, | 5480 | description: r##"Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`"##, |
4861 | }, | 5481 | }, |
4862 | LintCompletion { | 5482 | Lint { |
4863 | label: "clippy::exit", | 5483 | label: "clippy::exit", |
4864 | description: r##"`exit()` terminates the program and doesn't provide a\nstack trace."##, | 5484 | description: r##"`exit()` terminates the program and doesn't provide a |
5485 | stack trace."##, | ||
4865 | }, | 5486 | }, |
4866 | LintCompletion { | 5487 | Lint { |
4867 | label: "clippy::expect_fun_call", | 5488 | label: "clippy::expect_fun_call", |
4868 | description: r##"Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`,\netc., and suggests to use `unwrap_or_else` instead"##, | 5489 | description: r##"Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`, |
5490 | etc., and suggests to use `unwrap_or_else` instead"##, | ||
4869 | }, | 5491 | }, |
4870 | LintCompletion { | 5492 | Lint { |
4871 | label: "clippy::expect_used", | 5493 | label: "clippy::expect_used", |
4872 | description: r##"Checks for `.expect()` calls on `Option`s and `Result`s."##, | 5494 | description: r##"Checks for `.expect()` calls on `Option`s and `Result`s."##, |
4873 | }, | 5495 | }, |
4874 | LintCompletion { | 5496 | Lint { |
4875 | label: "clippy::expl_impl_clone_on_copy", | 5497 | label: "clippy::expl_impl_clone_on_copy", |
4876 | description: r##"Checks for explicit `Clone` implementations for `Copy`\ntypes."##, | 5498 | description: r##"Checks for explicit `Clone` implementations for `Copy` |
5499 | types."##, | ||
4877 | }, | 5500 | }, |
4878 | LintCompletion { | 5501 | Lint { |
4879 | label: "clippy::explicit_counter_loop", | 5502 | label: "clippy::explicit_counter_loop", |
4880 | description: r##"Checks `for` loops over slices with an explicit counter\nand suggests the use of `.enumerate()`."##, | 5503 | description: r##"Checks `for` loops over slices with an explicit counter |
5504 | and suggests the use of `.enumerate()`."##, | ||
4881 | }, | 5505 | }, |
4882 | LintCompletion { | 5506 | Lint { |
4883 | label: "clippy::explicit_deref_methods", | 5507 | label: "clippy::explicit_deref_methods", |
4884 | description: r##"Checks for explicit `deref()` or `deref_mut()` method calls."##, | 5508 | description: r##"Checks for explicit `deref()` or `deref_mut()` method calls."##, |
4885 | }, | 5509 | }, |
4886 | LintCompletion { | 5510 | Lint { |
4887 | label: "clippy::explicit_into_iter_loop", | 5511 | label: "clippy::explicit_into_iter_loop", |
4888 | description: r##"Checks for loops on `y.into_iter()` where `y` will do, and\nsuggests the latter."##, | 5512 | description: r##"Checks for loops on `y.into_iter()` where `y` will do, and |
5513 | suggests the latter."##, | ||
4889 | }, | 5514 | }, |
4890 | LintCompletion { | 5515 | Lint { |
4891 | label: "clippy::explicit_iter_loop", | 5516 | label: "clippy::explicit_iter_loop", |
4892 | description: r##"Checks for loops on `x.iter()` where `&x` will do, and\nsuggests the latter."##, | 5517 | description: r##"Checks for loops on `x.iter()` where `&x` will do, and |
5518 | suggests the latter."##, | ||
4893 | }, | 5519 | }, |
4894 | LintCompletion { | 5520 | Lint { |
4895 | label: "clippy::explicit_write", | 5521 | label: "clippy::explicit_write", |
4896 | description: r##"Checks for usage of `write!()` / `writeln()!` which can be\nreplaced with `(e)print!()` / `(e)println!()`"##, | 5522 | description: r##"Checks for usage of `write!()` / `writeln()!` which can be |
5523 | replaced with `(e)print!()` / `(e)println!()`"##, | ||
4897 | }, | 5524 | }, |
4898 | LintCompletion { | 5525 | Lint { |
4899 | label: "clippy::extend_from_slice", | 5526 | label: "clippy::extend_from_slice", |
4900 | description: r##"Nothing. This lint has been deprecated."##, | 5527 | description: r##"Nothing. This lint has been deprecated."##, |
4901 | }, | 5528 | }, |
4902 | LintCompletion { | 5529 | Lint { |
4903 | label: "clippy::extra_unused_lifetimes", | 5530 | label: "clippy::extra_unused_lifetimes", |
4904 | description: r##"Checks for lifetimes in generics that are never used\nanywhere else."##, | 5531 | description: r##"Checks for lifetimes in generics that are never used |
5532 | anywhere else."##, | ||
4905 | }, | 5533 | }, |
4906 | LintCompletion { | 5534 | Lint { |
4907 | label: "clippy::fallible_impl_from", | 5535 | label: "clippy::fallible_impl_from", |
4908 | description: r##"Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`"##, | 5536 | description: r##"Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`"##, |
4909 | }, | 5537 | }, |
4910 | LintCompletion { | 5538 | Lint { |
4911 | label: "clippy::field_reassign_with_default", | 5539 | label: "clippy::field_reassign_with_default", |
4912 | description: r##"Checks for immediate reassignment of fields initialized\nwith Default::default()."##, | 5540 | description: r##"Checks for immediate reassignment of fields initialized |
5541 | with Default::default()."##, | ||
4913 | }, | 5542 | }, |
4914 | LintCompletion { | 5543 | Lint { |
4915 | label: "clippy::filetype_is_file", | 5544 | label: "clippy::filetype_is_file", |
4916 | description: r##"Checks for `FileType::is_file()`."##, | 5545 | description: r##"Checks for `FileType::is_file()`."##, |
4917 | }, | 5546 | }, |
4918 | LintCompletion { | 5547 | Lint { |
4919 | label: "clippy::filter_map", | 5548 | label: "clippy::filter_map", |
4920 | description: r##"Checks for usage of `_.filter(_).map(_)`,\n`_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar."##, | 5549 | description: r##"Nothing. This lint has been deprecated."##, |
4921 | }, | 5550 | }, |
4922 | LintCompletion { | 5551 | Lint { |
4923 | label: "clippy::filter_map_identity", | 5552 | label: "clippy::filter_map_identity", |
4924 | description: r##"Checks for usage of `filter_map(|x| x)`."##, | 5553 | description: r##"Checks for usage of `filter_map(|x| x)`."##, |
4925 | }, | 5554 | }, |
4926 | LintCompletion { | 5555 | Lint { |
4927 | label: "clippy::filter_map_next", | 5556 | label: "clippy::filter_map_next", |
4928 | description: r##"Checks for usage of `_.filter_map(_).next()`."##, | 5557 | description: r##"Checks for usage of `_.filter_map(_).next()`."##, |
4929 | }, | 5558 | }, |
4930 | LintCompletion { | 5559 | Lint { |
4931 | label: "clippy::filter_next", | 5560 | label: "clippy::filter_next", |
4932 | description: r##"Checks for usage of `_.filter(_).next()`."##, | 5561 | description: r##"Checks for usage of `_.filter(_).next()`."##, |
4933 | }, | 5562 | }, |
4934 | LintCompletion { | 5563 | Lint { label: "clippy::find_map", description: r##"Nothing. This lint has been deprecated."## }, |
4935 | label: "clippy::find_map", | 5564 | Lint { |
4936 | description: r##"Nothing. This lint has been deprecated."##, | ||
4937 | }, | ||
4938 | LintCompletion { | ||
4939 | label: "clippy::flat_map_identity", | 5565 | label: "clippy::flat_map_identity", |
4940 | description: r##"Checks for usage of `flat_map(|x| x)`."##, | 5566 | description: r##"Checks for usage of `flat_map(|x| x)`."##, |
4941 | }, | 5567 | }, |
4942 | LintCompletion { | 5568 | Lint { |
4943 | label: "clippy::float_arithmetic", | 5569 | label: "clippy::flat_map_option", |
4944 | description: r##"Checks for float arithmetic."##, | 5570 | description: r##"Checks for usages of `Iterator::flat_map()` where `filter_map()` could be |
5571 | used instead."##, | ||
4945 | }, | 5572 | }, |
4946 | LintCompletion { | 5573 | Lint { label: "clippy::float_arithmetic", description: r##"Checks for float arithmetic."## }, |
5574 | Lint { | ||
4947 | label: "clippy::float_cmp", | 5575 | label: "clippy::float_cmp", |
4948 | description: r##"Checks for (in-)equality comparisons on floating-point\nvalues (apart from zero), except in functions called `*eq*` (which probably\nimplement equality for a type involving floats)."##, | 5576 | description: r##"Checks for (in-)equality comparisons on floating-point |
5577 | values (apart from zero), except in functions called `*eq*` (which probably | ||
5578 | implement equality for a type involving floats)."##, | ||
4949 | }, | 5579 | }, |
4950 | LintCompletion { | 5580 | Lint { |
4951 | label: "clippy::float_cmp_const", | 5581 | label: "clippy::float_cmp_const", |
4952 | description: r##"Checks for (in-)equality comparisons on floating-point\nvalue and constant, except in functions called `*eq*` (which probably\nimplement equality for a type involving floats)."##, | 5582 | description: r##"Checks for (in-)equality comparisons on floating-point |
5583 | value and constant, except in functions called `*eq*` (which probably | ||
5584 | implement equality for a type involving floats)."##, | ||
4953 | }, | 5585 | }, |
4954 | LintCompletion { | 5586 | Lint { |
4955 | label: "clippy::float_equality_without_abs", | 5587 | label: "clippy::float_equality_without_abs", |
4956 | description: r##"Checks for statements of the form `(a - b) < f32::EPSILON` or\n`(a - b) < f64::EPSILON`. Notes the missing `.abs()`."##, | 5588 | description: r##"Checks for statements of the form `(a - b) < f32::EPSILON` or |
5589 | `(a - b) < f64::EPSILON`. Notes the missing `.abs()`."##, | ||
4957 | }, | 5590 | }, |
4958 | LintCompletion { | 5591 | Lint { |
4959 | label: "clippy::fn_address_comparisons", | 5592 | label: "clippy::fn_address_comparisons", |
4960 | description: r##"Checks for comparisons with an address of a function item."##, | 5593 | description: r##"Checks for comparisons with an address of a function item."##, |
4961 | }, | 5594 | }, |
4962 | LintCompletion { | 5595 | Lint { |
4963 | label: "clippy::fn_params_excessive_bools", | 5596 | label: "clippy::fn_params_excessive_bools", |
4964 | description: r##"Checks for excessive use of\nbools in function definitions."##, | 5597 | description: r##"Checks for excessive use of |
5598 | bools in function definitions."##, | ||
4965 | }, | 5599 | }, |
4966 | LintCompletion { | 5600 | Lint { |
4967 | label: "clippy::fn_to_numeric_cast", | 5601 | label: "clippy::fn_to_numeric_cast", |
4968 | description: r##"Checks for casts of function pointers to something other than usize"##, | 5602 | description: r##"Checks for casts of function pointers to something other than usize"##, |
4969 | }, | 5603 | }, |
4970 | LintCompletion { | 5604 | Lint { |
4971 | label: "clippy::fn_to_numeric_cast_with_truncation", | 5605 | label: "clippy::fn_to_numeric_cast_with_truncation", |
4972 | description: r##"Checks for casts of a function pointer to a numeric type not wide enough to\nstore address."##, | 5606 | description: r##"Checks for casts of a function pointer to a numeric type not wide enough to |
5607 | store address."##, | ||
4973 | }, | 5608 | }, |
4974 | LintCompletion { | 5609 | Lint { |
4975 | label: "clippy::for_kv_map", | 5610 | label: "clippy::for_kv_map", |
4976 | description: r##"Checks for iterating a map (`HashMap` or `BTreeMap`) and\nignoring either the keys or values."##, | 5611 | description: r##"Checks for iterating a map (`HashMap` or `BTreeMap`) and |
5612 | ignoring either the keys or values."##, | ||
4977 | }, | 5613 | }, |
4978 | LintCompletion { | 5614 | Lint { |
4979 | label: "clippy::for_loops_over_fallibles", | 5615 | label: "clippy::for_loops_over_fallibles", |
4980 | description: r##"Checks for `for` loops over `Option` or `Result` values."##, | 5616 | description: r##"Checks for `for` loops over `Option` or `Result` values."##, |
4981 | }, | 5617 | }, |
4982 | LintCompletion { | 5618 | Lint { |
4983 | label: "clippy::forget_copy", | 5619 | label: "clippy::forget_copy", |
4984 | description: r##"Checks for calls to `std::mem::forget` with a value that\nderives the Copy trait"##, | 5620 | description: r##"Checks for calls to `std::mem::forget` with a value that |
5621 | derives the Copy trait"##, | ||
4985 | }, | 5622 | }, |
4986 | LintCompletion { | 5623 | Lint { |
4987 | label: "clippy::forget_ref", | 5624 | label: "clippy::forget_ref", |
4988 | description: r##"Checks for calls to `std::mem::forget` with a reference\ninstead of an owned value."##, | 5625 | description: r##"Checks for calls to `std::mem::forget` with a reference |
5626 | instead of an owned value."##, | ||
4989 | }, | 5627 | }, |
4990 | LintCompletion { | 5628 | Lint { |
4991 | label: "clippy::from_iter_instead_of_collect", | 5629 | label: "clippy::from_iter_instead_of_collect", |
4992 | description: r##"Checks for `from_iter()` function calls on types that implement the `FromIterator`\ntrait."##, | 5630 | description: r##"Checks for `from_iter()` function calls on types that implement the `FromIterator` |
5631 | trait."##, | ||
4993 | }, | 5632 | }, |
4994 | LintCompletion { | 5633 | Lint { |
4995 | label: "clippy::from_over_into", | 5634 | label: "clippy::from_over_into", |
4996 | description: r##"Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead."##, | 5635 | description: r##"Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead."##, |
4997 | }, | 5636 | }, |
4998 | LintCompletion { | 5637 | Lint { |
4999 | label: "clippy::from_str_radix_10", | 5638 | label: "clippy::from_str_radix_10", |
5000 | description: r##"Checks for function invocations of the form `primitive::from_str_radix(s, 10)`"##, | 5639 | description: r##"Checks for function invocations of the form `primitive::from_str_radix(s, 10)`"##, |
5001 | }, | 5640 | }, |
5002 | LintCompletion { | 5641 | Lint { |
5003 | label: "clippy::future_not_send", | 5642 | label: "clippy::future_not_send", |
5004 | description: r##"This lint requires Future implementations returned from\nfunctions and methods to implement the `Send` marker trait. It is mostly\nused by library authors (public and internal) that target an audience where\nmultithreaded executors are likely to be used for running these Futures."##, | 5643 | description: r##"This lint requires Future implementations returned from |
5644 | functions and methods to implement the `Send` marker trait. It is mostly | ||
5645 | used by library authors (public and internal) that target an audience where | ||
5646 | multithreaded executors are likely to be used for running these Futures."##, | ||
5005 | }, | 5647 | }, |
5006 | LintCompletion { | 5648 | Lint { |
5007 | label: "clippy::get_last_with_len", | 5649 | label: "clippy::get_last_with_len", |
5008 | description: r##"Checks for using `x.get(x.len() - 1)` instead of\n`x.last()`."##, | 5650 | description: r##"Checks for using `x.get(x.len() - 1)` instead of |
5651 | `x.last()`."##, | ||
5009 | }, | 5652 | }, |
5010 | LintCompletion { | 5653 | Lint { |
5011 | label: "clippy::get_unwrap", | 5654 | label: "clippy::get_unwrap", |
5012 | description: r##"Checks for use of `.get().unwrap()` (or\n`.get_mut().unwrap`) on a standard library type which implements `Index`"##, | 5655 | description: r##"Checks for use of `.get().unwrap()` (or |
5656 | `.get_mut().unwrap`) on a standard library type which implements `Index`"##, | ||
5013 | }, | 5657 | }, |
5014 | LintCompletion { | 5658 | Lint { |
5015 | label: "clippy::identity_op", | 5659 | label: "clippy::identity_op", |
5016 | description: r##"Checks for identity operations, e.g., `x + 0`."##, | 5660 | description: r##"Checks for identity operations, e.g., `x + 0`."##, |
5017 | }, | 5661 | }, |
5018 | LintCompletion { | 5662 | Lint { |
5019 | label: "clippy::if_let_mutex", | 5663 | label: "clippy::if_let_mutex", |
5020 | description: r##"Checks for `Mutex::lock` calls in `if let` expression\nwith lock calls in any of the else blocks."##, | 5664 | description: r##"Checks for `Mutex::lock` calls in `if let` expression |
5665 | with lock calls in any of the else blocks."##, | ||
5021 | }, | 5666 | }, |
5022 | LintCompletion { | 5667 | Lint { |
5023 | label: "clippy::if_let_redundant_pattern_matching", | 5668 | label: "clippy::if_let_redundant_pattern_matching", |
5024 | description: r##"Nothing. This lint has been deprecated."##, | 5669 | description: r##"Nothing. This lint has been deprecated."##, |
5025 | }, | 5670 | }, |
5026 | LintCompletion { | 5671 | Lint { |
5027 | label: "clippy::if_let_some_result", | 5672 | label: "clippy::if_let_some_result", |
5028 | description: r##"* Checks for unnecessary `ok()` in if let."##, | 5673 | description: r##"* Checks for unnecessary `ok()` in if let."##, |
5029 | }, | 5674 | }, |
5030 | LintCompletion { | 5675 | Lint { |
5031 | label: "clippy::if_not_else", | 5676 | label: "clippy::if_not_else", |
5032 | description: r##"Checks for usage of `!` or `!=` in an if condition with an\nelse branch."##, | 5677 | description: r##"Checks for usage of `!` or `!=` in an if condition with an |
5678 | else branch."##, | ||
5033 | }, | 5679 | }, |
5034 | LintCompletion { | 5680 | Lint { |
5035 | label: "clippy::if_same_then_else", | 5681 | label: "clippy::if_same_then_else", |
5036 | description: r##"Checks for `if/else` with the same body as the *then* part\nand the *else* part."##, | 5682 | description: r##"Checks for `if/else` with the same body as the *then* part |
5683 | and the *else* part."##, | ||
5684 | }, | ||
5685 | Lint { | ||
5686 | label: "clippy::if_then_some_else_none", | ||
5687 | description: r##"Checks for if-else that could be written to `bool::then`."##, | ||
5037 | }, | 5688 | }, |
5038 | LintCompletion { | 5689 | Lint { |
5039 | label: "clippy::ifs_same_cond", | 5690 | label: "clippy::ifs_same_cond", |
5040 | description: r##"Checks for consecutive `if`s with the same condition."##, | 5691 | description: r##"Checks for consecutive `if`s with the same condition."##, |
5041 | }, | 5692 | }, |
5042 | LintCompletion { | 5693 | Lint { |
5043 | label: "clippy::implicit_clone", | 5694 | label: "clippy::implicit_clone", |
5044 | description: r##"Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer."##, | 5695 | description: r##"Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer."##, |
5045 | }, | 5696 | }, |
5046 | LintCompletion { | 5697 | Lint { |
5047 | label: "clippy::implicit_hasher", | 5698 | label: "clippy::implicit_hasher", |
5048 | description: r##"Checks for public `impl` or `fn` missing generalization\nover different hashers and implicitly defaulting to the default hashing\nalgorithm (`SipHash`)."##, | 5699 | description: r##"Checks for public `impl` or `fn` missing generalization |
5700 | over different hashers and implicitly defaulting to the default hashing | ||
5701 | algorithm (`SipHash`)."##, | ||
5049 | }, | 5702 | }, |
5050 | LintCompletion { | 5703 | Lint { |
5051 | label: "clippy::implicit_return", | 5704 | label: "clippy::implicit_return", |
5052 | description: r##"Checks for missing return statements at the end of a block."##, | 5705 | description: r##"Checks for missing return statements at the end of a block."##, |
5053 | }, | 5706 | }, |
5054 | LintCompletion { | 5707 | Lint { |
5055 | label: "clippy::implicit_saturating_sub", | 5708 | label: "clippy::implicit_saturating_sub", |
5056 | description: r##"Checks for implicit saturating subtraction."##, | 5709 | description: r##"Checks for implicit saturating subtraction."##, |
5057 | }, | 5710 | }, |
5058 | LintCompletion { | 5711 | Lint { |
5059 | label: "clippy::imprecise_flops", | 5712 | label: "clippy::imprecise_flops", |
5060 | description: r##"Looks for floating-point expressions that\ncan be expressed using built-in methods to improve accuracy\nat the cost of performance."##, | 5713 | description: r##"Looks for floating-point expressions that |
5714 | can be expressed using built-in methods to improve accuracy | ||
5715 | at the cost of performance."##, | ||
5061 | }, | 5716 | }, |
5062 | LintCompletion { | 5717 | Lint { |
5063 | label: "clippy::inconsistent_digit_grouping", | 5718 | label: "clippy::inconsistent_digit_grouping", |
5064 | description: r##"Warns if an integral or floating-point constant is\ngrouped inconsistently with underscores."##, | 5719 | description: r##"Warns if an integral or floating-point constant is |
5720 | grouped inconsistently with underscores."##, | ||
5065 | }, | 5721 | }, |
5066 | LintCompletion { | 5722 | Lint { |
5067 | label: "clippy::inconsistent_struct_constructor", | 5723 | label: "clippy::inconsistent_struct_constructor", |
5068 | description: r##"Checks for struct constructors where the order of the field init\nshorthand in the constructor is inconsistent with the order in the struct definition."##, | 5724 | description: r##"Checks for struct constructors where all fields are shorthand and |
5725 | the order of the field init shorthand in the constructor is inconsistent | ||
5726 | with the order in the struct definition."##, | ||
5069 | }, | 5727 | }, |
5070 | LintCompletion { | 5728 | Lint { |
5071 | label: "clippy::indexing_slicing", | 5729 | label: "clippy::indexing_slicing", |
5072 | description: r##"Checks for usage of indexing or slicing. Arrays are special cases, this lint\ndoes report on arrays if we can tell that slicing operations are in bounds and does not\nlint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint."##, | 5730 | description: r##"Checks for usage of indexing or slicing. Arrays are special cases, this lint |
5731 | does report on arrays if we can tell that slicing operations are in bounds and does not | ||
5732 | lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint."##, | ||
5073 | }, | 5733 | }, |
5074 | LintCompletion { | 5734 | Lint { |
5075 | label: "clippy::ineffective_bit_mask", | 5735 | label: "clippy::ineffective_bit_mask", |
5076 | description: r##"Checks for bit masks in comparisons which can be removed\nwithout changing the outcome. The basic structure can be seen in the\nfollowing table:\n\n|Comparison| Bit Op |Example |equals |\n|----------|---------|-----------|-------|\n|`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`|\n|`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|"##, | 5736 | description: r##"Checks for bit masks in comparisons which can be removed |
5737 | without changing the outcome. The basic structure can be seen in the | ||
5738 | following table: | ||
5739 | |||
5740 | |Comparison| Bit Op |Example |equals | | ||
5741 | |----------|---------|-----------|-------| | ||
5742 | |`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`| | ||
5743 | |`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|"##, | ||
5077 | }, | 5744 | }, |
5078 | LintCompletion { | 5745 | Lint { |
5079 | label: "clippy::inefficient_to_string", | 5746 | label: "clippy::inefficient_to_string", |
5080 | description: r##"Checks for usage of `.to_string()` on an `&&T` where\n`T` implements `ToString` directly (like `&&str` or `&&String`)."##, | 5747 | description: r##"Checks for usage of `.to_string()` on an `&&T` where |
5748 | `T` implements `ToString` directly (like `&&str` or `&&String`)."##, | ||
5081 | }, | 5749 | }, |
5082 | LintCompletion { | 5750 | Lint { |
5083 | label: "clippy::infallible_destructuring_match", | 5751 | label: "clippy::infallible_destructuring_match", |
5084 | description: r##"Checks for matches being used to destructure a single-variant enum\nor tuple struct where a `let` will suffice."##, | 5752 | description: r##"Checks for matches being used to destructure a single-variant enum |
5753 | or tuple struct where a `let` will suffice."##, | ||
5085 | }, | 5754 | }, |
5086 | LintCompletion { | 5755 | Lint { |
5087 | label: "clippy::infinite_iter", | 5756 | label: "clippy::infinite_iter", |
5088 | description: r##"Checks for iteration that is guaranteed to be infinite."##, | 5757 | description: r##"Checks for iteration that is guaranteed to be infinite."##, |
5089 | }, | 5758 | }, |
5090 | LintCompletion { | 5759 | Lint { |
5091 | label: "clippy::inherent_to_string", | 5760 | label: "clippy::inherent_to_string", |
5092 | description: r##"Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`."##, | 5761 | description: r##"Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`."##, |
5093 | }, | 5762 | }, |
5094 | LintCompletion { | 5763 | Lint { |
5095 | label: "clippy::inherent_to_string_shadow_display", | 5764 | label: "clippy::inherent_to_string_shadow_display", |
5096 | description: r##"Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait."##, | 5765 | description: r##"Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait."##, |
5097 | }, | 5766 | }, |
5098 | LintCompletion { | 5767 | Lint { |
5099 | label: "clippy::inline_always", | 5768 | label: "clippy::inline_always", |
5100 | description: r##"Checks for items annotated with `#[inline(always)]`,\nunless the annotated function is empty or simply panics."##, | 5769 | description: r##"Checks for items annotated with `#[inline(always)]`, |
5770 | unless the annotated function is empty or simply panics."##, | ||
5101 | }, | 5771 | }, |
5102 | LintCompletion { | 5772 | Lint { |
5103 | label: "clippy::inline_asm_x86_att_syntax", | 5773 | label: "clippy::inline_asm_x86_att_syntax", |
5104 | description: r##"Checks for usage of AT&T x86 assembly syntax."##, | 5774 | description: r##"Checks for usage of AT&T x86 assembly syntax."##, |
5105 | }, | 5775 | }, |
5106 | LintCompletion { | 5776 | Lint { |
5107 | label: "clippy::inline_asm_x86_intel_syntax", | 5777 | label: "clippy::inline_asm_x86_intel_syntax", |
5108 | description: r##"Checks for usage of Intel x86 assembly syntax."##, | 5778 | description: r##"Checks for usage of Intel x86 assembly syntax."##, |
5109 | }, | 5779 | }, |
5110 | LintCompletion { | 5780 | Lint { |
5111 | label: "clippy::inline_fn_without_body", | 5781 | label: "clippy::inline_fn_without_body", |
5112 | description: r##"Checks for `#[inline]` on trait methods without bodies"##, | 5782 | description: r##"Checks for `#[inline]` on trait methods without bodies"##, |
5113 | }, | 5783 | }, |
5114 | LintCompletion { | 5784 | Lint { |
5115 | label: "clippy::inspect_for_each", | 5785 | label: "clippy::inspect_for_each", |
5116 | description: r##"Checks for usage of `inspect().for_each()`."##, | 5786 | description: r##"Checks for usage of `inspect().for_each()`."##, |
5117 | }, | 5787 | }, |
5118 | LintCompletion { | 5788 | Lint { |
5119 | label: "clippy::int_plus_one", | 5789 | label: "clippy::int_plus_one", |
5120 | description: r##"Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block"##, | 5790 | description: r##"Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block"##, |
5121 | }, | 5791 | }, |
5122 | LintCompletion { | 5792 | Lint { |
5123 | label: "clippy::integer_arithmetic", | 5793 | label: "clippy::integer_arithmetic", |
5124 | description: r##"Checks for integer arithmetic operations which could overflow or panic.\n\nSpecifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable\nof overflowing according to the [Rust\nReference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),\nor which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is\nattempted."##, | 5794 | description: r##"Checks for integer arithmetic operations which could overflow or panic. |
5125 | }, | 5795 | |
5126 | LintCompletion { | 5796 | Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable |
5127 | label: "clippy::integer_division", | 5797 | of overflowing according to the [Rust |
5128 | description: r##"Checks for division of integers"##, | 5798 | Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow), |
5129 | }, | 5799 | or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is |
5130 | LintCompletion { | 5800 | attempted."##, |
5131 | label: "clippy::into_iter_on_array", | ||
5132 | description: r##"Nothing. This lint has been deprecated."##, | ||
5133 | }, | 5801 | }, |
5134 | LintCompletion { | 5802 | Lint { label: "clippy::integer_division", description: r##"Checks for division of integers"## }, |
5803 | Lint { | ||
5135 | label: "clippy::into_iter_on_ref", | 5804 | label: "clippy::into_iter_on_ref", |
5136 | description: r##"Checks for `into_iter` calls on references which should be replaced by `iter`\nor `iter_mut`."##, | 5805 | description: r##"Checks for `into_iter` calls on references which should be replaced by `iter` |
5806 | or `iter_mut`."##, | ||
5137 | }, | 5807 | }, |
5138 | LintCompletion { | 5808 | Lint { |
5139 | label: "clippy::invalid_atomic_ordering", | 5809 | label: "clippy::invalid_atomic_ordering", |
5140 | description: r##"Checks for usage of invalid atomic\nordering in atomic loads/stores/exchanges/updates and\nmemory fences."##, | 5810 | description: r##"Checks for usage of invalid atomic |
5811 | ordering in atomic loads/stores/exchanges/updates and | ||
5812 | memory fences."##, | ||
5141 | }, | 5813 | }, |
5142 | LintCompletion { | 5814 | Lint { |
5143 | label: "clippy::invalid_ref", | 5815 | label: "clippy::invalid_null_ptr_usage", |
5144 | description: r##"Nothing. This lint has been deprecated."##, | 5816 | description: r##"This lint checks for invalid usages of `ptr::null`."##, |
5145 | }, | 5817 | }, |
5146 | LintCompletion { | 5818 | Lint { |
5147 | label: "clippy::invalid_regex", | 5819 | label: "clippy::invalid_regex", |
5148 | description: r##"Checks [regex](https://crates.io/crates/regex) creation\n(with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`) for correct\nregex syntax."##, | 5820 | description: r##"Checks [regex](https://crates.io/crates/regex) creation |
5821 | (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`) for correct | ||
5822 | regex syntax."##, | ||
5149 | }, | 5823 | }, |
5150 | LintCompletion { | 5824 | Lint { |
5151 | label: "clippy::invalid_upcast_comparisons", | 5825 | label: "clippy::invalid_upcast_comparisons", |
5152 | description: r##"Checks for comparisons where the relation is always either\ntrue or false, but where one side has been upcast so that the comparison is\nnecessary. Only integer types are checked."##, | 5826 | description: r##"Checks for comparisons where the relation is always either |
5827 | true or false, but where one side has been upcast so that the comparison is | ||
5828 | necessary. Only integer types are checked."##, | ||
5153 | }, | 5829 | }, |
5154 | LintCompletion { | 5830 | Lint { |
5155 | label: "clippy::invisible_characters", | 5831 | label: "clippy::invisible_characters", |
5156 | description: r##"Checks for invisible Unicode characters in the code."##, | 5832 | description: r##"Checks for invisible Unicode characters in the code."##, |
5157 | }, | 5833 | }, |
5158 | LintCompletion { | 5834 | Lint { |
5159 | label: "clippy::items_after_statements", | 5835 | label: "clippy::items_after_statements", |
5160 | description: r##"Checks for items declared after some statement in a block."##, | 5836 | description: r##"Checks for items declared after some statement in a block."##, |
5161 | }, | 5837 | }, |
5162 | LintCompletion { | 5838 | Lint { |
5163 | label: "clippy::iter_cloned_collect", | 5839 | label: "clippy::iter_cloned_collect", |
5164 | description: r##"Checks for the use of `.cloned().collect()` on slice to\ncreate a `Vec`."##, | 5840 | description: r##"Checks for the use of `.cloned().collect()` on slice to |
5841 | create a `Vec`."##, | ||
5165 | }, | 5842 | }, |
5166 | LintCompletion { | 5843 | Lint { |
5167 | label: "clippy::iter_next_loop", | 5844 | label: "clippy::iter_count", |
5168 | description: r##"Checks for loops on `x.next()`."##, | 5845 | description: r##"Checks for the use of `.iter().count()`."##, |
5169 | }, | 5846 | }, |
5170 | LintCompletion { | 5847 | Lint { label: "clippy::iter_next_loop", description: r##"Checks for loops on `x.next()`."## }, |
5848 | Lint { | ||
5171 | label: "clippy::iter_next_slice", | 5849 | label: "clippy::iter_next_slice", |
5172 | description: r##"Checks for usage of `iter().next()` on a Slice or an Array"##, | 5850 | description: r##"Checks for usage of `iter().next()` on a Slice or an Array"##, |
5173 | }, | 5851 | }, |
5174 | LintCompletion { | 5852 | Lint { |
5175 | label: "clippy::iter_nth", | 5853 | label: "clippy::iter_nth", |
5176 | description: r##"Checks for use of `.iter().nth()` (and the related\n`.iter_mut().nth()`) on standard library types with O(1) element access."##, | 5854 | description: r##"Checks for use of `.iter().nth()` (and the related |
5855 | `.iter_mut().nth()`) on standard library types with O(1) element access."##, | ||
5177 | }, | 5856 | }, |
5178 | LintCompletion { | 5857 | Lint { |
5179 | label: "clippy::iter_nth_zero", | 5858 | label: "clippy::iter_nth_zero", |
5180 | description: r##"Checks for the use of `iter.nth(0)`."##, | 5859 | description: r##"Checks for the use of `iter.nth(0)`."##, |
5181 | }, | 5860 | }, |
5182 | LintCompletion { | 5861 | Lint { |
5183 | label: "clippy::iter_skip_next", | 5862 | label: "clippy::iter_skip_next", |
5184 | description: r##"Checks for use of `.skip(x).next()` on iterators."##, | 5863 | description: r##"Checks for use of `.skip(x).next()` on iterators."##, |
5185 | }, | 5864 | }, |
5186 | LintCompletion { | 5865 | Lint { |
5187 | label: "clippy::iterator_step_by_zero", | 5866 | label: "clippy::iterator_step_by_zero", |
5188 | description: r##"Checks for calling `.step_by(0)` on iterators which panics."##, | 5867 | description: r##"Checks for calling `.step_by(0)` on iterators which panics."##, |
5189 | }, | 5868 | }, |
5190 | LintCompletion { | 5869 | Lint { |
5191 | label: "clippy::just_underscores_and_digits", | 5870 | label: "clippy::just_underscores_and_digits", |
5192 | description: r##"Checks if you have variables whose name consists of just\nunderscores and digits."##, | 5871 | description: r##"Checks if you have variables whose name consists of just |
5872 | underscores and digits."##, | ||
5193 | }, | 5873 | }, |
5194 | LintCompletion { | 5874 | Lint { |
5195 | label: "clippy::large_const_arrays", | 5875 | label: "clippy::large_const_arrays", |
5196 | description: r##"Checks for large `const` arrays that should\nbe defined as `static` instead."##, | 5876 | description: r##"Checks for large `const` arrays that should |
5877 | be defined as `static` instead."##, | ||
5197 | }, | 5878 | }, |
5198 | LintCompletion { | 5879 | Lint { |
5199 | label: "clippy::large_digit_groups", | 5880 | label: "clippy::large_digit_groups", |
5200 | description: r##"Warns if the digits of an integral or floating-point\nconstant are grouped into groups that\nare too large."##, | 5881 | description: r##"Warns if the digits of an integral or floating-point |
5882 | constant are grouped into groups that | ||
5883 | are too large."##, | ||
5201 | }, | 5884 | }, |
5202 | LintCompletion { | 5885 | Lint { |
5203 | label: "clippy::large_enum_variant", | 5886 | label: "clippy::large_enum_variant", |
5204 | description: r##"Checks for large size differences between variants on\n`enum`s."##, | 5887 | description: r##"Checks for large size differences between variants on |
5888 | `enum`s."##, | ||
5205 | }, | 5889 | }, |
5206 | LintCompletion { | 5890 | Lint { |
5207 | label: "clippy::large_stack_arrays", | 5891 | label: "clippy::large_stack_arrays", |
5208 | description: r##"Checks for local arrays that may be too large."##, | 5892 | description: r##"Checks for local arrays that may be too large."##, |
5209 | }, | 5893 | }, |
5210 | LintCompletion { | 5894 | Lint { |
5211 | label: "clippy::large_types_passed_by_value", | 5895 | label: "clippy::large_types_passed_by_value", |
5212 | description: r##"Checks for functions taking arguments by value, where\nthe argument type is `Copy` and large enough to be worth considering\npassing by reference. Does not trigger if the function is being exported,\nbecause that might induce API breakage, if the parameter is declared as mutable,\nor if the argument is a `self`."##, | 5896 | description: r##"Checks for functions taking arguments by value, where |
5897 | the argument type is `Copy` and large enough to be worth considering | ||
5898 | passing by reference. Does not trigger if the function is being exported, | ||
5899 | because that might induce API breakage, if the parameter is declared as mutable, | ||
5900 | or if the argument is a `self`."##, | ||
5213 | }, | 5901 | }, |
5214 | LintCompletion { | 5902 | Lint { |
5215 | label: "clippy::len_without_is_empty", | 5903 | label: "clippy::len_without_is_empty", |
5216 | description: r##"Checks for items that implement `.len()` but not\n`.is_empty()`."##, | 5904 | description: r##"Checks for items that implement `.len()` but not |
5905 | `.is_empty()`."##, | ||
5217 | }, | 5906 | }, |
5218 | LintCompletion { | 5907 | Lint { |
5219 | label: "clippy::len_zero", | 5908 | label: "clippy::len_zero", |
5220 | description: r##"Checks for getting the length of something via `.len()`\njust to compare to zero, and suggests using `.is_empty()` where applicable."##, | 5909 | description: r##"Checks for getting the length of something via `.len()` |
5910 | just to compare to zero, and suggests using `.is_empty()` where applicable."##, | ||
5221 | }, | 5911 | }, |
5222 | LintCompletion { | 5912 | Lint { |
5223 | label: "clippy::let_and_return", | 5913 | label: "clippy::let_and_return", |
5224 | description: r##"Checks for `let`-bindings, which are subsequently\nreturned."##, | 5914 | description: r##"Checks for `let`-bindings, which are subsequently |
5915 | returned."##, | ||
5225 | }, | 5916 | }, |
5226 | LintCompletion { | 5917 | Lint { |
5227 | label: "clippy::let_underscore_drop", | 5918 | label: "clippy::let_underscore_drop", |
5228 | description: r##"Checks for `let _ = <expr>`\nwhere expr has a type that implements `Drop`"##, | 5919 | description: r##"Checks for `let _ = <expr>` |
5920 | where expr has a type that implements `Drop`"##, | ||
5229 | }, | 5921 | }, |
5230 | LintCompletion { | 5922 | Lint { |
5231 | label: "clippy::let_underscore_lock", | 5923 | label: "clippy::let_underscore_lock", |
5232 | description: r##"Checks for `let _ = sync_lock`"##, | 5924 | description: r##"Checks for `let _ = sync_lock`"##, |
5233 | }, | 5925 | }, |
5234 | LintCompletion { | 5926 | Lint { |
5235 | label: "clippy::let_underscore_must_use", | 5927 | label: "clippy::let_underscore_must_use", |
5236 | description: r##"Checks for `let _ = <expr>`\nwhere expr is #[must_use]"##, | 5928 | description: r##"Checks for `let _ = <expr>` |
5237 | }, | 5929 | where expr is #[must_use]"##, |
5238 | LintCompletion { | ||
5239 | label: "clippy::let_unit_value", | ||
5240 | description: r##"Checks for binding a unit value."##, | ||
5241 | }, | 5930 | }, |
5242 | LintCompletion { | 5931 | Lint { label: "clippy::let_unit_value", description: r##"Checks for binding a unit value."## }, |
5932 | Lint { | ||
5243 | label: "clippy::linkedlist", | 5933 | label: "clippy::linkedlist", |
5244 | description: r##"Checks for usage of any `LinkedList`, suggesting to use a\n`Vec` or a `VecDeque` (formerly called `RingBuf`)."##, | 5934 | description: r##"Checks for usage of any `LinkedList`, suggesting to use a |
5935 | `Vec` or a `VecDeque` (formerly called `RingBuf`)."##, | ||
5245 | }, | 5936 | }, |
5246 | LintCompletion { | 5937 | Lint { |
5247 | label: "clippy::logic_bug", | 5938 | label: "clippy::logic_bug", |
5248 | description: r##"Checks for boolean expressions that contain terminals that\ncan be eliminated."##, | 5939 | description: r##"Checks for boolean expressions that contain terminals that |
5940 | can be eliminated."##, | ||
5249 | }, | 5941 | }, |
5250 | LintCompletion { | 5942 | Lint { |
5251 | label: "clippy::lossy_float_literal", | 5943 | label: "clippy::lossy_float_literal", |
5252 | description: r##"Checks for whole number float literals that\ncannot be represented as the underlying type without loss."##, | 5944 | description: r##"Checks for whole number float literals that |
5945 | cannot be represented as the underlying type without loss."##, | ||
5253 | }, | 5946 | }, |
5254 | LintCompletion { | 5947 | Lint { |
5255 | label: "clippy::macro_use_imports", | 5948 | label: "clippy::macro_use_imports", |
5256 | description: r##"Checks for `#[macro_use] use...`."##, | 5949 | description: r##"Checks for `#[macro_use] use...`."##, |
5257 | }, | 5950 | }, |
5258 | LintCompletion { | 5951 | Lint { |
5259 | label: "clippy::main_recursion", | 5952 | label: "clippy::main_recursion", |
5260 | description: r##"Checks for recursion using the entrypoint."##, | 5953 | description: r##"Checks for recursion using the entrypoint."##, |
5261 | }, | 5954 | }, |
5262 | LintCompletion { | 5955 | Lint { |
5263 | label: "clippy::manual_async_fn", | 5956 | label: "clippy::manual_async_fn", |
5264 | description: r##"It checks for manual implementations of `async` functions."##, | 5957 | description: r##"It checks for manual implementations of `async` functions."##, |
5265 | }, | 5958 | }, |
5266 | LintCompletion { | 5959 | Lint { |
5267 | label: "clippy::manual_filter_map", | 5960 | label: "clippy::manual_filter_map", |
5268 | description: r##"Checks for usage of `_.filter(_).map(_)` that can be written more simply\nas `filter_map(_)`."##, | 5961 | description: r##"Checks for usage of `_.filter(_).map(_)` that can be written more simply |
5962 | as `filter_map(_)`."##, | ||
5269 | }, | 5963 | }, |
5270 | LintCompletion { | 5964 | Lint { |
5271 | label: "clippy::manual_find_map", | 5965 | label: "clippy::manual_find_map", |
5272 | description: r##"Checks for usage of `_.find(_).map(_)` that can be written more simply\nas `find_map(_)`."##, | 5966 | description: r##"Checks for usage of `_.find(_).map(_)` that can be written more simply |
5967 | as `find_map(_)`."##, | ||
5273 | }, | 5968 | }, |
5274 | LintCompletion { | 5969 | Lint { |
5275 | label: "clippy::manual_flatten", | 5970 | label: "clippy::manual_flatten", |
5276 | description: r##"Check for unnecessary `if let` usage in a for loop\nwhere only the `Some` or `Ok` variant of the iterator element is used."##, | 5971 | description: r##"Check for unnecessary `if let` usage in a for loop |
5972 | where only the `Some` or `Ok` variant of the iterator element is used."##, | ||
5277 | }, | 5973 | }, |
5278 | LintCompletion { | 5974 | Lint { |
5279 | label: "clippy::manual_map", | 5975 | label: "clippy::manual_map", |
5280 | description: r##"Checks for usages of `match` which could be implemented using `map`"##, | 5976 | description: r##"Checks for usages of `match` which could be implemented using `map`"##, |
5281 | }, | 5977 | }, |
5282 | LintCompletion { | 5978 | Lint { |
5283 | label: "clippy::manual_memcpy", | 5979 | label: "clippy::manual_memcpy", |
5284 | description: r##"Checks for for-loops that manually copy items between\nslices that could be optimized by having a memcpy."##, | 5980 | description: r##"Checks for for-loops that manually copy items between |
5981 | slices that could be optimized by having a memcpy."##, | ||
5285 | }, | 5982 | }, |
5286 | LintCompletion { | 5983 | Lint { |
5287 | label: "clippy::manual_non_exhaustive", | 5984 | label: "clippy::manual_non_exhaustive", |
5288 | description: r##"Checks for manual implementations of the non-exhaustive pattern."##, | 5985 | description: r##"Checks for manual implementations of the non-exhaustive pattern."##, |
5289 | }, | 5986 | }, |
5290 | LintCompletion { | 5987 | Lint { |
5291 | label: "clippy::manual_ok_or", | 5988 | label: "clippy::manual_ok_or", |
5292 | description: r##"Finds patterns that reimplement `Option::ok_or`."##, | 5989 | description: r##"Finds patterns that reimplement `Option::ok_or`."##, |
5293 | }, | 5990 | }, |
5294 | LintCompletion { | 5991 | Lint { |
5295 | label: "clippy::manual_range_contains", | 5992 | label: "clippy::manual_range_contains", |
5296 | description: r##"Checks for expressions like `x >= 3 && x < 8` that could\nbe more readably expressed as `(3..8).contains(x)`."##, | 5993 | description: r##"Checks for expressions like `x >= 3 && x < 8` that could |
5994 | be more readably expressed as `(3..8).contains(x)`."##, | ||
5297 | }, | 5995 | }, |
5298 | LintCompletion { | 5996 | Lint { |
5299 | label: "clippy::manual_saturating_arithmetic", | 5997 | label: "clippy::manual_saturating_arithmetic", |
5300 | description: r##"Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`."##, | 5998 | description: r##"Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`."##, |
5301 | }, | 5999 | }, |
5302 | LintCompletion { | 6000 | Lint { |
5303 | label: "clippy::manual_strip", | 6001 | label: "clippy::manual_str_repeat", |
5304 | description: r##"Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using\nthe pattern's length."##, | 6002 | description: r##"Checks for manual implementations of `str::repeat`"##, |
5305 | }, | 6003 | }, |
5306 | LintCompletion { | 6004 | Lint { |
5307 | label: "clippy::manual_swap", | 6005 | label: "clippy::manual_strip", |
5308 | description: r##"Checks for manual swapping."##, | 6006 | description: r##"Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using |
6007 | the pattern's length."##, | ||
5309 | }, | 6008 | }, |
5310 | LintCompletion { | 6009 | Lint { label: "clippy::manual_swap", description: r##"Checks for manual swapping."## }, |
6010 | Lint { | ||
5311 | label: "clippy::manual_unwrap_or", | 6011 | label: "clippy::manual_unwrap_or", |
5312 | description: r##"Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`."##, | 6012 | description: r##"Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`."##, |
5313 | }, | 6013 | }, |
5314 | LintCompletion { | 6014 | Lint { |
5315 | label: "clippy::many_single_char_names", | 6015 | label: "clippy::many_single_char_names", |
5316 | description: r##"Checks for too many variables whose name consists of a\nsingle character."##, | 6016 | description: r##"Checks for too many variables whose name consists of a |
6017 | single character."##, | ||
5317 | }, | 6018 | }, |
5318 | LintCompletion { | 6019 | Lint { |
5319 | label: "clippy::map_clone", | 6020 | label: "clippy::map_clone", |
5320 | description: r##"Checks for usage of `map(|x| x.clone())` or\ndereferencing closures for `Copy` types, on `Iterator` or `Option`,\nand suggests `cloned()` or `copied()` instead"##, | 6021 | description: r##"Checks for usage of `map(|x| x.clone())` or |
6022 | dereferencing closures for `Copy` types, on `Iterator` or `Option`, | ||
6023 | and suggests `cloned()` or `copied()` instead"##, | ||
5321 | }, | 6024 | }, |
5322 | LintCompletion { | 6025 | Lint { |
5323 | label: "clippy::map_collect_result_unit", | 6026 | label: "clippy::map_collect_result_unit", |
5324 | description: r##"Checks for usage of `_.map(_).collect::<Result<(), _>()`."##, | 6027 | description: r##"Checks for usage of `_.map(_).collect::<Result<(), _>()`."##, |
5325 | }, | 6028 | }, |
5326 | LintCompletion { | 6029 | Lint { |
5327 | label: "clippy::map_entry", | 6030 | label: "clippy::map_entry", |
5328 | description: r##"Checks for uses of `contains_key` + `insert` on `HashMap`\nor `BTreeMap`."##, | 6031 | description: r##"Checks for uses of `contains_key` + `insert` on `HashMap` |
6032 | or `BTreeMap`."##, | ||
5329 | }, | 6033 | }, |
5330 | LintCompletion { | 6034 | Lint { |
5331 | label: "clippy::map_err_ignore", | 6035 | label: "clippy::map_err_ignore", |
5332 | description: r##"Checks for instances of `map_err(|_| Some::Enum)`"##, | 6036 | description: r##"Checks for instances of `map_err(|_| Some::Enum)`"##, |
5333 | }, | 6037 | }, |
5334 | LintCompletion { | 6038 | Lint { |
5335 | label: "clippy::map_flatten", | 6039 | label: "clippy::map_flatten", |
5336 | description: r##"Checks for usage of `_.map(_).flatten(_)`,"##, | 6040 | description: r##"Checks for usage of `_.map(_).flatten(_)` on `Iterator` and `Option`"##, |
5337 | }, | 6041 | }, |
5338 | LintCompletion { | 6042 | Lint { |
5339 | label: "clippy::map_identity", | 6043 | label: "clippy::map_identity", |
5340 | description: r##"Checks for instances of `map(f)` where `f` is the identity function."##, | 6044 | description: r##"Checks for instances of `map(f)` where `f` is the identity function."##, |
5341 | }, | 6045 | }, |
5342 | LintCompletion { | 6046 | Lint { |
5343 | label: "clippy::map_unwrap_or", | 6047 | label: "clippy::map_unwrap_or", |
5344 | description: r##"Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or\n`result.map(_).unwrap_or_else(_)`."##, | 6048 | description: r##"Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or |
6049 | `result.map(_).unwrap_or_else(_)`."##, | ||
5345 | }, | 6050 | }, |
5346 | LintCompletion { | 6051 | Lint { |
5347 | label: "clippy::match_as_ref", | 6052 | label: "clippy::match_as_ref", |
5348 | description: r##"Checks for match which is used to add a reference to an\n`Option` value."##, | 6053 | description: r##"Checks for match which is used to add a reference to an |
6054 | `Option` value."##, | ||
5349 | }, | 6055 | }, |
5350 | LintCompletion { | 6056 | Lint { |
5351 | label: "clippy::match_bool", | 6057 | label: "clippy::match_bool", |
5352 | description: r##"Checks for matches where match expression is a `bool`. It\nsuggests to replace the expression with an `if...else` block."##, | 6058 | description: r##"Checks for matches where match expression is a `bool`. It |
6059 | suggests to replace the expression with an `if...else` block."##, | ||
5353 | }, | 6060 | }, |
5354 | LintCompletion { | 6061 | Lint { |
5355 | label: "clippy::match_like_matches_macro", | 6062 | label: "clippy::match_like_matches_macro", |
5356 | description: r##"Checks for `match` or `if let` expressions producing a\n`bool` that could be written using `matches!`"##, | 6063 | description: r##"Checks for `match` or `if let` expressions producing a |
6064 | `bool` that could be written using `matches!`"##, | ||
5357 | }, | 6065 | }, |
5358 | LintCompletion { | 6066 | Lint { |
5359 | label: "clippy::match_on_vec_items", | 6067 | label: "clippy::match_on_vec_items", |
5360 | description: r##"Checks for `match vec[idx]` or `match vec[n..m]`."##, | 6068 | description: r##"Checks for `match vec[idx]` or `match vec[n..m]`."##, |
5361 | }, | 6069 | }, |
5362 | LintCompletion { | 6070 | Lint { |
5363 | label: "clippy::match_overlapping_arm", | 6071 | label: "clippy::match_overlapping_arm", |
5364 | description: r##"Checks for overlapping match arms."##, | 6072 | description: r##"Checks for overlapping match arms."##, |
5365 | }, | 6073 | }, |
5366 | LintCompletion { | 6074 | Lint { |
5367 | label: "clippy::match_ref_pats", | 6075 | label: "clippy::match_ref_pats", |
5368 | description: r##"Checks for matches where all arms match a reference,\nsuggesting to remove the reference and deref the matched expression\ninstead. It also checks for `if let &foo = bar` blocks."##, | 6076 | description: r##"Checks for matches where all arms match a reference, |
6077 | suggesting to remove the reference and deref the matched expression | ||
6078 | instead. It also checks for `if let &foo = bar` blocks."##, | ||
5369 | }, | 6079 | }, |
5370 | LintCompletion { | 6080 | Lint { |
5371 | label: "clippy::match_same_arms", | 6081 | label: "clippy::match_same_arms", |
5372 | description: r##"Checks for `match` with identical arm bodies."##, | 6082 | description: r##"Checks for `match` with identical arm bodies."##, |
5373 | }, | 6083 | }, |
5374 | LintCompletion { | 6084 | Lint { |
5375 | label: "clippy::match_single_binding", | 6085 | label: "clippy::match_single_binding", |
5376 | description: r##"Checks for useless match that binds to only one value."##, | 6086 | description: r##"Checks for useless match that binds to only one value."##, |
5377 | }, | 6087 | }, |
5378 | LintCompletion { | 6088 | Lint { |
5379 | label: "clippy::match_wild_err_arm", | 6089 | label: "clippy::match_wild_err_arm", |
5380 | description: r##"Checks for arm which matches all errors with `Err(_)`\nand take drastic actions like `panic!`."##, | 6090 | description: r##"Checks for arm which matches all errors with `Err(_)` |
6091 | and take drastic actions like `panic!`."##, | ||
5381 | }, | 6092 | }, |
5382 | LintCompletion { | 6093 | Lint { |
5383 | label: "clippy::match_wildcard_for_single_variants", | 6094 | label: "clippy::match_wildcard_for_single_variants", |
5384 | description: r##"Checks for wildcard enum matches for a single variant."##, | 6095 | description: r##"Checks for wildcard enum matches for a single variant."##, |
5385 | }, | 6096 | }, |
5386 | LintCompletion { | 6097 | Lint { |
5387 | label: "clippy::maybe_infinite_iter", | 6098 | label: "clippy::maybe_infinite_iter", |
5388 | description: r##"Checks for iteration that may be infinite."##, | 6099 | description: r##"Checks for iteration that may be infinite."##, |
5389 | }, | 6100 | }, |
5390 | LintCompletion { | 6101 | Lint { |
5391 | label: "clippy::mem_discriminant_non_enum", | 6102 | label: "clippy::mem_discriminant_non_enum", |
5392 | description: r##"Checks for calls of `mem::discriminant()` on a non-enum type."##, | 6103 | description: r##"Checks for calls of `mem::discriminant()` on a non-enum type."##, |
5393 | }, | 6104 | }, |
5394 | LintCompletion { | 6105 | Lint { |
5395 | label: "clippy::mem_forget", | 6106 | label: "clippy::mem_forget", |
5396 | description: r##"Checks for usage of `std::mem::forget(t)` where `t` is\n`Drop`."##, | 6107 | description: r##"Checks for usage of `std::mem::forget(t)` where `t` is |
6108 | `Drop`."##, | ||
5397 | }, | 6109 | }, |
5398 | LintCompletion { | 6110 | Lint { |
5399 | label: "clippy::mem_replace_option_with_none", | 6111 | label: "clippy::mem_replace_option_with_none", |
5400 | description: r##"Checks for `mem::replace()` on an `Option` with\n`None`."##, | 6112 | description: r##"Checks for `mem::replace()` on an `Option` with |
6113 | `None`."##, | ||
5401 | }, | 6114 | }, |
5402 | LintCompletion { | 6115 | Lint { |
5403 | label: "clippy::mem_replace_with_default", | 6116 | label: "clippy::mem_replace_with_default", |
5404 | description: r##"Checks for `std::mem::replace` on a value of type\n`T` with `T::default()`."##, | 6117 | description: r##"Checks for `std::mem::replace` on a value of type |
6118 | `T` with `T::default()`."##, | ||
5405 | }, | 6119 | }, |
5406 | LintCompletion { | 6120 | Lint { |
5407 | label: "clippy::mem_replace_with_uninit", | 6121 | label: "clippy::mem_replace_with_uninit", |
5408 | description: r##"Checks for `mem::replace(&mut _, mem::uninitialized())`\nand `mem::replace(&mut _, mem::zeroed())`."##, | 6122 | description: r##"Checks for `mem::replace(&mut _, mem::uninitialized())` |
6123 | and `mem::replace(&mut _, mem::zeroed())`."##, | ||
5409 | }, | 6124 | }, |
5410 | LintCompletion { | 6125 | Lint { |
5411 | label: "clippy::min_max", | 6126 | label: "clippy::min_max", |
5412 | description: r##"Checks for expressions where `std::cmp::min` and `max` are\nused to clamp values, but switched so that the result is constant."##, | 6127 | description: r##"Checks for expressions where `std::cmp::min` and `max` are |
6128 | used to clamp values, but switched so that the result is constant."##, | ||
5413 | }, | 6129 | }, |
5414 | LintCompletion { | 6130 | Lint { |
5415 | label: "clippy::misaligned_transmute", | 6131 | label: "clippy::misaligned_transmute", |
5416 | description: r##"Nothing. This lint has been deprecated."##, | 6132 | description: r##"Nothing. This lint has been deprecated."##, |
5417 | }, | 6133 | }, |
5418 | LintCompletion { | 6134 | Lint { |
5419 | label: "clippy::mismatched_target_os", | 6135 | label: "clippy::mismatched_target_os", |
5420 | description: r##"Checks for cfg attributes having operating systems used in target family position."##, | 6136 | description: r##"Checks for cfg attributes having operating systems used in target family position."##, |
5421 | }, | 6137 | }, |
5422 | LintCompletion { | 6138 | Lint { |
5423 | label: "clippy::misrefactored_assign_op", | 6139 | label: "clippy::misrefactored_assign_op", |
5424 | description: r##"Checks for `a op= a op b` or `a op= b op a` patterns."##, | 6140 | description: r##"Checks for `a op= a op b` or `a op= b op a` patterns."##, |
5425 | }, | 6141 | }, |
5426 | LintCompletion { | 6142 | Lint { |
5427 | label: "clippy::missing_const_for_fn", | 6143 | label: "clippy::missing_const_for_fn", |
5428 | description: r##"Suggests the use of `const` in functions and methods where possible."##, | 6144 | description: r##"Suggests the use of `const` in functions and methods where possible."##, |
5429 | }, | 6145 | }, |
5430 | LintCompletion { | 6146 | Lint { |
5431 | label: "clippy::missing_docs_in_private_items", | 6147 | label: "clippy::missing_docs_in_private_items", |
5432 | description: r##"Warns if there is missing doc for any documentable item\n(public or private)."##, | 6148 | description: r##"Warns if there is missing doc for any documentable item |
6149 | (public or private)."##, | ||
5433 | }, | 6150 | }, |
5434 | LintCompletion { | 6151 | Lint { |
5435 | label: "clippy::missing_errors_doc", | 6152 | label: "clippy::missing_errors_doc", |
5436 | description: r##"Checks the doc comments of publicly visible functions that\nreturn a `Result` type and warns if there is no `# Errors` section."##, | 6153 | description: r##"Checks the doc comments of publicly visible functions that |
6154 | return a `Result` type and warns if there is no `# Errors` section."##, | ||
5437 | }, | 6155 | }, |
5438 | LintCompletion { | 6156 | Lint { |
5439 | label: "clippy::missing_inline_in_public_items", | 6157 | label: "clippy::missing_inline_in_public_items", |
5440 | description: r##"it lints if an exported function, method, trait method with default impl,\nor trait method impl is not `#[inline]`."##, | 6158 | description: r##"it lints if an exported function, method, trait method with default impl, |
6159 | or trait method impl is not `#[inline]`."##, | ||
5441 | }, | 6160 | }, |
5442 | LintCompletion { | 6161 | Lint { |
5443 | label: "clippy::missing_panics_doc", | 6162 | label: "clippy::missing_panics_doc", |
5444 | description: r##"Checks the doc comments of publicly visible functions that\nmay panic and warns if there is no `# Panics` section."##, | 6163 | description: r##"Checks the doc comments of publicly visible functions that |
6164 | may panic and warns if there is no `# Panics` section."##, | ||
5445 | }, | 6165 | }, |
5446 | LintCompletion { | 6166 | Lint { |
5447 | label: "clippy::missing_safety_doc", | 6167 | label: "clippy::missing_safety_doc", |
5448 | description: r##"Checks for the doc comments of publicly visible\nunsafe functions and warns if there is no `# Safety` section."##, | 6168 | description: r##"Checks for the doc comments of publicly visible |
6169 | unsafe functions and warns if there is no `# Safety` section."##, | ||
5449 | }, | 6170 | }, |
5450 | LintCompletion { | 6171 | Lint { |
5451 | label: "clippy::mistyped_literal_suffixes", | 6172 | label: "clippy::mistyped_literal_suffixes", |
5452 | description: r##"Warns for mistyped suffix in literals"##, | 6173 | description: r##"Warns for mistyped suffix in literals"##, |
5453 | }, | 6174 | }, |
5454 | LintCompletion { | 6175 | Lint { |
5455 | label: "clippy::mixed_case_hex_literals", | 6176 | label: "clippy::mixed_case_hex_literals", |
5456 | description: r##"Warns on hexadecimal literals with mixed-case letter\ndigits."##, | 6177 | description: r##"Warns on hexadecimal literals with mixed-case letter |
6178 | digits."##, | ||
5457 | }, | 6179 | }, |
5458 | LintCompletion { | 6180 | Lint { |
5459 | label: "clippy::module_inception", | 6181 | label: "clippy::module_inception", |
5460 | description: r##"Checks for modules that have the same name as their\nparent module"##, | 6182 | description: r##"Checks for modules that have the same name as their |
6183 | parent module"##, | ||
5461 | }, | 6184 | }, |
5462 | LintCompletion { | 6185 | Lint { |
5463 | label: "clippy::module_name_repetitions", | 6186 | label: "clippy::module_name_repetitions", |
5464 | description: r##"Detects type names that are prefixed or suffixed by the\ncontaining module's name."##, | 6187 | description: r##"Detects type names that are prefixed or suffixed by the |
5465 | }, | 6188 | containing module's name."##, |
5466 | LintCompletion { | ||
5467 | label: "clippy::modulo_arithmetic", | ||
5468 | description: r##"Checks for modulo arithmetic."##, | ||
5469 | }, | 6189 | }, |
5470 | LintCompletion { | 6190 | Lint { label: "clippy::modulo_arithmetic", description: r##"Checks for modulo arithmetic."## }, |
6191 | Lint { | ||
5471 | label: "clippy::modulo_one", | 6192 | label: "clippy::modulo_one", |
5472 | description: r##"Checks for getting the remainder of a division by one or minus\none."##, | 6193 | description: r##"Checks for getting the remainder of a division by one or minus |
6194 | one."##, | ||
5473 | }, | 6195 | }, |
5474 | LintCompletion { | 6196 | Lint { |
5475 | label: "clippy::multiple_crate_versions", | 6197 | label: "clippy::multiple_crate_versions", |
5476 | description: r##"Checks to see if multiple versions of a crate are being\nused."##, | 6198 | description: r##"Checks to see if multiple versions of a crate are being |
6199 | used."##, | ||
5477 | }, | 6200 | }, |
5478 | LintCompletion { | 6201 | Lint { |
5479 | label: "clippy::multiple_inherent_impl", | 6202 | label: "clippy::multiple_inherent_impl", |
5480 | description: r##"Checks for multiple inherent implementations of a struct"##, | 6203 | description: r##"Checks for multiple inherent implementations of a struct"##, |
5481 | }, | 6204 | }, |
5482 | LintCompletion { | 6205 | Lint { |
5483 | label: "clippy::must_use_candidate", | 6206 | label: "clippy::must_use_candidate", |
5484 | description: r##"Checks for public functions that have no\n[`#[must_use]`] attribute, but return something not already marked\nmust-use, have no mutable arg and mutate no statics.\n\n[`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"##, | 6207 | description: r##"Checks for public functions that have no |
6208 | [`#[must_use]`] attribute, but return something not already marked | ||
6209 | must-use, have no mutable arg and mutate no statics. | ||
6210 | |||
6211 | [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"##, | ||
5485 | }, | 6212 | }, |
5486 | LintCompletion { | 6213 | Lint { |
5487 | label: "clippy::must_use_unit", | 6214 | label: "clippy::must_use_unit", |
5488 | description: r##"Checks for a [`#[must_use]`] attribute on\nunit-returning functions and methods.\n\n[`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"##, | 6215 | description: r##"Checks for a [`#[must_use]`] attribute on |
6216 | unit-returning functions and methods. | ||
6217 | |||
6218 | [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"##, | ||
5489 | }, | 6219 | }, |
5490 | LintCompletion { | 6220 | Lint { |
5491 | label: "clippy::mut_from_ref", | 6221 | label: "clippy::mut_from_ref", |
5492 | description: r##"This lint checks for functions that take immutable\nreferences and return mutable ones."##, | 6222 | description: r##"This lint checks for functions that take immutable |
6223 | references and return mutable ones."##, | ||
5493 | }, | 6224 | }, |
5494 | LintCompletion { | 6225 | Lint { |
5495 | label: "clippy::mut_mut", | 6226 | label: "clippy::mut_mut", |
5496 | description: r##"Checks for instances of `mut mut` references."##, | 6227 | description: r##"Checks for instances of `mut mut` references."##, |
5497 | }, | 6228 | }, |
5498 | LintCompletion { | 6229 | Lint { |
5499 | label: "clippy::mut_mutex_lock", | 6230 | label: "clippy::mut_mutex_lock", |
5500 | description: r##"Checks for `&mut Mutex::lock` calls"##, | 6231 | description: r##"Checks for `&mut Mutex::lock` calls"##, |
5501 | }, | 6232 | }, |
5502 | LintCompletion { | 6233 | Lint { |
5503 | label: "clippy::mut_range_bound", | 6234 | label: "clippy::mut_range_bound", |
5504 | description: r##"Checks for loops which have a range bound that is a mutable variable"##, | 6235 | description: r##"Checks for loops which have a range bound that is a mutable variable"##, |
5505 | }, | 6236 | }, |
5506 | LintCompletion { | 6237 | Lint { |
5507 | label: "clippy::mutable_key_type", | 6238 | label: "clippy::mutable_key_type", |
5508 | description: r##"Checks for sets/maps with mutable key types."##, | 6239 | description: r##"Checks for sets/maps with mutable key types."##, |
5509 | }, | 6240 | }, |
5510 | LintCompletion { | 6241 | Lint { |
5511 | label: "clippy::mutex_atomic", | 6242 | label: "clippy::mutex_atomic", |
5512 | description: r##"Checks for usages of `Mutex<X>` where an atomic will do."##, | 6243 | description: r##"Checks for usages of `Mutex<X>` where an atomic will do."##, |
5513 | }, | 6244 | }, |
5514 | LintCompletion { | 6245 | Lint { |
5515 | label: "clippy::mutex_integer", | 6246 | label: "clippy::mutex_integer", |
5516 | description: r##"Checks for usages of `Mutex<X>` where `X` is an integral\ntype."##, | 6247 | description: r##"Checks for usages of `Mutex<X>` where `X` is an integral |
5517 | }, | 6248 | type."##, |
5518 | LintCompletion { | ||
5519 | label: "clippy::naive_bytecount", | ||
5520 | description: r##"Checks for naive byte counts"##, | ||
5521 | }, | 6249 | }, |
5522 | LintCompletion { | 6250 | Lint { label: "clippy::naive_bytecount", description: r##"Checks for naive byte counts"## }, |
6251 | Lint { | ||
5523 | label: "clippy::needless_arbitrary_self_type", | 6252 | label: "clippy::needless_arbitrary_self_type", |
5524 | description: r##"The lint checks for `self` in fn parameters that\nspecify the `Self`-type explicitly"##, | 6253 | description: r##"The lint checks for `self` in fn parameters that |
6254 | specify the `Self`-type explicitly"##, | ||
5525 | }, | 6255 | }, |
5526 | LintCompletion { | 6256 | Lint { |
6257 | label: "clippy::needless_bitwise_bool", | ||
6258 | description: r##"Checks for uses of bitwise and/or operators between booleans, where performance may be improved by using | ||
6259 | a lazy and."##, | ||
6260 | }, | ||
6261 | Lint { | ||
5527 | label: "clippy::needless_bool", | 6262 | label: "clippy::needless_bool", |
5528 | description: r##"Checks for expressions of the form `if c { true } else {\nfalse }` (or vice versa) and suggests using the condition directly."##, | 6263 | description: r##"Checks for expressions of the form `if c { true } else { |
6264 | false }` (or vice versa) and suggests using the condition directly."##, | ||
5529 | }, | 6265 | }, |
5530 | LintCompletion { | 6266 | Lint { |
5531 | label: "clippy::needless_borrow", | 6267 | label: "clippy::needless_borrow", |
5532 | description: r##"Checks for address of operations (`&`) that are going to\nbe dereferenced immediately by the compiler."##, | 6268 | description: r##"Checks for address of operations (`&`) that are going to |
6269 | be dereferenced immediately by the compiler."##, | ||
5533 | }, | 6270 | }, |
5534 | LintCompletion { | 6271 | Lint { |
5535 | label: "clippy::needless_borrowed_reference", | 6272 | label: "clippy::needless_borrowed_reference", |
5536 | description: r##"Checks for useless borrowed references."##, | 6273 | description: r##"Checks for bindings that destructure a reference and borrow the inner |
6274 | value with `&ref`."##, | ||
5537 | }, | 6275 | }, |
5538 | LintCompletion { | 6276 | Lint { |
5539 | label: "clippy::needless_collect", | 6277 | label: "clippy::needless_collect", |
5540 | description: r##"Checks for functions collecting an iterator when collect\nis not needed."##, | 6278 | description: r##"Checks for functions collecting an iterator when collect |
6279 | is not needed."##, | ||
5541 | }, | 6280 | }, |
5542 | LintCompletion { | 6281 | Lint { |
5543 | label: "clippy::needless_continue", | 6282 | label: "clippy::needless_continue", |
5544 | description: r##"The lint checks for `if`-statements appearing in loops\nthat contain a `continue` statement in either their main blocks or their\n`else`-blocks, when omitting the `else`-block possibly with some\nrearrangement of code can make the code easier to understand."##, | 6283 | description: r##"The lint checks for `if`-statements appearing in loops |
6284 | that contain a `continue` statement in either their main blocks or their | ||
6285 | `else`-blocks, when omitting the `else`-block possibly with some | ||
6286 | rearrangement of code can make the code easier to understand."##, | ||
5545 | }, | 6287 | }, |
5546 | LintCompletion { | 6288 | Lint { |
5547 | label: "clippy::needless_doctest_main", | 6289 | label: "clippy::needless_doctest_main", |
5548 | description: r##"Checks for `fn main() { .. }` in doctests"##, | 6290 | description: r##"Checks for `fn main() { .. }` in doctests"##, |
5549 | }, | 6291 | }, |
5550 | LintCompletion { | 6292 | Lint { |
6293 | label: "clippy::needless_for_each", | ||
6294 | description: r##"Checks for usage of `for_each` that would be more simply written as a | ||
6295 | `for` loop."##, | ||
6296 | }, | ||
6297 | Lint { | ||
5551 | label: "clippy::needless_lifetimes", | 6298 | label: "clippy::needless_lifetimes", |
5552 | description: r##"Checks for lifetime annotations which can be removed by\nrelying on lifetime elision."##, | 6299 | description: r##"Checks for lifetime annotations which can be removed by |
6300 | relying on lifetime elision."##, | ||
5553 | }, | 6301 | }, |
5554 | LintCompletion { | 6302 | Lint { |
5555 | label: "clippy::needless_pass_by_value", | 6303 | label: "clippy::needless_pass_by_value", |
5556 | description: r##"Checks for functions taking arguments by value, but not\nconsuming them in its\nbody."##, | 6304 | description: r##"Checks for functions taking arguments by value, but not |
6305 | consuming them in its | ||
6306 | body."##, | ||
5557 | }, | 6307 | }, |
5558 | LintCompletion { | 6308 | Lint { |
5559 | label: "clippy::needless_question_mark", | 6309 | label: "clippy::needless_question_mark", |
5560 | description: r##"Suggests alternatives for useless applications of `?` in terminating expressions"##, | 6310 | description: r##"Suggests alternatives for useless applications of `?` in terminating expressions"##, |
5561 | }, | 6311 | }, |
5562 | LintCompletion { | 6312 | Lint { |
5563 | label: "clippy::needless_range_loop", | 6313 | label: "clippy::needless_range_loop", |
5564 | description: r##"Checks for looping over the range of `0..len` of some\ncollection just to get the values by index."##, | 6314 | description: r##"Checks for looping over the range of `0..len` of some |
6315 | collection just to get the values by index."##, | ||
5565 | }, | 6316 | }, |
5566 | LintCompletion { | 6317 | Lint { |
5567 | label: "clippy::needless_return", | 6318 | label: "clippy::needless_return", |
5568 | description: r##"Checks for return statements at the end of a block."##, | 6319 | description: r##"Checks for return statements at the end of a block."##, |
5569 | }, | 6320 | }, |
5570 | LintCompletion { | 6321 | Lint { |
5571 | label: "clippy::needless_update", | 6322 | label: "clippy::needless_update", |
5572 | description: r##"Checks for needlessly including a base struct on update\nwhen all fields are changed anyway.\n\nThis lint is not applied to structs marked with\n[non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html)."##, | 6323 | description: r##"Checks for needlessly including a base struct on update |
6324 | when all fields are changed anyway. | ||
6325 | |||
6326 | This lint is not applied to structs marked with | ||
6327 | [non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html)."##, | ||
5573 | }, | 6328 | }, |
5574 | LintCompletion { | 6329 | Lint { |
5575 | label: "clippy::neg_cmp_op_on_partial_ord", | 6330 | label: "clippy::neg_cmp_op_on_partial_ord", |
5576 | description: r##"Checks for the usage of negated comparison operators on types which only implement\n`PartialOrd` (e.g., `f64`)."##, | 6331 | description: r##"Checks for the usage of negated comparison operators on types which only implement |
6332 | `PartialOrd` (e.g., `f64`)."##, | ||
5577 | }, | 6333 | }, |
5578 | LintCompletion { | 6334 | Lint { |
5579 | label: "clippy::neg_multiply", | 6335 | label: "clippy::neg_multiply", |
5580 | description: r##"Checks for multiplication by -1 as a form of negation."##, | 6336 | description: r##"Checks for multiplication by -1 as a form of negation."##, |
5581 | }, | 6337 | }, |
5582 | LintCompletion { | 6338 | Lint { |
5583 | label: "clippy::never_loop", | 6339 | label: "clippy::never_loop", |
5584 | description: r##"Checks for loops that will always `break`, `return` or\n`continue` an outer loop."##, | 6340 | description: r##"Checks for loops that will always `break`, `return` or |
6341 | `continue` an outer loop."##, | ||
5585 | }, | 6342 | }, |
5586 | LintCompletion { | 6343 | Lint { |
5587 | label: "clippy::new_ret_no_self", | 6344 | label: "clippy::new_ret_no_self", |
5588 | description: r##"Checks for `new` not returning a type that contains `Self`."##, | 6345 | description: r##"Checks for `new` not returning a type that contains `Self`."##, |
5589 | }, | 6346 | }, |
5590 | LintCompletion { | 6347 | Lint { |
5591 | label: "clippy::new_without_default", | 6348 | label: "clippy::new_without_default", |
5592 | description: r##"Checks for types with a `fn new() -> Self` method and no\nimplementation of\n[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)."##, | 6349 | description: r##"Checks for types with a `fn new() -> Self` method and no |
6350 | implementation of | ||
6351 | [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)."##, | ||
5593 | }, | 6352 | }, |
5594 | LintCompletion { | 6353 | Lint { |
5595 | label: "clippy::no_effect", | 6354 | label: "clippy::no_effect", |
5596 | description: r##"Checks for statements which have no effect."##, | 6355 | description: r##"Checks for statements which have no effect."##, |
5597 | }, | 6356 | }, |
5598 | LintCompletion { | 6357 | Lint { |
5599 | label: "clippy::non_ascii_literal", | 6358 | label: "clippy::non_ascii_literal", |
5600 | description: r##"Checks for non-ASCII characters in string literals."##, | 6359 | description: r##"Checks for non-ASCII characters in string literals."##, |
5601 | }, | 6360 | }, |
5602 | LintCompletion { | 6361 | Lint { |
6362 | label: "clippy::non_octal_unix_permissions", | ||
6363 | description: r##"Checks for non-octal values used to set Unix file permissions."##, | ||
6364 | }, | ||
6365 | Lint { | ||
5603 | label: "clippy::nonminimal_bool", | 6366 | label: "clippy::nonminimal_bool", |
5604 | description: r##"Checks for boolean expressions that can be written more\nconcisely."##, | 6367 | description: r##"Checks for boolean expressions that can be written more |
6368 | concisely."##, | ||
5605 | }, | 6369 | }, |
5606 | LintCompletion { | 6370 | Lint { |
5607 | label: "clippy::nonsensical_open_options", | 6371 | label: "clippy::nonsensical_open_options", |
5608 | description: r##"Checks for duplicate open options as well as combinations\nthat make no sense."##, | 6372 | description: r##"Checks for duplicate open options as well as combinations |
6373 | that make no sense."##, | ||
5609 | }, | 6374 | }, |
5610 | LintCompletion { | 6375 | Lint { |
5611 | label: "clippy::not_unsafe_ptr_arg_deref", | 6376 | label: "clippy::not_unsafe_ptr_arg_deref", |
5612 | description: r##"Checks for public functions that dereference raw pointer\narguments but are not marked unsafe."##, | 6377 | description: r##"Checks for public functions that dereference raw pointer |
5613 | }, | 6378 | arguments but are not marked `unsafe`."##, |
5614 | LintCompletion { | ||
5615 | label: "clippy::ok_expect", | ||
5616 | description: r##"Checks for usage of `ok().expect(..)`."##, | ||
5617 | }, | 6379 | }, |
5618 | LintCompletion { | 6380 | Lint { label: "clippy::ok_expect", description: r##"Checks for usage of `ok().expect(..)`."## }, |
6381 | Lint { | ||
5619 | label: "clippy::op_ref", | 6382 | label: "clippy::op_ref", |
5620 | description: r##"Checks for arguments to `==` which have their address\ntaken to satisfy a bound\nand suggests to dereference the other argument instead"##, | 6383 | description: r##"Checks for arguments to `==` which have their address |
6384 | taken to satisfy a bound | ||
6385 | and suggests to dereference the other argument instead"##, | ||
5621 | }, | 6386 | }, |
5622 | LintCompletion { | 6387 | Lint { |
5623 | label: "clippy::option_as_ref_deref", | 6388 | label: "clippy::option_as_ref_deref", |
5624 | description: r##"Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str)."##, | 6389 | description: r##"Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str)."##, |
5625 | }, | 6390 | }, |
5626 | LintCompletion { | 6391 | Lint { |
5627 | label: "clippy::option_env_unwrap", | 6392 | label: "clippy::option_env_unwrap", |
5628 | description: r##"Checks for usage of `option_env!(...).unwrap()` and\nsuggests usage of the `env!` macro."##, | 6393 | description: r##"Checks for usage of `option_env!(...).unwrap()` and |
6394 | suggests usage of the `env!` macro."##, | ||
5629 | }, | 6395 | }, |
5630 | LintCompletion { | 6396 | Lint { |
6397 | label: "clippy::option_filter_map", | ||
6398 | description: r##"Checks for indirect collection of populated `Option`"##, | ||
6399 | }, | ||
6400 | Lint { | ||
5631 | label: "clippy::option_if_let_else", | 6401 | label: "clippy::option_if_let_else", |
5632 | description: r##"Lints usage of `if let Some(v) = ... { y } else { x }` which is more\nidiomatically done with `Option::map_or` (if the else bit is a pure\nexpression) or `Option::map_or_else` (if the else bit is an impure\nexpression)."##, | 6402 | description: r##"Lints usage of `if let Some(v) = ... { y } else { x }` which is more |
6403 | idiomatically done with `Option::map_or` (if the else bit is a pure | ||
6404 | expression) or `Option::map_or_else` (if the else bit is an impure | ||
6405 | expression)."##, | ||
5633 | }, | 6406 | }, |
5634 | LintCompletion { | 6407 | Lint { |
5635 | label: "clippy::option_map_or_none", | 6408 | label: "clippy::option_map_or_none", |
5636 | description: r##"Checks for usage of `_.map_or(None, _)`."##, | 6409 | description: r##"Checks for usage of `_.map_or(None, _)`."##, |
5637 | }, | 6410 | }, |
5638 | LintCompletion { | 6411 | Lint { |
5639 | label: "clippy::option_map_unit_fn", | 6412 | label: "clippy::option_map_unit_fn", |
5640 | description: r##"Checks for usage of `option.map(f)` where f is a function\nor closure that returns the unit type `()`."##, | 6413 | description: r##"Checks for usage of `option.map(f)` where f is a function |
6414 | or closure that returns the unit type `()`."##, | ||
5641 | }, | 6415 | }, |
5642 | LintCompletion { | 6416 | Lint { |
5643 | label: "clippy::option_option", | 6417 | label: "clippy::option_option", |
5644 | description: r##"Checks for use of `Option<Option<_>>` in function signatures and type\ndefinitions"##, | 6418 | description: r##"Checks for use of `Option<Option<_>>` in function signatures and type |
6419 | definitions"##, | ||
5645 | }, | 6420 | }, |
5646 | LintCompletion { | 6421 | Lint { |
5647 | label: "clippy::or_fun_call", | 6422 | label: "clippy::or_fun_call", |
5648 | description: r##"Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`,\netc., and suggests to use `or_else`, `unwrap_or_else`, etc., or\n`unwrap_or_default` instead."##, | 6423 | description: r##"Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`, |
6424 | etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or | ||
6425 | `unwrap_or_default` instead."##, | ||
5649 | }, | 6426 | }, |
5650 | LintCompletion { | 6427 | Lint { |
5651 | label: "clippy::out_of_bounds_indexing", | 6428 | label: "clippy::out_of_bounds_indexing", |
5652 | description: r##"Checks for out of bounds array indexing with a constant\nindex."##, | 6429 | description: r##"Checks for out of bounds array indexing with a constant |
6430 | index."##, | ||
5653 | }, | 6431 | }, |
5654 | LintCompletion { | 6432 | Lint { |
5655 | label: "clippy::overflow_check_conditional", | 6433 | label: "clippy::overflow_check_conditional", |
5656 | description: r##"Detects classic underflow/overflow checks."##, | 6434 | description: r##"Detects classic underflow/overflow checks."##, |
5657 | }, | 6435 | }, |
5658 | LintCompletion { label: "clippy::panic", description: r##"Checks for usage of `panic!`."## }, | 6436 | Lint { label: "clippy::panic", description: r##"Checks for usage of `panic!`."## }, |
5659 | LintCompletion { | 6437 | Lint { |
5660 | label: "clippy::panic_in_result_fn", | 6438 | label: "clippy::panic_in_result_fn", |
5661 | description: r##"Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result."##, | 6439 | description: r##"Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result."##, |
5662 | }, | 6440 | }, |
5663 | LintCompletion { | 6441 | Lint { |
5664 | label: "clippy::panic_params", | ||
5665 | description: r##"Nothing. This lint has been deprecated."##, | ||
5666 | }, | ||
5667 | LintCompletion { | ||
5668 | label: "clippy::panicking_unwrap", | 6442 | label: "clippy::panicking_unwrap", |
5669 | description: r##"Checks for calls of `unwrap[_err]()` that will always fail."##, | 6443 | description: r##"Checks for calls of `unwrap[_err]()` that will always fail."##, |
5670 | }, | 6444 | }, |
5671 | LintCompletion { | 6445 | Lint { |
5672 | label: "clippy::partialeq_ne_impl", | 6446 | label: "clippy::partialeq_ne_impl", |
5673 | description: r##"Checks for manual re-implementations of `PartialEq::ne`."##, | 6447 | description: r##"Checks for manual re-implementations of `PartialEq::ne`."##, |
5674 | }, | 6448 | }, |
5675 | LintCompletion { | 6449 | Lint { |
5676 | label: "clippy::path_buf_push_overwrite", | 6450 | label: "clippy::path_buf_push_overwrite", |
5677 | description: r##"* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)\ncalls on `PathBuf` that can cause overwrites."##, | 6451 | description: r##"* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push) |
6452 | calls on `PathBuf` that can cause overwrites."##, | ||
5678 | }, | 6453 | }, |
5679 | LintCompletion { | 6454 | Lint { |
5680 | label: "clippy::pattern_type_mismatch", | 6455 | label: "clippy::pattern_type_mismatch", |
5681 | description: r##"Checks for patterns that aren't exact representations of the types\nthey are applied to.\n\nTo satisfy this lint, you will have to adjust either the expression that is matched\nagainst or the pattern itself, as well as the bindings that are introduced by the\nadjusted patterns. For matching you will have to either dereference the expression\nwith the `*` operator, or amend the patterns to explicitly match against `&<pattern>`\nor `&mut <pattern>` depending on the reference mutability. For the bindings you need\nto use the inverse. You can leave them as plain bindings if you wish for the value\nto be copied, but you must use `ref mut <variable>` or `ref <variable>` to construct\na reference into the matched structure.\n\nIf you are looking for a way to learn about ownership semantics in more detail, it\nis recommended to look at IDE options available to you to highlight types, lifetimes\nand reference semantics in your code. The available tooling would expose these things\nin a general way even outside of the various pattern matching mechanics. Of course\nthis lint can still be used to highlight areas of interest and ensure a good understanding\nof ownership semantics."##, | 6456 | description: r##"Checks for patterns that aren't exact representations of the types |
5682 | }, | 6457 | they are applied to. |
5683 | LintCompletion { | 6458 | |
6459 | To satisfy this lint, you will have to adjust either the expression that is matched | ||
6460 | against or the pattern itself, as well as the bindings that are introduced by the | ||
6461 | adjusted patterns. For matching you will have to either dereference the expression | ||
6462 | with the `*` operator, or amend the patterns to explicitly match against `&<pattern>` | ||
6463 | or `&mut <pattern>` depending on the reference mutability. For the bindings you need | ||
6464 | to use the inverse. You can leave them as plain bindings if you wish for the value | ||
6465 | to be copied, but you must use `ref mut <variable>` or `ref <variable>` to construct | ||
6466 | a reference into the matched structure. | ||
6467 | |||
6468 | If you are looking for a way to learn about ownership semantics in more detail, it | ||
6469 | is recommended to look at IDE options available to you to highlight types, lifetimes | ||
6470 | and reference semantics in your code. The available tooling would expose these things | ||
6471 | in a general way even outside of the various pattern matching mechanics. Of course | ||
6472 | this lint can still be used to highlight areas of interest and ensure a good understanding | ||
6473 | of ownership semantics."##, | ||
6474 | }, | ||
6475 | Lint { | ||
5684 | label: "clippy::possible_missing_comma", | 6476 | label: "clippy::possible_missing_comma", |
5685 | description: r##"Checks for possible missing comma in an array. It lints if\nan array element is a binary operator expression and it lies on two lines."##, | 6477 | description: r##"Checks for possible missing comma in an array. It lints if |
6478 | an array element is a binary operator expression and it lies on two lines."##, | ||
5686 | }, | 6479 | }, |
5687 | LintCompletion { | 6480 | Lint { |
5688 | label: "clippy::precedence", | 6481 | label: "clippy::precedence", |
5689 | description: r##"Checks for operations where precedence may be unclear\nand suggests to add parentheses. Currently it catches the following:\n* mixed usage of arithmetic and bit shifting/combining operators without\nparentheses\n* a \"negative\" numeric literal (which is really a unary `-` followed by a\nnumeric literal)\n followed by a method call"##, | 6482 | description: r##"Checks for operations where precedence may be unclear |
5690 | }, | 6483 | and suggests to add parentheses. Currently it catches the following: |
5691 | LintCompletion { | 6484 | * mixed usage of arithmetic and bit shifting/combining operators without |
6485 | parentheses | ||
6486 | * a negative numeric literal (which is really a unary `-` followed by a | ||
6487 | numeric literal) | ||
6488 | followed by a method call"##, | ||
6489 | }, | ||
6490 | Lint { | ||
5692 | label: "clippy::print_literal", | 6491 | label: "clippy::print_literal", |
5693 | description: r##"This lint warns about the use of literals as `print!`/`println!` args."##, | 6492 | description: r##"This lint warns about the use of literals as `print!`/`println!` args."##, |
5694 | }, | 6493 | }, |
5695 | LintCompletion { | 6494 | Lint { |
5696 | label: "clippy::print_stderr", | 6495 | label: "clippy::print_stderr", |
5697 | description: r##"Checks for printing on *stderr*. The purpose of this lint\nis to catch debugging remnants."##, | 6496 | description: r##"Checks for printing on *stderr*. The purpose of this lint |
6497 | is to catch debugging remnants."##, | ||
5698 | }, | 6498 | }, |
5699 | LintCompletion { | 6499 | Lint { |
5700 | label: "clippy::print_stdout", | 6500 | label: "clippy::print_stdout", |
5701 | description: r##"Checks for printing on *stdout*. The purpose of this lint\nis to catch debugging remnants."##, | 6501 | description: r##"Checks for printing on *stdout*. The purpose of this lint |
6502 | is to catch debugging remnants."##, | ||
5702 | }, | 6503 | }, |
5703 | LintCompletion { | 6504 | Lint { |
5704 | label: "clippy::print_with_newline", | 6505 | label: "clippy::print_with_newline", |
5705 | description: r##"This lint warns when you use `print!()` with a format\nstring that ends in a newline."##, | 6506 | description: r##"This lint warns when you use `print!()` with a format |
6507 | string that ends in a newline."##, | ||
5706 | }, | 6508 | }, |
5707 | LintCompletion { | 6509 | Lint { |
5708 | label: "clippy::println_empty_string", | 6510 | label: "clippy::println_empty_string", |
5709 | description: r##"This lint warns when you use `println!(\"\")` to\nprint a newline."##, | 6511 | description: r##"This lint warns when you use `println!()` to |
6512 | print a newline."##, | ||
5710 | }, | 6513 | }, |
5711 | LintCompletion { | 6514 | Lint { |
5712 | label: "clippy::ptr_arg", | 6515 | label: "clippy::ptr_arg", |
5713 | description: r##"This lint checks for function arguments of type `&String`\nor `&Vec` unless the references are mutable. It will also suggest you\nreplace `.clone()` calls with the appropriate `.to_owned()`/`to_string()`\ncalls."##, | 6516 | description: r##"This lint checks for function arguments of type `&String` |
6517 | or `&Vec` unless the references are mutable. It will also suggest you | ||
6518 | replace `.clone()` calls with the appropriate `.to_owned()`/`to_string()` | ||
6519 | calls."##, | ||
5714 | }, | 6520 | }, |
5715 | LintCompletion { | 6521 | Lint { |
5716 | label: "clippy::ptr_as_ptr", | 6522 | label: "clippy::ptr_as_ptr", |
5717 | description: r##"Checks for `as` casts between raw pointers without changing its mutability,\nnamely `*const T` to `*const U` and `*mut T` to `*mut U`."##, | 6523 | description: r##"Checks for `as` casts between raw pointers without changing its mutability, |
6524 | namely `*const T` to `*const U` and `*mut T` to `*mut U`."##, | ||
5718 | }, | 6525 | }, |
5719 | LintCompletion { | 6526 | Lint { label: "clippy::ptr_eq", description: r##"Use `std::ptr::eq` when applicable"## }, |
5720 | label: "clippy::ptr_eq", | 6527 | Lint { |
5721 | description: r##"Use `std::ptr::eq` when applicable"##, | ||
5722 | }, | ||
5723 | LintCompletion { | ||
5724 | label: "clippy::ptr_offset_with_cast", | 6528 | label: "clippy::ptr_offset_with_cast", |
5725 | description: r##"Checks for usage of the `offset` pointer method with a `usize` casted to an\n`isize`."##, | 6529 | description: r##"Checks for usage of the `offset` pointer method with a `usize` casted to an |
6530 | `isize`."##, | ||
5726 | }, | 6531 | }, |
5727 | LintCompletion { | 6532 | Lint { |
5728 | label: "clippy::pub_enum_variant_names", | 6533 | label: "clippy::pub_enum_variant_names", |
5729 | description: r##"Detects public enumeration variants that are\nprefixed or suffixed by the same characters."##, | 6534 | description: r##"Nothing. This lint has been deprecated."##, |
5730 | }, | 6535 | }, |
5731 | LintCompletion { | 6536 | Lint { |
5732 | label: "clippy::question_mark", | 6537 | label: "clippy::question_mark", |
5733 | description: r##"Checks for expressions that could be replaced by the question mark operator."##, | 6538 | description: r##"Checks for expressions that could be replaced by the question mark operator."##, |
5734 | }, | 6539 | }, |
5735 | LintCompletion { | 6540 | Lint { |
5736 | label: "clippy::range_minus_one", | 6541 | label: "clippy::range_minus_one", |
5737 | description: r##"Checks for inclusive ranges where 1 is subtracted from\nthe upper bound, e.g., `x..=(y-1)`."##, | 6542 | description: r##"Checks for inclusive ranges where 1 is subtracted from |
6543 | the upper bound, e.g., `x..=(y-1)`."##, | ||
5738 | }, | 6544 | }, |
5739 | LintCompletion { | 6545 | Lint { |
5740 | label: "clippy::range_plus_one", | 6546 | label: "clippy::range_plus_one", |
5741 | description: r##"Checks for exclusive ranges where 1 is added to the\nupper bound, e.g., `x..(y+1)`."##, | 6547 | description: r##"Checks for exclusive ranges where 1 is added to the |
6548 | upper bound, e.g., `x..(y+1)`."##, | ||
5742 | }, | 6549 | }, |
5743 | LintCompletion { | 6550 | Lint { |
5744 | label: "clippy::range_step_by_zero", | 6551 | label: "clippy::range_step_by_zero", |
5745 | description: r##"Nothing. This lint has been deprecated."##, | 6552 | description: r##"Nothing. This lint has been deprecated."##, |
5746 | }, | 6553 | }, |
5747 | LintCompletion { | 6554 | Lint { |
5748 | label: "clippy::range_zip_with_len", | 6555 | label: "clippy::range_zip_with_len", |
5749 | description: r##"Checks for zipping a collection with the range of\n`0.._.len()`."##, | 6556 | description: r##"Checks for zipping a collection with the range of |
6557 | `0.._.len()`."##, | ||
5750 | }, | 6558 | }, |
5751 | LintCompletion { | 6559 | Lint { |
5752 | label: "clippy::rc_buffer", | 6560 | label: "clippy::rc_buffer", |
5753 | description: r##"Checks for `Rc<T>` and `Arc<T>` when `T` is a mutable buffer type such as `String` or `Vec`."##, | 6561 | description: r##"Checks for `Rc<T>` and `Arc<T>` when `T` is a mutable buffer type such as `String` or `Vec`."##, |
5754 | }, | 6562 | }, |
5755 | LintCompletion { | 6563 | Lint { |
5756 | label: "clippy::redundant_allocation", | 6564 | label: "clippy::redundant_allocation", |
5757 | description: r##"Checks for use of redundant allocations anywhere in the code."##, | 6565 | description: r##"Checks for use of redundant allocations anywhere in the code."##, |
5758 | }, | 6566 | }, |
5759 | LintCompletion { | 6567 | Lint { |
5760 | label: "clippy::redundant_clone", | 6568 | label: "clippy::redundant_clone", |
5761 | description: r##"Checks for a redundant `clone()` (and its relatives) which clones an owned\nvalue that is going to be dropped without further use."##, | 6569 | description: r##"Checks for a redundant `clone()` (and its relatives) which clones an owned |
6570 | value that is going to be dropped without further use."##, | ||
5762 | }, | 6571 | }, |
5763 | LintCompletion { | 6572 | Lint { |
5764 | label: "clippy::redundant_closure", | 6573 | label: "clippy::redundant_closure", |
5765 | description: r##"Checks for closures which just call another function where\nthe function can be called directly. `unsafe` functions or calls where types\nget adjusted are ignored."##, | 6574 | description: r##"Checks for closures which just call another function where |
6575 | the function can be called directly. `unsafe` functions or calls where types | ||
6576 | get adjusted are ignored."##, | ||
5766 | }, | 6577 | }, |
5767 | LintCompletion { | 6578 | Lint { |
5768 | label: "clippy::redundant_closure_call", | 6579 | label: "clippy::redundant_closure_call", |
5769 | description: r##"Detects closures called in the same expression where they\nare defined."##, | 6580 | description: r##"Detects closures called in the same expression where they |
6581 | are defined."##, | ||
5770 | }, | 6582 | }, |
5771 | LintCompletion { | 6583 | Lint { |
5772 | label: "clippy::redundant_closure_for_method_calls", | 6584 | label: "clippy::redundant_closure_for_method_calls", |
5773 | description: r##"Checks for closures which only invoke a method on the closure\nargument and can be replaced by referencing the method directly."##, | 6585 | description: r##"Checks for closures which only invoke a method on the closure |
6586 | argument and can be replaced by referencing the method directly."##, | ||
5774 | }, | 6587 | }, |
5775 | LintCompletion { | 6588 | Lint { |
5776 | label: "clippy::redundant_else", | 6589 | label: "clippy::redundant_else", |
5777 | description: r##"Checks for `else` blocks that can be removed without changing semantics."##, | 6590 | description: r##"Checks for `else` blocks that can be removed without changing semantics."##, |
5778 | }, | 6591 | }, |
5779 | LintCompletion { | 6592 | Lint { |
5780 | label: "clippy::redundant_field_names", | 6593 | label: "clippy::redundant_field_names", |
5781 | description: r##"Checks for fields in struct literals where shorthands\ncould be used."##, | 6594 | description: r##"Checks for fields in struct literals where shorthands |
6595 | could be used."##, | ||
5782 | }, | 6596 | }, |
5783 | LintCompletion { | 6597 | Lint { |
5784 | label: "clippy::redundant_pattern", | 6598 | label: "clippy::redundant_pattern", |
5785 | description: r##"Checks for patterns in the form `name @ _`."##, | 6599 | description: r##"Checks for patterns in the form `name @ _`."##, |
5786 | }, | 6600 | }, |
5787 | LintCompletion { | 6601 | Lint { |
5788 | label: "clippy::redundant_pattern_matching", | 6602 | label: "clippy::redundant_pattern_matching", |
5789 | description: r##"Lint for redundant pattern matching over `Result`, `Option`,\n`std::task::Poll` or `std::net::IpAddr`"##, | 6603 | description: r##"Lint for redundant pattern matching over `Result`, `Option`, |
6604 | `std::task::Poll` or `std::net::IpAddr`"##, | ||
5790 | }, | 6605 | }, |
5791 | LintCompletion { | 6606 | Lint { |
5792 | label: "clippy::redundant_pub_crate", | 6607 | label: "clippy::redundant_pub_crate", |
5793 | description: r##"Checks for items declared `pub(crate)` that are not crate visible because they\nare inside a private module."##, | 6608 | description: r##"Checks for items declared `pub(crate)` that are not crate visible because they |
6609 | are inside a private module."##, | ||
5794 | }, | 6610 | }, |
5795 | LintCompletion { | 6611 | Lint { |
5796 | label: "clippy::redundant_slicing", | 6612 | label: "clippy::redundant_slicing", |
5797 | description: r##"Checks for redundant slicing expressions which use the full range, and\ndo not change the type."##, | 6613 | description: r##"Checks for redundant slicing expressions which use the full range, and |
6614 | do not change the type."##, | ||
5798 | }, | 6615 | }, |
5799 | LintCompletion { | 6616 | Lint { |
5800 | label: "clippy::redundant_static_lifetimes", | 6617 | label: "clippy::redundant_static_lifetimes", |
5801 | description: r##"Checks for constants and statics with an explicit `'static` lifetime."##, | 6618 | description: r##"Checks for constants and statics with an explicit `'static` lifetime."##, |
5802 | }, | 6619 | }, |
5803 | LintCompletion { | 6620 | Lint { |
6621 | label: "clippy::ref_binding_to_reference", | ||
6622 | description: r##"Checks for `ref` bindings which create a reference to a reference."##, | ||
6623 | }, | ||
6624 | Lint { | ||
5804 | label: "clippy::ref_in_deref", | 6625 | label: "clippy::ref_in_deref", |
5805 | description: r##"Checks for references in expressions that use\nauto dereference."##, | 6626 | description: r##"Checks for references in expressions that use |
6627 | auto dereference."##, | ||
5806 | }, | 6628 | }, |
5807 | LintCompletion { | 6629 | Lint { |
5808 | label: "clippy::ref_option_ref", | 6630 | label: "clippy::ref_option_ref", |
5809 | description: r##"Checks for usage of `&Option<&T>`."##, | 6631 | description: r##"Checks for usage of `&Option<&T>`."##, |
5810 | }, | 6632 | }, |
5811 | LintCompletion { | 6633 | Lint { |
5812 | label: "clippy::regex_macro", | 6634 | label: "clippy::regex_macro", |
5813 | description: r##"Nothing. This lint has been deprecated."##, | 6635 | description: r##"Nothing. This lint has been deprecated."##, |
5814 | }, | 6636 | }, |
5815 | LintCompletion { | 6637 | Lint { |
5816 | label: "clippy::repeat_once", | 6638 | label: "clippy::repeat_once", |
5817 | description: r##"Checks for usage of `.repeat(1)` and suggest the following method for each types.\n- `.to_string()` for `str`\n- `.clone()` for `String`\n- `.to_vec()` for `slice`"##, | 6639 | description: r##"Checks for usage of `.repeat(1)` and suggest the following method for each types. |
6640 | - `.to_string()` for `str` | ||
6641 | - `.clone()` for `String` | ||
6642 | - `.to_vec()` for `slice`"##, | ||
5818 | }, | 6643 | }, |
5819 | LintCompletion { | 6644 | Lint { |
5820 | label: "clippy::replace_consts", | 6645 | label: "clippy::replace_consts", |
5821 | description: r##"Nothing. This lint has been deprecated."##, | 6646 | description: r##"Nothing. This lint has been deprecated."##, |
5822 | }, | 6647 | }, |
5823 | LintCompletion { | 6648 | Lint { |
5824 | label: "clippy::rest_pat_in_fully_bound_structs", | 6649 | label: "clippy::rest_pat_in_fully_bound_structs", |
5825 | description: r##"Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched."##, | 6650 | description: r##"Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched."##, |
5826 | }, | 6651 | }, |
5827 | LintCompletion { | 6652 | Lint { |
5828 | label: "clippy::result_map_or_into_option", | 6653 | label: "clippy::result_map_or_into_option", |
5829 | description: r##"Checks for usage of `_.map_or(None, Some)`."##, | 6654 | description: r##"Checks for usage of `_.map_or(None, Some)`."##, |
5830 | }, | 6655 | }, |
5831 | LintCompletion { | 6656 | Lint { |
5832 | label: "clippy::result_map_unit_fn", | 6657 | label: "clippy::result_map_unit_fn", |
5833 | description: r##"Checks for usage of `result.map(f)` where f is a function\nor closure that returns the unit type `()`."##, | 6658 | description: r##"Checks for usage of `result.map(f)` where f is a function |
6659 | or closure that returns the unit type `()`."##, | ||
5834 | }, | 6660 | }, |
5835 | LintCompletion { | 6661 | Lint { |
5836 | label: "clippy::result_unit_err", | 6662 | label: "clippy::result_unit_err", |
5837 | description: r##"Checks for public functions that return a `Result`\nwith an `Err` type of `()`. It suggests using a custom type that\nimplements [`std::error::Error`]."##, | 6663 | description: r##"Checks for public functions that return a `Result` |
6664 | with an `Err` type of `()`. It suggests using a custom type that | ||
6665 | implements `std::error::Error`."##, | ||
5838 | }, | 6666 | }, |
5839 | LintCompletion { | 6667 | Lint { |
5840 | label: "clippy::reversed_empty_ranges", | 6668 | label: "clippy::reversed_empty_ranges", |
5841 | description: r##"Checks for range expressions `x..y` where both `x` and `y`\nare constant and `x` is greater or equal to `y`."##, | 6669 | description: r##"Checks for range expressions `x..y` where both `x` and `y` |
6670 | are constant and `x` is greater or equal to `y`."##, | ||
5842 | }, | 6671 | }, |
5843 | LintCompletion { | 6672 | Lint { |
5844 | label: "clippy::same_functions_in_if_condition", | 6673 | label: "clippy::same_functions_in_if_condition", |
5845 | description: r##"Checks for consecutive `if`s with the same function call."##, | 6674 | description: r##"Checks for consecutive `if`s with the same function call."##, |
5846 | }, | 6675 | }, |
5847 | LintCompletion { | 6676 | Lint { |
5848 | label: "clippy::same_item_push", | 6677 | label: "clippy::same_item_push", |
5849 | description: r##"Checks whether a for loop is being used to push a constant\nvalue into a Vec."##, | 6678 | description: r##"Checks whether a for loop is being used to push a constant |
6679 | value into a Vec."##, | ||
5850 | }, | 6680 | }, |
5851 | LintCompletion { | 6681 | Lint { |
5852 | label: "clippy::search_is_some", | 6682 | label: "clippy::search_is_some", |
5853 | description: r##"Checks for an iterator or string search (such as `find()`,\n`position()`, or `rposition()`) followed by a call to `is_some()`."##, | 6683 | description: r##"Checks for an iterator or string search (such as `find()`, |
6684 | `position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`."##, | ||
5854 | }, | 6685 | }, |
5855 | LintCompletion { | 6686 | Lint { |
5856 | label: "clippy::self_assignment", | 6687 | label: "clippy::self_assignment", |
5857 | description: r##"Checks for explicit self-assignments."##, | 6688 | description: r##"Checks for explicit self-assignments."##, |
5858 | }, | 6689 | }, |
5859 | LintCompletion { | 6690 | Lint { |
5860 | label: "clippy::semicolon_if_nothing_returned", | 6691 | label: "clippy::semicolon_if_nothing_returned", |
5861 | description: r##"Looks for blocks of expressions and fires if the last expression returns `()`\nbut is not followed by a semicolon."##, | 6692 | description: r##"Looks for blocks of expressions and fires if the last expression returns |
6693 | `()` but is not followed by a semicolon."##, | ||
5862 | }, | 6694 | }, |
5863 | LintCompletion { | 6695 | Lint { |
5864 | label: "clippy::serde_api_misuse", | 6696 | label: "clippy::serde_api_misuse", |
5865 | description: r##"Checks for mis-uses of the serde API."##, | 6697 | description: r##"Checks for mis-uses of the serde API."##, |
5866 | }, | 6698 | }, |
5867 | LintCompletion { | 6699 | Lint { |
5868 | label: "clippy::shadow_reuse", | 6700 | label: "clippy::shadow_reuse", |
5869 | description: r##"Checks for bindings that shadow other bindings already in\nscope, while reusing the original value."##, | 6701 | description: r##"Checks for bindings that shadow other bindings already in |
6702 | scope, while reusing the original value."##, | ||
5870 | }, | 6703 | }, |
5871 | LintCompletion { | 6704 | Lint { |
5872 | label: "clippy::shadow_same", | 6705 | label: "clippy::shadow_same", |
5873 | description: r##"Checks for bindings that shadow other bindings already in\nscope, while just changing reference level or mutability."##, | 6706 | description: r##"Checks for bindings that shadow other bindings already in |
6707 | scope, while just changing reference level or mutability."##, | ||
5874 | }, | 6708 | }, |
5875 | LintCompletion { | 6709 | Lint { |
5876 | label: "clippy::shadow_unrelated", | 6710 | label: "clippy::shadow_unrelated", |
5877 | description: r##"Checks for bindings that shadow other bindings already in\nscope, either without a initialization or with one that does not even use\nthe original value."##, | 6711 | description: r##"Checks for bindings that shadow other bindings already in |
6712 | scope, either without a initialization or with one that does not even use | ||
6713 | the original value."##, | ||
5878 | }, | 6714 | }, |
5879 | LintCompletion { | 6715 | Lint { |
5880 | label: "clippy::short_circuit_statement", | 6716 | label: "clippy::short_circuit_statement", |
5881 | description: r##"Checks for the use of short circuit boolean conditions as\na\nstatement."##, | 6717 | description: r##"Checks for the use of short circuit boolean conditions as |
6718 | a | ||
6719 | statement."##, | ||
5882 | }, | 6720 | }, |
5883 | LintCompletion { | 6721 | Lint { |
5884 | label: "clippy::should_assert_eq", | 6722 | label: "clippy::should_assert_eq", |
5885 | description: r##"Nothing. This lint has been deprecated."##, | 6723 | description: r##"Nothing. This lint has been deprecated."##, |
5886 | }, | 6724 | }, |
5887 | LintCompletion { | 6725 | Lint { |
5888 | label: "clippy::should_implement_trait", | 6726 | label: "clippy::should_implement_trait", |
5889 | description: r##"Checks for methods that should live in a trait\nimplementation of a `std` trait (see [llogiq's blog\npost](http://llogiq.github.io/2015/07/30/traits.html) for further\ninformation) instead of an inherent implementation."##, | 6727 | description: r##"Checks for methods that should live in a trait |
6728 | implementation of a `std` trait (see [llogiq's blog | ||
6729 | post](http://llogiq.github.io/2015/07/30/traits.html) for further | ||
6730 | information) instead of an inherent implementation."##, | ||
5890 | }, | 6731 | }, |
5891 | LintCompletion { | 6732 | Lint { |
5892 | label: "clippy::similar_names", | 6733 | label: "clippy::similar_names", |
5893 | description: r##"Checks for names that are very similar and thus confusing."##, | 6734 | description: r##"Checks for names that are very similar and thus confusing."##, |
5894 | }, | 6735 | }, |
5895 | LintCompletion { | 6736 | Lint { |
5896 | label: "clippy::single_char_add_str", | 6737 | label: "clippy::single_char_add_str", |
5897 | description: r##"Warns when using `push_str`/`insert_str` with a single-character string literal\nwhere `push`/`insert` with a `char` would work fine."##, | 6738 | description: r##"Warns when using `push_str`/`insert_str` with a single-character string literal |
6739 | where `push`/`insert` with a `char` would work fine."##, | ||
5898 | }, | 6740 | }, |
5899 | LintCompletion { | 6741 | Lint { |
5900 | label: "clippy::single_char_pattern", | 6742 | label: "clippy::single_char_pattern", |
5901 | description: r##"Checks for string methods that receive a single-character\n`str` as an argument, e.g., `_.split(\"x\")`."##, | 6743 | description: r##"Checks for string methods that receive a single-character |
6744 | `str` as an argument, e.g., `_.split(x)`."##, | ||
5902 | }, | 6745 | }, |
5903 | LintCompletion { | 6746 | Lint { |
5904 | label: "clippy::single_component_path_imports", | 6747 | label: "clippy::single_component_path_imports", |
5905 | description: r##"Checking for imports with single component use path."##, | 6748 | description: r##"Checking for imports with single component use path."##, |
5906 | }, | 6749 | }, |
5907 | LintCompletion { | 6750 | Lint { |
5908 | label: "clippy::single_element_loop", | 6751 | label: "clippy::single_element_loop", |
5909 | description: r##"Checks whether a for loop has a single element."##, | 6752 | description: r##"Checks whether a for loop has a single element."##, |
5910 | }, | 6753 | }, |
5911 | LintCompletion { | 6754 | Lint { |
5912 | label: "clippy::single_match", | 6755 | label: "clippy::single_match", |
5913 | description: r##"Checks for matches with a single arm where an `if let`\nwill usually suffice."##, | 6756 | description: r##"Checks for matches with a single arm where an `if let` |
6757 | will usually suffice."##, | ||
5914 | }, | 6758 | }, |
5915 | LintCompletion { | 6759 | Lint { |
5916 | label: "clippy::single_match_else", | 6760 | label: "clippy::single_match_else", |
5917 | description: r##"Checks for matches with two arms where an `if let else` will\nusually suffice."##, | 6761 | description: r##"Checks for matches with two arms where an `if let else` will |
6762 | usually suffice."##, | ||
5918 | }, | 6763 | }, |
5919 | LintCompletion { | 6764 | Lint { |
5920 | label: "clippy::size_of_in_element_count", | 6765 | label: "clippy::size_of_in_element_count", |
5921 | description: r##"Detects expressions where\n`size_of::<T>` or `size_of_val::<T>` is used as a\ncount of elements of type `T`"##, | 6766 | description: r##"Detects expressions where |
6767 | `size_of::<T>` or `size_of_val::<T>` is used as a | ||
6768 | count of elements of type `T`"##, | ||
5922 | }, | 6769 | }, |
5923 | LintCompletion { | 6770 | Lint { |
5924 | label: "clippy::skip_while_next", | 6771 | label: "clippy::skip_while_next", |
5925 | description: r##"Checks for usage of `_.skip_while(condition).next()`."##, | 6772 | description: r##"Checks for usage of `_.skip_while(condition).next()`."##, |
5926 | }, | 6773 | }, |
5927 | LintCompletion { | 6774 | Lint { |
5928 | label: "clippy::slow_vector_initialization", | 6775 | label: "clippy::slow_vector_initialization", |
5929 | description: r##"Checks slow zero-filled vector initialization"##, | 6776 | description: r##"Checks slow zero-filled vector initialization"##, |
5930 | }, | 6777 | }, |
5931 | LintCompletion { | 6778 | Lint { |
5932 | label: "clippy::stable_sort_primitive", | 6779 | label: "clippy::stable_sort_primitive", |
5933 | description: r##"When sorting primitive values (integers, bools, chars, as well\nas arrays, slices, and tuples of such items), it is better to\nuse an unstable sort than a stable sort."##, | 6780 | description: r##"When sorting primitive values (integers, bools, chars, as well |
6781 | as arrays, slices, and tuples of such items), it is better to | ||
6782 | use an unstable sort than a stable sort."##, | ||
5934 | }, | 6783 | }, |
5935 | LintCompletion { | 6784 | Lint { |
5936 | label: "clippy::str_to_string", | 6785 | label: "clippy::str_to_string", |
5937 | description: r##"This lint checks for `.to_string()` method calls on values of type `&str`."##, | 6786 | description: r##"This lint checks for `.to_string()` method calls on values of type `&str`."##, |
5938 | }, | 6787 | }, |
5939 | LintCompletion { | 6788 | Lint { |
5940 | label: "clippy::string_add", | 6789 | label: "clippy::string_add", |
5941 | description: r##"Checks for all instances of `x + _` where `x` is of type\n`String`, but only if [`string_add_assign`](#string_add_assign) does *not*\nmatch."##, | 6790 | description: r##"Checks for all instances of `x + _` where `x` is of type |
6791 | `String`, but only if [`string_add_assign`](#string_add_assign) does *not* | ||
6792 | match."##, | ||
5942 | }, | 6793 | }, |
5943 | LintCompletion { | 6794 | Lint { |
5944 | label: "clippy::string_add_assign", | 6795 | label: "clippy::string_add_assign", |
5945 | description: r##"Checks for string appends of the form `x = x + y` (without\n`let`!)."##, | 6796 | description: r##"Checks for string appends of the form `x = x + y` (without |
6797 | `let`!)."##, | ||
5946 | }, | 6798 | }, |
5947 | LintCompletion { | 6799 | Lint { |
5948 | label: "clippy::string_extend_chars", | 6800 | label: "clippy::string_extend_chars", |
5949 | description: r##"Checks for the use of `.extend(s.chars())` where s is a\n`&str` or `String`."##, | 6801 | description: r##"Checks for the use of `.extend(s.chars())` where s is a |
6802 | `&str` or `String`."##, | ||
5950 | }, | 6803 | }, |
5951 | LintCompletion { | 6804 | Lint { |
5952 | label: "clippy::string_from_utf8_as_bytes", | 6805 | label: "clippy::string_from_utf8_as_bytes", |
5953 | description: r##"Check if the string is transformed to byte array and casted back to string."##, | 6806 | description: r##"Check if the string is transformed to byte array and casted back to string."##, |
5954 | }, | 6807 | }, |
5955 | LintCompletion { | 6808 | Lint { |
5956 | label: "clippy::string_lit_as_bytes", | 6809 | label: "clippy::string_lit_as_bytes", |
5957 | description: r##"Checks for the `as_bytes` method called on string literals\nthat contain only ASCII characters."##, | 6810 | description: r##"Checks for the `as_bytes` method called on string literals |
6811 | that contain only ASCII characters."##, | ||
5958 | }, | 6812 | }, |
5959 | LintCompletion { | 6813 | Lint { |
5960 | label: "clippy::string_to_string", | 6814 | label: "clippy::string_to_string", |
5961 | description: r##"This lint checks for `.to_string()` method calls on values of type `String`."##, | 6815 | description: r##"This lint checks for `.to_string()` method calls on values of type `String`."##, |
5962 | }, | 6816 | }, |
5963 | LintCompletion { | 6817 | Lint { |
5964 | label: "clippy::struct_excessive_bools", | 6818 | label: "clippy::struct_excessive_bools", |
5965 | description: r##"Checks for excessive\nuse of bools in structs."##, | 6819 | description: r##"Checks for excessive |
6820 | use of bools in structs."##, | ||
5966 | }, | 6821 | }, |
5967 | LintCompletion { | 6822 | Lint { |
5968 | label: "clippy::suboptimal_flops", | 6823 | label: "clippy::suboptimal_flops", |
5969 | description: r##"Looks for floating-point expressions that\ncan be expressed using built-in methods to improve both\naccuracy and performance."##, | 6824 | description: r##"Looks for floating-point expressions that |
6825 | can be expressed using built-in methods to improve both | ||
6826 | accuracy and performance."##, | ||
5970 | }, | 6827 | }, |
5971 | LintCompletion { | 6828 | Lint { |
5972 | label: "clippy::suspicious_arithmetic_impl", | 6829 | label: "clippy::suspicious_arithmetic_impl", |
5973 | description: r##"Lints for suspicious operations in impls of arithmetic operators, e.g.\nsubtracting elements in an Add impl."##, | 6830 | description: r##"Lints for suspicious operations in impls of arithmetic operators, e.g. |
6831 | subtracting elements in an Add impl."##, | ||
5974 | }, | 6832 | }, |
5975 | LintCompletion { | 6833 | Lint { |
5976 | label: "clippy::suspicious_assignment_formatting", | 6834 | label: "clippy::suspicious_assignment_formatting", |
5977 | description: r##"Checks for use of the non-existent `=*`, `=!` and `=-`\noperators."##, | 6835 | description: r##"Checks for use of the non-existent `=*`, `=!` and `=-` |
6836 | operators."##, | ||
5978 | }, | 6837 | }, |
5979 | LintCompletion { | 6838 | Lint { |
5980 | label: "clippy::suspicious_else_formatting", | 6839 | label: "clippy::suspicious_else_formatting", |
5981 | description: r##"Checks for formatting of `else`. It lints if the `else`\nis followed immediately by a newline or the `else` seems to be missing."##, | 6840 | description: r##"Checks for formatting of `else`. It lints if the `else` |
6841 | is followed immediately by a newline or the `else` seems to be missing."##, | ||
5982 | }, | 6842 | }, |
5983 | LintCompletion { | 6843 | Lint { |
5984 | label: "clippy::suspicious_map", | 6844 | label: "clippy::suspicious_map", |
5985 | description: r##"Checks for calls to `map` followed by a `count`."##, | 6845 | description: r##"Checks for calls to `map` followed by a `count`."##, |
5986 | }, | 6846 | }, |
5987 | LintCompletion { | 6847 | Lint { |
5988 | label: "clippy::suspicious_op_assign_impl", | 6848 | label: "clippy::suspicious_op_assign_impl", |
5989 | description: r##"Lints for suspicious operations in impls of OpAssign, e.g.\nsubtracting elements in an AddAssign impl."##, | 6849 | description: r##"Lints for suspicious operations in impls of OpAssign, e.g. |
6850 | subtracting elements in an AddAssign impl."##, | ||
5990 | }, | 6851 | }, |
5991 | LintCompletion { | 6852 | Lint { |
5992 | label: "clippy::suspicious_operation_groupings", | 6853 | label: "clippy::suspicious_operation_groupings", |
5993 | description: r##"Checks for unlikely usages of binary operators that are almost\ncertainly typos and/or copy/paste errors, given the other usages\nof binary operators nearby."##, | 6854 | description: r##"Checks for unlikely usages of binary operators that are almost |
6855 | certainly typos and/or copy/paste errors, given the other usages | ||
6856 | of binary operators nearby."##, | ||
6857 | }, | ||
6858 | Lint { | ||
6859 | label: "clippy::suspicious_splitn", | ||
6860 | description: r##"Checks for calls to [`splitn`] | ||
6861 | (https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and | ||
6862 | related functions with either zero or one splits."##, | ||
5994 | }, | 6863 | }, |
5995 | LintCompletion { | 6864 | Lint { |
5996 | label: "clippy::suspicious_unary_op_formatting", | 6865 | label: "clippy::suspicious_unary_op_formatting", |
5997 | description: r##"Checks the formatting of a unary operator on the right hand side\nof a binary operator. It lints if there is no space between the binary and unary operators,\nbut there is a space between the unary and its operand."##, | 6866 | description: r##"Checks the formatting of a unary operator on the right hand side |
6867 | of a binary operator. It lints if there is no space between the binary and unary operators, | ||
6868 | but there is a space between the unary and its operand."##, | ||
5998 | }, | 6869 | }, |
5999 | LintCompletion { | 6870 | Lint { |
6000 | label: "clippy::tabs_in_doc_comments", | 6871 | label: "clippy::tabs_in_doc_comments", |
6001 | description: r##"Checks doc comments for usage of tab characters."##, | 6872 | description: r##"Checks doc comments for usage of tab characters."##, |
6002 | }, | 6873 | }, |
6003 | LintCompletion { | 6874 | Lint { |
6004 | label: "clippy::temporary_assignment", | 6875 | label: "clippy::temporary_assignment", |
6005 | description: r##"Checks for construction of a structure or tuple just to\nassign a value in it."##, | 6876 | description: r##"Checks for construction of a structure or tuple just to |
6877 | assign a value in it."##, | ||
6006 | }, | 6878 | }, |
6007 | LintCompletion { | 6879 | Lint { |
6008 | label: "clippy::temporary_cstring_as_ptr", | ||
6009 | description: r##"Nothing. This lint has been deprecated."##, | ||
6010 | }, | ||
6011 | LintCompletion { | ||
6012 | label: "clippy::to_digit_is_some", | 6880 | label: "clippy::to_digit_is_some", |
6013 | description: r##"Checks for `.to_digit(..).is_some()` on `char`s."##, | 6881 | description: r##"Checks for `.to_digit(..).is_some()` on `char`s."##, |
6014 | }, | 6882 | }, |
6015 | LintCompletion { | 6883 | Lint { |
6016 | label: "clippy::to_string_in_display", | 6884 | label: "clippy::to_string_in_display", |
6017 | description: r##"Checks for uses of `to_string()` in `Display` traits."##, | 6885 | description: r##"Checks for uses of `to_string()` in `Display` traits."##, |
6018 | }, | 6886 | }, |
6019 | LintCompletion { label: "clippy::todo", description: r##"Checks for usage of `todo!`."## }, | 6887 | Lint { label: "clippy::todo", description: r##"Checks for usage of `todo!`."## }, |
6020 | LintCompletion { | 6888 | Lint { |
6021 | label: "clippy::too_many_arguments", | 6889 | label: "clippy::too_many_arguments", |
6022 | description: r##"Checks for functions with too many parameters."##, | 6890 | description: r##"Checks for functions with too many parameters."##, |
6023 | }, | 6891 | }, |
6024 | LintCompletion { | 6892 | Lint { |
6025 | label: "clippy::too_many_lines", | 6893 | label: "clippy::too_many_lines", |
6026 | description: r##"Checks for functions with a large amount of lines."##, | 6894 | description: r##"Checks for functions with a large amount of lines."##, |
6027 | }, | 6895 | }, |
6028 | LintCompletion { | 6896 | Lint { |
6029 | label: "clippy::toplevel_ref_arg", | 6897 | label: "clippy::toplevel_ref_arg", |
6030 | description: r##"Checks for function arguments and let bindings denoted as\n`ref`."##, | 6898 | description: r##"Checks for function arguments and let bindings denoted as |
6899 | `ref`."##, | ||
6031 | }, | 6900 | }, |
6032 | LintCompletion { | 6901 | Lint { |
6033 | label: "clippy::trait_duplication_in_bounds", | 6902 | label: "clippy::trait_duplication_in_bounds", |
6034 | description: r##"Checks for cases where generics are being used and multiple\nsyntax specifications for trait bounds are used simultaneously."##, | 6903 | description: r##"Checks for cases where generics are being used and multiple |
6904 | syntax specifications for trait bounds are used simultaneously."##, | ||
6035 | }, | 6905 | }, |
6036 | LintCompletion { | 6906 | Lint { |
6037 | label: "clippy::transmute_bytes_to_str", | 6907 | label: "clippy::transmute_bytes_to_str", |
6038 | description: r##"Checks for transmutes from a `&[u8]` to a `&str`."##, | 6908 | description: r##"Checks for transmutes from a `&[u8]` to a `&str`."##, |
6039 | }, | 6909 | }, |
6040 | LintCompletion { | 6910 | Lint { |
6041 | label: "clippy::transmute_float_to_int", | 6911 | label: "clippy::transmute_float_to_int", |
6042 | description: r##"Checks for transmutes from a float to an integer."##, | 6912 | description: r##"Checks for transmutes from a float to an integer."##, |
6043 | }, | 6913 | }, |
6044 | LintCompletion { | 6914 | Lint { |
6045 | label: "clippy::transmute_int_to_bool", | 6915 | label: "clippy::transmute_int_to_bool", |
6046 | description: r##"Checks for transmutes from an integer to a `bool`."##, | 6916 | description: r##"Checks for transmutes from an integer to a `bool`."##, |
6047 | }, | 6917 | }, |
6048 | LintCompletion { | 6918 | Lint { |
6049 | label: "clippy::transmute_int_to_char", | 6919 | label: "clippy::transmute_int_to_char", |
6050 | description: r##"Checks for transmutes from an integer to a `char`."##, | 6920 | description: r##"Checks for transmutes from an integer to a `char`."##, |
6051 | }, | 6921 | }, |
6052 | LintCompletion { | 6922 | Lint { |
6053 | label: "clippy::transmute_int_to_float", | 6923 | label: "clippy::transmute_int_to_float", |
6054 | description: r##"Checks for transmutes from an integer to a float."##, | 6924 | description: r##"Checks for transmutes from an integer to a float."##, |
6055 | }, | 6925 | }, |
6056 | LintCompletion { | 6926 | Lint { |
6057 | label: "clippy::transmute_ptr_to_ptr", | 6927 | label: "clippy::transmute_ptr_to_ptr", |
6058 | description: r##"Checks for transmutes from a pointer to a pointer, or\nfrom a reference to a reference."##, | 6928 | description: r##"Checks for transmutes from a pointer to a pointer, or |
6929 | from a reference to a reference."##, | ||
6059 | }, | 6930 | }, |
6060 | LintCompletion { | 6931 | Lint { |
6061 | label: "clippy::transmute_ptr_to_ref", | 6932 | label: "clippy::transmute_ptr_to_ref", |
6062 | description: r##"Checks for transmutes from a pointer to a reference."##, | 6933 | description: r##"Checks for transmutes from a pointer to a reference."##, |
6063 | }, | 6934 | }, |
6064 | LintCompletion { | 6935 | Lint { |
6065 | label: "clippy::transmutes_expressible_as_ptr_casts", | 6936 | label: "clippy::transmutes_expressible_as_ptr_casts", |
6066 | description: r##"Checks for transmutes that could be a pointer cast."##, | 6937 | description: r##"Checks for transmutes that could be a pointer cast."##, |
6067 | }, | 6938 | }, |
6068 | LintCompletion { | 6939 | Lint { |
6069 | label: "clippy::transmuting_null", | 6940 | label: "clippy::transmuting_null", |
6070 | description: r##"Checks for transmute calls which would receive a null pointer."##, | 6941 | description: r##"Checks for transmute calls which would receive a null pointer."##, |
6071 | }, | 6942 | }, |
6072 | LintCompletion { | 6943 | Lint { |
6073 | label: "clippy::trivial_regex", | 6944 | label: "clippy::trivial_regex", |
6074 | description: r##"Checks for trivial [regex](https://crates.io/crates/regex)\ncreation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`)."##, | 6945 | description: r##"Checks for trivial [regex](https://crates.io/crates/regex) |
6946 | creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`)."##, | ||
6075 | }, | 6947 | }, |
6076 | LintCompletion { | 6948 | Lint { |
6077 | label: "clippy::trivially_copy_pass_by_ref", | 6949 | label: "clippy::trivially_copy_pass_by_ref", |
6078 | description: r##"Checks for functions taking arguments by reference, where\nthe argument type is `Copy` and small enough to be more efficient to always\npass by value."##, | 6950 | description: r##"Checks for functions taking arguments by reference, where |
6079 | }, | 6951 | the argument type is `Copy` and small enough to be more efficient to always |
6080 | LintCompletion { | 6952 | pass by value."##, |
6081 | label: "clippy::try_err", | ||
6082 | description: r##"Checks for usages of `Err(x)?`."##, | ||
6083 | }, | 6953 | }, |
6084 | LintCompletion { | 6954 | Lint { label: "clippy::try_err", description: r##"Checks for usages of `Err(x)?`."## }, |
6955 | Lint { | ||
6085 | label: "clippy::type_complexity", | 6956 | label: "clippy::type_complexity", |
6086 | description: r##"Checks for types used in structs, parameters and `let`\ndeclarations above a certain complexity threshold."##, | 6957 | description: r##"Checks for types used in structs, parameters and `let` |
6958 | declarations above a certain complexity threshold."##, | ||
6087 | }, | 6959 | }, |
6088 | LintCompletion { | 6960 | Lint { |
6089 | label: "clippy::type_repetition_in_bounds", | 6961 | label: "clippy::type_repetition_in_bounds", |
6090 | description: r##"This lint warns about unnecessary type repetitions in trait bounds"##, | 6962 | description: r##"This lint warns about unnecessary type repetitions in trait bounds"##, |
6091 | }, | 6963 | }, |
6092 | LintCompletion { | 6964 | Lint { |
6093 | label: "clippy::undropped_manually_drops", | 6965 | label: "clippy::undropped_manually_drops", |
6094 | description: r##"Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`."##, | 6966 | description: r##"Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`."##, |
6095 | }, | 6967 | }, |
6096 | LintCompletion { | 6968 | Lint { |
6097 | label: "clippy::unicode_not_nfc", | 6969 | label: "clippy::unicode_not_nfc", |
6098 | description: r##"Checks for string literals that contain Unicode in a form\nthat is not equal to its\n[NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms)."##, | 6970 | description: r##"Checks for string literals that contain Unicode in a form |
6971 | that is not equal to its | ||
6972 | [NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms)."##, | ||
6099 | }, | 6973 | }, |
6100 | LintCompletion { | 6974 | Lint { |
6101 | label: "clippy::unimplemented", | 6975 | label: "clippy::unimplemented", |
6102 | description: r##"Checks for usage of `unimplemented!`."##, | 6976 | description: r##"Checks for usage of `unimplemented!`."##, |
6103 | }, | 6977 | }, |
6104 | LintCompletion { | 6978 | Lint { |
6105 | label: "clippy::uninit_assumed_init", | 6979 | label: "clippy::uninit_assumed_init", |
6106 | description: r##"Checks for `MaybeUninit::uninit().assume_init()`."##, | 6980 | description: r##"Checks for `MaybeUninit::uninit().assume_init()`."##, |
6107 | }, | 6981 | }, |
6108 | LintCompletion { | 6982 | Lint { |
6109 | label: "clippy::unit_arg", | 6983 | label: "clippy::unit_arg", |
6110 | description: r##"Checks for passing a unit value as an argument to a function without using a\nunit literal (`()`)."##, | 6984 | description: r##"Checks for passing a unit value as an argument to a function without using a |
6985 | unit literal (`()`)."##, | ||
6111 | }, | 6986 | }, |
6112 | LintCompletion { | 6987 | Lint { |
6113 | label: "clippy::unit_cmp", | 6988 | label: "clippy::unit_cmp", |
6114 | description: r##"Checks for comparisons to unit. This includes all binary\ncomparisons (like `==` and `<`) and asserts."##, | 6989 | description: r##"Checks for comparisons to unit. This includes all binary |
6990 | comparisons (like `==` and `<`) and asserts."##, | ||
6115 | }, | 6991 | }, |
6116 | LintCompletion { | 6992 | Lint { |
6117 | label: "clippy::unit_return_expecting_ord", | 6993 | label: "clippy::unit_return_expecting_ord", |
6118 | description: r##"Checks for functions that expect closures of type\nFn(...) -> Ord where the implemented closure returns the unit type.\nThe lint also suggests to remove the semi-colon at the end of the statement if present."##, | 6994 | description: r##"Checks for functions that expect closures of type |
6995 | Fn(...) -> Ord where the implemented closure returns the unit type. | ||
6996 | The lint also suggests to remove the semi-colon at the end of the statement if present."##, | ||
6119 | }, | 6997 | }, |
6120 | LintCompletion { | 6998 | Lint { |
6121 | label: "clippy::unknown_clippy_lints", | ||
6122 | description: r##"Nothing. This lint has been deprecated."##, | ||
6123 | }, | ||
6124 | LintCompletion { | ||
6125 | label: "clippy::unnecessary_cast", | 6999 | label: "clippy::unnecessary_cast", |
6126 | description: r##"Checks for casts to the same type, casts of int literals to integer types\nand casts of float literals to float types."##, | 7000 | description: r##"Checks for casts to the same type, casts of int literals to integer types |
7001 | and casts of float literals to float types."##, | ||
6127 | }, | 7002 | }, |
6128 | LintCompletion { | 7003 | Lint { |
6129 | label: "clippy::unnecessary_filter_map", | 7004 | label: "clippy::unnecessary_filter_map", |
6130 | description: r##"Checks for `filter_map` calls which could be replaced by `filter` or `map`.\nMore specifically it checks if the closure provided is only performing one of the\nfilter or map operations and suggests the appropriate option."##, | 7005 | description: r##"Checks for `filter_map` calls which could be replaced by `filter` or `map`. |
7006 | More specifically it checks if the closure provided is only performing one of the | ||
7007 | filter or map operations and suggests the appropriate option."##, | ||
6131 | }, | 7008 | }, |
6132 | LintCompletion { | 7009 | Lint { |
6133 | label: "clippy::unnecessary_fold", | 7010 | label: "clippy::unnecessary_fold", |
6134 | description: r##"Checks for using `fold` when a more succinct alternative exists.\nSpecifically, this checks for `fold`s which could be replaced by `any`, `all`,\n`sum` or `product`."##, | 7011 | description: r##"Checks for using `fold` when a more succinct alternative exists. |
7012 | Specifically, this checks for `fold`s which could be replaced by `any`, `all`, | ||
7013 | `sum` or `product`."##, | ||
6135 | }, | 7014 | }, |
6136 | LintCompletion { | 7015 | Lint { |
6137 | label: "clippy::unnecessary_lazy_evaluations", | 7016 | label: "clippy::unnecessary_lazy_evaluations", |
6138 | description: r##"As the counterpart to `or_fun_call`, this lint looks for unnecessary\nlazily evaluated closures on `Option` and `Result`.\n\nThis lint suggests changing the following functions, when eager evaluation results in\nsimpler code:\n - `unwrap_or_else` to `unwrap_or`\n - `and_then` to `and`\n - `or_else` to `or`\n - `get_or_insert_with` to `get_or_insert`\n - `ok_or_else` to `ok_or`"##, | 7017 | description: r##"As the counterpart to `or_fun_call`, this lint looks for unnecessary |
6139 | }, | 7018 | lazily evaluated closures on `Option` and `Result`. |
6140 | LintCompletion { | 7019 | |
7020 | This lint suggests changing the following functions, when eager evaluation results in | ||
7021 | simpler code: | ||
7022 | - `unwrap_or_else` to `unwrap_or` | ||
7023 | - `and_then` to `and` | ||
7024 | - `or_else` to `or` | ||
7025 | - `get_or_insert_with` to `get_or_insert` | ||
7026 | - `ok_or_else` to `ok_or`"##, | ||
7027 | }, | ||
7028 | Lint { | ||
6141 | label: "clippy::unnecessary_mut_passed", | 7029 | label: "clippy::unnecessary_mut_passed", |
6142 | description: r##"Detects passing a mutable reference to a function that only\nrequires an immutable reference."##, | 7030 | description: r##"Detects passing a mutable reference to a function that only |
7031 | requires an immutable reference."##, | ||
6143 | }, | 7032 | }, |
6144 | LintCompletion { | 7033 | Lint { |
6145 | label: "clippy::unnecessary_operation", | 7034 | label: "clippy::unnecessary_operation", |
6146 | description: r##"Checks for expression statements that can be reduced to a\nsub-expression."##, | 7035 | description: r##"Checks for expression statements that can be reduced to a |
7036 | sub-expression."##, | ||
6147 | }, | 7037 | }, |
6148 | LintCompletion { | 7038 | Lint { |
7039 | label: "clippy::unnecessary_self_imports", | ||
7040 | description: r##"Checks for imports ending in `::{self}`."##, | ||
7041 | }, | ||
7042 | Lint { | ||
6149 | label: "clippy::unnecessary_sort_by", | 7043 | label: "clippy::unnecessary_sort_by", |
6150 | description: r##"Detects uses of `Vec::sort_by` passing in a closure\nwhich compares the two arguments, either directly or indirectly."##, | 7044 | description: r##"Detects uses of `Vec::sort_by` passing in a closure |
7045 | which compares the two arguments, either directly or indirectly."##, | ||
6151 | }, | 7046 | }, |
6152 | LintCompletion { | 7047 | Lint { |
6153 | label: "clippy::unnecessary_unwrap", | 7048 | label: "clippy::unnecessary_unwrap", |
6154 | description: r##"Checks for calls of `unwrap[_err]()` that cannot fail."##, | 7049 | description: r##"Checks for calls of `unwrap[_err]()` that cannot fail."##, |
6155 | }, | 7050 | }, |
6156 | LintCompletion { | 7051 | Lint { |
6157 | label: "clippy::unnecessary_wraps", | 7052 | label: "clippy::unnecessary_wraps", |
6158 | description: r##"Checks for private functions that only return `Ok` or `Some`."##, | 7053 | description: r##"Checks for private functions that only return `Ok` or `Some`."##, |
6159 | }, | 7054 | }, |
6160 | LintCompletion { | 7055 | Lint { |
6161 | label: "clippy::unneeded_field_pattern", | 7056 | label: "clippy::unneeded_field_pattern", |
6162 | description: r##"Checks for structure field patterns bound to wildcards."##, | 7057 | description: r##"Checks for structure field patterns bound to wildcards."##, |
6163 | }, | 7058 | }, |
6164 | LintCompletion { | 7059 | Lint { |
6165 | label: "clippy::unneeded_wildcard_pattern", | 7060 | label: "clippy::unneeded_wildcard_pattern", |
6166 | description: r##"Checks for tuple patterns with a wildcard\npattern (`_`) is next to a rest pattern (`..`).\n\n_NOTE_: While `_, ..` means there is at least one element left, `..`\nmeans there are 0 or more elements left. This can make a difference\nwhen refactoring, but shouldn't result in errors in the refactored code,\nsince the wildcard pattern isn't used anyway."##, | 7061 | description: r##"Checks for tuple patterns with a wildcard |
7062 | pattern (`_`) is next to a rest pattern (`..`). | ||
7063 | |||
7064 | _NOTE_: While `_, ..` means there is at least one element left, `..` | ||
7065 | means there are 0 or more elements left. This can make a difference | ||
7066 | when refactoring, but shouldn't result in errors in the refactored code, | ||
7067 | since the wildcard pattern isn't used anyway."##, | ||
6167 | }, | 7068 | }, |
6168 | LintCompletion { | 7069 | Lint { |
6169 | label: "clippy::unnested_or_patterns", | 7070 | label: "clippy::unnested_or_patterns", |
6170 | description: r##"Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and\nsuggests replacing the pattern with a nested one, `Some(0 | 2)`.\n\nAnother way to think of this is that it rewrites patterns in\n*disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*."##, | 7071 | description: r##"Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and |
6171 | }, | 7072 | suggests replacing the pattern with a nested one, `Some(0 | 2)`. |
6172 | LintCompletion { | 7073 | |
6173 | label: "clippy::unreachable", | 7074 | Another way to think of this is that it rewrites patterns in |
6174 | description: r##"Checks for usage of `unreachable!`."##, | 7075 | *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*."##, |
6175 | }, | 7076 | }, |
6176 | LintCompletion { | 7077 | Lint { label: "clippy::unreachable", description: r##"Checks for usage of `unreachable!`."## }, |
7078 | Lint { | ||
6177 | label: "clippy::unreadable_literal", | 7079 | label: "clippy::unreadable_literal", |
6178 | description: r##"Warns if a long integral or floating-point constant does\nnot contain underscores."##, | 7080 | description: r##"Warns if a long integral or floating-point constant does |
7081 | not contain underscores."##, | ||
6179 | }, | 7082 | }, |
6180 | LintCompletion { | 7083 | Lint { |
6181 | label: "clippy::unsafe_derive_deserialize", | 7084 | label: "clippy::unsafe_derive_deserialize", |
6182 | description: r##"Checks for deriving `serde::Deserialize` on a type that\nhas methods using `unsafe`."##, | 7085 | description: r##"Checks for deriving `serde::Deserialize` on a type that |
7086 | has methods using `unsafe`."##, | ||
6183 | }, | 7087 | }, |
6184 | LintCompletion { | 7088 | Lint { |
6185 | label: "clippy::unsafe_removed_from_name", | 7089 | label: "clippy::unsafe_removed_from_name", |
6186 | description: r##"Checks for imports that remove \"unsafe\" from an item's\nname."##, | 7090 | description: r##"Checks for imports that remove unsafe from an item's |
7091 | name."##, | ||
6187 | }, | 7092 | }, |
6188 | LintCompletion { | 7093 | Lint { |
6189 | label: "clippy::unsafe_vector_initialization", | 7094 | label: "clippy::unsafe_vector_initialization", |
6190 | description: r##"Nothing. This lint has been deprecated."##, | 7095 | description: r##"Nothing. This lint has been deprecated."##, |
6191 | }, | 7096 | }, |
6192 | LintCompletion { | 7097 | Lint { |
6193 | label: "clippy::unseparated_literal_suffix", | 7098 | label: "clippy::unseparated_literal_suffix", |
6194 | description: r##"Warns if literal suffixes are not separated by an\nunderscore."##, | 7099 | description: r##"Warns if literal suffixes are not separated by an |
7100 | underscore."##, | ||
6195 | }, | 7101 | }, |
6196 | LintCompletion { | 7102 | Lint { |
6197 | label: "clippy::unsound_collection_transmute", | 7103 | label: "clippy::unsound_collection_transmute", |
6198 | description: r##"Checks for transmutes between collections whose\ntypes have different ABI, size or alignment."##, | 7104 | description: r##"Checks for transmutes between collections whose |
7105 | types have different ABI, size or alignment."##, | ||
6199 | }, | 7106 | }, |
6200 | LintCompletion { | 7107 | Lint { |
6201 | label: "clippy::unstable_as_mut_slice", | 7108 | label: "clippy::unstable_as_mut_slice", |
6202 | description: r##"Nothing. This lint has been deprecated."##, | 7109 | description: r##"Nothing. This lint has been deprecated."##, |
6203 | }, | 7110 | }, |
6204 | LintCompletion { | 7111 | Lint { |
6205 | label: "clippy::unstable_as_slice", | 7112 | label: "clippy::unstable_as_slice", |
6206 | description: r##"Nothing. This lint has been deprecated."##, | 7113 | description: r##"Nothing. This lint has been deprecated."##, |
6207 | }, | 7114 | }, |
6208 | LintCompletion { | 7115 | Lint { |
7116 | label: "clippy::unused_async", | ||
7117 | description: r##"Checks for functions that are declared `async` but have no `.await`s inside of them."##, | ||
7118 | }, | ||
7119 | Lint { | ||
6209 | label: "clippy::unused_collect", | 7120 | label: "clippy::unused_collect", |
6210 | description: r##"Nothing. This lint has been deprecated."##, | 7121 | description: r##"Nothing. This lint has been deprecated."##, |
6211 | }, | 7122 | }, |
6212 | LintCompletion { | 7123 | Lint { |
6213 | label: "clippy::unused_io_amount", | 7124 | label: "clippy::unused_io_amount", |
6214 | description: r##"Checks for unused written/read amount."##, | 7125 | description: r##"Checks for unused written/read amount."##, |
6215 | }, | 7126 | }, |
6216 | LintCompletion { | 7127 | Lint { |
6217 | label: "clippy::unused_label", | ||
6218 | description: r##"Nothing. This lint has been deprecated."##, | ||
6219 | }, | ||
6220 | LintCompletion { | ||
6221 | label: "clippy::unused_self", | 7128 | label: "clippy::unused_self", |
6222 | description: r##"Checks methods that contain a `self` argument but don't use it"##, | 7129 | description: r##"Checks methods that contain a `self` argument but don't use it"##, |
6223 | }, | 7130 | }, |
6224 | LintCompletion { | 7131 | Lint { |
6225 | label: "clippy::unused_unit", | 7132 | label: "clippy::unused_unit", |
6226 | description: r##"Checks for unit (`()`) expressions that can be removed."##, | 7133 | description: r##"Checks for unit (`()`) expressions that can be removed."##, |
6227 | }, | 7134 | }, |
6228 | LintCompletion { | 7135 | Lint { |
6229 | label: "clippy::unusual_byte_groupings", | 7136 | label: "clippy::unusual_byte_groupings", |
6230 | description: r##"Warns if hexadecimal or binary literals are not grouped\nby nibble or byte."##, | 7137 | description: r##"Warns if hexadecimal or binary literals are not grouped |
7138 | by nibble or byte."##, | ||
6231 | }, | 7139 | }, |
6232 | LintCompletion { | 7140 | Lint { |
6233 | label: "clippy::unwrap_in_result", | 7141 | label: "clippy::unwrap_in_result", |
6234 | description: r##"Checks for functions of type Result that contain `expect()` or `unwrap()`"##, | 7142 | description: r##"Checks for functions of type Result that contain `expect()` or `unwrap()`"##, |
6235 | }, | 7143 | }, |
6236 | LintCompletion { | 7144 | Lint { |
6237 | label: "clippy::unwrap_used", | 7145 | label: "clippy::unwrap_used", |
6238 | description: r##"Checks for `.unwrap()` calls on `Option`s and on `Result`s."##, | 7146 | description: r##"Checks for `.unwrap()` calls on `Option`s and on `Result`s."##, |
6239 | }, | 7147 | }, |
6240 | LintCompletion { | 7148 | Lint { |
6241 | label: "clippy::upper_case_acronyms", | 7149 | label: "clippy::upper_case_acronyms", |
6242 | description: r##"Checks for fully capitalized names and optionally names containing a capitalized acronym."##, | 7150 | description: r##"Checks for fully capitalized names and optionally names containing a capitalized acronym."##, |
6243 | }, | 7151 | }, |
6244 | LintCompletion { | 7152 | Lint { |
6245 | label: "clippy::use_debug", | 7153 | label: "clippy::use_debug", |
6246 | description: r##"Checks for use of `Debug` formatting. The purpose of this\nlint is to catch debugging remnants."##, | 7154 | description: r##"Checks for use of `Debug` formatting. The purpose of this |
7155 | lint is to catch debugging remnants."##, | ||
6247 | }, | 7156 | }, |
6248 | LintCompletion { | 7157 | Lint { |
6249 | label: "clippy::use_self", | 7158 | label: "clippy::use_self", |
6250 | description: r##"Checks for unnecessary repetition of structure name when a\nreplacement with `Self` is applicable."##, | 7159 | description: r##"Checks for unnecessary repetition of structure name when a |
7160 | replacement with `Self` is applicable."##, | ||
6251 | }, | 7161 | }, |
6252 | LintCompletion { | 7162 | Lint { |
6253 | label: "clippy::used_underscore_binding", | 7163 | label: "clippy::used_underscore_binding", |
6254 | description: r##"Checks for the use of bindings with a single leading\nunderscore."##, | 7164 | description: r##"Checks for the use of bindings with a single leading |
7165 | underscore."##, | ||
6255 | }, | 7166 | }, |
6256 | LintCompletion { | 7167 | Lint { |
6257 | label: "clippy::useless_asref", | 7168 | label: "clippy::useless_asref", |
6258 | description: r##"Checks for usage of `.as_ref()` or `.as_mut()` where the\ntypes before and after the call are the same."##, | 7169 | description: r##"Checks for usage of `.as_ref()` or `.as_mut()` where the |
7170 | types before and after the call are the same."##, | ||
6259 | }, | 7171 | }, |
6260 | LintCompletion { | 7172 | Lint { |
6261 | label: "clippy::useless_attribute", | 7173 | label: "clippy::useless_attribute", |
6262 | description: r##"Checks for `extern crate` and `use` items annotated with\nlint attributes.\n\nThis lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`,\n`#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and\n`#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on\n`extern crate` items with a `#[macro_use]` attribute."##, | 7174 | description: r##"Checks for `extern crate` and `use` items annotated with |
7175 | lint attributes. | ||
7176 | |||
7177 | This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`, | ||
7178 | `#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and | ||
7179 | `#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on | ||
7180 | `extern crate` items with a `#[macro_use]` attribute."##, | ||
6263 | }, | 7181 | }, |
6264 | LintCompletion { | 7182 | Lint { |
6265 | label: "clippy::useless_conversion", | 7183 | label: "clippy::useless_conversion", |
6266 | description: r##"Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` calls\nwhich uselessly convert to the same type."##, | 7184 | description: r##"Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` calls |
7185 | which uselessly convert to the same type."##, | ||
6267 | }, | 7186 | }, |
6268 | LintCompletion { | 7187 | Lint { |
6269 | label: "clippy::useless_format", | 7188 | label: "clippy::useless_format", |
6270 | description: r##"Checks for the use of `format!(\"string literal with no\nargument\")` and `format!(\"{}\", foo)` where `foo` is a string."##, | 7189 | description: r##"Checks for the use of `format!(string literal with no |
7190 | argument)` and `format!({}, foo)` where `foo` is a string."##, | ||
6271 | }, | 7191 | }, |
6272 | LintCompletion { | 7192 | Lint { |
6273 | label: "clippy::useless_let_if_seq", | 7193 | label: "clippy::useless_let_if_seq", |
6274 | description: r##"Checks for variable declarations immediately followed by a\nconditional affectation."##, | 7194 | description: r##"Checks for variable declarations immediately followed by a |
7195 | conditional affectation."##, | ||
6275 | }, | 7196 | }, |
6276 | LintCompletion { | 7197 | Lint { |
6277 | label: "clippy::useless_transmute", | 7198 | label: "clippy::useless_transmute", |
6278 | description: r##"Checks for transmutes to the original type of the object\nand transmutes that could be a cast."##, | 7199 | description: r##"Checks for transmutes to the original type of the object |
7200 | and transmutes that could be a cast."##, | ||
6279 | }, | 7201 | }, |
6280 | LintCompletion { | 7202 | Lint { |
6281 | label: "clippy::useless_vec", | 7203 | label: "clippy::useless_vec", |
6282 | description: r##"Checks for usage of `&vec![..]` when using `&[..]` would\nbe possible."##, | 7204 | description: r##"Checks for usage of `&vec![..]` when using `&[..]` would |
7205 | be possible."##, | ||
6283 | }, | 7206 | }, |
6284 | LintCompletion { | 7207 | Lint { |
6285 | label: "clippy::vec_box", | 7208 | label: "clippy::vec_box", |
6286 | description: r##"Checks for use of `Vec<Box<T>>` where T: Sized anywhere in the code.\nCheck the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information."##, | 7209 | description: r##"Checks for use of `Vec<Box<T>>` where T: Sized anywhere in the code. |
7210 | Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information."##, | ||
6287 | }, | 7211 | }, |
6288 | LintCompletion { | 7212 | Lint { |
6289 | label: "clippy::vec_init_then_push", | 7213 | label: "clippy::vec_init_then_push", |
6290 | description: r##"Checks for calls to `push` immediately after creating a new `Vec`."##, | 7214 | description: r##"Checks for calls to `push` immediately after creating a new `Vec`."##, |
6291 | }, | 7215 | }, |
6292 | LintCompletion { | 7216 | Lint { |
6293 | label: "clippy::vec_resize_to_zero", | 7217 | label: "clippy::vec_resize_to_zero", |
6294 | description: r##"Finds occurrences of `Vec::resize(0, an_int)`"##, | 7218 | description: r##"Finds occurrences of `Vec::resize(0, an_int)`"##, |
6295 | }, | 7219 | }, |
6296 | LintCompletion { | 7220 | Lint { |
6297 | label: "clippy::verbose_bit_mask", | 7221 | label: "clippy::verbose_bit_mask", |
6298 | description: r##"Checks for bit masks that can be replaced by a call\nto `trailing_zeros`"##, | 7222 | description: r##"Checks for bit masks that can be replaced by a call |
7223 | to `trailing_zeros`"##, | ||
6299 | }, | 7224 | }, |
6300 | LintCompletion { | 7225 | Lint { |
6301 | label: "clippy::verbose_file_reads", | 7226 | label: "clippy::verbose_file_reads", |
6302 | description: r##"Checks for use of File::read_to_end and File::read_to_string."##, | 7227 | description: r##"Checks for use of File::read_to_end and File::read_to_string."##, |
6303 | }, | 7228 | }, |
6304 | LintCompletion { | 7229 | Lint { |
6305 | label: "clippy::vtable_address_comparisons", | 7230 | label: "clippy::vtable_address_comparisons", |
6306 | description: r##"Checks for comparisons with an address of a trait vtable."##, | 7231 | description: r##"Checks for comparisons with an address of a trait vtable."##, |
6307 | }, | 7232 | }, |
6308 | LintCompletion { | 7233 | Lint { |
6309 | label: "clippy::while_immutable_condition", | 7234 | label: "clippy::while_immutable_condition", |
6310 | description: r##"Checks whether variables used within while loop condition\ncan be (and are) mutated in the body."##, | 7235 | description: r##"Checks whether variables used within while loop condition |
7236 | can be (and are) mutated in the body."##, | ||
6311 | }, | 7237 | }, |
6312 | LintCompletion { | 7238 | Lint { |
6313 | label: "clippy::while_let_loop", | 7239 | label: "clippy::while_let_loop", |
6314 | description: r##"Detects `loop + match` combinations that are easier\nwritten as a `while let` loop."##, | 7240 | description: r##"Detects `loop + match` combinations that are easier |
7241 | written as a `while let` loop."##, | ||
6315 | }, | 7242 | }, |
6316 | LintCompletion { | 7243 | Lint { |
6317 | label: "clippy::while_let_on_iterator", | 7244 | label: "clippy::while_let_on_iterator", |
6318 | description: r##"Checks for `while let` expressions on iterators."##, | 7245 | description: r##"Checks for `while let` expressions on iterators."##, |
6319 | }, | 7246 | }, |
6320 | LintCompletion { | 7247 | Lint { |
6321 | label: "clippy::wildcard_dependencies", | 7248 | label: "clippy::wildcard_dependencies", |
6322 | description: r##"Checks for wildcard dependencies in the `Cargo.toml`."##, | 7249 | description: r##"Checks for wildcard dependencies in the `Cargo.toml`."##, |
6323 | }, | 7250 | }, |
6324 | LintCompletion { | 7251 | Lint { |
6325 | label: "clippy::wildcard_enum_match_arm", | 7252 | label: "clippy::wildcard_enum_match_arm", |
6326 | description: r##"Checks for wildcard enum matches using `_`."##, | 7253 | description: r##"Checks for wildcard enum matches using `_`."##, |
6327 | }, | 7254 | }, |
6328 | LintCompletion { | 7255 | Lint { |
6329 | label: "clippy::wildcard_imports", | 7256 | label: "clippy::wildcard_imports", |
6330 | description: r##"Checks for wildcard imports `use _::*`."##, | 7257 | description: r##"Checks for wildcard imports `use _::*`."##, |
6331 | }, | 7258 | }, |
6332 | LintCompletion { | 7259 | Lint { |
6333 | label: "clippy::wildcard_in_or_patterns", | 7260 | label: "clippy::wildcard_in_or_patterns", |
6334 | description: r##"Checks for wildcard pattern used with others patterns in same match arm."##, | 7261 | description: r##"Checks for wildcard pattern used with others patterns in same match arm."##, |
6335 | }, | 7262 | }, |
6336 | LintCompletion { | 7263 | Lint { |
6337 | label: "clippy::write_literal", | 7264 | label: "clippy::write_literal", |
6338 | description: r##"This lint warns about the use of literals as `write!`/`writeln!` args."##, | 7265 | description: r##"This lint warns about the use of literals as `write!`/`writeln!` args."##, |
6339 | }, | 7266 | }, |
6340 | LintCompletion { | 7267 | Lint { |
6341 | label: "clippy::write_with_newline", | 7268 | label: "clippy::write_with_newline", |
6342 | description: r##"This lint warns when you use `write!()` with a format\nstring that\nends in a newline."##, | 7269 | description: r##"This lint warns when you use `write!()` with a format |
7270 | string that | ||
7271 | ends in a newline."##, | ||
6343 | }, | 7272 | }, |
6344 | LintCompletion { | 7273 | Lint { |
6345 | label: "clippy::writeln_empty_string", | 7274 | label: "clippy::writeln_empty_string", |
6346 | description: r##"This lint warns when you use `writeln!(buf, \"\")` to\nprint a newline."##, | 7275 | description: r##"This lint warns when you use `writeln!(buf, )` to |
7276 | print a newline."##, | ||
6347 | }, | 7277 | }, |
6348 | LintCompletion { | 7278 | Lint { |
6349 | label: "clippy::wrong_pub_self_convention", | 7279 | label: "clippy::wrong_pub_self_convention", |
6350 | description: r##"This is the same as\n[`wrong_self_convention`](#wrong_self_convention), but for public items."##, | 7280 | description: r##"Nothing. This lint has been deprecated."##, |
6351 | }, | 7281 | }, |
6352 | LintCompletion { | 7282 | Lint { |
6353 | label: "clippy::wrong_self_convention", | 7283 | label: "clippy::wrong_self_convention", |
6354 | description: r##"Checks for methods with certain name prefixes and which\ndoesn't match how self is taken. The actual rules are:\n\n|Prefix |`self` taken |\n|-------|----------------------|\n|`as_` |`&self` or `&mut self`|\n|`from_`| none |\n|`into_`|`self` |\n|`is_` |`&self` or none |\n|`to_` |`&self` |"##, | 7284 | description: r##"Checks for methods with certain name prefixes and which |
6355 | }, | 7285 | doesn't match how self is taken. The actual rules are: |
6356 | LintCompletion { | 7286 | |
7287 | |Prefix |Postfix |`self` taken | `self` type | | ||
7288 | |-------|------------|-----------------------|--------------| | ||
7289 | |`as_` | none |`&self` or `&mut self` | any | | ||
7290 | |`from_`| none | none | any | | ||
7291 | |`into_`| none |`self` | any | | ||
7292 | |`is_` | none |`&self` or none | any | | ||
7293 | |`to_` | `_mut` |`&mut self` | any | | ||
7294 | |`to_` | not `_mut` |`self` | `Copy` | | ||
7295 | |`to_` | not `_mut` |`&self` | not `Copy` | | ||
7296 | |||
7297 | Note: Clippy doesn't trigger methods with `to_` prefix in: | ||
7298 | - Traits definition. | ||
7299 | Clippy can not tell if a type that implements a trait is `Copy` or not. | ||
7300 | - Traits implementation, when `&self` is taken. | ||
7301 | The method signature is controlled by the trait and often `&self` is required for all types that implement the trait | ||
7302 | (see e.g. the `std::string::ToString` trait). | ||
7303 | |||
7304 | Please find more info here: | ||
7305 | https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv"##, | ||
7306 | }, | ||
7307 | Lint { | ||
6357 | label: "clippy::wrong_transmute", | 7308 | label: "clippy::wrong_transmute", |
6358 | description: r##"Checks for transmutes that can't ever be correct on any\narchitecture."##, | 7309 | description: r##"Checks for transmutes that can't ever be correct on any |
6359 | }, | 7310 | architecture."##, |
6360 | LintCompletion { | ||
6361 | label: "clippy::zero_divided_by_zero", | ||
6362 | description: r##"Checks for `0.0 / 0.0`."##, | ||
6363 | }, | 7311 | }, |
6364 | LintCompletion { | 7312 | Lint { label: "clippy::zero_divided_by_zero", description: r##"Checks for `0.0 / 0.0`."## }, |
7313 | Lint { | ||
6365 | label: "clippy::zero_prefixed_literal", | 7314 | label: "clippy::zero_prefixed_literal", |
6366 | description: r##"Warns if an integral constant literal starts with `0`."##, | 7315 | description: r##"Warns if an integral constant literal starts with `0`."##, |
6367 | }, | 7316 | }, |
6368 | LintCompletion { | 7317 | Lint { |
6369 | label: "clippy::zero_ptr", | 7318 | label: "clippy::zero_ptr", |
6370 | description: r##"Catch casts from `0` to some pointer type"##, | 7319 | description: r##"Catch casts from `0` to some pointer type"##, |
6371 | }, | 7320 | }, |
6372 | LintCompletion { | 7321 | Lint { |
6373 | label: "clippy::zero_sized_map_values", | 7322 | label: "clippy::zero_sized_map_values", |
6374 | description: r##"Checks for maps with zero-sized value types anywhere in the code."##, | 7323 | description: r##"Checks for maps with zero-sized value types anywhere in the code."##, |
6375 | }, | 7324 | }, |
6376 | LintCompletion { | 7325 | Lint { |
6377 | label: "clippy::zst_offset", | 7326 | label: "clippy::zst_offset", |
6378 | description: r##"Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to\nzero-sized types"##, | 7327 | description: r##"Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to |
7328 | zero-sized types"##, | ||
6379 | }, | 7329 | }, |
6380 | ]; | 7330 | ]; |
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index ae52dd8bb..9634d872e 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs | |||
@@ -323,7 +323,7 @@ fn import_for_item( | |||
323 | } | 323 | } |
324 | 324 | ||
325 | let segment_import = | 325 | let segment_import = |
326 | find_import_for_segment(db, original_item_candidate, &unresolved_first_segment)?; | 326 | find_import_for_segment(db, original_item_candidate, unresolved_first_segment)?; |
327 | let trait_item_to_import = item_as_assoc(db, original_item) | 327 | let trait_item_to_import = item_as_assoc(db, original_item) |
328 | .and_then(|assoc| assoc.containing_trait(db)) | 328 | .and_then(|assoc| assoc.containing_trait(db)) |
329 | .map(|trait_| ItemInNs::from(ModuleDef::from(trait_))); | 329 | .map(|trait_| ItemInNs::from(ModuleDef::from(trait_))); |
@@ -383,7 +383,7 @@ fn find_import_for_segment( | |||
383 | original_item | 383 | original_item |
384 | } else { | 384 | } else { |
385 | let matching_module = | 385 | let matching_module = |
386 | module_with_segment_name(db, &unresolved_first_segment, original_item)?; | 386 | module_with_segment_name(db, unresolved_first_segment, original_item)?; |
387 | ItemInNs::from(ModuleDef::from(matching_module)) | 387 | ItemInNs::from(ModuleDef::from(matching_module)) |
388 | }) | 388 | }) |
389 | } | 389 | } |
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs index aa61c5bcb..10bbafe77 100644 --- a/crates/ide_db/src/helpers/insert_use.rs +++ b/crates/ide_db/src/helpers/insert_use.rs | |||
@@ -120,15 +120,19 @@ impl ImportScope { | |||
120 | if eq_visibility(prev_vis, curr_vis.clone()) && eq_attrs(prev_attrs, curr_attrs.clone()) | 120 | if eq_visibility(prev_vis, curr_vis.clone()) && eq_attrs(prev_attrs, curr_attrs.clone()) |
121 | { | 121 | { |
122 | if let Some((prev_path, curr_path)) = prev.path().zip(curr.path()) { | 122 | if let Some((prev_path, curr_path)) = prev.path().zip(curr.path()) { |
123 | if let Some(_) = common_prefix(&prev_path, &curr_path) { | 123 | if let Some((prev_prefix, _)) = common_prefix(&prev_path, &curr_path) { |
124 | if prev.use_tree_list().is_none() && curr.use_tree_list().is_none() { | 124 | if prev.use_tree_list().is_none() && curr.use_tree_list().is_none() { |
125 | // Same prefix but no use tree lists so this has to be of item style. | 125 | let prefix_c = prev_prefix.qualifiers().count(); |
126 | break ImportGranularityGuess::Item; // this overwrites CrateOrModule, technically the file doesn't adhere to anything here. | 126 | let curr_c = curr_path.qualifiers().count() - prefix_c; |
127 | } else { | 127 | let prev_c = prev_path.qualifiers().count() - prefix_c; |
128 | // Same prefix with item tree lists, has to be module style as it | 128 | if curr_c <= 1 || prev_c <= 1 { |
129 | // can't be crate style since the trees wouldn't share a prefix then. | 129 | // Same prefix but no use tree lists so this has to be of item style. |
130 | break ImportGranularityGuess::Module; | 130 | break ImportGranularityGuess::Item; // this overwrites CrateOrModule, technically the file doesn't adhere to anything here. |
131 | } | ||
131 | } | 132 | } |
133 | // Same prefix with item tree lists, has to be module style as it | ||
134 | // can't be crate style since the trees wouldn't share a prefix then. | ||
135 | break ImportGranularityGuess::Module; | ||
132 | } | 136 | } |
133 | } | 137 | } |
134 | } | 138 | } |
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs index 78a2a87b3..70b11bf81 100644 --- a/crates/ide_db/src/helpers/insert_use/tests.rs +++ b/crates/ide_db/src/helpers/insert_use/tests.rs | |||
@@ -663,6 +663,13 @@ use foo::bar::qux; | |||
663 | ", | 663 | ", |
664 | ImportGranularityGuess::Item, | 664 | ImportGranularityGuess::Item, |
665 | ); | 665 | ); |
666 | check_guess( | ||
667 | r" | ||
668 | use foo::bar::Bar; | ||
669 | use foo::baz; | ||
670 | ", | ||
671 | ImportGranularityGuess::Item, | ||
672 | ); | ||
666 | } | 673 | } |
667 | 674 | ||
668 | #[test] | 675 | #[test] |
@@ -682,6 +689,14 @@ use foo::{baz::{qux, quux}, bar}; | |||
682 | ", | 689 | ", |
683 | ImportGranularityGuess::Module, | 690 | ImportGranularityGuess::Module, |
684 | ); | 691 | ); |
692 | check_guess( | ||
693 | r" | ||
694 | use foo::bar::Bar; | ||
695 | use foo::baz::Baz; | ||
696 | use foo::{Foo, Qux}; | ||
697 | ", | ||
698 | ImportGranularityGuess::Module, | ||
699 | ); | ||
685 | } | 700 | } |
686 | 701 | ||
687 | #[test] | 702 | #[test] |
diff --git a/crates/ide_db/src/helpers/merge_imports.rs b/crates/ide_db/src/helpers/merge_imports.rs index 0dbabb44f..ec29476a4 100644 --- a/crates/ide_db/src/helpers/merge_imports.rs +++ b/crates/ide_db/src/helpers/merge_imports.rs | |||
@@ -124,7 +124,7 @@ fn recursive_merge( | |||
124 | .map(|tree_list| tree_list.use_trees().any(tree_is_self)) | 124 | .map(|tree_list| tree_list.use_trees().any(tree_is_self)) |
125 | .unwrap_or(false) | 125 | .unwrap_or(false) |
126 | }; | 126 | }; |
127 | match (tree_contains_self(&lhs_t), tree_contains_self(&rhs_t)) { | 127 | match (tree_contains_self(lhs_t), tree_contains_self(&rhs_t)) { |
128 | (true, false) => continue, | 128 | (true, false) => continue, |
129 | (false, true) => { | 129 | (false, true) => { |
130 | *lhs_t = rhs_t; | 130 | *lhs_t = rhs_t; |
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 1f900aef4..105607dca 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs | |||
@@ -93,6 +93,7 @@ impl RootDatabase { | |||
93 | db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); | 93 | db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); |
94 | db.set_local_roots_with_durability(Default::default(), Durability::HIGH); | 94 | db.set_local_roots_with_durability(Default::default(), Durability::HIGH); |
95 | db.set_library_roots_with_durability(Default::default(), Durability::HIGH); | 95 | db.set_library_roots_with_durability(Default::default(), Durability::HIGH); |
96 | db.set_enable_proc_attr_macros(Default::default()); | ||
96 | db.update_lru_capacity(lru_capacity); | 97 | db.update_lru_capacity(lru_capacity); |
97 | db | 98 | db |
98 | } | 99 | } |
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index 67840602b..a840e06a6 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs | |||
@@ -8,7 +8,8 @@ use std::{convert::TryInto, mem}; | |||
8 | 8 | ||
9 | use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt}; | 9 | use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt}; |
10 | use hir::{ | 10 | use hir::{ |
11 | DefWithBody, HasAttrs, HasSource, InFile, ModuleDef, ModuleSource, Semantics, Visibility, | 11 | AsAssocItem, DefWithBody, HasAttrs, HasSource, InFile, ModuleDef, ModuleSource, Semantics, |
12 | Visibility, | ||
12 | }; | 13 | }; |
13 | use once_cell::unsync::Lazy; | 14 | use once_cell::unsync::Lazy; |
14 | use rustc_hash::FxHashMap; | 15 | use rustc_hash::FxHashMap; |
@@ -303,13 +304,13 @@ impl Definition { | |||
303 | } | 304 | } |
304 | } | 305 | } |
305 | 306 | ||
306 | pub fn usages<'a>(&'a self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> { | 307 | pub fn usages<'a>(self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> { |
307 | FindUsages { def: self, sema, scope: None, include_self_kw_refs: None } | 308 | FindUsages { def: self, sema, scope: None, include_self_kw_refs: None } |
308 | } | 309 | } |
309 | } | 310 | } |
310 | 311 | ||
311 | pub struct FindUsages<'a> { | 312 | pub struct FindUsages<'a> { |
312 | def: &'a Definition, | 313 | def: Definition, |
313 | sema: &'a Semantics<'a, RootDatabase>, | 314 | sema: &'a Semantics<'a, RootDatabase>, |
314 | scope: Option<SearchScope>, | 315 | scope: Option<SearchScope>, |
315 | include_self_kw_refs: Option<hir::Type>, | 316 | include_self_kw_refs: Option<hir::Type>, |
@@ -318,7 +319,7 @@ pub struct FindUsages<'a> { | |||
318 | impl<'a> FindUsages<'a> { | 319 | impl<'a> FindUsages<'a> { |
319 | /// Enable searching for `Self` when the definition is a type. | 320 | /// Enable searching for `Self` when the definition is a type. |
320 | pub fn include_self_refs(mut self) -> FindUsages<'a> { | 321 | pub fn include_self_refs(mut self) -> FindUsages<'a> { |
321 | self.include_self_kw_refs = def_to_ty(self.sema, self.def); | 322 | self.include_self_kw_refs = def_to_ty(self.sema, &self.def); |
322 | self | 323 | self |
323 | } | 324 | } |
324 | 325 | ||
@@ -408,7 +409,7 @@ impl<'a> FindUsages<'a> { | |||
408 | if let Some(ast::NameLike::NameRef(name_ref)) = | 409 | if let Some(ast::NameLike::NameRef(name_ref)) = |
409 | sema.find_node_at_offset_with_descend(&tree, offset) | 410 | sema.find_node_at_offset_with_descend(&tree, offset) |
410 | { | 411 | { |
411 | if self.found_self_ty_name_ref(&self_ty, &name_ref, sink) { | 412 | if self.found_self_ty_name_ref(self_ty, &name_ref, sink) { |
412 | return; | 413 | return; |
413 | } | 414 | } |
414 | } | 415 | } |
@@ -423,7 +424,7 @@ impl<'a> FindUsages<'a> { | |||
423 | name_ref: &ast::NameRef, | 424 | name_ref: &ast::NameRef, |
424 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, | 425 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, |
425 | ) -> bool { | 426 | ) -> bool { |
426 | match NameRefClass::classify(self.sema, &name_ref) { | 427 | match NameRefClass::classify(self.sema, name_ref) { |
427 | Some(NameRefClass::Definition(Definition::SelfType(impl_))) | 428 | Some(NameRefClass::Definition(Definition::SelfType(impl_))) |
428 | if impl_.self_ty(self.sema.db) == *self_ty => | 429 | if impl_.self_ty(self.sema.db) == *self_ty => |
429 | { | 430 | { |
@@ -445,7 +446,7 @@ impl<'a> FindUsages<'a> { | |||
445 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, | 446 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, |
446 | ) -> bool { | 447 | ) -> bool { |
447 | match NameRefClass::classify_lifetime(self.sema, lifetime) { | 448 | match NameRefClass::classify_lifetime(self.sema, lifetime) { |
448 | Some(NameRefClass::Definition(def)) if &def == self.def => { | 449 | Some(NameRefClass::Definition(def)) if def == self.def => { |
449 | let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax()); | 450 | let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax()); |
450 | let reference = FileReference { | 451 | let reference = FileReference { |
451 | range, | 452 | range, |
@@ -463,13 +464,13 @@ impl<'a> FindUsages<'a> { | |||
463 | name_ref: &ast::NameRef, | 464 | name_ref: &ast::NameRef, |
464 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, | 465 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, |
465 | ) -> bool { | 466 | ) -> bool { |
466 | match NameRefClass::classify(self.sema, &name_ref) { | 467 | match NameRefClass::classify(self.sema, name_ref) { |
467 | Some(NameRefClass::Definition(def)) if &def == self.def => { | 468 | Some(NameRefClass::Definition(def)) if def == self.def => { |
468 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); | 469 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); |
469 | let reference = FileReference { | 470 | let reference = FileReference { |
470 | range, | 471 | range, |
471 | name: ast::NameLike::NameRef(name_ref.clone()), | 472 | name: ast::NameLike::NameRef(name_ref.clone()), |
472 | access: reference_access(&def, &name_ref), | 473 | access: reference_access(&def, name_ref), |
473 | }; | 474 | }; |
474 | sink(file_id, reference) | 475 | sink(file_id, reference) |
475 | } | 476 | } |
@@ -479,7 +480,7 @@ impl<'a> FindUsages<'a> { | |||
479 | let reference = FileReference { | 480 | let reference = FileReference { |
480 | range, | 481 | range, |
481 | name: ast::NameLike::NameRef(name_ref.clone()), | 482 | name: ast::NameLike::NameRef(name_ref.clone()), |
482 | access: reference_access(&def, &name_ref), | 483 | access: reference_access(&def, name_ref), |
483 | }; | 484 | }; |
484 | sink(file_id, reference) | 485 | sink(file_id, reference) |
485 | } else { | 486 | } else { |
@@ -489,11 +490,9 @@ impl<'a> FindUsages<'a> { | |||
489 | Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { | 490 | Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { |
490 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); | 491 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); |
491 | let access = match self.def { | 492 | let access = match self.def { |
492 | Definition::Field(_) if &field == self.def => { | 493 | Definition::Field(_) if field == self.def => reference_access(&field, name_ref), |
493 | reference_access(&field, &name_ref) | 494 | Definition::Local(l) if local == l => { |
494 | } | 495 | reference_access(&Definition::Local(local), name_ref) |
495 | Definition::Local(l) if &local == l => { | ||
496 | reference_access(&Definition::Local(local), &name_ref) | ||
497 | } | 496 | } |
498 | _ => return false, | 497 | _ => return false, |
499 | }; | 498 | }; |
@@ -513,7 +512,7 @@ impl<'a> FindUsages<'a> { | |||
513 | match NameClass::classify(self.sema, name) { | 512 | match NameClass::classify(self.sema, name) { |
514 | Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) | 513 | Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) |
515 | if matches!( | 514 | if matches!( |
516 | self.def, Definition::Field(_) if &field_ref == self.def | 515 | self.def, Definition::Field(_) if field_ref == self.def |
517 | ) => | 516 | ) => |
518 | { | 517 | { |
519 | let FileRange { file_id, range } = self.sema.original_range(name.syntax()); | 518 | let FileRange { file_id, range } = self.sema.original_range(name.syntax()); |
@@ -525,12 +524,38 @@ impl<'a> FindUsages<'a> { | |||
525 | }; | 524 | }; |
526 | sink(file_id, reference) | 525 | sink(file_id, reference) |
527 | } | 526 | } |
528 | Some(NameClass::ConstReference(def)) if *self.def == def => { | 527 | Some(NameClass::ConstReference(def)) if self.def == def => { |
529 | let FileRange { file_id, range } = self.sema.original_range(name.syntax()); | 528 | let FileRange { file_id, range } = self.sema.original_range(name.syntax()); |
530 | let reference = | 529 | let reference = |
531 | FileReference { range, name: ast::NameLike::Name(name.clone()), access: None }; | 530 | FileReference { range, name: ast::NameLike::Name(name.clone()), access: None }; |
532 | sink(file_id, reference) | 531 | sink(file_id, reference) |
533 | } | 532 | } |
533 | // Resolve trait impl function definitions to the trait definition's version if self.def is the trait definition's | ||
534 | Some(NameClass::Definition(Definition::ModuleDef(mod_def))) => { | ||
535 | /* poor man's try block */ | ||
536 | (|| { | ||
537 | let this = match self.def { | ||
538 | Definition::ModuleDef(this) if this != mod_def => this, | ||
539 | _ => return None, | ||
540 | }; | ||
541 | let this_trait = this | ||
542 | .as_assoc_item(self.sema.db)? | ||
543 | .containing_trait_or_trait_impl(self.sema.db)?; | ||
544 | let trait_ = mod_def | ||
545 | .as_assoc_item(self.sema.db)? | ||
546 | .containing_trait_or_trait_impl(self.sema.db)?; | ||
547 | (trait_ == this_trait).then(|| { | ||
548 | let FileRange { file_id, range } = self.sema.original_range(name.syntax()); | ||
549 | let reference = FileReference { | ||
550 | range, | ||
551 | name: ast::NameLike::Name(name.clone()), | ||
552 | access: None, | ||
553 | }; | ||
554 | sink(file_id, reference) | ||
555 | }) | ||
556 | })() | ||
557 | .unwrap_or(false) | ||
558 | } | ||
534 | _ => false, | 559 | _ => false, |
535 | } | 560 | } |
536 | } | 561 | } |
diff --git a/crates/ide_db/src/symbol_index.rs b/crates/ide_db/src/symbol_index.rs index 5c372a7e5..000f87a85 100644 --- a/crates/ide_db/src/symbol_index.rs +++ b/crates/ide_db/src/symbol_index.rs | |||
@@ -197,6 +197,7 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> { | |||
197 | } | 197 | } |
198 | 198 | ||
199 | pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<FileSymbol> { | 199 | pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<FileSymbol> { |
200 | let _p = profile::span("crate_symbols").detail(|| format!("{:?}", query)); | ||
200 | // FIXME(#4842): This now depends on CrateDefMap, why not build the entire symbol index from | 201 | // FIXME(#4842): This now depends on CrateDefMap, why not build the entire symbol index from |
201 | // that instead? | 202 | // that instead? |
202 | 203 | ||
@@ -321,6 +322,7 @@ impl SymbolIndex { | |||
321 | 322 | ||
322 | impl Query { | 323 | impl Query { |
323 | pub(crate) fn search(self, indices: &[&SymbolIndex]) -> Vec<FileSymbol> { | 324 | pub(crate) fn search(self, indices: &[&SymbolIndex]) -> Vec<FileSymbol> { |
325 | let _p = profile::span("symbol_index::Query::search"); | ||
324 | let mut op = fst::map::OpBuilder::new(); | 326 | let mut op = fst::map::OpBuilder::new(); |
325 | for file_symbols in indices.iter() { | 327 | for file_symbols in indices.iter() { |
326 | let automaton = fst::automaton::Subsequence::new(&self.lowercased); | 328 | let automaton = fst::automaton::Subsequence::new(&self.lowercased); |
diff --git a/crates/ide_ssr/Cargo.toml b/crates/ide_ssr/Cargo.toml index 5d2221ebc..727d17bac 100644 --- a/crates/ide_ssr/Cargo.toml +++ b/crates/ide_ssr/Cargo.toml | |||
@@ -11,7 +11,7 @@ edition = "2018" | |||
11 | doctest = false | 11 | doctest = false |
12 | 12 | ||
13 | [dependencies] | 13 | [dependencies] |
14 | cov-mark = { version = "1.1", features = ["thread-local"] } | 14 | cov-mark = "2.0.0-pre.1" |
15 | rustc-hash = "1.1.0" | 15 | rustc-hash = "1.1.0" |
16 | itertools = "0.10.0" | 16 | itertools = "0.10.0" |
17 | 17 | ||
diff --git a/crates/ide_ssr/src/matching.rs b/crates/ide_ssr/src/matching.rs index b3072fb9f..fb92a0ccc 100644 --- a/crates/ide_ssr/src/matching.rs +++ b/crates/ide_ssr/src/matching.rs | |||
@@ -382,7 +382,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> { | |||
382 | code: Option<T>, | 382 | code: Option<T>, |
383 | ) -> Result<(), MatchFailed> { | 383 | ) -> Result<(), MatchFailed> { |
384 | match (pattern, code) { | 384 | match (pattern, code) { |
385 | (Some(p), Some(c)) => self.attempt_match_node(phase, &p.syntax(), &c.syntax()), | 385 | (Some(p), Some(c)) => self.attempt_match_node(phase, p.syntax(), c.syntax()), |
386 | (None, None) => Ok(()), | 386 | (None, None) => Ok(()), |
387 | (Some(p), None) => fail_match!("Pattern `{}` had nothing to match", p.syntax().text()), | 387 | (Some(p), None) => fail_match!("Pattern `{}` had nothing to match", p.syntax().text()), |
388 | (None, Some(c)) => { | 388 | (None, Some(c)) => { |
@@ -478,7 +478,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> { | |||
478 | if Some(first_token.text()) == next_pattern_token.as_deref() { | 478 | if Some(first_token.text()) == next_pattern_token.as_deref() { |
479 | if let Some(SyntaxElement::Node(p)) = pattern.next() { | 479 | if let Some(SyntaxElement::Node(p)) = pattern.next() { |
480 | // We have a subtree that starts with the next token in our pattern. | 480 | // We have a subtree that starts with the next token in our pattern. |
481 | self.attempt_match_token_tree(phase, &p, &n)?; | 481 | self.attempt_match_token_tree(phase, &p, n)?; |
482 | break; | 482 | break; |
483 | } | 483 | } |
484 | } | 484 | } |
@@ -609,7 +609,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> { | |||
609 | expr: &ast::Expr, | 609 | expr: &ast::Expr, |
610 | ) -> Result<usize, MatchFailed> { | 610 | ) -> Result<usize, MatchFailed> { |
611 | use hir::HirDisplay; | 611 | use hir::HirDisplay; |
612 | let code_type = self.sema.type_of_expr(&expr).ok_or_else(|| { | 612 | let code_type = self.sema.type_of_expr(expr).ok_or_else(|| { |
613 | match_error!("Failed to get receiver type for `{}`", expr.syntax().text()) | 613 | match_error!("Failed to get receiver type for `{}`", expr.syntax().text()) |
614 | })?; | 614 | })?; |
615 | // Temporary needed to make the borrow checker happy. | 615 | // Temporary needed to make the borrow checker happy. |
diff --git a/crates/ide_ssr/src/replacing.rs b/crates/ide_ssr/src/replacing.rs index c9ccc1961..9265af7c1 100644 --- a/crates/ide_ssr/src/replacing.rs +++ b/crates/ide_ssr/src/replacing.rs | |||
@@ -84,16 +84,16 @@ impl ReplacementRenderer<'_> { | |||
84 | fn render_node_or_token(&mut self, node_or_token: &SyntaxElement) { | 84 | fn render_node_or_token(&mut self, node_or_token: &SyntaxElement) { |
85 | match node_or_token { | 85 | match node_or_token { |
86 | SyntaxElement::Token(token) => { | 86 | SyntaxElement::Token(token) => { |
87 | self.render_token(&token); | 87 | self.render_token(token); |
88 | } | 88 | } |
89 | SyntaxElement::Node(child_node) => { | 89 | SyntaxElement::Node(child_node) => { |
90 | self.render_node(&child_node); | 90 | self.render_node(child_node); |
91 | } | 91 | } |
92 | } | 92 | } |
93 | } | 93 | } |
94 | 94 | ||
95 | fn render_node(&mut self, node: &SyntaxNode) { | 95 | fn render_node(&mut self, node: &SyntaxNode) { |
96 | if let Some(mod_path) = self.match_info.rendered_template_paths.get(&node) { | 96 | if let Some(mod_path) = self.match_info.rendered_template_paths.get(node) { |
97 | self.out.push_str(&mod_path.to_string()); | 97 | self.out.push_str(&mod_path.to_string()); |
98 | // Emit everything except for the segment's name-ref, since we already effectively | 98 | // Emit everything except for the segment's name-ref, since we already effectively |
99 | // emitted that as part of `mod_path`. | 99 | // emitted that as part of `mod_path`. |
@@ -107,12 +107,12 @@ impl ReplacementRenderer<'_> { | |||
107 | } | 107 | } |
108 | } | 108 | } |
109 | } else { | 109 | } else { |
110 | self.render_node_children(&node); | 110 | self.render_node_children(node); |
111 | } | 111 | } |
112 | } | 112 | } |
113 | 113 | ||
114 | fn render_token(&mut self, token: &SyntaxToken) { | 114 | fn render_token(&mut self, token: &SyntaxToken) { |
115 | if let Some(placeholder) = self.rule.get_placeholder(&token) { | 115 | if let Some(placeholder) = self.rule.get_placeholder(token) { |
116 | if let Some(placeholder_value) = | 116 | if let Some(placeholder_value) = |
117 | self.match_info.placeholder_values.get(&placeholder.ident) | 117 | self.match_info.placeholder_values.get(&placeholder.ident) |
118 | { | 118 | { |
diff --git a/crates/ide_ssr/src/resolving.rs b/crates/ide_ssr/src/resolving.rs index 541da4122..a66a7a4a8 100644 --- a/crates/ide_ssr/src/resolving.rs +++ b/crates/ide_ssr/src/resolving.rs | |||
@@ -211,7 +211,7 @@ impl<'db> ResolutionScope<'db> { | |||
211 | // First try resolving the whole path. This will work for things like | 211 | // First try resolving the whole path. This will work for things like |
212 | // `std::collections::HashMap`, but will fail for things like | 212 | // `std::collections::HashMap`, but will fail for things like |
213 | // `std::collections::HashMap::new`. | 213 | // `std::collections::HashMap::new`. |
214 | if let Some(resolution) = self.scope.speculative_resolve(&path) { | 214 | if let Some(resolution) = self.scope.speculative_resolve(path) { |
215 | return Some(resolution); | 215 | return Some(resolution); |
216 | } | 216 | } |
217 | // Resolution failed, try resolving the qualifier (e.g. `std::collections::HashMap` and if | 217 | // Resolution failed, try resolving the qualifier (e.g. `std::collections::HashMap` and if |
diff --git a/crates/ide_ssr/src/search.rs b/crates/ide_ssr/src/search.rs index 28cef742c..f2056919e 100644 --- a/crates/ide_ssr/src/search.rs +++ b/crates/ide_ssr/src/search.rs | |||
@@ -173,7 +173,7 @@ impl<'db> MatchFinder<'db> { | |||
173 | if !is_search_permitted(code) { | 173 | if !is_search_permitted(code) { |
174 | return; | 174 | return; |
175 | } | 175 | } |
176 | self.try_add_match(rule, &code, restrict_range, matches_out); | 176 | self.try_add_match(rule, code, restrict_range, matches_out); |
177 | // If we've got a macro call, we already tried matching it pre-expansion, which is the only | 177 | // If we've got a macro call, we already tried matching it pre-expansion, which is the only |
178 | // way to match the whole macro, now try expanding it and matching the expansion. | 178 | // way to match the whole macro, now try expanding it and matching the expansion. |
179 | if let Some(macro_call) = ast::MacroCall::cast(code.clone()) { | 179 | if let Some(macro_call) = ast::MacroCall::cast(code.clone()) { |
diff --git a/crates/ide_ssr/src/tests.rs b/crates/ide_ssr/src/tests.rs index 1d8565dc0..444c6b0af 100644 --- a/crates/ide_ssr/src/tests.rs +++ b/crates/ide_ssr/src/tests.rs | |||
@@ -75,7 +75,7 @@ pub(crate) fn single_file(code: &str) -> (ide_db::RootDatabase, FilePosition, Ve | |||
75 | match range_or_offset { | 75 | match range_or_offset { |
76 | RangeOrOffset::Range(range) => { | 76 | RangeOrOffset::Range(range) => { |
77 | position = FilePosition { file_id, offset: range.start() }; | 77 | position = FilePosition { file_id, offset: range.start() }; |
78 | selections = vec![FileRange { file_id, range: range }]; | 78 | selections = vec![FileRange { file_id, range }]; |
79 | } | 79 | } |
80 | RangeOrOffset::Offset(offset) => { | 80 | RangeOrOffset::Offset(offset) => { |
81 | position = FilePosition { file_id, offset }; | 81 | position = FilePosition { file_id, offset }; |
@@ -129,7 +129,7 @@ fn assert_matches(pattern: &str, code: &str, expected: &[&str]) { | |||
129 | let matched_strings: Vec<String> = | 129 | let matched_strings: Vec<String> = |
130 | match_finder.matches().flattened().matches.iter().map(|m| m.matched_text()).collect(); | 130 | match_finder.matches().flattened().matches.iter().map(|m| m.matched_text()).collect(); |
131 | if matched_strings != expected && !expected.is_empty() { | 131 | if matched_strings != expected && !expected.is_empty() { |
132 | print_match_debug_info(&match_finder, position.file_id, &expected[0]); | 132 | print_match_debug_info(&match_finder, position.file_id, expected[0]); |
133 | } | 133 | } |
134 | assert_eq!(matched_strings, expected); | 134 | assert_eq!(matched_strings, expected); |
135 | } | 135 | } |
diff --git a/crates/mbe/Cargo.toml b/crates/mbe/Cargo.toml index 8856787c0..f3092d9aa 100644 --- a/crates/mbe/Cargo.toml +++ b/crates/mbe/Cargo.toml | |||
@@ -10,7 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = { version = "1.1", features = ["thread-local"] } | 13 | cov-mark = "2.0.0-pre.1" |
14 | rustc-hash = "1.1.0" | 14 | rustc-hash = "1.1.0" |
15 | smallvec = "1.2.0" | 15 | smallvec = "1.2.0" |
16 | log = "0.4.8" | 16 | log = "0.4.8" |
diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs index 38707ffa5..18eb97f0d 100644 --- a/crates/mbe/src/benchmark.rs +++ b/crates/mbe/src/benchmark.rs | |||
@@ -187,7 +187,7 @@ fn invocation_fixtures(rules: &FxHashMap<String, MacroRules>) -> Vec<(String, tt | |||
187 | let a = 1664525; | 187 | let a = 1664525; |
188 | let c = 1013904223; | 188 | let c = 1013904223; |
189 | *seed = usize::wrapping_add(usize::wrapping_mul(*seed, a), c); | 189 | *seed = usize::wrapping_add(usize::wrapping_mul(*seed, a), c); |
190 | return *seed; | 190 | *seed |
191 | } | 191 | } |
192 | fn make_ident(ident: &str) -> tt::TokenTree { | 192 | fn make_ident(ident: &str) -> tt::TokenTree { |
193 | tt::Leaf::Ident(tt::Ident { id: tt::TokenId::unspecified(), text: SmolStr::new(ident) }) | 193 | tt::Leaf::Ident(tt::Ident { id: tt::TokenId::unspecified(), text: SmolStr::new(ident) }) |
diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs index 84ca3ff87..c2a9a38c9 100644 --- a/crates/mbe/src/expander/matcher.rs +++ b/crates/mbe/src/expander/matcher.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! An NFA-based parser, which is porting from rustc mbe parsing code | 1 | //! An NFA-based parser, which is porting from rustc mbe parsing code |
2 | //! | 2 | //! |
3 | //! See https://github.com/rust-lang/rust/blob/70b18bc2cbac4712020019f5bf57c00905373205/compiler/rustc_expand/src/mbe/macro_parser.rs | 3 | //! See <https://github.com/rust-lang/rust/blob/70b18bc2cbac4712020019f5bf57c00905373205/compiler/rustc_expand/src/mbe/macro_parser.rs> |
4 | //! Here is a quick intro to how the parser works, copied from rustc: | 4 | //! Here is a quick intro to how the parser works, copied from rustc: |
5 | //! | 5 | //! |
6 | //! A 'position' is a dot in the middle of a matcher, usually represented as a | 6 | //! A 'position' is a dot in the middle of a matcher, usually represented as a |
@@ -121,7 +121,7 @@ impl Match { | |||
121 | 121 | ||
122 | /// Matching errors are added to the `Match`. | 122 | /// Matching errors are added to the `Match`. |
123 | pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match { | 123 | pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match { |
124 | let mut res = match_loop(pattern, &input); | 124 | let mut res = match_loop(pattern, input); |
125 | res.bound_count = count(res.bindings.bindings()); | 125 | res.bound_count = count(res.bindings.bindings()); |
126 | return res; | 126 | return res; |
127 | 127 | ||
@@ -202,7 +202,7 @@ impl BindingsBuilder { | |||
202 | } | 202 | } |
203 | 203 | ||
204 | fn push_nested(&mut self, parent: &mut BindingsIdx, child: &BindingsIdx) { | 204 | fn push_nested(&mut self, parent: &mut BindingsIdx, child: &BindingsIdx) { |
205 | let BindingsIdx(idx, nidx) = self.copy(&child); | 205 | let BindingsIdx(idx, nidx) = self.copy(child); |
206 | self.nodes[parent.0].push(LinkNode::Node(Rc::new(BindingKind::Nested(idx, nidx)))); | 206 | self.nodes[parent.0].push(LinkNode::Node(Rc::new(BindingKind::Nested(idx, nidx)))); |
207 | } | 207 | } |
208 | 208 | ||
@@ -219,9 +219,9 @@ impl BindingsBuilder { | |||
219 | bindings | 219 | bindings |
220 | } | 220 | } |
221 | 221 | ||
222 | fn build_inner(&self, bindings: &mut Bindings, link_nodes: &Vec<LinkNode<Rc<BindingKind>>>) { | 222 | fn build_inner(&self, bindings: &mut Bindings, link_nodes: &[LinkNode<Rc<BindingKind>>]) { |
223 | let mut nodes = Vec::new(); | 223 | let mut nodes = Vec::new(); |
224 | self.collect_nodes(&link_nodes, &mut nodes); | 224 | self.collect_nodes(link_nodes, &mut nodes); |
225 | 225 | ||
226 | for cmd in nodes { | 226 | for cmd in nodes { |
227 | match &**cmd { | 227 | match &**cmd { |
@@ -282,7 +282,7 @@ impl BindingsBuilder { | |||
282 | 282 | ||
283 | nested_refs.into_iter().for_each(|iter| { | 283 | nested_refs.into_iter().for_each(|iter| { |
284 | let mut child_bindings = Bindings::default(); | 284 | let mut child_bindings = Bindings::default(); |
285 | self.build_inner(&mut child_bindings, &iter); | 285 | self.build_inner(&mut child_bindings, iter); |
286 | nested.push(child_bindings) | 286 | nested.push(child_bindings) |
287 | }) | 287 | }) |
288 | } | 288 | } |
@@ -301,7 +301,7 @@ impl BindingsBuilder { | |||
301 | 301 | ||
302 | fn collect_nodes<'a>( | 302 | fn collect_nodes<'a>( |
303 | &'a self, | 303 | &'a self, |
304 | link_nodes: &'a Vec<LinkNode<Rc<BindingKind>>>, | 304 | link_nodes: &'a [LinkNode<Rc<BindingKind>>], |
305 | nodes: &mut Vec<&'a Rc<BindingKind>>, | 305 | nodes: &mut Vec<&'a Rc<BindingKind>>, |
306 | ) { | 306 | ) { |
307 | link_nodes.iter().for_each(|it| match it { | 307 | link_nodes.iter().for_each(|it| match it { |
@@ -417,7 +417,7 @@ fn match_loop_inner<'t>( | |||
417 | let sep_len = item.sep.as_ref().map_or(0, Separator::tt_count); | 417 | let sep_len = item.sep.as_ref().map_or(0, Separator::tt_count); |
418 | if item.sep.is_some() && sep_idx != sep_len { | 418 | if item.sep.is_some() && sep_idx != sep_len { |
419 | let sep = item.sep.as_ref().unwrap(); | 419 | let sep = item.sep.as_ref().unwrap(); |
420 | if src.clone().expect_separator(&sep, sep_idx) { | 420 | if src.clone().expect_separator(sep, sep_idx) { |
421 | item.dot.next(); | 421 | item.dot.next(); |
422 | item.sep_parsed = Some(sep_idx + 1); | 422 | item.sep_parsed = Some(sep_idx + 1); |
423 | try_push!(next_items, item); | 423 | try_push!(next_items, item); |
@@ -487,22 +487,15 @@ fn match_loop_inner<'t>( | |||
487 | item.meta_result = Some((fork, match_res)); | 487 | item.meta_result = Some((fork, match_res)); |
488 | try_push!(bb_items, item); | 488 | try_push!(bb_items, item); |
489 | } else { | 489 | } else { |
490 | bindings_builder.push_optional(&mut item.bindings, &name); | 490 | bindings_builder.push_optional(&mut item.bindings, name); |
491 | item.dot.next(); | 491 | item.dot.next(); |
492 | cur_items.push(item); | 492 | cur_items.push(item); |
493 | } | 493 | } |
494 | } | 494 | } |
495 | Some(err) => { | 495 | Some(err) => { |
496 | res.add_err(err); | 496 | res.add_err(err); |
497 | match match_res.value { | 497 | if let Some(fragment) = match_res.value { |
498 | Some(fragment) => { | 498 | bindings_builder.push_fragment(&mut item.bindings, name, fragment); |
499 | bindings_builder.push_fragment( | ||
500 | &mut item.bindings, | ||
501 | &name, | ||
502 | fragment, | ||
503 | ); | ||
504 | } | ||
505 | _ => {} | ||
506 | } | 499 | } |
507 | item.is_error = true; | 500 | item.is_error = true; |
508 | error_items.push(item); | 501 | error_items.push(item); |
@@ -511,7 +504,7 @@ fn match_loop_inner<'t>( | |||
511 | } | 504 | } |
512 | } | 505 | } |
513 | OpDelimited::Op(Op::Leaf(leaf)) => { | 506 | OpDelimited::Op(Op::Leaf(leaf)) => { |
514 | if let Err(err) = match_leaf(&leaf, &mut src.clone()) { | 507 | if let Err(err) = match_leaf(leaf, &mut src.clone()) { |
515 | res.add_err(err); | 508 | res.add_err(err); |
516 | item.is_error = true; | 509 | item.is_error = true; |
517 | } else { | 510 | } else { |
@@ -578,9 +571,9 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match { | |||
578 | ); | 571 | ); |
579 | stdx::always!(cur_items.is_empty()); | 572 | stdx::always!(cur_items.is_empty()); |
580 | 573 | ||
581 | if error_items.len() > 0 { | 574 | if !error_items.is_empty() { |
582 | error_recover_item = error_items.pop().map(|it| it.bindings); | 575 | error_recover_item = error_items.pop().map(|it| it.bindings); |
583 | } else if eof_items.len() > 0 { | 576 | } else if !eof_items.is_empty() { |
584 | error_recover_item = Some(eof_items[0].bindings.clone()); | 577 | error_recover_item = Some(eof_items[0].bindings.clone()); |
585 | } | 578 | } |
586 | 579 | ||
@@ -647,10 +640,10 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match { | |||
647 | let (iter, match_res) = item.meta_result.take().unwrap(); | 640 | let (iter, match_res) = item.meta_result.take().unwrap(); |
648 | match match_res.value { | 641 | match match_res.value { |
649 | Some(fragment) => { | 642 | Some(fragment) => { |
650 | bindings_builder.push_fragment(&mut item.bindings, &name, fragment); | 643 | bindings_builder.push_fragment(&mut item.bindings, name, fragment); |
651 | } | 644 | } |
652 | None if match_res.err.is_none() => { | 645 | None if match_res.err.is_none() => { |
653 | bindings_builder.push_optional(&mut item.bindings, &name); | 646 | bindings_builder.push_optional(&mut item.bindings, name); |
654 | } | 647 | } |
655 | _ => {} | 648 | _ => {} |
656 | } | 649 | } |
@@ -793,7 +786,7 @@ impl<'a> TtIter<'a> { | |||
793 | _ => (), | 786 | _ => (), |
794 | } | 787 | } |
795 | 788 | ||
796 | let tt = self.next().ok_or_else(|| ())?.clone(); | 789 | let tt = self.next().ok_or(())?.clone(); |
797 | let punct = match tt { | 790 | let punct = match tt { |
798 | tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if punct.spacing == tt::Spacing::Joint => { | 791 | tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if punct.spacing == tt::Spacing::Joint => { |
799 | punct | 792 | punct |
diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs index dd7fa97d7..49a137577 100644 --- a/crates/mbe/src/expander/transcriber.rs +++ b/crates/mbe/src/expander/transcriber.rs | |||
@@ -55,7 +55,7 @@ pub(super) fn transcribe( | |||
55 | template: &MetaTemplate, | 55 | template: &MetaTemplate, |
56 | bindings: &Bindings, | 56 | bindings: &Bindings, |
57 | ) -> ExpandResult<tt::Subtree> { | 57 | ) -> ExpandResult<tt::Subtree> { |
58 | let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new() }; | 58 | let mut ctx = ExpandCtx { bindings, nesting: Vec::new() }; |
59 | let mut arena: Vec<tt::TokenTree> = Vec::new(); | 59 | let mut arena: Vec<tt::TokenTree> = Vec::new(); |
60 | expand_subtree(&mut ctx, template, None, &mut arena) | 60 | expand_subtree(&mut ctx, template, None, &mut arena) |
61 | } | 61 | } |
@@ -91,12 +91,12 @@ fn expand_subtree( | |||
91 | Op::Leaf(tt) => arena.push(tt.clone().into()), | 91 | Op::Leaf(tt) => arena.push(tt.clone().into()), |
92 | Op::Subtree { tokens, delimiter } => { | 92 | Op::Subtree { tokens, delimiter } => { |
93 | let ExpandResult { value: tt, err: e } = | 93 | let ExpandResult { value: tt, err: e } = |
94 | expand_subtree(ctx, &tokens, *delimiter, arena); | 94 | expand_subtree(ctx, tokens, *delimiter, arena); |
95 | err = err.or(e); | 95 | err = err.or(e); |
96 | arena.push(tt.into()); | 96 | arena.push(tt.into()); |
97 | } | 97 | } |
98 | Op::Var { name, id, .. } => { | 98 | Op::Var { name, id, .. } => { |
99 | let ExpandResult { value: fragment, err: e } = expand_var(ctx, &name, *id); | 99 | let ExpandResult { value: fragment, err: e } = expand_var(ctx, name, *id); |
100 | err = err.or(e); | 100 | err = err.or(e); |
101 | push_fragment(arena, fragment); | 101 | push_fragment(arena, fragment); |
102 | } | 102 | } |
@@ -141,7 +141,7 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr, id: tt::TokenId) -> ExpandResult | |||
141 | .into(); | 141 | .into(); |
142 | ExpandResult::ok(Fragment::Tokens(tt)) | 142 | ExpandResult::ok(Fragment::Tokens(tt)) |
143 | } else { | 143 | } else { |
144 | ctx.bindings.get(&v, &mut ctx.nesting).map_or_else( | 144 | ctx.bindings.get(v, &mut ctx.nesting).map_or_else( |
145 | |e| ExpandResult { value: Fragment::Tokens(tt::TokenTree::empty()), err: Some(e) }, | 145 | |e| ExpandResult { value: Fragment::Tokens(tt::TokenTree::empty()), err: Some(e) }, |
146 | |b| ExpandResult::ok(b.clone()), | 146 | |b| ExpandResult::ok(b.clone()), |
147 | ) | 147 | ) |
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index b95374b76..8c8528aaf 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs | |||
@@ -280,8 +280,8 @@ impl Rule { | |||
280 | .expect_subtree() | 280 | .expect_subtree() |
281 | .map_err(|()| ParseError::Expected("expected subtree".to_string()))?; | 281 | .map_err(|()| ParseError::Expected("expected subtree".to_string()))?; |
282 | 282 | ||
283 | let lhs = MetaTemplate(parse_pattern(&lhs)?); | 283 | let lhs = MetaTemplate(parse_pattern(lhs)?); |
284 | let rhs = MetaTemplate(parse_template(&rhs)?); | 284 | let rhs = MetaTemplate(parse_template(rhs)?); |
285 | 285 | ||
286 | Ok(crate::Rule { lhs, rhs }) | 286 | Ok(crate::Rule { lhs, rhs }) |
287 | } | 287 | } |
@@ -290,13 +290,13 @@ impl Rule { | |||
290 | fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> { | 290 | fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> { |
291 | for op in pattern.iter() { | 291 | for op in pattern.iter() { |
292 | match op { | 292 | match op { |
293 | Op::Subtree { tokens, .. } => validate(&tokens)?, | 293 | Op::Subtree { tokens, .. } => validate(tokens)?, |
294 | Op::Repeat { tokens: subtree, separator, .. } => { | 294 | Op::Repeat { tokens: subtree, separator, .. } => { |
295 | // Checks that no repetition which could match an empty token | 295 | // Checks that no repetition which could match an empty token |
296 | // https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558 | 296 | // https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558 |
297 | 297 | ||
298 | if separator.is_none() { | 298 | if separator.is_none() |
299 | if subtree.iter().all(|child_op| { | 299 | && subtree.iter().all(|child_op| { |
300 | match child_op { | 300 | match child_op { |
301 | Op::Var { kind, .. } => { | 301 | Op::Var { kind, .. } => { |
302 | // vis is optional | 302 | // vis is optional |
@@ -314,9 +314,9 @@ fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> { | |||
314 | Op::Subtree { .. } => {} | 314 | Op::Subtree { .. } => {} |
315 | } | 315 | } |
316 | false | 316 | false |
317 | }) { | 317 | }) |
318 | return Err(ParseError::RepetitionEmptyTokenTree); | 318 | { |
319 | } | 319 | return Err(ParseError::RepetitionEmptyTokenTree); |
320 | } | 320 | } |
321 | validate(subtree)? | 321 | validate(subtree)? |
322 | } | 322 | } |
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs index 61b2a4955..deed884d2 100644 --- a/crates/mbe/src/parser.rs +++ b/crates/mbe/src/parser.rs | |||
@@ -42,7 +42,7 @@ impl<'a> OpDelimitedIter<'a> { | |||
42 | } | 42 | } |
43 | 43 | ||
44 | pub(crate) fn reset(&self) -> Self { | 44 | pub(crate) fn reset(&self) -> Self { |
45 | Self { inner: &self.inner, idx: 0, delimited: self.delimited } | 45 | Self { inner: self.inner, idx: 0, delimited: self.delimited } |
46 | } | 46 | } |
47 | } | 47 | } |
48 | 48 | ||
@@ -126,11 +126,11 @@ impl Separator { | |||
126 | } | 126 | } |
127 | 127 | ||
128 | pub(crate) fn parse_template(template: &tt::Subtree) -> Result<Vec<Op>, ParseError> { | 128 | pub(crate) fn parse_template(template: &tt::Subtree) -> Result<Vec<Op>, ParseError> { |
129 | parse_inner(&template, Mode::Template).into_iter().collect() | 129 | parse_inner(template, Mode::Template).into_iter().collect() |
130 | } | 130 | } |
131 | 131 | ||
132 | pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Result<Vec<Op>, ParseError> { | 132 | pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Result<Vec<Op>, ParseError> { |
133 | parse_inner(&pattern, Mode::Pattern).into_iter().collect() | 133 | parse_inner(pattern, Mode::Pattern).into_iter().collect() |
134 | } | 134 | } |
135 | 135 | ||
136 | #[derive(Clone, Copy)] | 136 | #[derive(Clone, Copy)] |
@@ -140,7 +140,7 @@ enum Mode { | |||
140 | } | 140 | } |
141 | 141 | ||
142 | fn parse_inner(tt: &tt::Subtree, mode: Mode) -> Vec<Result<Op, ParseError>> { | 142 | fn parse_inner(tt: &tt::Subtree, mode: Mode) -> Vec<Result<Op, ParseError>> { |
143 | let mut src = TtIter::new(&tt); | 143 | let mut src = TtIter::new(tt); |
144 | std::iter::from_fn(move || { | 144 | std::iter::from_fn(move || { |
145 | let first = src.next()?; | 145 | let first = src.next()?; |
146 | Some(next_op(first, &mut src, mode)) | 146 | Some(next_op(first, &mut src, mode)) |
@@ -171,7 +171,7 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul | |||
171 | match second { | 171 | match second { |
172 | tt::TokenTree::Subtree(subtree) => { | 172 | tt::TokenTree::Subtree(subtree) => { |
173 | let (separator, kind) = parse_repeat(src)?; | 173 | let (separator, kind) = parse_repeat(src)?; |
174 | let tokens = parse_inner(&subtree, mode) | 174 | let tokens = parse_inner(subtree, mode) |
175 | .into_iter() | 175 | .into_iter() |
176 | .collect::<Result<Vec<Op>, ParseError>>()?; | 176 | .collect::<Result<Vec<Op>, ParseError>>()?; |
177 | Op::Repeat { tokens: MetaTemplate(tokens), separator, kind } | 177 | Op::Repeat { tokens: MetaTemplate(tokens), separator, kind } |
@@ -191,7 +191,7 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul | |||
191 | Op::Var { name, kind, id } | 191 | Op::Var { name, kind, id } |
192 | } | 192 | } |
193 | tt::Leaf::Literal(lit) => { | 193 | tt::Leaf::Literal(lit) => { |
194 | if is_boolean_literal(&lit) { | 194 | if is_boolean_literal(lit) { |
195 | let name = lit.text.clone(); | 195 | let name = lit.text.clone(); |
196 | let kind = eat_fragment_kind(src, mode)?; | 196 | let kind = eat_fragment_kind(src, mode)?; |
197 | let id = lit.id; | 197 | let id = lit.id; |
@@ -206,14 +206,14 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul | |||
206 | tt::TokenTree::Leaf(tt) => Op::Leaf(tt.clone()), | 206 | tt::TokenTree::Leaf(tt) => Op::Leaf(tt.clone()), |
207 | tt::TokenTree::Subtree(subtree) => { | 207 | tt::TokenTree::Subtree(subtree) => { |
208 | let tokens = | 208 | let tokens = |
209 | parse_inner(&subtree, mode).into_iter().collect::<Result<Vec<Op>, ParseError>>()?; | 209 | parse_inner(subtree, mode).into_iter().collect::<Result<Vec<Op>, ParseError>>()?; |
210 | Op::Subtree { tokens: MetaTemplate(tokens), delimiter: subtree.delimiter } | 210 | Op::Subtree { tokens: MetaTemplate(tokens), delimiter: subtree.delimiter } |
211 | } | 211 | } |
212 | }; | 212 | }; |
213 | Ok(res) | 213 | Ok(res) |
214 | } | 214 | } |
215 | 215 | ||
216 | fn eat_fragment_kind<'a>(src: &mut TtIter<'a>, mode: Mode) -> Result<Option<SmolStr>, ParseError> { | 216 | fn eat_fragment_kind(src: &mut TtIter<'_>, mode: Mode) -> Result<Option<SmolStr>, ParseError> { |
217 | if let Mode::Pattern = mode { | 217 | if let Mode::Pattern = mode { |
218 | src.expect_char(':').map_err(|()| err!("bad fragment specifier 1"))?; | 218 | src.expect_char(':').map_err(|()| err!("bad fragment specifier 1"))?; |
219 | let ident = src.expect_ident().map_err(|()| err!("bad fragment specifier 1"))?; | 219 | let ident = src.expect_ident().map_err(|()| err!("bad fragment specifier 1"))?; |
diff --git a/crates/mbe/src/subtree_source.rs b/crates/mbe/src/subtree_source.rs index bde370fdb..ee80807ad 100644 --- a/crates/mbe/src/subtree_source.rs +++ b/crates/mbe/src/subtree_source.rs | |||
@@ -22,7 +22,7 @@ impl<'a> SubtreeTokenSource { | |||
22 | #[cfg(test)] | 22 | #[cfg(test)] |
23 | pub(crate) fn text(&self) -> SmolStr { | 23 | pub(crate) fn text(&self) -> SmolStr { |
24 | match self.cached.get(self.curr.1) { | 24 | match self.cached.get(self.curr.1) { |
25 | Some(ref tt) => tt.text.clone(), | 25 | Some(tt) => tt.text.clone(), |
26 | _ => SmolStr::new(""), | 26 | _ => SmolStr::new(""), |
27 | } | 27 | } |
28 | } | 28 | } |
@@ -59,7 +59,7 @@ impl<'a> SubtreeTokenSource { | |||
59 | 59 | ||
60 | current = match tt { | 60 | current = match tt { |
61 | Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => { | 61 | Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => { |
62 | cached.push(convert_leaf(&leaf)); | 62 | cached.push(convert_leaf(leaf)); |
63 | cursor.bump() | 63 | cursor.bump() |
64 | } | 64 | } |
65 | Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => { | 65 | Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => { |
@@ -114,7 +114,7 @@ impl<'a> TokenSource for SubtreeTokenSource { | |||
114 | /// Is the current token a specified keyword? | 114 | /// Is the current token a specified keyword? |
115 | fn is_keyword(&self, kw: &str) -> bool { | 115 | fn is_keyword(&self, kw: &str) -> bool { |
116 | match self.cached.get(self.curr.1) { | 116 | match self.cached.get(self.curr.1) { |
117 | Some(ref t) => t.text == *kw, | 117 | Some(t) => t.text == *kw, |
118 | _ => false, | 118 | _ => false, |
119 | } | 119 | } |
120 | } | 120 | } |
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs index b11172caf..cdc22425d 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs | |||
@@ -243,8 +243,7 @@ trait TokenConvertor { | |||
243 | type Token: SrcToken; | 243 | type Token: SrcToken; |
244 | 244 | ||
245 | fn go(&mut self) -> tt::Subtree { | 245 | fn go(&mut self) -> tt::Subtree { |
246 | let mut subtree = tt::Subtree::default(); | 246 | let mut subtree = tt::Subtree { delimiter: None, ..Default::default() }; |
247 | subtree.delimiter = None; | ||
248 | while self.peek().is_some() { | 247 | while self.peek().is_some() { |
249 | self.collect_leaf(&mut subtree.token_trees); | 248 | self.collect_leaf(&mut subtree.token_trees); |
250 | } | 249 | } |
@@ -506,7 +505,7 @@ impl TokenConvertor for Convertor { | |||
506 | 505 | ||
507 | fn peek(&self) -> Option<Self::Token> { | 506 | fn peek(&self) -> Option<Self::Token> { |
508 | if let Some((punct, mut offset)) = self.punct_offset.clone() { | 507 | if let Some((punct, mut offset)) = self.punct_offset.clone() { |
509 | offset = offset + TextSize::of('.'); | 508 | offset += TextSize::of('.'); |
510 | if usize::from(offset) < punct.text().len() { | 509 | if usize::from(offset) < punct.text().len() { |
511 | return Some(SynToken::Punch(punct, offset)); | 510 | return Some(SynToken::Punch(punct, offset)); |
512 | } | 511 | } |
@@ -634,7 +633,7 @@ impl<'a> TreeSink for TtTreeSink<'a> { | |||
634 | } | 633 | } |
635 | } | 634 | } |
636 | }; | 635 | }; |
637 | self.buf += &text; | 636 | self.buf += text; |
638 | self.text_pos += TextSize::of(text); | 637 | self.text_pos += TextSize::of(text); |
639 | } | 638 | } |
640 | 639 | ||
diff --git a/crates/mbe/src/tests/expand.rs b/crates/mbe/src/tests/expand.rs index 5f173f513..c788e427e 100644 --- a/crates/mbe/src/tests/expand.rs +++ b/crates/mbe/src/tests/expand.rs | |||
@@ -490,7 +490,7 @@ [email protected] | |||
490 | 490 | ||
491 | fn to_subtree(tt: &tt::TokenTree) -> &tt::Subtree { | 491 | fn to_subtree(tt: &tt::TokenTree) -> &tt::Subtree { |
492 | if let tt::TokenTree::Subtree(subtree) = tt { | 492 | if let tt::TokenTree::Subtree(subtree) = tt { |
493 | return &subtree; | 493 | return subtree; |
494 | } | 494 | } |
495 | unreachable!("It is not a subtree"); | 495 | unreachable!("It is not a subtree"); |
496 | } | 496 | } |
@@ -1846,16 +1846,17 @@ fn test_no_space_after_semi_colon() { | |||
1846 | [email protected] | 1846 | [email protected] |
1847 | [email protected] "#" | 1847 | [email protected] "#" |
1848 | [email protected] "[" | 1848 | [email protected] "[" |
1849 | [email protected] | 1849 | [email protected] |
1850 | [email protected] | 1850 | [email protected] |
1851 | [email protected] | 1851 | [email protected] |
1852 | [email protected] "cfg" | 1852 | [email protected] |
1853 | [email protected] | 1853 | [email protected] "cfg" |
1854 | [email protected] "(" | 1854 | [email protected] |
1855 | [email protected] "feature" | 1855 | [email protected] "(" |
1856 | [email protected] "=" | 1856 | [email protected] "feature" |
1857 | [email protected] "\"std\"" | 1857 | [email protected] "=" |
1858 | [email protected] ")" | 1858 | [email protected] "\"std\"" |
1859 | [email protected] ")" | ||
1859 | [email protected] "]" | 1860 | [email protected] "]" |
1860 | [email protected] "mod" | 1861 | [email protected] "mod" |
1861 | [email protected] | 1862 | [email protected] |
@@ -1865,16 +1866,17 @@ fn test_no_space_after_semi_colon() { | |||
1865 | [email protected] | 1866 | [email protected] |
1866 | [email protected] "#" | 1867 | [email protected] "#" |
1867 | [email protected] "[" | 1868 | [email protected] "[" |
1868 | [email protected] | 1869 | [email protected] |
1869 | [email protected] | 1870 | [email protected] |
1870 | [email protected] | 1871 | [email protected] |
1871 | [email protected] "cfg" | 1872 | [email protected] |
1872 | [email protected] | 1873 | [email protected] "cfg" |
1873 | [email protected] "(" | 1874 | [email protected] |
1874 | [email protected] "feature" | 1875 | [email protected] "(" |
1875 | [email protected] "=" | 1876 | [email protected] "feature" |
1876 | [email protected] "\"std\"" | 1877 | [email protected] "=" |
1877 | [email protected] ")" | 1878 | [email protected] "\"std\"" |
1879 | [email protected] ")" | ||
1878 | [email protected] "]" | 1880 | [email protected] "]" |
1879 | [email protected] "mod" | 1881 | [email protected] "mod" |
1880 | [email protected] | 1882 | [email protected] |
diff --git a/crates/mbe/src/tt_iter.rs b/crates/mbe/src/tt_iter.rs index 99a8d250b..5a4eca7bf 100644 --- a/crates/mbe/src/tt_iter.rs +++ b/crates/mbe/src/tt_iter.rs | |||
@@ -115,7 +115,7 @@ impl<'a> TtIter<'a> { | |||
115 | } | 115 | } |
116 | } | 116 | } |
117 | 117 | ||
118 | let buffer = TokenBuffer::from_tokens(&self.inner.as_slice()); | 118 | let buffer = TokenBuffer::from_tokens(self.inner.as_slice()); |
119 | let mut src = SubtreeTokenSource::new(&buffer); | 119 | let mut src = SubtreeTokenSource::new(&buffer); |
120 | let mut sink = OffsetTokenSink { cursor: buffer.begin(), error: false }; | 120 | let mut sink = OffsetTokenSink { cursor: buffer.begin(), error: false }; |
121 | 121 | ||
@@ -138,7 +138,7 @@ impl<'a> TtIter<'a> { | |||
138 | } | 138 | } |
139 | } | 139 | } |
140 | self.inner = self.inner.as_slice()[res.len()..].iter(); | 140 | self.inner = self.inner.as_slice()[res.len()..].iter(); |
141 | if res.len() == 0 && err.is_none() { | 141 | if res.is_empty() && err.is_none() { |
142 | err = Some(err!("no tokens consumed")); | 142 | err = Some(err!("no tokens consumed")); |
143 | } | 143 | } |
144 | let res = match res.len() { | 144 | let res = match res.len() { |
diff --git a/crates/parser/src/grammar/attributes.rs b/crates/parser/src/grammar/attributes.rs index b8242cd2f..a44c5e484 100644 --- a/crates/parser/src/grammar/attributes.rs +++ b/crates/parser/src/grammar/attributes.rs | |||
@@ -13,6 +13,7 @@ pub(super) fn outer_attrs(p: &mut Parser) { | |||
13 | } | 13 | } |
14 | 14 | ||
15 | pub(super) fn meta(p: &mut Parser) { | 15 | pub(super) fn meta(p: &mut Parser) { |
16 | let meta = p.start(); | ||
16 | paths::use_path(p); | 17 | paths::use_path(p); |
17 | 18 | ||
18 | match p.current() { | 19 | match p.current() { |
@@ -25,6 +26,8 @@ pub(super) fn meta(p: &mut Parser) { | |||
25 | T!['('] | T!['['] | T!['{'] => items::token_tree(p), | 26 | T!['('] | T!['['] | T!['{'] => items::token_tree(p), |
26 | _ => {} | 27 | _ => {} |
27 | } | 28 | } |
29 | |||
30 | meta.complete(p, META); | ||
28 | } | 31 | } |
29 | 32 | ||
30 | fn attr(p: &mut Parser, inner: bool) { | 33 | fn attr(p: &mut Parser, inner: bool) { |
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs index 9d22e1950..686a64345 100644 --- a/crates/parser/src/grammar/expressions.rs +++ b/crates/parser/src/grammar/expressions.rs | |||
@@ -208,7 +208,7 @@ struct Restrictions { | |||
208 | 208 | ||
209 | /// Binding powers of operators for a Pratt parser. | 209 | /// Binding powers of operators for a Pratt parser. |
210 | /// | 210 | /// |
211 | /// See https://www.oilshell.org/blog/2016/11/03.html | 211 | /// See <https://www.oilshell.org/blog/2016/11/03.html> |
212 | #[rustfmt::skip] | 212 | #[rustfmt::skip] |
213 | fn current_op(p: &Parser) -> (u8, SyntaxKind) { | 213 | fn current_op(p: &Parser) -> (u8, SyntaxKind) { |
214 | const NOT_AN_OP: (u8, SyntaxKind) = (0, T![@]); | 214 | const NOT_AN_OP: (u8, SyntaxKind) = (0, T![@]); |
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs index 269f223e6..abdfca1fe 100644 --- a/crates/parser/src/grammar/expressions/atom.rs +++ b/crates/parser/src/grammar/expressions/atom.rs | |||
@@ -252,12 +252,10 @@ fn closure_expr(p: &mut Parser) -> CompletedMarker { | |||
252 | // test lambda_ret_block | 252 | // test lambda_ret_block |
253 | // fn main() { || -> i32 { 92 }(); } | 253 | // fn main() { || -> i32 { 92 }(); } |
254 | block_expr(p); | 254 | block_expr(p); |
255 | } else if p.at_ts(EXPR_FIRST) { | ||
256 | expr(p); | ||
255 | } else { | 257 | } else { |
256 | if p.at_ts(EXPR_FIRST) { | 258 | p.error("expected expression"); |
257 | expr(p); | ||
258 | } else { | ||
259 | p.error("expected expression"); | ||
260 | } | ||
261 | } | 259 | } |
262 | m.complete(p, CLOSURE_EXPR) | 260 | m.complete(p, CLOSURE_EXPR) |
263 | } | 261 | } |
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs index bcefd183a..5f10b82de 100644 --- a/crates/parser/src/syntax_kind/generated.rs +++ b/crates/parser/src/syntax_kind/generated.rs | |||
@@ -220,7 +220,7 @@ pub enum SyntaxKind { | |||
220 | ITEM_LIST, | 220 | ITEM_LIST, |
221 | ASSOC_ITEM_LIST, | 221 | ASSOC_ITEM_LIST, |
222 | ATTR, | 222 | ATTR, |
223 | META_ITEM, | 223 | META, |
224 | USE_TREE, | 224 | USE_TREE, |
225 | USE_TREE_LIST, | 225 | USE_TREE_LIST, |
226 | PATH, | 226 | PATH, |
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs index f09ad37e3..48dac14c4 100644 --- a/crates/paths/src/lib.rs +++ b/crates/paths/src/lib.rs | |||
@@ -244,7 +244,7 @@ impl RelPath { | |||
244 | } | 244 | } |
245 | } | 245 | } |
246 | 246 | ||
247 | /// Taken from https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85 | 247 | /// Taken from <https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85> |
248 | fn normalize_path(path: &Path) -> PathBuf { | 248 | fn normalize_path(path: &Path) -> PathBuf { |
249 | let mut components = path.components().peekable(); | 249 | let mut components = path.components().peekable(); |
250 | let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { | 250 | let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { |
diff --git a/crates/proc_macro_api/src/msg.rs b/crates/proc_macro_api/src/msg.rs index f525df152..899895578 100644 --- a/crates/proc_macro_api/src/msg.rs +++ b/crates/proc_macro_api/src/msg.rs | |||
@@ -59,7 +59,7 @@ pub trait Message: Serialize + DeserializeOwned { | |||
59 | Ok(match read_json(inp, buf)? { | 59 | Ok(match read_json(inp, buf)? { |
60 | None => None, | 60 | None => None, |
61 | Some(text) => { | 61 | Some(text) => { |
62 | let mut deserializer = serde_json::Deserializer::from_str(&text); | 62 | let mut deserializer = serde_json::Deserializer::from_str(text); |
63 | // Note that some proc-macro generate very deep syntax tree | 63 | // Note that some proc-macro generate very deep syntax tree |
64 | // We have to disable the current limit of serde here | 64 | // We have to disable the current limit of serde here |
65 | deserializer.disable_recursion_limit(); | 65 | deserializer.disable_recursion_limit(); |
@@ -92,7 +92,7 @@ fn read_json<'a>( | |||
92 | 92 | ||
93 | // Some ill behaved macro try to use stdout for debugging | 93 | // Some ill behaved macro try to use stdout for debugging |
94 | // We ignore it here | 94 | // We ignore it here |
95 | if !buf.starts_with("{") { | 95 | if !buf.starts_with('{') { |
96 | log::error!("proc-macro tried to print : {}", buf); | 96 | log::error!("proc-macro tried to print : {}", buf); |
97 | continue; | 97 | continue; |
98 | } | 98 | } |
diff --git a/crates/proc_macro_api/src/process.rs b/crates/proc_macro_api/src/process.rs index 99d05aef3..38eac6c17 100644 --- a/crates/proc_macro_api/src/process.rs +++ b/crates/proc_macro_api/src/process.rs | |||
@@ -76,9 +76,7 @@ impl ProcMacroProcessSrv { | |||
76 | .map_err(|_| tt::ExpansionError::Unknown("proc macro server crashed".into()))?; | 76 | .map_err(|_| tt::ExpansionError::Unknown("proc macro server crashed".into()))?; |
77 | 77 | ||
78 | match res { | 78 | match res { |
79 | Some(Response::Error(err)) => { | 79 | Some(Response::Error(err)) => Err(tt::ExpansionError::ExpansionError(err.message)), |
80 | return Err(tt::ExpansionError::ExpansionError(err.message)); | ||
81 | } | ||
82 | Some(res) => Ok(res.try_into().map_err(|err| { | 80 | Some(res) => Ok(res.try_into().map_err(|err| { |
83 | tt::ExpansionError::Unknown(format!("Fail to get response, reason : {:#?} ", err)) | 81 | tt::ExpansionError::Unknown(format!("Fail to get response, reason : {:#?} ", err)) |
84 | })?), | 82 | })?), |
diff --git a/crates/proc_macro_api/src/version.rs b/crates/proc_macro_api/src/version.rs index 6dbac50b4..28a4ac086 100644 --- a/crates/proc_macro_api/src/version.rs +++ b/crates/proc_macro_api/src/version.rs | |||
@@ -95,7 +95,7 @@ fn read_section<'a>(dylib_binary: &'a [u8], section_name: &str) -> io::Result<&' | |||
95 | /// * [version string bytes encoded in utf8] <- GET THIS BOI | 95 | /// * [version string bytes encoded in utf8] <- GET THIS BOI |
96 | /// * [some more bytes that we don really care but still there] :-) | 96 | /// * [some more bytes that we don really care but still there] :-) |
97 | /// Check this issue for more about the bytes layout: | 97 | /// Check this issue for more about the bytes layout: |
98 | /// https://github.com/rust-analyzer/rust-analyzer/issues/6174 | 98 | /// <https://github.com/rust-analyzer/rust-analyzer/issues/6174> |
99 | fn read_version(dylib_path: &Path) -> io::Result<String> { | 99 | fn read_version(dylib_path: &Path) -> io::Result<String> { |
100 | let dylib_file = File::open(dylib_path)?; | 100 | let dylib_file = File::open(dylib_path)?; |
101 | let dylib_mmaped = unsafe { Mmap::map(&dylib_file) }?; | 101 | let dylib_mmaped = unsafe { Mmap::map(&dylib_file) }?; |
diff --git a/crates/proc_macro_srv/src/dylib.rs b/crates/proc_macro_srv/src/dylib.rs index cccc53220..5133e7c50 100644 --- a/crates/proc_macro_srv/src/dylib.rs +++ b/crates/proc_macro_srv/src/dylib.rs | |||
@@ -188,7 +188,9 @@ impl Expander { | |||
188 | /// Copy the dylib to temp directory to prevent locking in Windows | 188 | /// Copy the dylib to temp directory to prevent locking in Windows |
189 | #[cfg(windows)] | 189 | #[cfg(windows)] |
190 | fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> { | 190 | fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> { |
191 | use std::{ffi::OsString, time::SystemTime}; | 191 | use std::collections::hash_map::RandomState; |
192 | use std::ffi::OsString; | ||
193 | use std::hash::{BuildHasher, Hasher}; | ||
192 | 194 | ||
193 | let mut to = std::env::temp_dir(); | 195 | let mut to = std::env::temp_dir(); |
194 | 196 | ||
@@ -199,10 +201,11 @@ fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> { | |||
199 | ) | 201 | ) |
200 | })?; | 202 | })?; |
201 | 203 | ||
202 | // generate a time deps unique number | 204 | // Generate a unique number by abusing `HashMap`'s hasher. |
203 | let t = SystemTime::now().duration_since(std::time::UNIX_EPOCH).expect("Time went backwards"); | 205 | // Maybe this will also "inspire" a libs team member to finally put `rand` in libstd. |
206 | let t = RandomState::new().build_hasher().finish(); | ||
204 | 207 | ||
205 | let mut unique_name = OsString::from(t.as_millis().to_string()); | 208 | let mut unique_name = OsString::from(t.to_string()); |
206 | unique_name.push(file_name); | 209 | unique_name.push(file_name); |
207 | 210 | ||
208 | to.push(unique_name); | 211 | to.push(unique_name); |
diff --git a/crates/proc_macro_srv/src/lib.rs b/crates/proc_macro_srv/src/lib.rs index d4f04ee06..f54cbcd61 100644 --- a/crates/proc_macro_srv/src/lib.rs +++ b/crates/proc_macro_srv/src/lib.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! RA Proc Macro Server | 1 | //! RA Proc Macro Server |
2 | //! | 2 | //! |
3 | //! This library is able to call compiled Rust custom derive dynamic libraries on arbitrary code. | 3 | //! This library is able to call compiled Rust custom derive dynamic libraries on arbitrary code. |
4 | //! The general idea here is based on https://github.com/fedochet/rust-proc-macro-expander. | 4 | //! The general idea here is based on <https://github.com/fedochet/rust-proc-macro-expander>. |
5 | //! | 5 | //! |
6 | //! But we adapt it to better fit RA needs: | 6 | //! But we adapt it to better fit RA needs: |
7 | //! | 7 | //! |
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/buffer.rs b/crates/proc_macro_srv/src/proc_macro/bridge/buffer.rs index dae6ff1d1..3b2afe01f 100644 --- a/crates/proc_macro_srv/src/proc_macro/bridge/buffer.rs +++ b/crates/proc_macro_srv/src/proc_macro/bridge/buffer.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! lib-proc-macro Buffer management for same-process client<->server communication. | 1 | //! lib-proc-macro Buffer management for same-process client<->server communication. |
2 | //! | 2 | //! |
3 | //! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/buffer.rs | 3 | //! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/buffer.rs> |
4 | //! augmented with removing unstable features | 4 | //! augmented with removing unstable features |
5 | 5 | ||
6 | use std::io::{self, Write}; | 6 | use std::io::{self, Write}; |
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/client.rs b/crates/proc_macro_srv/src/proc_macro/bridge/client.rs index b036d4e20..c135cf7a2 100644 --- a/crates/proc_macro_srv/src/proc_macro/bridge/client.rs +++ b/crates/proc_macro_srv/src/proc_macro/bridge/client.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! lib-proc-macro Client-side types. | 1 | //! lib-proc-macro Client-side types. |
2 | //! | 2 | //! |
3 | //! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/client.rs | 3 | //! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/client.rs> |
4 | //! augmented with removing unstable features | 4 | //! augmented with removing unstable features |
5 | 5 | ||
6 | use super::*; | 6 | use super::*; |
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/closure.rs b/crates/proc_macro_srv/src/proc_macro/bridge/closure.rs index 273a97715..f5b6d897e 100644 --- a/crates/proc_macro_srv/src/proc_macro/bridge/closure.rs +++ b/crates/proc_macro_srv/src/proc_macro/bridge/closure.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! lib-proc-macro Closure type (equivalent to `&mut dyn FnMut(A) -> R`) that's `repr(C)`. | 1 | //! lib-proc-macro Closure type (equivalent to `&mut dyn FnMut(A) -> R`) that's `repr(C)`. |
2 | //! | 2 | //! |
3 | //! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/closure.rs# | 3 | //! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/closure.rs> |
4 | //! augmented with removing unstable features | 4 | //! augmented with removing unstable features |
5 | 5 | ||
6 | #[repr(C)] | 6 | #[repr(C)] |
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/handle.rs b/crates/proc_macro_srv/src/proc_macro/bridge/handle.rs index a2f77b5ac..d2a65d249 100644 --- a/crates/proc_macro_srv/src/proc_macro/bridge/handle.rs +++ b/crates/proc_macro_srv/src/proc_macro/bridge/handle.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! lib-proc-macro Server-side handles and storage for per-handle data. | 1 | //! lib-proc-macro Server-side handles and storage for per-handle data. |
2 | //! | 2 | //! |
3 | //! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/handle.rs | 3 | //! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/handle.rs> |
4 | //! augmented with removing unstable features | 4 | //! augmented with removing unstable features |
5 | 5 | ||
6 | use std::collections::{BTreeMap, HashMap}; | 6 | use std::collections::{BTreeMap, HashMap}; |
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs b/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs index e67902682..375396d1b 100644 --- a/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs +++ b/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! lib-proc-macro Internal interface for communicating between a `proc_macro` client | 1 | //! lib-proc-macro Internal interface for communicating between a `proc_macro` client |
2 | //! | 2 | //! |
3 | //! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/mod.rs | 3 | //! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/mod.rs> |
4 | //! augmented with removing unstable features | 4 | //! augmented with removing unstable features |
5 | //! | 5 | //! |
6 | //! Internal interface for communicating between a `proc_macro` client | 6 | //! Internal interface for communicating between a `proc_macro` client |
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs b/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs index bd1e7c2fc..69928ec84 100644 --- a/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs +++ b/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! lib-proc-macro Serialization for client-server communication. | 1 | //! lib-proc-macro Serialization for client-server communication. |
2 | //! | 2 | //! |
3 | //! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/rpc.rs | 3 | //! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/rpc.rs> |
4 | //! augmented with removing unstable features | 4 | //! augmented with removing unstable features |
5 | //! | 5 | //! |
6 | //! Serialization for client-server communication. | 6 | //! Serialization for client-server communication. |
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/scoped_cell.rs b/crates/proc_macro_srv/src/proc_macro/bridge/scoped_cell.rs index 6ef7ea43c..0436bc418 100644 --- a/crates/proc_macro_srv/src/proc_macro/bridge/scoped_cell.rs +++ b/crates/proc_macro_srv/src/proc_macro/bridge/scoped_cell.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! lib-proc-macro `Cell` variant for (scoped) existential lifetimes. | 1 | //! lib-proc-macro `Cell` variant for (scoped) existential lifetimes. |
2 | //! | 2 | //! |
3 | //! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/scoped_cell.rs#L1 | 3 | //! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/scoped_cell.rs#L1> |
4 | //! augmented with removing unstable features | 4 | //! augmented with removing unstable features |
5 | 5 | ||
6 | use std::cell::Cell; | 6 | use std::cell::Cell; |
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/server.rs b/crates/proc_macro_srv/src/proc_macro/bridge/server.rs index 88fbdc078..cc9afd84b 100644 --- a/crates/proc_macro_srv/src/proc_macro/bridge/server.rs +++ b/crates/proc_macro_srv/src/proc_macro/bridge/server.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! lib-proc-macro server-side traits | 1 | //! lib-proc-macro server-side traits |
2 | //! | 2 | //! |
3 | //! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/server.rs | 3 | //! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/server.rs> |
4 | //! augmented with removing unstable features | 4 | //! augmented with removing unstable features |
5 | 5 | ||
6 | use super::*; | 6 | use super::*; |
diff --git a/crates/proc_macro_srv/src/proc_macro/diagnostic.rs b/crates/proc_macro_srv/src/proc_macro/diagnostic.rs index 55d93917c..3c5b7bc01 100644 --- a/crates/proc_macro_srv/src/proc_macro/diagnostic.rs +++ b/crates/proc_macro_srv/src/proc_macro/diagnostic.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! lib-proc-macro diagnostic | 1 | //! lib-proc-macro diagnostic |
2 | //! | 2 | //! |
3 | //! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/diagnostic.rs | 3 | //! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/diagnostic.rs> |
4 | //! augmented with removing unstable features | 4 | //! augmented with removing unstable features |
5 | 5 | ||
6 | use crate::proc_macro::Span; | 6 | use crate::proc_macro::Span; |
@@ -91,7 +91,7 @@ impl<'a> Iterator for Children<'a> { | |||
91 | impl Diagnostic { | 91 | impl Diagnostic { |
92 | /// Creates a new diagnostic with the given `level` and `message`. | 92 | /// Creates a new diagnostic with the given `level` and `message`. |
93 | pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic { | 93 | pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic { |
94 | Diagnostic { level: level, message: message.into(), spans: vec![], children: vec![] } | 94 | Diagnostic { level, message: message.into(), spans: vec![], children: vec![] } |
95 | } | 95 | } |
96 | 96 | ||
97 | /// Creates a new diagnostic with the given `level` and `message` pointing to | 97 | /// Creates a new diagnostic with the given `level` and `message` pointing to |
@@ -101,12 +101,7 @@ impl Diagnostic { | |||
101 | S: MultiSpan, | 101 | S: MultiSpan, |
102 | T: Into<String>, | 102 | T: Into<String>, |
103 | { | 103 | { |
104 | Diagnostic { | 104 | Diagnostic { level, message: message.into(), spans: spans.into_spans(), children: vec![] } |
105 | level: level, | ||
106 | message: message.into(), | ||
107 | spans: spans.into_spans(), | ||
108 | children: vec![], | ||
109 | } | ||
110 | } | 105 | } |
111 | 106 | ||
112 | diagnostic_child_methods!(span_error, error, Level::Error); | 107 | diagnostic_child_methods!(span_error, error, Level::Error); |
diff --git a/crates/proc_macro_srv/src/proc_macro/mod.rs b/crates/proc_macro_srv/src/proc_macro/mod.rs index fc6e7344f..b7c1d04b5 100644 --- a/crates/proc_macro_srv/src/proc_macro/mod.rs +++ b/crates/proc_macro_srv/src/proc_macro/mod.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! lib-proc-macro main module | 1 | //! lib-proc-macro main module |
2 | //! | 2 | //! |
3 | //! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/lib.rs | 3 | //! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/lib.rs> |
4 | //! augmented with removing unstable features | 4 | //! augmented with removing unstable features |
5 | 5 | ||
6 | // NOTE(@edwin0cheng): | 6 | // NOTE(@edwin0cheng): |
diff --git a/crates/proc_macro_srv/src/rustc_server.rs b/crates/proc_macro_srv/src/rustc_server.rs index 5d765f6e2..65ca3eb6c 100644 --- a/crates/proc_macro_srv/src/rustc_server.rs +++ b/crates/proc_macro_srv/src/rustc_server.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! Rustc proc-macro server implementation with tt | 1 | //! Rustc proc-macro server implementation with tt |
2 | //! | 2 | //! |
3 | //! Based on idea from https://github.com/fedochet/rust-proc-macro-expander | 3 | //! Based on idea from <https://github.com/fedochet/rust-proc-macro-expander> |
4 | //! The lib-proc-macro server backend is `TokenStream`-agnostic, such that | 4 | //! The lib-proc-macro server backend is `TokenStream`-agnostic, such that |
5 | //! we could provide any TokenStream implementation. | 5 | //! we could provide any TokenStream implementation. |
6 | //! The original idea from fedochet is using proc-macro2 as backend, | 6 | //! The original idea from fedochet is using proc-macro2 as backend, |
@@ -539,7 +539,7 @@ impl server::Literal for Rustc { | |||
539 | } else { | 539 | } else { |
540 | n.parse::<u128>().unwrap().to_string() | 540 | n.parse::<u128>().unwrap().to_string() |
541 | }; | 541 | }; |
542 | return Literal { text: n.into(), id: tt::TokenId::unspecified() }; | 542 | Literal { text: n.into(), id: tt::TokenId::unspecified() } |
543 | } | 543 | } |
544 | 544 | ||
545 | fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal { | 545 | fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal { |
diff --git a/crates/proc_macro_srv/src/tests/utils.rs b/crates/proc_macro_srv/src/tests/utils.rs index 2e950c113..2c093aa0a 100644 --- a/crates/proc_macro_srv/src/tests/utils.rs +++ b/crates/proc_macro_srv/src/tests/utils.rs | |||
@@ -7,35 +7,8 @@ use proc_macro_api::ListMacrosTask; | |||
7 | use std::str::FromStr; | 7 | use std::str::FromStr; |
8 | 8 | ||
9 | pub mod fixtures { | 9 | pub mod fixtures { |
10 | use cargo_metadata::Message; | ||
11 | use std::path::PathBuf; | ||
12 | use std::process::Command; | ||
13 | |||
14 | // Use current project metadata to get the proc-macro dylib path | ||
15 | pub fn proc_macro_test_dylib_path() -> std::path::PathBuf { | 10 | pub fn proc_macro_test_dylib_path() -> std::path::PathBuf { |
16 | let name = "proc_macro_test"; | 11 | proc_macro_test::PROC_MACRO_TEST_LOCATION.into() |
17 | let version = "0.0.0"; | ||
18 | let command = Command::new(toolchain::cargo()) | ||
19 | .args(&["check", "--tests", "--message-format", "json"]) | ||
20 | .output() | ||
21 | .unwrap() | ||
22 | .stdout; | ||
23 | |||
24 | for message in Message::parse_stream(command.as_slice()) { | ||
25 | match message.unwrap() { | ||
26 | Message::CompilerArtifact(artifact) => { | ||
27 | if artifact.target.kind.contains(&"proc-macro".to_string()) { | ||
28 | let repr = format!("{} {}", name, version); | ||
29 | if artifact.package_id.repr.starts_with(&repr) { | ||
30 | return PathBuf::from(&artifact.filenames[0]); | ||
31 | } | ||
32 | } | ||
33 | } | ||
34 | _ => (), // Unknown message | ||
35 | } | ||
36 | } | ||
37 | |||
38 | panic!("No proc-macro dylib for {} found!", name); | ||
39 | } | 12 | } |
40 | } | 13 | } |
41 | 14 | ||
diff --git a/crates/proc_macro_test/Cargo.toml b/crates/proc_macro_test/Cargo.toml index 753443be2..1a88e361e 100644 --- a/crates/proc_macro_test/Cargo.toml +++ b/crates/proc_macro_test/Cargo.toml | |||
@@ -8,4 +8,8 @@ publish = false | |||
8 | 8 | ||
9 | [lib] | 9 | [lib] |
10 | doctest = false | 10 | doctest = false |
11 | proc-macro = true | 11 | |
12 | [build-dependencies] | ||
13 | proc_macro_test_impl = { path = "imp", version = "0.0.0" } | ||
14 | toolchain = { path = "../toolchain", version = "0.0.0" } | ||
15 | cargo_metadata = "0.13" | ||
diff --git a/crates/proc_macro_test/build.rs b/crates/proc_macro_test/build.rs new file mode 100644 index 000000000..4653a93dd --- /dev/null +++ b/crates/proc_macro_test/build.rs | |||
@@ -0,0 +1,48 @@ | |||
1 | //! This will build the proc macro in `imp`, and copy the resulting dylib artifact into the | ||
2 | //! `OUT_DIR`. | ||
3 | //! | ||
4 | //! `proc_macro_test` itself contains only a path to that artifact. | ||
5 | |||
6 | use std::{ | ||
7 | env, fs, | ||
8 | path::{Path, PathBuf}, | ||
9 | process::Command, | ||
10 | }; | ||
11 | |||
12 | use cargo_metadata::Message; | ||
13 | |||
14 | fn main() { | ||
15 | let out_dir = env::var_os("OUT_DIR").unwrap(); | ||
16 | let out_dir = Path::new(&out_dir); | ||
17 | |||
18 | let name = "proc_macro_test_impl"; | ||
19 | let version = "0.0.0"; | ||
20 | let output = Command::new(toolchain::cargo()) | ||
21 | .current_dir("imp") | ||
22 | .args(&["build", "-p", "proc_macro_test_impl", "--message-format", "json"]) | ||
23 | .output() | ||
24 | .unwrap(); | ||
25 | assert!(output.status.success()); | ||
26 | |||
27 | let mut artifact_path = None; | ||
28 | for message in Message::parse_stream(output.stdout.as_slice()) { | ||
29 | match message.unwrap() { | ||
30 | Message::CompilerArtifact(artifact) => { | ||
31 | if artifact.target.kind.contains(&"proc-macro".to_string()) { | ||
32 | let repr = format!("{} {}", name, version); | ||
33 | if artifact.package_id.repr.starts_with(&repr) { | ||
34 | artifact_path = Some(PathBuf::from(&artifact.filenames[0])); | ||
35 | } | ||
36 | } | ||
37 | } | ||
38 | _ => (), // Unknown message | ||
39 | } | ||
40 | } | ||
41 | |||
42 | let src_path = artifact_path.expect("no dylib for proc_macro_test_impl found"); | ||
43 | let dest_path = out_dir.join(src_path.file_name().unwrap()); | ||
44 | fs::copy(src_path, &dest_path).unwrap(); | ||
45 | |||
46 | let info_path = out_dir.join("proc_macro_test_location.txt"); | ||
47 | fs::write(info_path, dest_path.to_str().unwrap()).unwrap(); | ||
48 | } | ||
diff --git a/crates/proc_macro_test/imp/.gitignore b/crates/proc_macro_test/imp/.gitignore new file mode 100644 index 000000000..2c96eb1b6 --- /dev/null +++ b/crates/proc_macro_test/imp/.gitignore | |||
@@ -0,0 +1,2 @@ | |||
1 | target/ | ||
2 | Cargo.lock | ||
diff --git a/crates/proc_macro_test/imp/Cargo.toml b/crates/proc_macro_test/imp/Cargo.toml new file mode 100644 index 000000000..1c2e75401 --- /dev/null +++ b/crates/proc_macro_test/imp/Cargo.toml | |||
@@ -0,0 +1,17 @@ | |||
1 | [package] | ||
2 | name = "proc_macro_test_impl" | ||
3 | version = "0.0.0" | ||
4 | license = "MIT OR Apache-2.0" | ||
5 | authors = ["rust-analyzer developers"] | ||
6 | edition = "2018" | ||
7 | publish = false | ||
8 | |||
9 | [lib] | ||
10 | doctest = false | ||
11 | proc-macro = true | ||
12 | |||
13 | [workspace] | ||
14 | |||
15 | [dependencies] | ||
16 | # this crate should not have any dependencies, since it uses its own workspace, | ||
17 | # and its own `Cargo.lock` | ||
diff --git a/crates/proc_macro_test/imp/src/lib.rs b/crates/proc_macro_test/imp/src/lib.rs new file mode 100644 index 000000000..4b26d2472 --- /dev/null +++ b/crates/proc_macro_test/imp/src/lib.rs | |||
@@ -0,0 +1,48 @@ | |||
1 | //! Exports a few trivial procedural macros for testing. | ||
2 | |||
3 | use proc_macro::TokenStream; | ||
4 | |||
5 | #[proc_macro] | ||
6 | pub fn fn_like_noop(args: TokenStream) -> TokenStream { | ||
7 | args | ||
8 | } | ||
9 | |||
10 | #[proc_macro] | ||
11 | pub fn fn_like_panic(args: TokenStream) -> TokenStream { | ||
12 | panic!("fn_like_panic!({})", args); | ||
13 | } | ||
14 | |||
15 | #[proc_macro] | ||
16 | pub fn fn_like_error(args: TokenStream) -> TokenStream { | ||
17 | format!("compile_error!(\"fn_like_error!({})\");", args).parse().unwrap() | ||
18 | } | ||
19 | |||
20 | #[proc_macro_attribute] | ||
21 | pub fn attr_noop(_args: TokenStream, item: TokenStream) -> TokenStream { | ||
22 | item | ||
23 | } | ||
24 | |||
25 | #[proc_macro_attribute] | ||
26 | pub fn attr_panic(args: TokenStream, item: TokenStream) -> TokenStream { | ||
27 | panic!("#[attr_panic {}] {}", args, item); | ||
28 | } | ||
29 | |||
30 | #[proc_macro_attribute] | ||
31 | pub fn attr_error(args: TokenStream, item: TokenStream) -> TokenStream { | ||
32 | format!("compile_error!(\"#[attr_error({})] {}\");", args, item).parse().unwrap() | ||
33 | } | ||
34 | |||
35 | #[proc_macro_derive(DeriveEmpty)] | ||
36 | pub fn derive_empty(_item: TokenStream) -> TokenStream { | ||
37 | TokenStream::new() | ||
38 | } | ||
39 | |||
40 | #[proc_macro_derive(DerivePanic)] | ||
41 | pub fn derive_panic(item: TokenStream) -> TokenStream { | ||
42 | panic!("#[derive(DerivePanic)] {}", item); | ||
43 | } | ||
44 | |||
45 | #[proc_macro_derive(DeriveError)] | ||
46 | pub fn derive_error(item: TokenStream) -> TokenStream { | ||
47 | format!("compile_error!(\"#[derive(DeriveError)] {}\");", item).parse().unwrap() | ||
48 | } | ||
diff --git a/crates/proc_macro_test/src/lib.rs b/crates/proc_macro_test/src/lib.rs index 4b26d2472..2edf23a63 100644 --- a/crates/proc_macro_test/src/lib.rs +++ b/crates/proc_macro_test/src/lib.rs | |||
@@ -1,48 +1,4 @@ | |||
1 | //! Exports a few trivial procedural macros for testing. | 1 | //! Exports a few trivial procedural macros for testing. |
2 | 2 | ||
3 | use proc_macro::TokenStream; | 3 | pub static PROC_MACRO_TEST_LOCATION: &str = |
4 | 4 | include_str!(concat!(env!("OUT_DIR"), "/proc_macro_test_location.txt")); | |
5 | #[proc_macro] | ||
6 | pub fn fn_like_noop(args: TokenStream) -> TokenStream { | ||
7 | args | ||
8 | } | ||
9 | |||
10 | #[proc_macro] | ||
11 | pub fn fn_like_panic(args: TokenStream) -> TokenStream { | ||
12 | panic!("fn_like_panic!({})", args); | ||
13 | } | ||
14 | |||
15 | #[proc_macro] | ||
16 | pub fn fn_like_error(args: TokenStream) -> TokenStream { | ||
17 | format!("compile_error!(\"fn_like_error!({})\");", args).parse().unwrap() | ||
18 | } | ||
19 | |||
20 | #[proc_macro_attribute] | ||
21 | pub fn attr_noop(_args: TokenStream, item: TokenStream) -> TokenStream { | ||
22 | item | ||
23 | } | ||
24 | |||
25 | #[proc_macro_attribute] | ||
26 | pub fn attr_panic(args: TokenStream, item: TokenStream) -> TokenStream { | ||
27 | panic!("#[attr_panic {}] {}", args, item); | ||
28 | } | ||
29 | |||
30 | #[proc_macro_attribute] | ||
31 | pub fn attr_error(args: TokenStream, item: TokenStream) -> TokenStream { | ||
32 | format!("compile_error!(\"#[attr_error({})] {}\");", args, item).parse().unwrap() | ||
33 | } | ||
34 | |||
35 | #[proc_macro_derive(DeriveEmpty)] | ||
36 | pub fn derive_empty(_item: TokenStream) -> TokenStream { | ||
37 | TokenStream::new() | ||
38 | } | ||
39 | |||
40 | #[proc_macro_derive(DerivePanic)] | ||
41 | pub fn derive_panic(item: TokenStream) -> TokenStream { | ||
42 | panic!("#[derive(DerivePanic)] {}", item); | ||
43 | } | ||
44 | |||
45 | #[proc_macro_derive(DeriveError)] | ||
46 | pub fn derive_error(item: TokenStream) -> TokenStream { | ||
47 | format!("compile_error!(\"#[derive(DeriveError)] {}\");", item).parse().unwrap() | ||
48 | } | ||
diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml index 1a8c8f862..653d3d983 100644 --- a/crates/profile/Cargo.toml +++ b/crates/profile/Cargo.toml | |||
@@ -20,6 +20,9 @@ jemalloc-ctl = { version = "0.4.1", package = "tikv-jemalloc-ctl", optional = tr | |||
20 | [target.'cfg(target_os = "linux")'.dependencies] | 20 | [target.'cfg(target_os = "linux")'.dependencies] |
21 | perf-event = "0.4" | 21 | perf-event = "0.4" |
22 | 22 | ||
23 | [target.'cfg(windows)'.dependencies] | ||
24 | winapi = { version = "0.3.8", features = ["psapi"] } | ||
25 | |||
23 | [features] | 26 | [features] |
24 | cpu_profiler = [] | 27 | cpu_profiler = [] |
25 | jemalloc = ["jemalloc-ctl"] | 28 | jemalloc = ["jemalloc-ctl"] |
diff --git a/crates/profile/src/lib.rs b/crates/profile/src/lib.rs index 7cd01a7df..5ea5039db 100644 --- a/crates/profile/src/lib.rs +++ b/crates/profile/src/lib.rs | |||
@@ -50,10 +50,10 @@ impl Drop for Scope { | |||
50 | /// A wrapper around google_cpu_profiler. | 50 | /// A wrapper around google_cpu_profiler. |
51 | /// | 51 | /// |
52 | /// Usage: | 52 | /// Usage: |
53 | /// 1. Install gpref_tools (https://github.com/gperftools/gperftools), probably packaged with your Linux distro. | 53 | /// 1. Install gpref_tools (<https://github.com/gperftools/gperftools>), probably packaged with your Linux distro. |
54 | /// 2. Build with `cpu_profiler` feature. | 54 | /// 2. Build with `cpu_profiler` feature. |
55 | /// 3. Run the code, the *raw* output would be in the `./out.profile` file. | 55 | /// 3. Run the code, the *raw* output would be in the `./out.profile` file. |
56 | /// 4. Install pprof for visualization (https://github.com/google/pprof). | 56 | /// 4. Install pprof for visualization (<https://github.com/google/pprof>). |
57 | /// 5. Bump sampling frequency to once per ms: `export CPUPROFILE_FREQUENCY=1000` | 57 | /// 5. Bump sampling frequency to once per ms: `export CPUPROFILE_FREQUENCY=1000` |
58 | /// 6. Use something like `pprof -svg target/release/rust-analyzer ./out.profile` to see the results. | 58 | /// 6. Use something like `pprof -svg target/release/rust-analyzer ./out.profile` to see the results. |
59 | /// | 59 | /// |
@@ -75,7 +75,7 @@ impl Drop for Scope { | |||
75 | /// | 75 | /// |
76 | /// See this diff for how to profile completions: | 76 | /// See this diff for how to profile completions: |
77 | /// | 77 | /// |
78 | /// https://github.com/rust-analyzer/rust-analyzer/pull/5306 | 78 | /// <https://github.com/rust-analyzer/rust-analyzer/pull/5306> |
79 | #[derive(Debug)] | 79 | #[derive(Debug)] |
80 | pub struct CpuSpan { | 80 | pub struct CpuSpan { |
81 | _private: (), | 81 | _private: (), |
diff --git a/crates/profile/src/memory_usage.rs b/crates/profile/src/memory_usage.rs index 2917ded60..fbcb9e3c2 100644 --- a/crates/profile/src/memory_usage.rs +++ b/crates/profile/src/memory_usage.rs | |||
@@ -32,9 +32,23 @@ impl MemoryUsage { | |||
32 | allocated: Bytes(jemalloc_ctl::stats::allocated::read().unwrap() as isize), | 32 | allocated: Bytes(jemalloc_ctl::stats::allocated::read().unwrap() as isize), |
33 | } | 33 | } |
34 | } else if #[cfg(all(target_os = "linux", target_env = "gnu"))] { | 34 | } else if #[cfg(all(target_os = "linux", target_env = "gnu"))] { |
35 | // Note: This is incredibly slow. | 35 | memusage_linux() |
36 | let alloc = unsafe { libc::mallinfo() }.uordblks as isize; | 36 | } else if #[cfg(windows)] { |
37 | MemoryUsage { allocated: Bytes(alloc) } | 37 | // There doesn't seem to be an API for determining heap usage, so we try to |
38 | // approximate that by using the Commit Charge value. | ||
39 | |||
40 | use winapi::um::processthreadsapi::*; | ||
41 | use winapi::um::psapi::*; | ||
42 | use std::mem::{MaybeUninit, size_of}; | ||
43 | |||
44 | let proc = unsafe { GetCurrentProcess() }; | ||
45 | let mut mem_counters = MaybeUninit::uninit(); | ||
46 | let cb = size_of::<PROCESS_MEMORY_COUNTERS>(); | ||
47 | let ret = unsafe { GetProcessMemoryInfo(proc, mem_counters.as_mut_ptr(), cb as u32) }; | ||
48 | assert!(ret != 0); | ||
49 | |||
50 | let usage = unsafe { mem_counters.assume_init().PagefileUsage }; | ||
51 | MemoryUsage { allocated: Bytes(usage as isize) } | ||
38 | } else { | 52 | } else { |
39 | MemoryUsage { allocated: Bytes(0) } | 53 | MemoryUsage { allocated: Bytes(0) } |
40 | } | 54 | } |
@@ -42,6 +56,37 @@ impl MemoryUsage { | |||
42 | } | 56 | } |
43 | } | 57 | } |
44 | 58 | ||
59 | #[cfg(all(target_os = "linux", target_env = "gnu", not(feature = "jemalloc")))] | ||
60 | fn memusage_linux() -> MemoryUsage { | ||
61 | // Linux/glibc has 2 APIs for allocator introspection that we can use: mallinfo and mallinfo2. | ||
62 | // mallinfo uses `int` fields and cannot handle memory usage exceeding 2 GB. | ||
63 | // mallinfo2 is very recent, so its presence needs to be detected at runtime. | ||
64 | // Both are abysmally slow. | ||
65 | |||
66 | use std::ffi::CStr; | ||
67 | use std::sync::atomic::{AtomicUsize, Ordering}; | ||
68 | |||
69 | static MALLINFO2: AtomicUsize = AtomicUsize::new(1); | ||
70 | |||
71 | let mut mallinfo2 = MALLINFO2.load(Ordering::Relaxed); | ||
72 | if mallinfo2 == 1 { | ||
73 | let cstr = CStr::from_bytes_with_nul(b"mallinfo2\0").unwrap(); | ||
74 | mallinfo2 = unsafe { libc::dlsym(libc::RTLD_DEFAULT, cstr.as_ptr()) } as usize; | ||
75 | // NB: races don't matter here, since they'll always store the same value | ||
76 | MALLINFO2.store(mallinfo2, Ordering::Relaxed); | ||
77 | } | ||
78 | |||
79 | if mallinfo2 == 0 { | ||
80 | // mallinfo2 does not exist, use mallinfo. | ||
81 | let alloc = unsafe { libc::mallinfo() }.uordblks as isize; | ||
82 | MemoryUsage { allocated: Bytes(alloc) } | ||
83 | } else { | ||
84 | let mallinfo2: fn() -> libc::mallinfo2 = unsafe { std::mem::transmute(mallinfo2) }; | ||
85 | let alloc = mallinfo2().uordblks as isize; | ||
86 | MemoryUsage { allocated: Bytes(alloc) } | ||
87 | } | ||
88 | } | ||
89 | |||
45 | #[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] | 90 | #[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] |
46 | pub struct Bytes(isize); | 91 | pub struct Bytes(isize); |
47 | 92 | ||
diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs index 33a4f8168..53cb4bae7 100644 --- a/crates/project_model/src/build_data.rs +++ b/crates/project_model/src/build_data.rs | |||
@@ -184,7 +184,7 @@ impl WorkspaceBuildData { | |||
184 | 184 | ||
185 | // Copy-pasted from existing cargo_metadata. It seems like we | 185 | // Copy-pasted from existing cargo_metadata. It seems like we |
186 | // should be using sered_stacker here? | 186 | // should be using sered_stacker here? |
187 | let mut deserializer = serde_json::Deserializer::from_str(&line); | 187 | let mut deserializer = serde_json::Deserializer::from_str(line); |
188 | deserializer.disable_recursion_limit(); | 188 | deserializer.disable_recursion_limit(); |
189 | let message = Message::deserialize(&mut deserializer) | 189 | let message = Message::deserialize(&mut deserializer) |
190 | .unwrap_or(Message::TextLine(line.to_string())); | 190 | .unwrap_or(Message::TextLine(line.to_string())); |
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs index b8ad08364..ac079f83e 100644 --- a/crates/project_model/src/cargo_workspace.rs +++ b/crates/project_model/src/cargo_workspace.rs | |||
@@ -278,7 +278,7 @@ impl CargoWorkspace { | |||
278 | id, edition, name, manifest_path, version, metadata, .. | 278 | id, edition, name, manifest_path, version, metadata, .. |
279 | } = meta_pkg; | 279 | } = meta_pkg; |
280 | let meta = from_value::<PackageMetadata>(metadata.clone()).unwrap_or_default(); | 280 | let meta = from_value::<PackageMetadata>(metadata.clone()).unwrap_or_default(); |
281 | let is_member = ws_members.contains(&id); | 281 | let is_member = ws_members.contains(id); |
282 | let edition = edition | 282 | let edition = edition |
283 | .parse::<Edition>() | 283 | .parse::<Edition>() |
284 | .with_context(|| format!("Failed to parse edition {}", edition))?; | 284 | .with_context(|| format!("Failed to parse edition {}", edition))?; |
diff --git a/crates/project_model/src/sysroot.rs b/crates/project_model/src/sysroot.rs index 4e39d6dd3..a22f79c15 100644 --- a/crates/project_model/src/sysroot.rs +++ b/crates/project_model/src/sysroot.rs | |||
@@ -142,12 +142,12 @@ fn discover_sysroot_src_dir( | |||
142 | log::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core); | 142 | log::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core); |
143 | } | 143 | } |
144 | 144 | ||
145 | get_rust_src(&sysroot_path) | 145 | get_rust_src(sysroot_path) |
146 | .or_else(|| { | 146 | .or_else(|| { |
147 | let mut rustup = Command::new(toolchain::rustup()); | 147 | let mut rustup = Command::new(toolchain::rustup()); |
148 | rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]); | 148 | rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]); |
149 | utf8_stdout(rustup).ok()?; | 149 | utf8_stdout(rustup).ok()?; |
150 | get_rust_src(&sysroot_path) | 150 | get_rust_src(sysroot_path) |
151 | }) | 151 | }) |
152 | .ok_or_else(|| { | 152 | .ok_or_else(|| { |
153 | format_err!( | 153 | format_err!( |
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs index 84990075f..ef0f3c9e4 100644 --- a/crates/project_model/src/workspace.rs +++ b/crates/project_model/src/workspace.rs | |||
@@ -185,7 +185,7 @@ impl ProjectWorkspace { | |||
185 | 185 | ||
186 | pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> { | 186 | pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> { |
187 | let sysroot = Sysroot::discover( | 187 | let sysroot = Sysroot::discover( |
188 | &detached_files.first().ok_or_else(|| format_err!("No detached files to load"))?, | 188 | detached_files.first().ok_or_else(|| format_err!("No detached files to load"))?, |
189 | )?; | 189 | )?; |
190 | let rustc_cfg = rustc_cfg::get(None, None); | 190 | let rustc_cfg = rustc_cfg::get(None, None); |
191 | Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg }) | 191 | Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg }) |
@@ -324,7 +324,7 @@ impl ProjectWorkspace { | |||
324 | pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) { | 324 | pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) { |
325 | match self { | 325 | match self { |
326 | ProjectWorkspace::Cargo { cargo, .. } => { | 326 | ProjectWorkspace::Cargo { cargo, .. } => { |
327 | collector.add_config(&cargo.workspace_root(), cargo.build_data_config().clone()); | 327 | collector.add_config(cargo.workspace_root(), cargo.build_data_config().clone()); |
328 | } | 328 | } |
329 | _ => {} | 329 | _ => {} |
330 | } | 330 | } |
@@ -348,7 +348,7 @@ fn project_json_to_crate_graph( | |||
348 | .crates() | 348 | .crates() |
349 | .filter_map(|(crate_id, krate)| { | 349 | .filter_map(|(crate_id, krate)| { |
350 | let file_path = &krate.root_module; | 350 | let file_path = &krate.root_module; |
351 | let file_id = load(&file_path)?; | 351 | let file_id = load(file_path)?; |
352 | Some((crate_id, krate, file_id)) | 352 | Some((crate_id, krate, file_id)) |
353 | }) | 353 | }) |
354 | .map(|(crate_id, krate, file_id)| { | 354 | .map(|(crate_id, krate, file_id)| { |
@@ -534,7 +534,7 @@ fn detached_files_to_crate_graph( | |||
534 | cfg_options.extend(rustc_cfg); | 534 | cfg_options.extend(rustc_cfg); |
535 | 535 | ||
536 | for detached_file in detached_files { | 536 | for detached_file in detached_files { |
537 | let file_id = match load(&detached_file) { | 537 | let file_id = match load(detached_file) { |
538 | Some(file_id) => file_id, | 538 | Some(file_id) => file_id, |
539 | None => { | 539 | None => { |
540 | log::error!("Failed to load detached file {:?}", detached_file); | 540 | log::error!("Failed to load detached file {:?}", detached_file); |
@@ -602,7 +602,7 @@ fn handle_rustc_crates( | |||
602 | crate_graph, | 602 | crate_graph, |
603 | &rustc_workspace[pkg], | 603 | &rustc_workspace[pkg], |
604 | rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)), | 604 | rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)), |
605 | &cfg_options, | 605 | cfg_options, |
606 | proc_macro_loader, | 606 | proc_macro_loader, |
607 | file_id, | 607 | file_id, |
608 | &rustc_workspace[tgt].name, | 608 | &rustc_workspace[tgt].name, |
@@ -685,7 +685,7 @@ fn add_target_crate_root( | |||
685 | let proc_macro = build_data | 685 | let proc_macro = build_data |
686 | .as_ref() | 686 | .as_ref() |
687 | .and_then(|it| it.proc_macro_dylib_path.as_ref()) | 687 | .and_then(|it| it.proc_macro_dylib_path.as_ref()) |
688 | .map(|it| proc_macro_loader(&it)) | 688 | .map(|it| proc_macro_loader(it)) |
689 | .unwrap_or_default(); | 689 | .unwrap_or_default(); |
690 | 690 | ||
691 | let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string()); | 691 | let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string()); |
diff --git a/crates/rust-analyzer/src/bin/flags.rs b/crates/rust-analyzer/src/bin/flags.rs index 63953098a..19173241b 100644 --- a/crates/rust-analyzer/src/bin/flags.rs +++ b/crates/rust-analyzer/src/bin/flags.rs | |||
@@ -67,10 +67,10 @@ xflags::xflags! { | |||
67 | /// Don't load sysroot crates (`std`, `core` & friends). | 67 | /// Don't load sysroot crates (`std`, `core` & friends). |
68 | optional --no-sysroot | 68 | optional --no-sysroot |
69 | 69 | ||
70 | /// Load OUT_DIR values by running `cargo check` before analysis. | 70 | /// Don't run build scripts or load `OUT_DIR` values by running `cargo check` before analysis. |
71 | optional --load-output-dirs | 71 | optional --disable-build-scripts |
72 | /// Use proc-macro-srv for proc-macro expanding. | 72 | /// Don't use expand proc macros. |
73 | optional --with-proc-macro | 73 | optional --disable-proc-macros |
74 | /// Only resolve names, don't run type inference. | 74 | /// Only resolve names, don't run type inference. |
75 | optional --skip-inference | 75 | optional --skip-inference |
76 | } | 76 | } |
@@ -79,10 +79,10 @@ xflags::xflags! { | |||
79 | /// Directory with Cargo.toml. | 79 | /// Directory with Cargo.toml. |
80 | required path: PathBuf | 80 | required path: PathBuf |
81 | { | 81 | { |
82 | /// Load OUT_DIR values by running `cargo check` before analysis. | 82 | /// Don't run build scripts or load `OUT_DIR` values by running `cargo check` before analysis. |
83 | optional --load-output-dirs | 83 | optional --disable-build-scripts |
84 | /// Use proc-macro-srv for proc-macro expanding. | 84 | /// Don't use expand proc macros. |
85 | optional --with-proc-macro | 85 | optional --disable-proc-macros |
86 | } | 86 | } |
87 | 87 | ||
88 | cmd ssr | 88 | cmd ssr |
@@ -158,8 +158,8 @@ pub struct AnalysisStats { | |||
158 | pub only: Option<String>, | 158 | pub only: Option<String>, |
159 | pub with_deps: bool, | 159 | pub with_deps: bool, |
160 | pub no_sysroot: bool, | 160 | pub no_sysroot: bool, |
161 | pub load_output_dirs: bool, | 161 | pub disable_build_scripts: bool, |
162 | pub with_proc_macro: bool, | 162 | pub disable_proc_macros: bool, |
163 | pub skip_inference: bool, | 163 | pub skip_inference: bool, |
164 | } | 164 | } |
165 | 165 | ||
@@ -167,8 +167,8 @@ pub struct AnalysisStats { | |||
167 | pub struct Diagnostics { | 167 | pub struct Diagnostics { |
168 | pub path: PathBuf, | 168 | pub path: PathBuf, |
169 | 169 | ||
170 | pub load_output_dirs: bool, | 170 | pub disable_build_scripts: bool, |
171 | pub with_proc_macro: bool, | 171 | pub disable_proc_macros: bool, |
172 | } | 172 | } |
173 | 173 | ||
174 | #[derive(Debug)] | 174 | #[derive(Debug)] |
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 2b842d393..afc96505f 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs | |||
@@ -91,14 +91,14 @@ fn try_main() -> Result<()> { | |||
91 | with_deps: cmd.with_deps, | 91 | with_deps: cmd.with_deps, |
92 | no_sysroot: cmd.no_sysroot, | 92 | no_sysroot: cmd.no_sysroot, |
93 | path: cmd.path, | 93 | path: cmd.path, |
94 | load_output_dirs: cmd.load_output_dirs, | 94 | enable_build_scripts: !cmd.disable_build_scripts, |
95 | with_proc_macro: cmd.with_proc_macro, | 95 | enable_proc_macros: !cmd.disable_proc_macros, |
96 | skip_inference: cmd.skip_inference, | 96 | skip_inference: cmd.skip_inference, |
97 | } | 97 | } |
98 | .run(verbosity)?, | 98 | .run(verbosity)?, |
99 | 99 | ||
100 | flags::RustAnalyzerCmd::Diagnostics(cmd) => { | 100 | flags::RustAnalyzerCmd::Diagnostics(cmd) => { |
101 | cli::diagnostics(&cmd.path, cmd.load_output_dirs, cmd.with_proc_macro)? | 101 | cli::diagnostics(&cmd.path, !cmd.disable_build_scripts, !cmd.disable_proc_macros)? |
102 | } | 102 | } |
103 | flags::RustAnalyzerCmd::Ssr(cmd) => cli::apply_ssr_rules(cmd.rule)?, | 103 | flags::RustAnalyzerCmd::Ssr(cmd) => cli::apply_ssr_rules(cmd.rule)?, |
104 | flags::RustAnalyzerCmd::Search(cmd) => cli::search_for_patterns(cmd.pattern, cmd.debug)?, | 104 | flags::RustAnalyzerCmd::Search(cmd) => cli::search_for_patterns(cmd.pattern, cmd.debug)?, |
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs index f4cd43448..5d8547152 100644 --- a/crates/rust-analyzer/src/cargo_target_spec.rs +++ b/crates/rust-analyzer/src/cargo_target_spec.rs | |||
@@ -123,7 +123,7 @@ impl CargoTargetSpec { | |||
123 | let res = CargoTargetSpec { | 123 | let res = CargoTargetSpec { |
124 | workspace_root: cargo_ws.workspace_root().to_path_buf(), | 124 | workspace_root: cargo_ws.workspace_root().to_path_buf(), |
125 | cargo_toml: package_data.manifest.clone(), | 125 | cargo_toml: package_data.manifest.clone(), |
126 | package: cargo_ws.package_flag(&package_data), | 126 | package: cargo_ws.package_flag(package_data), |
127 | target: target_data.name.clone(), | 127 | target: target_data.name.clone(), |
128 | target_kind: target_data.kind, | 128 | target_kind: target_data.kind, |
129 | }; | 129 | }; |
diff --git a/crates/rust-analyzer/src/cli.rs b/crates/rust-analyzer/src/cli.rs index 76b666dc2..25ebcc0ec 100644 --- a/crates/rust-analyzer/src/cli.rs +++ b/crates/rust-analyzer/src/cli.rs | |||
@@ -16,7 +16,6 @@ use vfs::Vfs; | |||
16 | pub use self::{ | 16 | pub use self::{ |
17 | analysis_stats::AnalysisStatsCmd, | 17 | analysis_stats::AnalysisStatsCmd, |
18 | diagnostics::diagnostics, | 18 | diagnostics::diagnostics, |
19 | load_cargo::{load_workspace, load_workspace_at, LoadCargoConfig}, | ||
20 | ssr::{apply_ssr_rules, search_for_patterns}, | 19 | ssr::{apply_ssr_rules, search_for_patterns}, |
21 | }; | 20 | }; |
22 | 21 | ||
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 14dbbb20d..843c0ca37 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs | |||
@@ -51,8 +51,8 @@ pub struct AnalysisStatsCmd { | |||
51 | pub with_deps: bool, | 51 | pub with_deps: bool, |
52 | pub no_sysroot: bool, | 52 | pub no_sysroot: bool, |
53 | pub path: PathBuf, | 53 | pub path: PathBuf, |
54 | pub load_output_dirs: bool, | 54 | pub enable_build_scripts: bool, |
55 | pub with_proc_macro: bool, | 55 | pub enable_proc_macros: bool, |
56 | pub skip_inference: bool, | 56 | pub skip_inference: bool, |
57 | } | 57 | } |
58 | 58 | ||
@@ -67,9 +67,10 @@ impl AnalysisStatsCmd { | |||
67 | let mut cargo_config = CargoConfig::default(); | 67 | let mut cargo_config = CargoConfig::default(); |
68 | cargo_config.no_sysroot = self.no_sysroot; | 68 | cargo_config.no_sysroot = self.no_sysroot; |
69 | let load_cargo_config = LoadCargoConfig { | 69 | let load_cargo_config = LoadCargoConfig { |
70 | load_out_dirs_from_check: self.load_output_dirs, | 70 | load_out_dirs_from_check: self.enable_build_scripts, |
71 | wrap_rustc: false, | 71 | wrap_rustc: false, |
72 | with_proc_macro: self.with_proc_macro, | 72 | with_proc_macro: self.enable_proc_macros, |
73 | prefill_caches: false, | ||
73 | }; | 74 | }; |
74 | let (host, vfs, _proc_macro) = | 75 | let (host, vfs, _proc_macro) = |
75 | load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?; | 76 | load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?; |
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs index c33c8179c..dc9a390fe 100644 --- a/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/crates/rust-analyzer/src/cli/diagnostics.rs | |||
@@ -34,8 +34,12 @@ pub fn diagnostics( | |||
34 | with_proc_macro: bool, | 34 | with_proc_macro: bool, |
35 | ) -> Result<()> { | 35 | ) -> Result<()> { |
36 | let cargo_config = Default::default(); | 36 | let cargo_config = Default::default(); |
37 | let load_cargo_config = | 37 | let load_cargo_config = LoadCargoConfig { |
38 | LoadCargoConfig { load_out_dirs_from_check, with_proc_macro, wrap_rustc: false }; | 38 | load_out_dirs_from_check, |
39 | with_proc_macro, | ||
40 | wrap_rustc: false, | ||
41 | prefill_caches: false, | ||
42 | }; | ||
39 | let (host, _vfs, _proc_macro) = | 43 | let (host, _vfs, _proc_macro) = |
40 | load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?; | 44 | load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?; |
41 | let db = host.raw_database(); | 45 | let db = host.raw_database(); |
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 75bad1112..b5f5519b4 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs | |||
@@ -4,6 +4,7 @@ use std::{path::Path, sync::Arc}; | |||
4 | 4 | ||
5 | use anyhow::Result; | 5 | use anyhow::Result; |
6 | use crossbeam_channel::{unbounded, Receiver}; | 6 | use crossbeam_channel::{unbounded, Receiver}; |
7 | use hir::db::DefDatabase; | ||
7 | use ide::{AnalysisHost, Change}; | 8 | use ide::{AnalysisHost, Change}; |
8 | use ide_db::base_db::CrateGraph; | 9 | use ide_db::base_db::CrateGraph; |
9 | use project_model::{ | 10 | use project_model::{ |
@@ -13,13 +14,14 @@ use vfs::{loader::Handle, AbsPath, AbsPathBuf}; | |||
13 | 14 | ||
14 | use crate::reload::{ProjectFolders, SourceRootConfig}; | 15 | use crate::reload::{ProjectFolders, SourceRootConfig}; |
15 | 16 | ||
16 | pub struct LoadCargoConfig { | 17 | pub(crate) struct LoadCargoConfig { |
17 | pub load_out_dirs_from_check: bool, | 18 | pub(crate) load_out_dirs_from_check: bool, |
18 | pub wrap_rustc: bool, | 19 | pub(crate) wrap_rustc: bool, |
19 | pub with_proc_macro: bool, | 20 | pub(crate) with_proc_macro: bool, |
21 | pub(crate) prefill_caches: bool, | ||
20 | } | 22 | } |
21 | 23 | ||
22 | pub fn load_workspace_at( | 24 | pub(crate) fn load_workspace_at( |
23 | root: &Path, | 25 | root: &Path, |
24 | cargo_config: &CargoConfig, | 26 | cargo_config: &CargoConfig, |
25 | load_config: &LoadCargoConfig, | 27 | load_config: &LoadCargoConfig, |
@@ -32,7 +34,7 @@ pub fn load_workspace_at( | |||
32 | load_workspace(workspace, load_config, progress) | 34 | load_workspace(workspace, load_config, progress) |
33 | } | 35 | } |
34 | 36 | ||
35 | pub fn load_workspace( | 37 | fn load_workspace( |
36 | ws: ProjectWorkspace, | 38 | ws: ProjectWorkspace, |
37 | config: &LoadCargoConfig, | 39 | config: &LoadCargoConfig, |
38 | progress: &dyn Fn(String), | 40 | progress: &dyn Fn(String), |
@@ -81,6 +83,10 @@ pub fn load_workspace( | |||
81 | log::debug!("crate graph: {:?}", crate_graph); | 83 | log::debug!("crate graph: {:?}", crate_graph); |
82 | let host = | 84 | let host = |
83 | load_crate_graph(crate_graph, project_folders.source_root_config, &mut vfs, &receiver); | 85 | load_crate_graph(crate_graph, project_folders.source_root_config, &mut vfs, &receiver); |
86 | |||
87 | if config.prefill_caches { | ||
88 | host.analysis().prime_caches(|_| {})?; | ||
89 | } | ||
84 | Ok((host, vfs, proc_macro_client)) | 90 | Ok((host, vfs, proc_macro_client)) |
85 | } | 91 | } |
86 | 92 | ||
@@ -94,6 +100,8 @@ fn load_crate_graph( | |||
94 | let mut host = AnalysisHost::new(lru_cap); | 100 | let mut host = AnalysisHost::new(lru_cap); |
95 | let mut analysis_change = Change::new(); | 101 | let mut analysis_change = Change::new(); |
96 | 102 | ||
103 | host.raw_database_mut().set_enable_proc_attr_macros(true); | ||
104 | |||
97 | // wait until Vfs has loaded all roots | 105 | // wait until Vfs has loaded all roots |
98 | for task in receiver { | 106 | for task in receiver { |
99 | match task { | 107 | match task { |
@@ -118,7 +126,7 @@ fn load_crate_graph( | |||
118 | } | 126 | } |
119 | } | 127 | } |
120 | } | 128 | } |
121 | let source_roots = source_root_config.partition(&vfs); | 129 | let source_roots = source_root_config.partition(vfs); |
122 | analysis_change.set_roots(source_roots); | 130 | analysis_change.set_roots(source_roots); |
123 | 131 | ||
124 | analysis_change.set_crate_graph(crate_graph); | 132 | analysis_change.set_crate_graph(crate_graph); |
@@ -141,6 +149,7 @@ mod tests { | |||
141 | load_out_dirs_from_check: false, | 149 | load_out_dirs_from_check: false, |
142 | wrap_rustc: false, | 150 | wrap_rustc: false, |
143 | with_proc_macro: false, | 151 | with_proc_macro: false, |
152 | prefill_caches: false, | ||
144 | }; | 153 | }; |
145 | let (host, _vfs, _proc_macro) = | 154 | let (host, _vfs, _proc_macro) = |
146 | load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?; | 155 | load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?; |
diff --git a/crates/rust-analyzer/src/cli/ssr.rs b/crates/rust-analyzer/src/cli/ssr.rs index 1fd9b5a9b..0b3475e09 100644 --- a/crates/rust-analyzer/src/cli/ssr.rs +++ b/crates/rust-analyzer/src/cli/ssr.rs | |||
@@ -13,6 +13,7 @@ pub fn apply_ssr_rules(rules: Vec<SsrRule>) -> Result<()> { | |||
13 | load_out_dirs_from_check: true, | 13 | load_out_dirs_from_check: true, |
14 | wrap_rustc: false, | 14 | wrap_rustc: false, |
15 | with_proc_macro: true, | 15 | with_proc_macro: true, |
16 | prefill_caches: false, | ||
16 | }; | 17 | }; |
17 | let (host, vfs, _proc_macro) = | 18 | let (host, vfs, _proc_macro) = |
18 | load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?; | 19 | load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?; |
@@ -39,8 +40,12 @@ pub fn search_for_patterns(patterns: Vec<SsrPattern>, debug_snippet: Option<Stri | |||
39 | use ide_db::base_db::SourceDatabaseExt; | 40 | use ide_db::base_db::SourceDatabaseExt; |
40 | use ide_db::symbol_index::SymbolsDatabase; | 41 | use ide_db::symbol_index::SymbolsDatabase; |
41 | let cargo_config = Default::default(); | 42 | let cargo_config = Default::default(); |
42 | let load_cargo_config = | 43 | let load_cargo_config = LoadCargoConfig { |
43 | LoadCargoConfig { load_out_dirs_from_check: true, wrap_rustc: true, with_proc_macro: true }; | 44 | load_out_dirs_from_check: true, |
45 | wrap_rustc: true, | ||
46 | with_proc_macro: true, | ||
47 | prefill_caches: false, | ||
48 | }; | ||
44 | let (host, _vfs, _proc_macro) = | 49 | let (host, _vfs, _proc_macro) = |
45 | load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?; | 50 | load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?; |
46 | let db = host.raw_database(); | 51 | let db = host.raw_database(); |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index ae78fd4f6..3b20d741a 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -92,6 +92,7 @@ config_data! { | |||
92 | checkOnSave_overrideCommand: Option<Vec<String>> = "null", | 92 | checkOnSave_overrideCommand: Option<Vec<String>> = "null", |
93 | 93 | ||
94 | /// Whether to add argument snippets when completing functions. | 94 | /// Whether to add argument snippets when completing functions. |
95 | /// Only applies when `#rust-analyzer.completion.addCallParenthesis#` is set. | ||
95 | completion_addCallArgumentSnippets: bool = "true", | 96 | completion_addCallArgumentSnippets: bool = "true", |
96 | /// Whether to add parenthesis when completing functions. | 97 | /// Whether to add parenthesis when completing functions. |
97 | completion_addCallParenthesis: bool = "true", | 98 | completion_addCallParenthesis: bool = "true", |
@@ -125,6 +126,9 @@ config_data! { | |||
125 | /// and a blue icon in the `Problems Panel`. | 126 | /// and a blue icon in the `Problems Panel`. |
126 | diagnostics_warningsAsInfo: Vec<String> = "[]", | 127 | diagnostics_warningsAsInfo: Vec<String> = "[]", |
127 | 128 | ||
129 | /// Expand attribute macros. | ||
130 | experimental_procAttrMacros: bool = "false", | ||
131 | |||
128 | /// Controls file watching implementation. | 132 | /// Controls file watching implementation. |
129 | files_watcher: String = "\"client\"", | 133 | files_watcher: String = "\"client\"", |
130 | /// These directories will be ignored by rust-analyzer. | 134 | /// These directories will be ignored by rust-analyzer. |
@@ -148,6 +152,9 @@ config_data! { | |||
148 | /// Whether to show `Implementations` action. Only applies when | 152 | /// Whether to show `Implementations` action. Only applies when |
149 | /// `#rust-analyzer.hoverActions.enable#` is set. | 153 | /// `#rust-analyzer.hoverActions.enable#` is set. |
150 | hoverActions_implementations: bool = "true", | 154 | hoverActions_implementations: bool = "true", |
155 | /// Whether to show `References` action. Only applies when | ||
156 | /// `#rust-analyzer.hoverActions.enable#` is set. | ||
157 | hoverActions_references: bool = "false", | ||
151 | /// Whether to show `Run` action. Only applies when | 158 | /// Whether to show `Run` action. Only applies when |
152 | /// `#rust-analyzer.hoverActions.enable#` is set. | 159 | /// `#rust-analyzer.hoverActions.enable#` is set. |
153 | hoverActions_run: bool = "true", | 160 | hoverActions_run: bool = "true", |
@@ -545,6 +552,9 @@ impl Config { | |||
545 | let path = self.data.procMacro_server.clone().or_else(|| std::env::current_exe().ok())?; | 552 | let path = self.data.procMacro_server.clone().or_else(|| std::env::current_exe().ok())?; |
546 | Some((path, vec!["proc-macro".into()])) | 553 | Some((path, vec!["proc-macro".into()])) |
547 | } | 554 | } |
555 | pub fn expand_proc_attr_macros(&self) -> bool { | ||
556 | self.data.experimental_procAttrMacros | ||
557 | } | ||
548 | pub fn files(&self) -> FilesConfig { | 558 | pub fn files(&self) -> FilesConfig { |
549 | FilesConfig { | 559 | FilesConfig { |
550 | watcher: match self.data.files_watcher.as_str() { | 560 | watcher: match self.data.files_watcher.as_str() { |
@@ -712,6 +722,7 @@ impl Config { | |||
712 | HoverConfig { | 722 | HoverConfig { |
713 | implementations: self.data.hoverActions_enable | 723 | implementations: self.data.hoverActions_enable |
714 | && self.data.hoverActions_implementations, | 724 | && self.data.hoverActions_implementations, |
725 | references: self.data.hoverActions_enable && self.data.hoverActions_references, | ||
715 | run: self.data.hoverActions_enable && self.data.hoverActions_run, | 726 | run: self.data.hoverActions_enable && self.data.hoverActions_run, |
716 | debug: self.data.hoverActions_enable && self.data.hoverActions_debug, | 727 | debug: self.data.hoverActions_enable && self.data.hoverActions_debug, |
717 | goto_type_def: self.data.hoverActions_enable && self.data.hoverActions_gotoTypeDef, | 728 | goto_type_def: self.data.hoverActions_enable && self.data.hoverActions_gotoTypeDef, |
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs index d4b9db362..2f63c26ce 100644 --- a/crates/rust-analyzer/src/diagnostics.rs +++ b/crates/rust-analyzer/src/diagnostics.rs | |||
@@ -47,7 +47,7 @@ impl DiagnosticCollection { | |||
47 | ) { | 47 | ) { |
48 | let diagnostics = self.check.entry(file_id).or_default(); | 48 | let diagnostics = self.check.entry(file_id).or_default(); |
49 | for existing_diagnostic in diagnostics.iter() { | 49 | for existing_diagnostic in diagnostics.iter() { |
50 | if are_diagnostics_equal(&existing_diagnostic, &diagnostic) { | 50 | if are_diagnostics_equal(existing_diagnostic, &diagnostic) { |
51 | return; | 51 | return; |
52 | } | 52 | } |
53 | } | 53 | } |
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index 82dd0da9a..8594d923c 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs | |||
@@ -224,7 +224,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
224 | 224 | ||
225 | let mut message = rd.message.clone(); | 225 | let mut message = rd.message.clone(); |
226 | for child in &rd.children { | 226 | for child in &rd.children { |
227 | let child = map_rust_child_diagnostic(config, workspace_root, &child); | 227 | let child = map_rust_child_diagnostic(config, workspace_root, child); |
228 | match child { | 228 | match child { |
229 | MappedRustChildDiagnostic::SubDiagnostic(sub) => { | 229 | MappedRustChildDiagnostic::SubDiagnostic(sub) => { |
230 | subdiagnostics.push(sub); | 230 | subdiagnostics.push(sub); |
@@ -268,7 +268,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
268 | primary_spans | 268 | primary_spans |
269 | .iter() | 269 | .iter() |
270 | .flat_map(|primary_span| { | 270 | .flat_map(|primary_span| { |
271 | let primary_location = primary_location(config, workspace_root, &primary_span); | 271 | let primary_location = primary_location(config, workspace_root, primary_span); |
272 | 272 | ||
273 | let mut message = message.clone(); | 273 | let mut message = message.clone(); |
274 | if needs_primary_span_label { | 274 | if needs_primary_span_label { |
@@ -298,7 +298,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
298 | // generated that code. | 298 | // generated that code. |
299 | let is_in_macro_call = i != 0; | 299 | let is_in_macro_call = i != 0; |
300 | 300 | ||
301 | let secondary_location = location(config, workspace_root, &span); | 301 | let secondary_location = location(config, workspace_root, span); |
302 | if secondary_location == primary_location { | 302 | if secondary_location == primary_location { |
303 | continue; | 303 | continue; |
304 | } | 304 | } |
diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs index 2011a4132..5991e72b5 100644 --- a/crates/rust-analyzer/src/dispatch.rs +++ b/crates/rust-analyzer/src/dispatch.rs | |||
@@ -104,7 +104,7 @@ impl<'a> RequestDispatcher<'a> { | |||
104 | 104 | ||
105 | let res = crate::from_json(R::METHOD, req.params); | 105 | let res = crate::from_json(R::METHOD, req.params); |
106 | match res { | 106 | match res { |
107 | Ok(params) => return Some((req.id, params)), | 107 | Ok(params) => Some((req.id, params)), |
108 | Err(err) => { | 108 | Err(err) => { |
109 | let response = lsp_server::Response::new_err( | 109 | let response = lsp_server::Response::new_err( |
110 | req.id, | 110 | req.id, |
@@ -112,7 +112,7 @@ impl<'a> RequestDispatcher<'a> { | |||
112 | err.to_string(), | 112 | err.to_string(), |
113 | ); | 113 | ); |
114 | self.global_state.respond(response); | 114 | self.global_state.respond(response); |
115 | return None; | 115 | None |
116 | } | 116 | } |
117 | } | 117 | } |
118 | } | 118 | } |
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index ea9dbf7fc..583900cfe 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -119,12 +119,12 @@ impl GlobalState { | |||
119 | 119 | ||
120 | let analysis_host = AnalysisHost::new(config.lru_capacity()); | 120 | let analysis_host = AnalysisHost::new(config.lru_capacity()); |
121 | let (flycheck_sender, flycheck_receiver) = unbounded(); | 121 | let (flycheck_sender, flycheck_receiver) = unbounded(); |
122 | GlobalState { | 122 | let mut this = GlobalState { |
123 | sender, | 123 | sender, |
124 | req_queue: ReqQueue::default(), | 124 | req_queue: ReqQueue::default(), |
125 | task_pool, | 125 | task_pool, |
126 | loader, | 126 | loader, |
127 | config: Arc::new(config), | 127 | config: Arc::new(config.clone()), |
128 | analysis_host, | 128 | analysis_host, |
129 | diagnostics: Default::default(), | 129 | diagnostics: Default::default(), |
130 | mem_docs: FxHashMap::default(), | 130 | mem_docs: FxHashMap::default(), |
@@ -151,7 +151,10 @@ impl GlobalState { | |||
151 | 151 | ||
152 | fetch_build_data_queue: OpQueue::default(), | 152 | fetch_build_data_queue: OpQueue::default(), |
153 | latest_requests: Default::default(), | 153 | latest_requests: Default::default(), |
154 | } | 154 | }; |
155 | // Apply any required database inputs from the config. | ||
156 | this.update_configuration(config); | ||
157 | this | ||
155 | } | 158 | } |
156 | 159 | ||
157 | pub(crate) fn process_changes(&mut self) -> bool { | 160 | pub(crate) fn process_changes(&mut self) -> bool { |
@@ -191,7 +194,7 @@ impl GlobalState { | |||
191 | change.change_file(file.file_id, text); | 194 | change.change_file(file.file_id, text); |
192 | } | 195 | } |
193 | if has_fs_changes { | 196 | if has_fs_changes { |
194 | let roots = self.source_root_config.partition(&vfs); | 197 | let roots = self.source_root_config.partition(vfs); |
195 | change.set_roots(roots); | 198 | change.set_roots(roots); |
196 | } | 199 | } |
197 | change | 200 | change |
@@ -288,7 +291,7 @@ impl GlobalStateSnapshot { | |||
288 | } | 291 | } |
289 | 292 | ||
290 | pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> { | 293 | pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> { |
291 | let path = from_proto::vfs_path(&url).ok()?; | 294 | let path = from_proto::vfs_path(url).ok()?; |
292 | Some(self.mem_docs.get(&path)?.version) | 295 | Some(self.mem_docs.get(&path)?.version) |
293 | } | 296 | } |
294 | 297 | ||
@@ -297,7 +300,7 @@ impl GlobalStateSnapshot { | |||
297 | base.pop(); | 300 | base.pop(); |
298 | let path = base.join(&path.path).unwrap(); | 301 | let path = base.join(&path.path).unwrap(); |
299 | let path = path.as_path().unwrap(); | 302 | let path = path.as_path().unwrap(); |
300 | url_from_abs_path(&path) | 303 | url_from_abs_path(path) |
301 | } | 304 | } |
302 | 305 | ||
303 | pub(crate) fn cargo_target_for_crate_root( | 306 | pub(crate) fn cargo_target_for_crate_root( |
@@ -309,7 +312,7 @@ impl GlobalStateSnapshot { | |||
309 | let path = path.as_path()?; | 312 | let path = path.as_path()?; |
310 | self.workspaces.iter().find_map(|ws| match ws { | 313 | self.workspaces.iter().find_map(|ws| match ws { |
311 | ProjectWorkspace::Cargo { cargo, .. } => { | 314 | ProjectWorkspace::Cargo { cargo, .. } => { |
312 | cargo.target_by_root(&path).map(|it| (cargo, it)) | 315 | cargo.target_by_root(path).map(|it| (cargo, it)) |
313 | } | 316 | } |
314 | ProjectWorkspace::Json { .. } => None, | 317 | ProjectWorkspace::Json { .. } => None, |
315 | ProjectWorkspace::DetachedFiles { .. } => None, | 318 | ProjectWorkspace::DetachedFiles { .. } => None, |
@@ -320,7 +323,7 @@ impl GlobalStateSnapshot { | |||
320 | pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url { | 323 | pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url { |
321 | let path = vfs.file_path(id); | 324 | let path = vfs.file_path(id); |
322 | let path = path.as_path().unwrap(); | 325 | let path = path.as_path().unwrap(); |
323 | url_from_abs_path(&path) | 326 | url_from_abs_path(path) |
324 | } | 327 | } |
325 | 328 | ||
326 | pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> Result<FileId> { | 329 | pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> Result<FileId> { |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 456744603..ccf66294f 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -781,7 +781,7 @@ pub(crate) fn handle_completion_resolve( | |||
781 | let resolve_data = match original_completion | 781 | let resolve_data = match original_completion |
782 | .data | 782 | .data |
783 | .take() | 783 | .take() |
784 | .map(|data| serde_json::from_value::<CompletionResolveData>(data)) | 784 | .map(serde_json::from_value::<CompletionResolveData>) |
785 | .transpose()? | 785 | .transpose()? |
786 | { | 786 | { |
787 | Some(data) => data, | 787 | Some(data) => data, |
@@ -1229,14 +1229,13 @@ pub(crate) fn publish_diagnostics( | |||
1229 | .map(|d| Diagnostic { | 1229 | .map(|d| Diagnostic { |
1230 | range: to_proto::range(&line_index, d.range), | 1230 | range: to_proto::range(&line_index, d.range), |
1231 | severity: Some(to_proto::diagnostic_severity(d.severity)), | 1231 | severity: Some(to_proto::diagnostic_severity(d.severity)), |
1232 | code: d.code.map(|d| d.as_str().to_owned()).map(NumberOrString::String), | 1232 | code: Some(NumberOrString::String(d.code.as_str().to_string())), |
1233 | code_description: d.code.and_then(|code| { | 1233 | code_description: Some(lsp_types::CodeDescription { |
1234 | lsp_types::Url::parse(&format!( | 1234 | href: lsp_types::Url::parse(&format!( |
1235 | "https://rust-analyzer.github.io/manual.html#{}", | 1235 | "https://rust-analyzer.github.io/manual.html#{}", |
1236 | code.as_str() | 1236 | d.code.as_str() |
1237 | )) | 1237 | )) |
1238 | .ok() | 1238 | .unwrap(), |
1239 | .map(|href| lsp_types::CodeDescription { href }) | ||
1240 | }), | 1239 | }), |
1241 | source: Some("rust-analyzer".to_string()), | 1240 | source: Some("rust-analyzer".to_string()), |
1242 | message: d.message, | 1241 | message: d.message, |
@@ -1396,7 +1395,7 @@ pub(crate) fn handle_semantic_tokens_full_delta( | |||
1396 | 1395 | ||
1397 | if let Some(prev_id) = &cached_tokens.result_id { | 1396 | if let Some(prev_id) = &cached_tokens.result_id { |
1398 | if *prev_id == params.previous_result_id { | 1397 | if *prev_id == params.previous_result_id { |
1399 | let delta = to_proto::semantic_token_delta(&cached_tokens, &semantic_tokens); | 1398 | let delta = to_proto::semantic_token_delta(cached_tokens, &semantic_tokens); |
1400 | *cached_tokens = semantic_tokens; | 1399 | *cached_tokens = semantic_tokens; |
1401 | return Ok(Some(delta.into())); | 1400 | return Ok(Some(delta.into())); |
1402 | } | 1401 | } |
@@ -1506,11 +1505,41 @@ fn show_impl_command_link( | |||
1506 | None | 1505 | None |
1507 | } | 1506 | } |
1508 | 1507 | ||
1508 | fn show_ref_command_link( | ||
1509 | snap: &GlobalStateSnapshot, | ||
1510 | position: &FilePosition, | ||
1511 | ) -> Option<lsp_ext::CommandLinkGroup> { | ||
1512 | if snap.config.hover().references { | ||
1513 | if let Some(ref_search_res) = snap.analysis.find_all_refs(*position, None).unwrap_or(None) { | ||
1514 | let uri = to_proto::url(snap, position.file_id); | ||
1515 | let line_index = snap.file_line_index(position.file_id).ok()?; | ||
1516 | let position = to_proto::position(&line_index, position.offset); | ||
1517 | let locations: Vec<_> = ref_search_res | ||
1518 | .references | ||
1519 | .into_iter() | ||
1520 | .flat_map(|(file_id, ranges)| { | ||
1521 | ranges.into_iter().filter_map(move |(range, _)| { | ||
1522 | to_proto::location(snap, FileRange { file_id, range }).ok() | ||
1523 | }) | ||
1524 | }) | ||
1525 | .collect(); | ||
1526 | let title = to_proto::reference_title(locations.len()); | ||
1527 | let command = to_proto::command::show_references(title, &uri, position, locations); | ||
1528 | |||
1529 | return Some(lsp_ext::CommandLinkGroup { | ||
1530 | commands: vec![to_command_link(command, "Go to references".into())], | ||
1531 | ..Default::default() | ||
1532 | }); | ||
1533 | } | ||
1534 | } | ||
1535 | None | ||
1536 | } | ||
1537 | |||
1509 | fn runnable_action_links( | 1538 | fn runnable_action_links( |
1510 | snap: &GlobalStateSnapshot, | 1539 | snap: &GlobalStateSnapshot, |
1511 | runnable: Runnable, | 1540 | runnable: Runnable, |
1512 | ) -> Option<lsp_ext::CommandLinkGroup> { | 1541 | ) -> Option<lsp_ext::CommandLinkGroup> { |
1513 | let cargo_spec = CargoTargetSpec::for_file(&snap, runnable.nav.file_id).ok()?; | 1542 | let cargo_spec = CargoTargetSpec::for_file(snap, runnable.nav.file_id).ok()?; |
1514 | let hover_config = snap.config.hover(); | 1543 | let hover_config = snap.config.hover(); |
1515 | if !hover_config.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) { | 1544 | if !hover_config.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) { |
1516 | return None; | 1545 | return None; |
@@ -1566,6 +1595,7 @@ fn prepare_hover_actions( | |||
1566 | .iter() | 1595 | .iter() |
1567 | .filter_map(|it| match it { | 1596 | .filter_map(|it| match it { |
1568 | HoverAction::Implementation(position) => show_impl_command_link(snap, position), | 1597 | HoverAction::Implementation(position) => show_impl_command_link(snap, position), |
1598 | HoverAction::Reference(position) => show_ref_command_link(snap, position), | ||
1569 | HoverAction::Runnable(r) => runnable_action_links(snap, r.clone()), | 1599 | HoverAction::Runnable(r) => runnable_action_links(snap, r.clone()), |
1570 | HoverAction::GoToType(targets) => goto_type_action_links(snap, targets), | 1600 | HoverAction::GoToType(targets) => goto_type_action_links(snap, targets), |
1571 | }) | 1601 | }) |
@@ -1593,7 +1623,7 @@ fn run_rustfmt( | |||
1593 | text_document: TextDocumentIdentifier, | 1623 | text_document: TextDocumentIdentifier, |
1594 | range: Option<lsp_types::Range>, | 1624 | range: Option<lsp_types::Range>, |
1595 | ) -> Result<Option<Vec<lsp_types::TextEdit>>> { | 1625 | ) -> Result<Option<Vec<lsp_types::TextEdit>>> { |
1596 | let file_id = from_proto::file_id(&snap, &text_document.uri)?; | 1626 | let file_id = from_proto::file_id(snap, &text_document.uri)?; |
1597 | let file = snap.analysis.file_text(file_id)?; | 1627 | let file = snap.analysis.file_text(file_id)?; |
1598 | let crate_ids = snap.analysis.crate_for(file_id)?; | 1628 | let crate_ids = snap.analysis.crate_for(file_id)?; |
1599 | 1629 | ||
@@ -1640,7 +1670,7 @@ fn run_rustfmt( | |||
1640 | .into()); | 1670 | .into()); |
1641 | } | 1671 | } |
1642 | 1672 | ||
1643 | let frange = from_proto::file_range(&snap, text_document.clone(), range)?; | 1673 | let frange = from_proto::file_range(snap, text_document, range)?; |
1644 | let start_line = line_index.index.line_col(frange.range.start()).line; | 1674 | let start_line = line_index.index.line_col(frange.range.start()).line; |
1645 | let end_line = line_index.index.line_col(frange.range.end()).line; | 1675 | let end_line = line_index.index.line_col(frange.range.end()).line; |
1646 | 1676 | ||
diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs index ec36a5f5c..8ddeb59f7 100644 --- a/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/crates/rust-analyzer/src/integrated_benchmarks.rs | |||
@@ -37,6 +37,7 @@ fn integrated_highlighting_benchmark() { | |||
37 | load_out_dirs_from_check: true, | 37 | load_out_dirs_from_check: true, |
38 | wrap_rustc: false, | 38 | wrap_rustc: false, |
39 | with_proc_macro: false, | 39 | with_proc_macro: false, |
40 | prefill_caches: false, | ||
40 | }; | 41 | }; |
41 | 42 | ||
42 | let (mut host, vfs, _proc_macro) = { | 43 | let (mut host, vfs, _proc_macro) = { |
@@ -91,6 +92,7 @@ fn integrated_completion_benchmark() { | |||
91 | load_out_dirs_from_check: true, | 92 | load_out_dirs_from_check: true, |
92 | wrap_rustc: false, | 93 | wrap_rustc: false, |
93 | with_proc_macro: false, | 94 | with_proc_macro: false, |
95 | prefill_caches: true, | ||
94 | }; | 96 | }; |
95 | 97 | ||
96 | let (mut host, vfs, _proc_macro) = { | 98 | let (mut host, vfs, _proc_macro) = { |
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs index 8000b5490..087c26a71 100644 --- a/crates/rust-analyzer/src/lsp_utils.rs +++ b/crates/rust-analyzer/src/lsp_utils.rs | |||
@@ -124,7 +124,7 @@ pub(crate) fn apply_document_changes( | |||
124 | match change.range { | 124 | match change.range { |
125 | Some(range) => { | 125 | Some(range) => { |
126 | if !index_valid.covers(range.end.line) { | 126 | if !index_valid.covers(range.end.line) { |
127 | line_index.index = Arc::new(ide::LineIndex::new(&old_text)); | 127 | line_index.index = Arc::new(ide::LineIndex::new(old_text)); |
128 | } | 128 | } |
129 | index_valid = IndexValid::UpToLineExclusive(range.start.line); | 129 | index_valid = IndexValid::UpToLineExclusive(range.start.line); |
130 | let range = from_proto::text_range(&line_index, range); | 130 | let range = from_proto::text_range(&line_index, range); |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 31d8ea9e7..1417d8379 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -701,7 +701,7 @@ impl GlobalState { | |||
701 | }, | 701 | }, |
702 | ); | 702 | ); |
703 | 703 | ||
704 | return Ok(()); | 704 | Ok(()) |
705 | })? | 705 | })? |
706 | .on::<lsp_types::notification::DidChangeWatchedFiles>(|this, params| { | 706 | .on::<lsp_types::notification::DidChangeWatchedFiles>(|this, params| { |
707 | for change in params.changes { | 707 | for change in params.changes { |
@@ -740,7 +740,7 @@ impl GlobalState { | |||
740 | let subscriptions = self | 740 | let subscriptions = self |
741 | .mem_docs | 741 | .mem_docs |
742 | .keys() | 742 | .keys() |
743 | .map(|path| self.vfs.read().0.file_id(&path).unwrap()) | 743 | .map(|path| self.vfs.read().0.file_id(path).unwrap()) |
744 | .collect::<Vec<_>>(); | 744 | .collect::<Vec<_>>(); |
745 | 745 | ||
746 | log::trace!("updating notifications for {:?}", subscriptions); | 746 | log::trace!("updating notifications for {:?}", subscriptions); |
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 93b5ff55f..bd31d1d13 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs | |||
@@ -2,6 +2,7 @@ | |||
2 | use std::{mem, sync::Arc}; | 2 | use std::{mem, sync::Arc}; |
3 | 3 | ||
4 | use flycheck::{FlycheckConfig, FlycheckHandle}; | 4 | use flycheck::{FlycheckConfig, FlycheckHandle}; |
5 | use hir::db::DefDatabase; | ||
5 | use ide::Change; | 6 | use ide::Change; |
6 | use ide_db::base_db::{CrateGraph, SourceRoot, VfsPath}; | 7 | use ide_db::base_db::{CrateGraph, SourceRoot, VfsPath}; |
7 | use project_model::{BuildDataCollector, BuildDataResult, ProcMacroClient, ProjectWorkspace}; | 8 | use project_model::{BuildDataCollector, BuildDataResult, ProcMacroClient, ProjectWorkspace}; |
@@ -47,6 +48,11 @@ impl GlobalState { | |||
47 | } else if self.config.flycheck() != old_config.flycheck() { | 48 | } else if self.config.flycheck() != old_config.flycheck() { |
48 | self.reload_flycheck(); | 49 | self.reload_flycheck(); |
49 | } | 50 | } |
51 | |||
52 | // Apply experimental feature flags. | ||
53 | self.analysis_host | ||
54 | .raw_database_mut() | ||
55 | .set_enable_proc_attr_macros(self.config.expand_proc_attr_macros()); | ||
50 | } | 56 | } |
51 | pub(crate) fn maybe_refresh(&mut self, changes: &[(AbsPathBuf, ChangeKind)]) { | 57 | pub(crate) fn maybe_refresh(&mut self, changes: &[(AbsPathBuf, ChangeKind)]) { |
52 | if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) { | 58 | if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) { |
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs index db216d951..c20642231 100644 --- a/crates/rust-analyzer/src/semantic_tokens.rs +++ b/crates/rust-analyzer/src/semantic_tokens.rs | |||
@@ -112,7 +112,7 @@ impl ops::BitOrAssign<SemanticTokenModifier> for ModifierSet { | |||
112 | 112 | ||
113 | /// Tokens are encoded relative to each other. | 113 | /// Tokens are encoded relative to each other. |
114 | /// | 114 | /// |
115 | /// This is a direct port of https://github.com/microsoft/vscode-languageserver-node/blob/f425af9de46a0187adb78ec8a46b9b2ce80c5412/server/src/sematicTokens.proposed.ts#L45 | 115 | /// This is a direct port of <https://github.com/microsoft/vscode-languageserver-node/blob/f425af9de46a0187adb78ec8a46b9b2ce80c5412/server/src/sematicTokens.proposed.ts#L45> |
116 | pub(crate) struct SemanticTokensBuilder { | 116 | pub(crate) struct SemanticTokensBuilder { |
117 | id: String, | 117 | id: String, |
118 | prev_line: u32, | 118 | prev_line: u32, |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 7428a3043..e53cd3c7b 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -405,7 +405,7 @@ pub(crate) fn semantic_tokens( | |||
405 | text_range = | 405 | text_range = |
406 | TextRange::new(text_range.start(), text_range.end() - TextSize::of('\n')); | 406 | TextRange::new(text_range.start(), text_range.end() - TextSize::of('\n')); |
407 | } | 407 | } |
408 | let range = range(&line_index, text_range); | 408 | let range = range(line_index, text_range); |
409 | builder.push(range, token_index, modifier_bitset); | 409 | builder.push(range, token_index, modifier_bitset); |
410 | } | 410 | } |
411 | } | 411 | } |
@@ -781,7 +781,7 @@ pub(crate) fn snippet_workspace_edit( | |||
781 | document_changes.extend_from_slice(&ops); | 781 | document_changes.extend_from_slice(&ops); |
782 | } | 782 | } |
783 | for (file_id, edit) in source_change.source_file_edits { | 783 | for (file_id, edit) in source_change.source_file_edits { |
784 | let edit = snippet_text_document_edit(&snap, source_change.is_snippet, file_id, edit)?; | 784 | let edit = snippet_text_document_edit(snap, source_change.is_snippet, file_id, edit)?; |
785 | document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); | 785 | document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); |
786 | } | 786 | } |
787 | let mut workspace_edit = lsp_ext::SnippetWorkspaceEdit { | 787 | let mut workspace_edit = lsp_ext::SnippetWorkspaceEdit { |
@@ -957,7 +957,7 @@ pub(crate) fn code_lens( | |||
957 | let annotation_range = range(&line_index, annotation.range); | 957 | let annotation_range = range(&line_index, annotation.range); |
958 | 958 | ||
959 | let action = run.action(); | 959 | let action = run.action(); |
960 | let r = runnable(&snap, run)?; | 960 | let r = runnable(snap, run)?; |
961 | 961 | ||
962 | let command = if debug { | 962 | let command = if debug { |
963 | command::debug_single(&r) | 963 | command::debug_single(&r) |
@@ -1236,12 +1236,12 @@ fn main() { | |||
1236 | assert_eq!(folds.len(), 4); | 1236 | assert_eq!(folds.len(), 4); |
1237 | 1237 | ||
1238 | let line_index = LineIndex { | 1238 | let line_index = LineIndex { |
1239 | index: Arc::new(ide::LineIndex::new(&text)), | 1239 | index: Arc::new(ide::LineIndex::new(text)), |
1240 | endings: LineEndings::Unix, | 1240 | endings: LineEndings::Unix, |
1241 | encoding: OffsetEncoding::Utf16, | 1241 | encoding: OffsetEncoding::Utf16, |
1242 | }; | 1242 | }; |
1243 | let converted: Vec<lsp_types::FoldingRange> = | 1243 | let converted: Vec<lsp_types::FoldingRange> = |
1244 | folds.into_iter().map(|it| folding_range(&text, &line_index, true, it)).collect(); | 1244 | folds.into_iter().map(|it| folding_range(text, &line_index, true, it)).collect(); |
1245 | 1245 | ||
1246 | let expected_lines = [(0, 2), (4, 10), (5, 6), (7, 9)]; | 1246 | let expected_lines = [(0, 2), (4, 10), (5, 6), (7, 9)]; |
1247 | assert_eq!(converted.len(), expected_lines.len()); | 1247 | assert_eq!(converted.len(), expected_lines.len()); |
diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index 9e89209ea..3585132d4 100644 --- a/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/crates/rust-analyzer/tests/slow-tests/main.rs | |||
@@ -493,7 +493,7 @@ fn preserves_dos_line_endings() { | |||
493 | } | 493 | } |
494 | 494 | ||
495 | let server = Project::with_fixture( | 495 | let server = Project::with_fixture( |
496 | &" | 496 | " |
497 | //- /Cargo.toml | 497 | //- /Cargo.toml |
498 | [package] | 498 | [package] |
499 | name = \"foo\" | 499 | name = \"foo\" |
@@ -758,7 +758,7 @@ pub fn foo(_input: TokenStream) -> TokenStream { | |||
758 | ```rust | 758 | ```rust |
759 | fn bar() | 759 | fn bar() |
760 | ```"#]] | 760 | ```"#]] |
761 | .assert_eq(&value); | 761 | .assert_eq(value); |
762 | } | 762 | } |
763 | 763 | ||
764 | #[test] | 764 | #[test] |
@@ -795,7 +795,7 @@ fn main() {} | |||
795 | 795 | ||
796 | "#; | 796 | "#; |
797 | let server = | 797 | let server = |
798 | Project::with_fixture(&code).tmp_dir(tmp_dir).server().wait_until_workspace_is_loaded(); | 798 | Project::with_fixture(code).tmp_dir(tmp_dir).server().wait_until_workspace_is_loaded(); |
799 | 799 | ||
800 | //rename same level file | 800 | //rename same level file |
801 | server.request::<WillRenameFiles>( | 801 | server.request::<WillRenameFiles>( |
diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs index 75e677762..e22c295f9 100644 --- a/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/crates/rust-analyzer/tests/slow-tests/support.rs | |||
@@ -323,7 +323,7 @@ fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Valu | |||
323 | 323 | ||
324 | if !l.is_empty() { | 324 | if !l.is_empty() { |
325 | assert!(!r.is_empty()); | 325 | assert!(!r.is_empty()); |
326 | Some((&l[0], &r[0])) | 326 | Some((l[0], r[0])) |
327 | } else { | 327 | } else { |
328 | assert_eq!(r.len(), 0); | 328 | assert_eq!(r.len(), 0); |
329 | None | 329 | None |
diff --git a/crates/stdx/src/panic_context.rs b/crates/stdx/src/panic_context.rs index 8d51e20d3..26debf3c4 100644 --- a/crates/stdx/src/panic_context.rs +++ b/crates/stdx/src/panic_context.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! A micro-crate to enhance panic messages with context info. | 1 | //! A micro-crate to enhance panic messages with context info. |
2 | //! | 2 | //! |
3 | //! FIXME: upstream to https://github.com/kriomant/panic-context ? | 3 | //! FIXME: upstream to <https://github.com/kriomant/panic-context> ? |
4 | 4 | ||
5 | use std::{cell::RefCell, panic, sync::Once}; | 5 | use std::{cell::RefCell, panic, sync::Once}; |
6 | 6 | ||
diff --git a/crates/stdx/src/process.rs b/crates/stdx/src/process.rs index b0fa12f76..692a2ab3d 100644 --- a/crates/stdx/src/process.rs +++ b/crates/stdx/src/process.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Read both stdout and stderr of child without deadlocks. | 1 | //! Read both stdout and stderr of child without deadlocks. |
2 | //! | 2 | //! |
3 | //! https://github.com/rust-lang/cargo/blob/905af549966f23a9288e9993a85d1249a5436556/crates/cargo-util/src/read2.rs | 3 | //! <https://github.com/rust-lang/cargo/blob/905af549966f23a9288e9993a85d1249a5436556/crates/cargo-util/src/read2.rs> |
4 | //! https://github.com/rust-lang/cargo/blob/58a961314437258065e23cb6316dfc121d96fb71/crates/cargo-util/src/process_builder.rs#L231 | 4 | //! <https://github.com/rust-lang/cargo/blob/58a961314437258065e23cb6316dfc121d96fb71/crates/cargo-util/src/process_builder.rs#L231> |
5 | 5 | ||
6 | use std::{ | 6 | use std::{ |
7 | io, | 7 | io, |
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index 2106732cd..f1525a649 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml | |||
@@ -11,7 +11,7 @@ edition = "2018" | |||
11 | doctest = false | 11 | doctest = false |
12 | 12 | ||
13 | [dependencies] | 13 | [dependencies] |
14 | cov-mark = { version = "1.1", features = ["thread-local"] } | 14 | cov-mark = "2.0.0-pre.1" |
15 | itertools = "0.10.0" | 15 | itertools = "0.10.0" |
16 | rowan = "=0.13.0-pre.6" | 16 | rowan = "=0.13.0-pre.6" |
17 | rustc_lexer = { version = "721.0.0", package = "rustc-ap-rustc_lexer" } | 17 | rustc_lexer = { version = "721.0.0", package = "rustc-ap-rustc_lexer" } |
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 19107ee38..2663c0759 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs | |||
@@ -30,7 +30,7 @@ impl ast::UseTree { | |||
30 | let suffix = if self.path().as_ref() == Some(prefix) && self.use_tree_list().is_none() { | 30 | let suffix = if self.path().as_ref() == Some(prefix) && self.use_tree_list().is_none() { |
31 | make::path_unqualified(make::path_segment_self()) | 31 | make::path_unqualified(make::path_segment_self()) |
32 | } else { | 32 | } else { |
33 | match split_path_prefix(&prefix) { | 33 | match split_path_prefix(prefix) { |
34 | Some(it) => it, | 34 | Some(it) => it, |
35 | None => return self.clone(), | 35 | None => return self.clone(), |
36 | } | 36 | } |
@@ -95,7 +95,7 @@ impl fmt::Display for IndentLevel { | |||
95 | let indent = if len <= spaces.len() { | 95 | let indent = if len <= spaces.len() { |
96 | &spaces[..len] | 96 | &spaces[..len] |
97 | } else { | 97 | } else { |
98 | buf = iter::repeat(' ').take(len).collect::<String>(); | 98 | buf = " ".repeat(len); |
99 | &buf | 99 | &buf |
100 | }; | 100 | }; |
101 | fmt::Display::fmt(indent, f) | 101 | fmt::Display::fmt(indent, f) |
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index 9a88fdb56..702de59a9 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs | |||
@@ -150,10 +150,7 @@ impl Attr { | |||
150 | pub fn pound_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![#]) } | 150 | pub fn pound_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![#]) } |
151 | pub fn excl_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![!]) } | 151 | pub fn excl_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![!]) } |
152 | pub fn l_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['[']) } | 152 | pub fn l_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['[']) } |
153 | pub fn path(&self) -> Option<Path> { support::child(&self.syntax) } | 153 | pub fn meta(&self) -> Option<Meta> { support::child(&self.syntax) } |
154 | pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) } | ||
155 | pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } | ||
156 | pub fn token_tree(&self) -> Option<TokenTree> { support::child(&self.syntax) } | ||
157 | pub fn r_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![']']) } | 154 | pub fn r_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![']']) } |
158 | } | 155 | } |
159 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 156 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
@@ -632,6 +629,16 @@ impl WherePred { | |||
632 | pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) } | 629 | pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) } |
633 | } | 630 | } |
634 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 631 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
632 | pub struct Meta { | ||
633 | pub(crate) syntax: SyntaxNode, | ||
634 | } | ||
635 | impl Meta { | ||
636 | pub fn path(&self) -> Option<Path> { support::child(&self.syntax) } | ||
637 | pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) } | ||
638 | pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } | ||
639 | pub fn token_tree(&self) -> Option<TokenTree> { support::child(&self.syntax) } | ||
640 | } | ||
641 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
635 | pub struct ExprStmt { | 642 | pub struct ExprStmt { |
636 | pub(crate) syntax: SyntaxNode, | 643 | pub(crate) syntax: SyntaxNode, |
637 | } | 644 | } |
@@ -2072,6 +2079,17 @@ impl AstNode for WherePred { | |||
2072 | } | 2079 | } |
2073 | fn syntax(&self) -> &SyntaxNode { &self.syntax } | 2080 | fn syntax(&self) -> &SyntaxNode { &self.syntax } |
2074 | } | 2081 | } |
2082 | impl AstNode for Meta { | ||
2083 | fn can_cast(kind: SyntaxKind) -> bool { kind == META } | ||
2084 | fn cast(syntax: SyntaxNode) -> Option<Self> { | ||
2085 | if Self::can_cast(syntax.kind()) { | ||
2086 | Some(Self { syntax }) | ||
2087 | } else { | ||
2088 | None | ||
2089 | } | ||
2090 | } | ||
2091 | fn syntax(&self) -> &SyntaxNode { &self.syntax } | ||
2092 | } | ||
2075 | impl AstNode for ExprStmt { | 2093 | impl AstNode for ExprStmt { |
2076 | fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_STMT } | 2094 | fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_STMT } |
2077 | fn cast(syntax: SyntaxNode) -> Option<Self> { | 2095 | fn cast(syntax: SyntaxNode) -> Option<Self> { |
@@ -3887,6 +3905,11 @@ impl std::fmt::Display for WherePred { | |||
3887 | std::fmt::Display::fmt(self.syntax(), f) | 3905 | std::fmt::Display::fmt(self.syntax(), f) |
3888 | } | 3906 | } |
3889 | } | 3907 | } |
3908 | impl std::fmt::Display for Meta { | ||
3909 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
3910 | std::fmt::Display::fmt(self.syntax(), f) | ||
3911 | } | ||
3912 | } | ||
3890 | impl std::fmt::Display for ExprStmt { | 3913 | impl std::fmt::Display for ExprStmt { |
3891 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | 3914 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
3892 | std::fmt::Display::fmt(self.syntax(), f) | 3915 | std::fmt::Display::fmt(self.syntax(), f) |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 0cf170626..4c3c9661d 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -580,12 +580,11 @@ pub fn fn_( | |||
580 | pub fn struct_( | 580 | pub fn struct_( |
581 | visibility: Option<ast::Visibility>, | 581 | visibility: Option<ast::Visibility>, |
582 | strukt_name: ast::Name, | 582 | strukt_name: ast::Name, |
583 | type_params: Option<ast::GenericParamList>, | 583 | generic_param_list: Option<ast::GenericParamList>, |
584 | field_list: ast::FieldList, | 584 | field_list: ast::FieldList, |
585 | ) -> ast::Struct { | 585 | ) -> ast::Struct { |
586 | let semicolon = if matches!(field_list, ast::FieldList::TupleFieldList(_)) { ";" } else { "" }; | 586 | let semicolon = if matches!(field_list, ast::FieldList::TupleFieldList(_)) { ";" } else { "" }; |
587 | let type_params = | 587 | let type_params = generic_param_list.map_or_else(String::new, |it| it.to_string()); |
588 | if let Some(type_params) = type_params { format!("<{}>", type_params) } else { "".into() }; | ||
589 | let visibility = match visibility { | 588 | let visibility = match visibility { |
590 | None => String::new(), | 589 | None => String::new(), |
591 | Some(it) => format!("{} ", it), | 590 | Some(it) => format!("{} ", it), |
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 884fe0739..3d27d2c1a 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs | |||
@@ -144,19 +144,20 @@ impl AttrKind { | |||
144 | 144 | ||
145 | impl ast::Attr { | 145 | impl ast::Attr { |
146 | pub fn as_simple_atom(&self) -> Option<SmolStr> { | 146 | pub fn as_simple_atom(&self) -> Option<SmolStr> { |
147 | if self.eq_token().is_some() || self.token_tree().is_some() { | 147 | let meta = self.meta()?; |
148 | if meta.eq_token().is_some() || meta.token_tree().is_some() { | ||
148 | return None; | 149 | return None; |
149 | } | 150 | } |
150 | self.simple_name() | 151 | self.simple_name() |
151 | } | 152 | } |
152 | 153 | ||
153 | pub fn as_simple_call(&self) -> Option<(SmolStr, ast::TokenTree)> { | 154 | pub fn as_simple_call(&self) -> Option<(SmolStr, ast::TokenTree)> { |
154 | let tt = self.token_tree()?; | 155 | let tt = self.meta()?.token_tree()?; |
155 | Some((self.simple_name()?, tt)) | 156 | Some((self.simple_name()?, tt)) |
156 | } | 157 | } |
157 | 158 | ||
158 | pub fn simple_name(&self) -> Option<SmolStr> { | 159 | pub fn simple_name(&self) -> Option<SmolStr> { |
159 | let path = self.path()?; | 160 | let path = self.meta()?.path()?; |
160 | match (path.segment(), path.qualifier()) { | 161 | match (path.segment(), path.qualifier()) { |
161 | (Some(segment), None) => Some(segment.syntax().first_token()?.text().into()), | 162 | (Some(segment), None) => Some(segment.syntax().first_token()?.text().into()), |
162 | _ => None, | 163 | _ => None, |
@@ -174,6 +175,18 @@ impl ast::Attr { | |||
174 | _ => AttrKind::Outer, | 175 | _ => AttrKind::Outer, |
175 | } | 176 | } |
176 | } | 177 | } |
178 | |||
179 | pub fn path(&self) -> Option<ast::Path> { | ||
180 | self.meta()?.path() | ||
181 | } | ||
182 | |||
183 | pub fn expr(&self) -> Option<ast::Expr> { | ||
184 | self.meta()?.expr() | ||
185 | } | ||
186 | |||
187 | pub fn token_tree(&self) -> Option<ast::TokenTree> { | ||
188 | self.meta()?.token_tree() | ||
189 | } | ||
177 | } | 190 | } |
178 | 191 | ||
179 | #[derive(Debug, Clone, PartialEq, Eq)] | 192 | #[derive(Debug, Clone, PartialEq, Eq)] |
@@ -259,11 +272,14 @@ impl ast::Path { | |||
259 | } | 272 | } |
260 | 273 | ||
261 | pub fn segments(&self) -> impl Iterator<Item = ast::PathSegment> + Clone { | 274 | pub fn segments(&self) -> impl Iterator<Item = ast::PathSegment> + Clone { |
262 | // cant make use of SyntaxNode::siblings, because the returned Iterator is not clone | ||
263 | successors(self.first_segment(), |p| { | 275 | successors(self.first_segment(), |p| { |
264 | p.parent_path().parent_path().and_then(|p| p.segment()) | 276 | p.parent_path().parent_path().and_then(|p| p.segment()) |
265 | }) | 277 | }) |
266 | } | 278 | } |
279 | |||
280 | pub fn qualifiers(&self) -> impl Iterator<Item = ast::Path> + Clone { | ||
281 | successors(self.qualifier(), |p| p.qualifier()) | ||
282 | } | ||
267 | } | 283 | } |
268 | impl ast::UseTree { | 284 | impl ast::UseTree { |
269 | pub fn is_simple_path(&self) -> bool { | 285 | pub fn is_simple_path(&self) -> bool { |
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index 4b1e1ccee..ad52d9f54 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs | |||
@@ -242,7 +242,7 @@ impl ast::ByteString { | |||
242 | (Ok(c), true) if char_range.len() == 1 && Some(c) == text_iter.next() => (), | 242 | (Ok(c), true) if char_range.len() == 1 && Some(c) == text_iter.next() => (), |
243 | (Ok(c), true) => { | 243 | (Ok(c), true) => { |
244 | buf.reserve_exact(text.len()); | 244 | buf.reserve_exact(text.len()); |
245 | buf.extend_from_slice(&text[..char_range.start].as_bytes()); | 245 | buf.extend_from_slice(text[..char_range.start].as_bytes()); |
246 | buf.push(c as u8); | 246 | buf.push(c as u8); |
247 | } | 247 | } |
248 | (Err(_), _) => has_error = true, | 248 | (Err(_), _) => has_error = true, |
diff --git a/crates/syntax/src/parsing.rs b/crates/syntax/src/parsing.rs index 431ed0699..001921343 100644 --- a/crates/syntax/src/parsing.rs +++ b/crates/syntax/src/parsing.rs | |||
@@ -15,7 +15,7 @@ use crate::{syntax_node::GreenNode, AstNode, SyntaxError, SyntaxNode}; | |||
15 | pub(crate) use crate::parsing::{lexer::*, reparsing::incremental_reparse}; | 15 | pub(crate) use crate::parsing::{lexer::*, reparsing::incremental_reparse}; |
16 | 16 | ||
17 | pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) { | 17 | pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) { |
18 | let (tokens, lexer_errors) = tokenize(&text); | 18 | let (tokens, lexer_errors) = tokenize(text); |
19 | 19 | ||
20 | let mut token_source = TextTokenSource::new(text, &tokens); | 20 | let mut token_source = TextTokenSource::new(text, &tokens); |
21 | let mut tree_sink = TextTreeSink::new(text, &tokens); | 21 | let mut tree_sink = TextTreeSink::new(text, &tokens); |
@@ -33,7 +33,7 @@ pub(crate) fn parse_text_fragment<T: AstNode>( | |||
33 | text: &str, | 33 | text: &str, |
34 | fragment_kind: parser::FragmentKind, | 34 | fragment_kind: parser::FragmentKind, |
35 | ) -> Result<T, ()> { | 35 | ) -> Result<T, ()> { |
36 | let (tokens, lexer_errors) = tokenize(&text); | 36 | let (tokens, lexer_errors) = tokenize(text); |
37 | if !lexer_errors.is_empty() { | 37 | if !lexer_errors.is_empty() { |
38 | return Err(()); | 38 | return Err(()); |
39 | } | 39 | } |
diff --git a/crates/syntax/src/parsing/lexer.rs b/crates/syntax/src/parsing/lexer.rs index 7c8d0a4c4..ae4844e48 100644 --- a/crates/syntax/src/parsing/lexer.rs +++ b/crates/syntax/src/parsing/lexer.rs | |||
@@ -144,7 +144,7 @@ fn rustc_token_kind_to_syntax_kind( | |||
144 | } | 144 | } |
145 | 145 | ||
146 | rustc_lexer::TokenKind::RawIdent => IDENT, | 146 | rustc_lexer::TokenKind::RawIdent => IDENT, |
147 | rustc_lexer::TokenKind::Literal { kind, .. } => return match_literal_kind(&kind), | 147 | rustc_lexer::TokenKind::Literal { kind, .. } => return match_literal_kind(kind), |
148 | 148 | ||
149 | rustc_lexer::TokenKind::Lifetime { starts_with_number: false } => LIFETIME_IDENT, | 149 | rustc_lexer::TokenKind::Lifetime { starts_with_number: false } => LIFETIME_IDENT, |
150 | rustc_lexer::TokenKind::Lifetime { starts_with_number: true } => { | 150 | rustc_lexer::TokenKind::Lifetime { starts_with_number: true } => { |
diff --git a/crates/syntax/src/parsing/reparsing.rs b/crates/syntax/src/parsing/reparsing.rs index 4ad50ab72..186cc9e74 100644 --- a/crates/syntax/src/parsing/reparsing.rs +++ b/crates/syntax/src/parsing/reparsing.rs | |||
@@ -26,18 +26,18 @@ pub(crate) fn incremental_reparse( | |||
26 | edit: &Indel, | 26 | edit: &Indel, |
27 | errors: Vec<SyntaxError>, | 27 | errors: Vec<SyntaxError>, |
28 | ) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { | 28 | ) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { |
29 | if let Some((green, new_errors, old_range)) = reparse_token(node, &edit) { | 29 | if let Some((green, new_errors, old_range)) = reparse_token(node, edit) { |
30 | return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range)); | 30 | return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range)); |
31 | } | 31 | } |
32 | 32 | ||
33 | if let Some((green, new_errors, old_range)) = reparse_block(node, &edit) { | 33 | if let Some((green, new_errors, old_range)) = reparse_block(node, edit) { |
34 | return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range)); | 34 | return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range)); |
35 | } | 35 | } |
36 | None | 36 | None |
37 | } | 37 | } |
38 | 38 | ||
39 | fn reparse_token<'node>( | 39 | fn reparse_token( |
40 | root: &'node SyntaxNode, | 40 | root: &SyntaxNode, |
41 | edit: &Indel, | 41 | edit: &Indel, |
42 | ) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { | 42 | ) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { |
43 | let prev_token = root.covering_element(edit.delete).as_token()?.clone(); | 43 | let prev_token = root.covering_element(edit.delete).as_token()?.clone(); |
@@ -52,7 +52,7 @@ fn reparse_token<'node>( | |||
52 | } | 52 | } |
53 | } | 53 | } |
54 | 54 | ||
55 | let mut new_text = get_text_after_edit(prev_token.clone().into(), &edit); | 55 | let mut new_text = get_text_after_edit(prev_token.clone().into(), edit); |
56 | let (new_token_kind, new_err) = lex_single_syntax_kind(&new_text)?; | 56 | let (new_token_kind, new_err) = lex_single_syntax_kind(&new_text)?; |
57 | 57 | ||
58 | if new_token_kind != prev_token_kind | 58 | if new_token_kind != prev_token_kind |
@@ -84,8 +84,8 @@ fn reparse_token<'node>( | |||
84 | } | 84 | } |
85 | } | 85 | } |
86 | 86 | ||
87 | fn reparse_block<'node>( | 87 | fn reparse_block( |
88 | root: &'node SyntaxNode, | 88 | root: &SyntaxNode, |
89 | edit: &Indel, | 89 | edit: &Indel, |
90 | ) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { | 90 | ) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> { |
91 | let (node, reparser) = find_reparsable_node(root, edit.delete)?; | 91 | let (node, reparser) = find_reparsable_node(root, edit.delete)?; |
diff --git a/crates/syntax/src/tests.rs b/crates/syntax/src/tests.rs index 45f3c800f..4961ca08d 100644 --- a/crates/syntax/src/tests.rs +++ b/crates/syntax/src/tests.rs | |||
@@ -69,13 +69,13 @@ fn parser_tests() { | |||
69 | dir_tests(&test_data_dir(), &["parser/inline/ok", "parser/ok"], "rast", |text, path| { | 69 | dir_tests(&test_data_dir(), &["parser/inline/ok", "parser/ok"], "rast", |text, path| { |
70 | let parse = SourceFile::parse(text); | 70 | let parse = SourceFile::parse(text); |
71 | let errors = parse.errors(); | 71 | let errors = parse.errors(); |
72 | assert_errors_are_absent(&errors, path); | 72 | assert_errors_are_absent(errors, path); |
73 | parse.debug_dump() | 73 | parse.debug_dump() |
74 | }); | 74 | }); |
75 | dir_tests(&test_data_dir(), &["parser/err", "parser/inline/err"], "rast", |text, path| { | 75 | dir_tests(&test_data_dir(), &["parser/err", "parser/inline/err"], "rast", |text, path| { |
76 | let parse = SourceFile::parse(text); | 76 | let parse = SourceFile::parse(text); |
77 | let errors = parse.errors(); | 77 | let errors = parse.errors(); |
78 | assert_errors_are_present(&errors, path); | 78 | assert_errors_are_present(errors, path); |
79 | parse.debug_dump() | 79 | parse.debug_dump() |
80 | }); | 80 | }); |
81 | } | 81 | } |
@@ -236,7 +236,7 @@ where | |||
236 | } | 236 | } |
237 | }); | 237 | }); |
238 | dir_tests(&test_data_dir(), err_paths, "rast", |text, path| { | 238 | dir_tests(&test_data_dir(), err_paths, "rast", |text, path| { |
239 | if let Ok(_) = f(text) { | 239 | if f(text).is_ok() { |
240 | panic!("'{:?}' successfully parsed when it should have errored", path); | 240 | panic!("'{:?}' successfully parsed when it should have errored", path); |
241 | } else { | 241 | } else { |
242 | "ERROR\n".to_owned() | 242 | "ERROR\n".to_owned() |
diff --git a/crates/syntax/test_data/parser/err/0005_attribute_recover.rast b/crates/syntax/test_data/parser/err/0005_attribute_recover.rast index 4845a6563..6202c8bfe 100644 --- a/crates/syntax/test_data/parser/err/0005_attribute_recover.rast +++ b/crates/syntax/test_data/parser/err/0005_attribute_recover.rast | |||
@@ -3,20 +3,21 @@ [email protected] | |||
3 | [email protected] | 3 | [email protected] |
4 | [email protected] "#" | 4 | [email protected] "#" |
5 | [email protected] "[" | 5 | [email protected] "[" |
6 | [email protected] | 6 | [email protected] |
7 | [email protected] | 7 | [email protected] |
8 | [email protected] | 8 | [email protected] |
9 | [email protected] "foo" | 9 | [email protected] |
10 | [email protected] | 10 | [email protected] "foo" |
11 | [email protected] "(" | 11 | [email protected] |
12 | [email protected] "foo" | 12 | [email protected] "(" |
13 | [email protected] "," | 13 | [email protected] "foo" |
14 | [email protected] " " | 14 | [email protected] "," |
15 | [email protected] "+" | 15 | [email protected] " " |
16 | [email protected] "," | 16 | [email protected] "+" |
17 | [email protected] " " | 17 | [email protected] "," |
18 | [email protected] "92" | 18 | [email protected] " " |
19 | [email protected] ")" | 19 | [email protected] "92" |
20 | [email protected] ")" | ||
20 | [email protected] "]" | 21 | [email protected] "]" |
21 | [email protected] "\n" | 22 | [email protected] "\n" |
22 | [email protected] "fn" | 23 | [email protected] "fn" |
@@ -35,24 +36,25 @@ [email protected] | |||
35 | [email protected] | 36 | [email protected] |
36 | [email protected] "#" | 37 | [email protected] "#" |
37 | [email protected] "[" | 38 | [email protected] "[" |
38 | [email protected] | 39 | [email protected] |
39 | [email protected] | 40 | [email protected] |
40 | [email protected] | 41 | [email protected] |
41 | [email protected] "foo" | 42 | [email protected] |
42 | [email protected] | 43 | [email protected] "foo" |
43 | [email protected] "(" | 44 | [email protected] |
44 | [email protected] "\n" | 45 | [email protected] "(" |
45 | [email protected] "fn" | 46 | [email protected] "\n" |
46 | [email protected] " " | 47 | [email protected] "fn" |
47 | [email protected] "foo" | 48 | [email protected] " " |
48 | [email protected] | 49 | [email protected] "foo" |
49 | [email protected] "(" | 50 | [email protected] |
50 | [email protected] ")" | 51 | [email protected] "(" |
51 | [email protected] " " | 52 | [email protected] ")" |
52 | [email protected] | 53 | [email protected] " " |
53 | [email protected] "{" | 54 | [email protected] |
54 | [email protected] "\n" | 55 | [email protected] "{" |
55 | [email protected] "}" | 56 | [email protected] "\n" |
57 | [email protected] "}" | ||
56 | [email protected] "\n" | 58 | [email protected] "\n" |
57 | error 53..53: expected R_PAREN | 59 | error 53..53: expected R_PAREN |
58 | error 53..53: expected `]` | 60 | error 53..53: expected `]` |
diff --git a/crates/syntax/test_data/parser/err/0031_block_inner_attrs.rast b/crates/syntax/test_data/parser/err/0031_block_inner_attrs.rast index a443b37db..846279748 100644 --- a/crates/syntax/test_data/parser/err/0031_block_inner_attrs.rast +++ b/crates/syntax/test_data/parser/err/0031_block_inner_attrs.rast | |||
@@ -27,14 +27,15 @@ [email protected] | |||
27 | [email protected] "#" | 27 | [email protected] "#" |
28 | [email protected] "!" | 28 | [email protected] "!" |
29 | [email protected] "[" | 29 | [email protected] "[" |
30 | [email protected] | 30 | [email protected] |
31 | [email protected] | 31 | [email protected] |
32 | [email protected] | 32 | [email protected] |
33 | [email protected] "doc" | 33 | [email protected] |
34 | [email protected] | 34 | [email protected] "doc" |
35 | [email protected] "(" | 35 | [email protected] |
36 | [email protected] "\"Inner attributes not ..." | 36 | [email protected] "(" |
37 | [email protected] ")" | 37 | [email protected] "\"Inner attributes not ..." |
38 | [email protected] ")" | ||
38 | [email protected] "]" | 39 | [email protected] "]" |
39 | [email protected] "\n " | 40 | [email protected] "\n " |
40 | [email protected] "//! Nor are ModuleDoc ..." | 41 | [email protected] "//! Nor are ModuleDoc ..." |
@@ -57,28 +58,30 @@ [email protected] | |||
57 | [email protected] "#" | 58 | [email protected] "#" |
58 | [email protected] "!" | 59 | [email protected] "!" |
59 | [email protected] "[" | 60 | [email protected] "[" |
60 | [email protected] | 61 | [email protected] |
61 | [email protected] | 62 | [email protected] |
62 | [email protected] | 63 | [email protected] |
63 | [email protected] "doc" | 64 | [email protected] |
64 | [email protected] | 65 | [email protected] "doc" |
65 | [email protected] "(" | 66 | [email protected] |
66 | [email protected] "\"Nor here\"" | 67 | [email protected] "(" |
67 | [email protected] ")" | 68 | [email protected] "\"Nor here\"" |
69 | [email protected] ")" | ||
68 | [email protected] "]" | 70 | [email protected] "]" |
69 | [email protected] "\n " | 71 | [email protected] "\n " |
70 | [email protected] | 72 | [email protected] |
71 | [email protected] "#" | 73 | [email protected] "#" |
72 | [email protected] "!" | 74 | [email protected] "!" |
73 | [email protected] "[" | 75 | [email protected] "[" |
74 | [email protected] | 76 | [email protected] |
75 | [email protected] | 77 | [email protected] |
76 | [email protected] | 78 | [email protected] |
77 | [email protected] "doc" | 79 | [email protected] |
78 | [email protected] | 80 | [email protected] "doc" |
79 | [email protected] "(" | 81 | [email protected] |
80 | [email protected] "\"We error on each attr\"" | 82 | [email protected] "(" |
81 | [email protected] ")" | 83 | [email protected] "\"We error on each attr\"" |
84 | [email protected] ")" | ||
82 | [email protected] "]" | 85 | [email protected] "]" |
83 | [email protected] "\n " | 86 | [email protected] "\n " |
84 | [email protected] "//! Nor are ModuleDoc ..." | 87 | [email protected] "//! Nor are ModuleDoc ..." |
@@ -99,14 +102,15 @@ [email protected] | |||
99 | [email protected] "#" | 102 | [email protected] "#" |
100 | [email protected] "!" | 103 | [email protected] "!" |
101 | [email protected] "[" | 104 | [email protected] "[" |
102 | [email protected] | 105 | [email protected] |
103 | [email protected] | 106 | [email protected] |
104 | [email protected] | 107 | [email protected] |
105 | [email protected] "doc" | 108 | [email protected] |
106 | [email protected] | 109 | [email protected] "doc" |
107 | [email protected] "(" | 110 | [email protected] |
108 | [email protected] "\"Nor here\"" | 111 | [email protected] "(" |
109 | [email protected] ")" | 112 | [email protected] "\"Nor here\"" |
113 | [email protected] ")" | ||
110 | [email protected] "]" | 114 | [email protected] "]" |
111 | [email protected] "\n " | 115 | [email protected] "\n " |
112 | [email protected] "//! Nor are ModuleDoc ..." | 116 | [email protected] "//! Nor are ModuleDoc ..." |
diff --git a/crates/syntax/test_data/parser/err/0032_match_arms_inner_attrs.rast b/crates/syntax/test_data/parser/err/0032_match_arms_inner_attrs.rast index 672dd054a..b6209639d 100644 --- a/crates/syntax/test_data/parser/err/0032_match_arms_inner_attrs.rast +++ b/crates/syntax/test_data/parser/err/0032_match_arms_inner_attrs.rast | |||
@@ -135,14 +135,15 @@ [email protected] | |||
135 | [email protected] | 135 | [email protected] |
136 | [email protected] "#" | 136 | [email protected] "#" |
137 | [email protected] "[" | 137 | [email protected] "[" |
138 | [email protected] | 138 | [email protected] |
139 | [email protected] | 139 | [email protected] |
140 | [email protected] | 140 | [email protected] |
141 | [email protected] "cfg" | 141 | [email protected] |
142 | [email protected] | 142 | [email protected] "cfg" |
143 | [email protected] "(" | 143 | [email protected] |
144 | [email protected] "test" | 144 | [email protected] "(" |
145 | [email protected] ")" | 145 | [email protected] "test" |
146 | [email protected] ")" | ||
146 | [email protected] "]" | 147 | [email protected] "]" |
147 | [email protected] "\n " | 148 | [email protected] "\n " |
148 | [email protected] | 149 | [email protected] |
diff --git a/crates/syntax/test_data/parser/err/0033_match_arms_outer_attrs.rast b/crates/syntax/test_data/parser/err/0033_match_arms_outer_attrs.rast index 33bb085e9..84c8e9ee7 100644 --- a/crates/syntax/test_data/parser/err/0033_match_arms_outer_attrs.rast +++ b/crates/syntax/test_data/parser/err/0033_match_arms_outer_attrs.rast | |||
@@ -47,14 +47,15 @@ [email protected] | |||
47 | [email protected] | 47 | [email protected] |
48 | [email protected] "#" | 48 | [email protected] "#" |
49 | [email protected] "[" | 49 | [email protected] "[" |
50 | [email protected] | 50 | [email protected] |
51 | [email protected] | 51 | [email protected] |
52 | [email protected] | 52 | [email protected] |
53 | [email protected] "cfg" | 53 | [email protected] |
54 | [email protected] | 54 | [email protected] "cfg" |
55 | [email protected] "(" | 55 | [email protected] |
56 | [email protected] "test" | 56 | [email protected] "(" |
57 | [email protected] ")" | 57 | [email protected] "test" |
58 | [email protected] ")" | ||
58 | [email protected] "]" | 59 | [email protected] "]" |
59 | [email protected] "\n " | 60 | [email protected] "\n " |
60 | [email protected] "}" | 61 | [email protected] "}" |
diff --git a/crates/syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast b/crates/syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast index 8fd8d5e59..7b8b7284f 100644 --- a/crates/syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast +++ b/crates/syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast | |||
@@ -15,10 +15,11 @@ [email protected] | |||
15 | [email protected] | 15 | [email protected] |
16 | [email protected] "#" | 16 | [email protected] "#" |
17 | [email protected] "[" | 17 | [email protected] "[" |
18 | [email protected] | 18 | [email protected] |
19 | [email protected] | 19 | [email protected] |
20 | [email protected] | 20 | [email protected] |
21 | [email protected] "A" | 21 | [email protected] |
22 | [email protected] "A" | ||
22 | [email protected] "]" | 23 | [email protected] "]" |
23 | [email protected] " " | 24 | [email protected] " " |
24 | [email protected] | 25 | [email protected] |
@@ -35,10 +36,11 @@ [email protected] | |||
35 | [email protected] | 36 | [email protected] |
36 | [email protected] "#" | 37 | [email protected] "#" |
37 | [email protected] "[" | 38 | [email protected] "[" |
38 | [email protected] | 39 | [email protected] |
39 | [email protected] | 40 | [email protected] |
40 | [email protected] | 41 | [email protected] |
41 | [email protected] "B" | 42 | [email protected] |
43 | [email protected] "B" | ||
42 | [email protected] "]" | 44 | [email protected] "]" |
43 | [email protected] " " | 45 | [email protected] " " |
44 | [email protected] | 46 | [email protected] |
diff --git a/crates/syntax/test_data/parser/inline/ok/0054_record_field_attrs.rast b/crates/syntax/test_data/parser/inline/ok/0054_record_field_attrs.rast index 9ae271817..402950bcc 100644 --- a/crates/syntax/test_data/parser/inline/ok/0054_record_field_attrs.rast +++ b/crates/syntax/test_data/parser/inline/ok/0054_record_field_attrs.rast | |||
@@ -12,18 +12,19 @@ [email protected] | |||
12 | [email protected] | 12 | [email protected] |
13 | [email protected] "#" | 13 | [email protected] "#" |
14 | [email protected] "[" | 14 | [email protected] "[" |
15 | [email protected] | 15 | [email protected] |
16 | [email protected] | 16 | [email protected] |
17 | [email protected] | 17 | [email protected] |
18 | [email protected] "serde" | 18 | [email protected] |
19 | [email protected] | 19 | [email protected] "serde" |
20 | [email protected] "(" | 20 | [email protected] |
21 | [email protected] "with" | 21 | [email protected] "(" |
22 | [email protected] " " | 22 | [email protected] "with" |
23 | [email protected] "=" | 23 | [email protected] " " |
24 | [email protected] " " | 24 | [email protected] "=" |
25 | [email protected] "\"url_serde\"" | 25 | [email protected] " " |
26 | [email protected] ")" | 26 | [email protected] "\"url_serde\"" |
27 | [email protected] ")" | ||
27 | [email protected] "]" | 28 | [email protected] "]" |
28 | [email protected] "\n " | 29 | [email protected] "\n " |
29 | [email protected] | 30 | [email protected] |
diff --git a/crates/syntax/test_data/parser/inline/ok/0115_tuple_field_attrs.rast b/crates/syntax/test_data/parser/inline/ok/0115_tuple_field_attrs.rast index 4d09c9f50..db2b645b0 100644 --- a/crates/syntax/test_data/parser/inline/ok/0115_tuple_field_attrs.rast +++ b/crates/syntax/test_data/parser/inline/ok/0115_tuple_field_attrs.rast | |||
@@ -12,18 +12,19 @@ [email protected] | |||
12 | [email protected] | 12 | [email protected] |
13 | [email protected] "#" | 13 | [email protected] "#" |
14 | [email protected] "[" | 14 | [email protected] "[" |
15 | [email protected] | 15 | [email protected] |
16 | [email protected] | 16 | [email protected] |
17 | [email protected] | 17 | [email protected] |
18 | [email protected] "serde" | 18 | [email protected] |
19 | [email protected] | 19 | [email protected] "serde" |
20 | [email protected] "(" | 20 | [email protected] |
21 | [email protected] "with" | 21 | [email protected] "(" |
22 | [email protected] " " | 22 | [email protected] "with" |
23 | [email protected] "=" | 23 | [email protected] " " |
24 | [email protected] " " | 24 | [email protected] "=" |
25 | [email protected] "\"url_serde\"" | 25 | [email protected] " " |
26 | [email protected] ")" | 26 | [email protected] "\"url_serde\"" |
27 | [email protected] ")" | ||
27 | [email protected] "]" | 28 | [email protected] "]" |
28 | [email protected] "\n " | 29 | [email protected] "\n " |
29 | [email protected] | 30 | [email protected] |
diff --git a/crates/syntax/test_data/parser/inline/ok/0118_impl_inner_attributes.rast b/crates/syntax/test_data/parser/inline/ok/0118_impl_inner_attributes.rast index 141a7b203..24ac1d66a 100644 --- a/crates/syntax/test_data/parser/inline/ok/0118_impl_inner_attributes.rast +++ b/crates/syntax/test_data/parser/inline/ok/0118_impl_inner_attributes.rast | |||
@@ -26,14 +26,15 @@ [email protected] | |||
26 | [email protected] "#" | 26 | [email protected] "#" |
27 | [email protected] "!" | 27 | [email protected] "!" |
28 | [email protected] "[" | 28 | [email protected] "[" |
29 | [email protected] | 29 | [email protected] |
30 | [email protected] | 30 | [email protected] |
31 | [email protected] | 31 | [email protected] |
32 | [email protected] "doc" | 32 | [email protected] |
33 | [email protected] | 33 | [email protected] "doc" |
34 | [email protected] "(" | 34 | [email protected] |
35 | [email protected] "\"This is also a doc c ..." | 35 | [email protected] "(" |
36 | [email protected] ")" | 36 | [email protected] "\"This is also a doc c ..." |
37 | [email protected] ")" | ||
37 | [email protected] "]" | 38 | [email protected] "]" |
38 | [email protected] "\n" | 39 | [email protected] "\n" |
39 | [email protected] "}" | 40 | [email protected] "}" |
diff --git a/crates/syntax/test_data/parser/inline/ok/0120_match_arms_inner_attribute.rast b/crates/syntax/test_data/parser/inline/ok/0120_match_arms_inner_attribute.rast index ec7a00f1d..7fbeee203 100644 --- a/crates/syntax/test_data/parser/inline/ok/0120_match_arms_inner_attribute.rast +++ b/crates/syntax/test_data/parser/inline/ok/0120_match_arms_inner_attribute.rast | |||
@@ -25,42 +25,45 @@ [email protected] | |||
25 | [email protected] "#" | 25 | [email protected] "#" |
26 | [email protected] "!" | 26 | [email protected] "!" |
27 | [email protected] "[" | 27 | [email protected] "[" |
28 | [email protected] | 28 | [email protected] |
29 | [email protected] | 29 | [email protected] |
30 | [email protected] | 30 | [email protected] |
31 | [email protected] "doc" | 31 | [email protected] |
32 | [email protected] | 32 | [email protected] "doc" |
33 | [email protected] "(" | 33 | [email protected] |
34 | [email protected] "\"Inner attribute\"" | 34 | [email protected] "(" |
35 | [email protected] ")" | 35 | [email protected] "\"Inner attribute\"" |
36 | [email protected] ")" | ||
36 | [email protected] "]" | 37 | [email protected] "]" |
37 | [email protected] "\n " | 38 | [email protected] "\n " |
38 | [email protected] | 39 | [email protected] |
39 | [email protected] "#" | 40 | [email protected] "#" |
40 | [email protected] "!" | 41 | [email protected] "!" |
41 | [email protected] "[" | 42 | [email protected] "[" |
42 | [email protected] | 43 | [email protected] |
43 | [email protected] | 44 | [email protected] |
44 | [email protected] | 45 | [email protected] |
45 | [email protected] "doc" | 46 | [email protected] |
46 | [email protected] | 47 | [email protected] "doc" |
47 | [email protected] "(" | 48 | [email protected] |
48 | [email protected] "\"Can be\"" | 49 | [email protected] "(" |
49 | [email protected] ")" | 50 | [email protected] "\"Can be\"" |
51 | [email protected] ")" | ||
50 | [email protected] "]" | 52 | [email protected] "]" |
51 | [email protected] "\n " | 53 | [email protected] "\n " |
52 | [email protected] | 54 | [email protected] |
53 | [email protected] "#" | 55 | [email protected] "#" |
54 | [email protected] "!" | 56 | [email protected] "!" |
55 | [email protected] "[" | 57 | [email protected] "[" |
56 | [email protected] | 58 | [email protected] |
57 | [email protected] | 59 | [email protected] |
58 | [email protected] | 60 | [email protected] |
59 | [email protected] "doc" | 61 | [email protected] |
60 | [email protected] | 62 | [email protected] "doc" |
61 | [email protected] "(" | 63 | [email protected] |
62 | [email protected] "\"Stacked\"" | 64 | [email protected] "(" |
63 | [email protected] ")" | 65 | [email protected] "\"Stacked\"" |
66 | [email protected] ")" | ||
64 | [email protected] "]" | 67 | [email protected] "]" |
65 | [email protected] "\n " | 68 | [email protected] "\n " |
66 | [email protected] | 69 | [email protected] |
diff --git a/crates/syntax/test_data/parser/inline/ok/0121_match_arms_outer_attributes.rast b/crates/syntax/test_data/parser/inline/ok/0121_match_arms_outer_attributes.rast index 97924da05..40852f514 100644 --- a/crates/syntax/test_data/parser/inline/ok/0121_match_arms_outer_attributes.rast +++ b/crates/syntax/test_data/parser/inline/ok/0121_match_arms_outer_attributes.rast | |||
@@ -25,18 +25,19 @@ [email protected] | |||
25 | [email protected] | 25 | [email protected] |
26 | [email protected] "#" | 26 | [email protected] "#" |
27 | [email protected] "[" | 27 | [email protected] "[" |
28 | [email protected] | 28 | [email protected] |
29 | [email protected] | 29 | [email protected] |
30 | [email protected] | 30 | [email protected] |
31 | [email protected] "cfg" | 31 | [email protected] |
32 | [email protected] | 32 | [email protected] "cfg" |
33 | [email protected] "(" | 33 | [email protected] |
34 | [email protected] "feature" | 34 | [email protected] "(" |
35 | [email protected] " " | 35 | [email protected] "feature" |
36 | [email protected] "=" | 36 | [email protected] " " |
37 | [email protected] " " | 37 | [email protected] "=" |
38 | [email protected] "\"some\"" | 38 | [email protected] " " |
39 | [email protected] ")" | 39 | [email protected] "\"some\"" |
40 | [email protected] ")" | ||
40 | [email protected] "]" | 41 | [email protected] "]" |
41 | [email protected] "\n " | 42 | [email protected] "\n " |
42 | [email protected] | 43 | [email protected] |
@@ -53,18 +54,19 @@ [email protected] | |||
53 | [email protected] | 54 | [email protected] |
54 | [email protected] "#" | 55 | [email protected] "#" |
55 | [email protected] "[" | 56 | [email protected] "[" |
56 | [email protected] | 57 | [email protected] |
57 | [email protected] | 58 | [email protected] |
58 | [email protected] | 59 | [email protected] |
59 | [email protected] "cfg" | 60 | [email protected] |
60 | [email protected] | 61 | [email protected] "cfg" |
61 | [email protected] "(" | 62 | [email protected] |
62 | [email protected] "feature" | 63 | [email protected] "(" |
63 | [email protected] " " | 64 | [email protected] "feature" |
64 | [email protected] "=" | 65 | [email protected] " " |
65 | [email protected] " " | 66 | [email protected] "=" |
66 | [email protected] "\"other\"" | 67 | [email protected] " " |
67 | [email protected] ")" | 68 | [email protected] "\"other\"" |
69 | [email protected] ")" | ||
68 | [email protected] "]" | 70 | [email protected] "]" |
69 | [email protected] "\n " | 71 | [email protected] "\n " |
70 | [email protected] | 72 | [email protected] |
@@ -81,52 +83,55 @@ [email protected] | |||
81 | [email protected] | 83 | [email protected] |
82 | [email protected] "#" | 84 | [email protected] "#" |
83 | [email protected] "[" | 85 | [email protected] "[" |
84 | [email protected] | 86 | [email protected] |
85 | [email protected] | 87 | [email protected] |
86 | [email protected] | 88 | [email protected] |
87 | [email protected] "cfg" | 89 | [email protected] |
88 | [email protected] | 90 | [email protected] "cfg" |
89 | [email protected] "(" | 91 | [email protected] |
90 | [email protected] "feature" | 92 | [email protected] "(" |
91 | [email protected] " " | 93 | [email protected] "feature" |
92 | [email protected] "=" | 94 | [email protected] " " |
93 | [email protected] " " | 95 | [email protected] "=" |
94 | [email protected] "\"many\"" | 96 | [email protected] " " |
95 | [email protected] ")" | 97 | [email protected] "\"many\"" |
98 | [email protected] ")" | ||
96 | [email protected] "]" | 99 | [email protected] "]" |
97 | [email protected] "\n " | 100 | [email protected] "\n " |
98 | [email protected] | 101 | [email protected] |
99 | [email protected] "#" | 102 | [email protected] "#" |
100 | [email protected] "[" | 103 | [email protected] "[" |
101 | [email protected] | 104 | [email protected] |
102 | [email protected] | 105 | [email protected] |
103 | [email protected] | 106 | [email protected] |
104 | [email protected] "cfg" | 107 | [email protected] |
105 | [email protected] | 108 | [email protected] "cfg" |
106 | [email protected] "(" | 109 | [email protected] |
107 | [email protected] "feature" | 110 | [email protected] "(" |
108 | [email protected] " " | 111 | [email protected] "feature" |
109 | [email protected] "=" | 112 | [email protected] " " |
110 | [email protected] " " | 113 | [email protected] "=" |
111 | [email protected] "\"attributes\"" | 114 | [email protected] " " |
112 | [email protected] ")" | 115 | [email protected] "\"attributes\"" |
116 | [email protected] ")" | ||
113 | [email protected] "]" | 117 | [email protected] "]" |
114 | [email protected] "\n " | 118 | [email protected] "\n " |
115 | [email protected] | 119 | [email protected] |
116 | [email protected] "#" | 120 | [email protected] "#" |
117 | [email protected] "[" | 121 | [email protected] "[" |
118 | [email protected] | 122 | [email protected] |
119 | [email protected] | 123 | [email protected] |
120 | [email protected] | 124 | [email protected] |
121 | [email protected] "cfg" | 125 | [email protected] |
122 | [email protected] | 126 | [email protected] "cfg" |
123 | [email protected] "(" | 127 | [email protected] |
124 | [email protected] "feature" | 128 | [email protected] "(" |
125 | [email protected] " " | 129 | [email protected] "feature" |
126 | [email protected] "=" | 130 | [email protected] " " |
127 | [email protected] " " | 131 | [email protected] "=" |
128 | [email protected] "\"before\"" | 132 | [email protected] " " |
129 | [email protected] ")" | 133 | [email protected] "\"before\"" |
134 | [email protected] ")" | ||
130 | [email protected] "]" | 135 | [email protected] "]" |
131 | [email protected] "\n " | 136 | [email protected] "\n " |
132 | [email protected] | 137 | [email protected] |
diff --git a/crates/syntax/test_data/parser/inline/ok/0122_generic_lifetime_type_attribute.rast b/crates/syntax/test_data/parser/inline/ok/0122_generic_lifetime_type_attribute.rast index 616aa984e..840181383 100644 --- a/crates/syntax/test_data/parser/inline/ok/0122_generic_lifetime_type_attribute.rast +++ b/crates/syntax/test_data/parser/inline/ok/0122_generic_lifetime_type_attribute.rast | |||
@@ -10,14 +10,15 @@ [email protected] | |||
10 | [email protected] | 10 | [email protected] |
11 | [email protected] "#" | 11 | [email protected] "#" |
12 | [email protected] "[" | 12 | [email protected] "[" |
13 | [email protected] | 13 | [email protected] |
14 | [email protected] | 14 | [email protected] |
15 | [email protected] | 15 | [email protected] |
16 | [email protected] "derive" | 16 | [email protected] |
17 | [email protected] | 17 | [email protected] "derive" |
18 | [email protected] "(" | 18 | [email protected] |
19 | [email protected] "Lifetime" | 19 | [email protected] "(" |
20 | [email protected] ")" | 20 | [email protected] "Lifetime" |
21 | [email protected] ")" | ||
21 | [email protected] "]" | 22 | [email protected] "]" |
22 | [email protected] " " | 23 | [email protected] " " |
23 | [email protected] | 24 | [email protected] |
@@ -28,14 +29,15 @@ [email protected] | |||
28 | [email protected] | 29 | [email protected] |
29 | [email protected] "#" | 30 | [email protected] "#" |
30 | [email protected] "[" | 31 | [email protected] "[" |
31 | [email protected] | 32 | [email protected] |
32 | [email protected] | 33 | [email protected] |
33 | [email protected] | 34 | [email protected] |
34 | [email protected] "derive" | 35 | [email protected] |
35 | [email protected] | 36 | [email protected] "derive" |
36 | [email protected] "(" | 37 | [email protected] |
37 | [email protected] "Type" | 38 | [email protected] "(" |
38 | [email protected] ")" | 39 | [email protected] "Type" |
40 | [email protected] ")" | ||
39 | [email protected] "]" | 41 | [email protected] "]" |
40 | [email protected] " " | 42 | [email protected] " " |
41 | [email protected] | 43 | [email protected] |
diff --git a/crates/syntax/test_data/parser/inline/ok/0125_record_literal_field_with_attr.rast b/crates/syntax/test_data/parser/inline/ok/0125_record_literal_field_with_attr.rast index 54ea2c7c6..5e82214c0 100644 --- a/crates/syntax/test_data/parser/inline/ok/0125_record_literal_field_with_attr.rast +++ b/crates/syntax/test_data/parser/inline/ok/0125_record_literal_field_with_attr.rast | |||
@@ -24,14 +24,15 @@ [email protected] | |||
24 | [email protected] | 24 | [email protected] |
25 | [email protected] "#" | 25 | [email protected] "#" |
26 | [email protected] "[" | 26 | [email protected] "[" |
27 | [email protected] | 27 | [email protected] |
28 | [email protected] | 28 | [email protected] |
29 | [email protected] | 29 | [email protected] |
30 | [email protected] "cfg" | 30 | [email protected] |
31 | [email protected] | 31 | [email protected] "cfg" |
32 | [email protected] "(" | 32 | [email protected] |
33 | [email protected] "test" | 33 | [email protected] "(" |
34 | [email protected] ")" | 34 | [email protected] "test" |
35 | [email protected] ")" | ||
35 | [email protected] "]" | 36 | [email protected] "]" |
36 | [email protected] " " | 37 | [email protected] " " |
37 | [email protected] | 38 | [email protected] |
diff --git a/crates/syntax/test_data/parser/inline/ok/0126_attr_on_expr_stmt.rast b/crates/syntax/test_data/parser/inline/ok/0126_attr_on_expr_stmt.rast index 0342e64f3..178204fec 100644 --- a/crates/syntax/test_data/parser/inline/ok/0126_attr_on_expr_stmt.rast +++ b/crates/syntax/test_data/parser/inline/ok/0126_attr_on_expr_stmt.rast | |||
@@ -15,10 +15,11 @@ [email protected] | |||
15 | [email protected] | 15 | [email protected] |
16 | [email protected] "#" | 16 | [email protected] "#" |
17 | [email protected] "[" | 17 | [email protected] "[" |
18 | [email protected] | 18 | [email protected] |
19 | [email protected] | 19 | [email protected] |
20 | [email protected] | 20 | [email protected] |
21 | [email protected] "A" | 21 | [email protected] |
22 | [email protected] "A" | ||
22 | [email protected] "]" | 23 | [email protected] "]" |
23 | [email protected] " " | 24 | [email protected] " " |
24 | [email protected] | 25 | [email protected] |
@@ -36,10 +37,11 @@ [email protected] | |||
36 | [email protected] | 37 | [email protected] |
37 | [email protected] "#" | 38 | [email protected] "#" |
38 | [email protected] "[" | 39 | [email protected] "[" |
39 | [email protected] | 40 | [email protected] |
40 | [email protected] | 41 | [email protected] |
41 | [email protected] | 42 | [email protected] |
42 | [email protected] "B" | 43 | [email protected] |
44 | [email protected] "B" | ||
43 | [email protected] "]" | 45 | [email protected] "]" |
44 | [email protected] " " | 46 | [email protected] " " |
45 | [email protected] | 47 | [email protected] |
@@ -56,19 +58,21 @@ [email protected] | |||
56 | [email protected] | 58 | [email protected] |
57 | [email protected] "#" | 59 | [email protected] "#" |
58 | [email protected] "[" | 60 | [email protected] "[" |
59 | [email protected] | 61 | [email protected] |
60 | [email protected] | 62 | [email protected] |
61 | [email protected] | 63 | [email protected] |
62 | [email protected] "C" | 64 | [email protected] |
65 | [email protected] "C" | ||
63 | [email protected] "]" | 66 | [email protected] "]" |
64 | [email protected] " " | 67 | [email protected] " " |
65 | [email protected] | 68 | [email protected] |
66 | [email protected] "#" | 69 | [email protected] "#" |
67 | [email protected] "[" | 70 | [email protected] "[" |
68 | [email protected] | 71 | [email protected] |
69 | [email protected] | 72 | [email protected] |
70 | [email protected] | 73 | [email protected] |
71 | [email protected] "D" | 74 | [email protected] |
75 | [email protected] "D" | ||
72 | [email protected] "]" | 76 | [email protected] "]" |
73 | [email protected] " " | 77 | [email protected] " " |
74 | [email protected] | 78 | [email protected] |
@@ -79,10 +83,11 @@ [email protected] | |||
79 | [email protected] | 83 | [email protected] |
80 | [email protected] "#" | 84 | [email protected] "#" |
81 | [email protected] "[" | 85 | [email protected] "[" |
82 | [email protected] | 86 | [email protected] |
83 | [email protected] | 87 | [email protected] |
84 | [email protected] | 88 | [email protected] |
85 | [email protected] "D" | 89 | [email protected] |
90 | [email protected] "D" | ||
86 | [email protected] "]" | 91 | [email protected] "]" |
87 | [email protected] " " | 92 | [email protected] " " |
88 | [email protected] | 93 | [email protected] |
diff --git a/crates/syntax/test_data/parser/inline/ok/0127_attr_on_last_expr_in_block.rast b/crates/syntax/test_data/parser/inline/ok/0127_attr_on_last_expr_in_block.rast index 3b46e5b47..9daac234a 100644 --- a/crates/syntax/test_data/parser/inline/ok/0127_attr_on_last_expr_in_block.rast +++ b/crates/syntax/test_data/parser/inline/ok/0127_attr_on_last_expr_in_block.rast | |||
@@ -19,10 +19,11 @@ [email protected] | |||
19 | [email protected] | 19 | [email protected] |
20 | [email protected] "#" | 20 | [email protected] "#" |
21 | [email protected] "[" | 21 | [email protected] "[" |
22 | [email protected] | 22 | [email protected] |
23 | [email protected] | 23 | [email protected] |
24 | [email protected] | 24 | [email protected] |
25 | [email protected] "A" | 25 | [email protected] |
26 | [email protected] "A" | ||
26 | [email protected] "]" | 27 | [email protected] "]" |
27 | [email protected] " " | 28 | [email protected] " " |
28 | [email protected] | 29 | [email protected] |
@@ -42,10 +43,11 @@ [email protected] | |||
42 | [email protected] | 43 | [email protected] |
43 | [email protected] "#" | 44 | [email protected] "#" |
44 | [email protected] "[" | 45 | [email protected] "[" |
45 | [email protected] | 46 | [email protected] |
46 | [email protected] | 47 | [email protected] |
47 | [email protected] | 48 | [email protected] |
48 | [email protected] "B" | 49 | [email protected] |
50 | [email protected] "B" | ||
49 | [email protected] "]" | 51 | [email protected] "]" |
50 | [email protected] " " | 52 | [email protected] " " |
51 | [email protected] "&" | 53 | [email protected] "&" |
diff --git a/crates/syntax/test_data/parser/inline/ok/0130_let_stmt.rast b/crates/syntax/test_data/parser/inline/ok/0130_let_stmt.rast index 4c07cefa6..c3a79836a 100644 --- a/crates/syntax/test_data/parser/inline/ok/0130_let_stmt.rast +++ b/crates/syntax/test_data/parser/inline/ok/0130_let_stmt.rast | |||
@@ -109,10 +109,11 @@ [email protected] | |||
109 | [email protected] | 109 | [email protected] |
110 | [email protected] "#" | 110 | [email protected] "#" |
111 | [email protected] "[" | 111 | [email protected] "[" |
112 | [email protected] | 112 | [email protected] |
113 | [email protected] | 113 | [email protected] |
114 | [email protected] | 114 | [email protected] |
115 | [email protected] "attr" | 115 | [email protected] |
116 | [email protected] "attr" | ||
116 | [email protected] "]" | 117 | [email protected] "]" |
117 | [email protected] | 118 | [email protected] |
118 | [email protected] "|" | 119 | [email protected] "|" |
diff --git a/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast b/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast index c54e64e3f..891eace59 100644 --- a/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast +++ b/crates/syntax/test_data/parser/inline/ok/0138_self_param_outer_attr.rast | |||
@@ -10,10 +10,11 @@ [email protected] | |||
10 | [email protected] | 10 | [email protected] |
11 | [email protected] "#" | 11 | [email protected] "#" |
12 | [email protected] "[" | 12 | [email protected] "[" |
13 | [email protected] | 13 | [email protected] |
14 | [email protected] | 14 | [email protected] |
15 | [email protected] | 15 | [email protected] |
16 | [email protected] "must_use" | 16 | [email protected] |
17 | [email protected] "must_use" | ||
17 | [email protected] "]" | 18 | [email protected] "]" |
18 | [email protected] " " | 19 | [email protected] " " |
19 | [email protected] | 20 | [email protected] |
diff --git a/crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast b/crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast index a84088bf3..a363e592b 100644 --- a/crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast +++ b/crates/syntax/test_data/parser/inline/ok/0139_param_outer_arg.rast | |||
@@ -10,10 +10,11 @@ [email protected] | |||
10 | [email protected] | 10 | [email protected] |
11 | [email protected] "#" | 11 | [email protected] "#" |
12 | [email protected] "[" | 12 | [email protected] "[" |
13 | [email protected] | 13 | [email protected] |
14 | [email protected] | 14 | [email protected] |
15 | [email protected] | 15 | [email protected] |
16 | [email protected] "attr1" | 16 | [email protected] |
17 | [email protected] "attr1" | ||
17 | [email protected] "]" | 18 | [email protected] "]" |
18 | [email protected] " " | 19 | [email protected] " " |
19 | [email protected] | 20 | [email protected] |
diff --git a/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast index e9202a612..c606a7c9b 100644 --- a/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast +++ b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast | |||
@@ -87,17 +87,18 @@ [email protected] | |||
87 | [email protected] | 87 | [email protected] |
88 | [email protected] "#" | 88 | [email protected] "#" |
89 | [email protected] "[" | 89 | [email protected] "[" |
90 | [email protected] | 90 | [email protected] |
91 | [email protected] | 91 | [email protected] |
92 | [email protected] | 92 | [email protected] |
93 | [email protected] "cfg" | 93 | [email protected] |
94 | [email protected] | 94 | [email protected] "cfg" |
95 | [email protected] "(" | 95 | [email protected] |
96 | [email protected] "any" | 96 | [email protected] "(" |
97 | [email protected] | 97 | [email protected] "any" |
98 | [email protected] "(" | 98 | [email protected] |
99 | [email protected] ")" | 99 | [email protected] "(" |
100 | [email protected] ")" | 100 | [email protected] ")" |
101 | [email protected] ")" | ||
101 | [email protected] "]" | 102 | [email protected] "]" |
102 | [email protected] " " | 103 | [email protected] " " |
103 | [email protected] | 104 | [email protected] |
diff --git a/crates/syntax/test_data/parser/inline/ok/0150_array_attrs.rast b/crates/syntax/test_data/parser/inline/ok/0150_array_attrs.rast index f284aafcd..26cdc2945 100644 --- a/crates/syntax/test_data/parser/inline/ok/0150_array_attrs.rast +++ b/crates/syntax/test_data/parser/inline/ok/0150_array_attrs.rast | |||
@@ -31,14 +31,15 @@ [email protected] | |||
31 | [email protected] | 31 | [email protected] |
32 | [email protected] "#" | 32 | [email protected] "#" |
33 | [email protected] "[" | 33 | [email protected] "[" |
34 | [email protected] | 34 | [email protected] |
35 | [email protected] | 35 | [email protected] |
36 | [email protected] | 36 | [email protected] |
37 | [email protected] "cfg" | 37 | [email protected] |
38 | [email protected] | 38 | [email protected] "cfg" |
39 | [email protected] "(" | 39 | [email protected] |
40 | [email protected] "test" | 40 | [email protected] "(" |
41 | [email protected] ")" | 41 | [email protected] "test" |
42 | [email protected] ")" | ||
42 | [email protected] "]" | 43 | [email protected] "]" |
43 | [email protected] " " | 44 | [email protected] " " |
44 | [email protected] "2" | 45 | [email protected] "2" |
diff --git a/crates/syntax/test_data/parser/inline/ok/0152_arg_with_attr.rast b/crates/syntax/test_data/parser/inline/ok/0152_arg_with_attr.rast index 2905c5f1a..1d20765b0 100644 --- a/crates/syntax/test_data/parser/inline/ok/0152_arg_with_attr.rast +++ b/crates/syntax/test_data/parser/inline/ok/0152_arg_with_attr.rast | |||
@@ -23,10 +23,11 @@ [email protected] | |||
23 | [email protected] | 23 | [email protected] |
24 | [email protected] "#" | 24 | [email protected] "#" |
25 | [email protected] "[" | 25 | [email protected] "[" |
26 | [email protected] | 26 | [email protected] |
27 | [email protected] | 27 | [email protected] |
28 | [email protected] | 28 | [email protected] |
29 | [email protected] "attr" | 29 | [email protected] |
30 | [email protected] "attr" | ||
30 | [email protected] "]" | 31 | [email protected] "]" |
31 | [email protected] " " | 32 | [email protected] " " |
32 | [email protected] "92" | 33 | [email protected] "92" |
diff --git a/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rast b/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rast index d34b21abe..3b6612677 100644 --- a/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rast +++ b/crates/syntax/test_data/parser/inline/ok/0154_tuple_attrs.rast | |||
@@ -34,14 +34,15 @@ [email protected] | |||
34 | [email protected] | 34 | [email protected] |
35 | [email protected] "#" | 35 | [email protected] "#" |
36 | [email protected] "[" | 36 | [email protected] "[" |
37 | [email protected] | 37 | [email protected] |
38 | [email protected] | 38 | [email protected] |
39 | [email protected] | 39 | [email protected] |
40 | [email protected] "cfg" | 40 | [email protected] |
41 | [email protected] | 41 | [email protected] "cfg" |
42 | [email protected] "(" | 42 | [email protected] |
43 | [email protected] "test" | 43 | [email protected] "(" |
44 | [email protected] ")" | 44 | [email protected] "test" |
45 | [email protected] ")" | ||
45 | [email protected] "]" | 46 | [email protected] "]" |
46 | [email protected] " " | 47 | [email protected] " " |
47 | [email protected] "2" | 48 | [email protected] "2" |
diff --git a/crates/syntax/test_data/parser/ok/0006_inner_attributes.rast b/crates/syntax/test_data/parser/ok/0006_inner_attributes.rast index 42587243a..be2d1dc12 100644 --- a/crates/syntax/test_data/parser/ok/0006_inner_attributes.rast +++ b/crates/syntax/test_data/parser/ok/0006_inner_attributes.rast | |||
@@ -3,182 +3,192 @@ [email protected] | |||
3 | [email protected] "#" | 3 | [email protected] "#" |
4 | [email protected] "!" | 4 | [email protected] "!" |
5 | [email protected] "[" | 5 | [email protected] "[" |
6 | [email protected] | 6 | [email protected] |
7 | [email protected] | 7 | [email protected] |
8 | [email protected] | 8 | [email protected] |
9 | [email protected] "attr" | 9 | [email protected] |
10 | [email protected] "attr" | ||
10 | [email protected] "]" | 11 | [email protected] "]" |
11 | [email protected] "\n" | 12 | [email protected] "\n" |
12 | [email protected] | 13 | [email protected] |
13 | [email protected] "#" | 14 | [email protected] "#" |
14 | [email protected] "!" | 15 | [email protected] "!" |
15 | [email protected] "[" | 16 | [email protected] "[" |
16 | [email protected] | 17 | [email protected] |
17 | [email protected] | 18 | [email protected] |
18 | [email protected] | 19 | [email protected] |
19 | [email protected] "attr" | 20 | [email protected] |
20 | [email protected] | 21 | [email protected] "attr" |
21 | [email protected] "(" | 22 | [email protected] |
22 | [email protected] "true" | 23 | [email protected] "(" |
23 | [email protected] ")" | 24 | [email protected] "true" |
25 | [email protected] ")" | ||
24 | [email protected] "]" | 26 | [email protected] "]" |
25 | [email protected] "\n" | 27 | [email protected] "\n" |
26 | [email protected] | 28 | [email protected] |
27 | [email protected] "#" | 29 | [email protected] "#" |
28 | [email protected] "!" | 30 | [email protected] "!" |
29 | [email protected] "[" | 31 | [email protected] "[" |
30 | [email protected] | 32 | [email protected] |
31 | [email protected] | 33 | [email protected] |
32 | [email protected] | 34 | [email protected] |
33 | [email protected] "attr" | 35 | [email protected] |
34 | [email protected] | 36 | [email protected] "attr" |
35 | [email protected] "(" | 37 | [email protected] |
36 | [email protected] "ident" | 38 | [email protected] "(" |
37 | [email protected] ")" | 39 | [email protected] "ident" |
40 | [email protected] ")" | ||
38 | [email protected] "]" | 41 | [email protected] "]" |
39 | [email protected] "\n" | 42 | [email protected] "\n" |
40 | [email protected] | 43 | [email protected] |
41 | [email protected] "#" | 44 | [email protected] "#" |
42 | [email protected] "!" | 45 | [email protected] "!" |
43 | [email protected] "[" | 46 | [email protected] "[" |
44 | [email protected] | 47 | [email protected] |
45 | [email protected] | 48 | [email protected] |
46 | [email protected] | 49 | [email protected] |
47 | [email protected] "attr" | 50 | [email protected] |
48 | [email protected] | 51 | [email protected] "attr" |
49 | [email protected] "(" | 52 | [email protected] |
50 | [email protected] "ident" | 53 | [email protected] "(" |
51 | [email protected] "," | 54 | [email protected] "ident" |
52 | [email protected] " " | 55 | [email protected] "," |
53 | [email protected] "100" | 56 | [email protected] " " |
54 | [email protected] "," | 57 | [email protected] "100" |
55 | [email protected] " " | 58 | [email protected] "," |
56 | [email protected] "true" | 59 | [email protected] " " |
57 | [email protected] "," | 60 | [email protected] "true" |
58 | [email protected] " " | 61 | [email protected] "," |
59 | [email protected] "\"true\"" | 62 | [email protected] " " |
60 | [email protected] "," | 63 | [email protected] "\"true\"" |
61 | [email protected] " " | 64 | [email protected] "," |
62 | [email protected] "ident" | 65 | [email protected] " " |
63 | [email protected] " " | 66 | [email protected] "ident" |
64 | [email protected] "=" | 67 | [email protected] " " |
65 | [email protected] " " | 68 | [email protected] "=" |
66 | [email protected] "100" | 69 | [email protected] " " |
67 | [email protected] "," | 70 | [email protected] "100" |
68 | [email protected] " " | 71 | [email protected] "," |
69 | [email protected] "ident" | 72 | [email protected] " " |
70 | [email protected] " " | 73 | [email protected] "ident" |
71 | [email protected] "=" | 74 | [email protected] " " |
72 | [email protected] " " | 75 | [email protected] "=" |
73 | [email protected] "\"hello\"" | 76 | [email protected] " " |
74 | [email protected] "," | 77 | [email protected] "\"hello\"" |
75 | [email protected] " " | 78 | [email protected] "," |
76 | [email protected] "ident" | 79 | [email protected] " " |
77 | [email protected] | 80 | [email protected] "ident" |
78 | [email protected] "(" | 81 | [email protected] |
79 | [email protected] "100" | 82 | [email protected] "(" |
80 | [email protected] ")" | 83 | [email protected] "100" |
81 | [email protected] ")" | 84 | [email protected] ")" |
85 | [email protected] ")" | ||
82 | [email protected] "]" | 86 | [email protected] "]" |
83 | [email protected] "\n" | 87 | [email protected] "\n" |
84 | [email protected] | 88 | [email protected] |
85 | [email protected] "#" | 89 | [email protected] "#" |
86 | [email protected] "!" | 90 | [email protected] "!" |
87 | [email protected] "[" | 91 | [email protected] "[" |
88 | [email protected] | 92 | [email protected] |
89 | [email protected] | 93 | [email protected] |
90 | [email protected] | 94 | [email protected] |
91 | [email protected] "attr" | 95 | [email protected] |
92 | [email protected] | 96 | [email protected] "attr" |
93 | [email protected] "(" | 97 | [email protected] |
94 | [email protected] "100" | 98 | [email protected] "(" |
95 | [email protected] ")" | 99 | [email protected] "100" |
100 | [email protected] ")" | ||
96 | [email protected] "]" | 101 | [email protected] "]" |
97 | [email protected] "\n" | 102 | [email protected] "\n" |
98 | [email protected] | 103 | [email protected] |
99 | [email protected] "#" | 104 | [email protected] "#" |
100 | [email protected] "!" | 105 | [email protected] "!" |
101 | [email protected] "[" | 106 | [email protected] "[" |
102 | [email protected] | 107 | [email protected] |
103 | [email protected] | 108 | [email protected] |
104 | [email protected] | 109 | [email protected] |
105 | [email protected] "attr" | 110 | [email protected] |
106 | [email protected] | 111 | [email protected] "attr" |
107 | [email protected] "(" | 112 | [email protected] |
108 | [email protected] "enabled" | 113 | [email protected] "(" |
109 | [email protected] " " | 114 | [email protected] "enabled" |
110 | [email protected] "=" | 115 | [email protected] " " |
111 | [email protected] " " | 116 | [email protected] "=" |
112 | [email protected] "true" | 117 | [email protected] " " |
113 | [email protected] ")" | 118 | [email protected] "true" |
119 | [email protected] ")" | ||
114 | [email protected] "]" | 120 | [email protected] "]" |
115 | [email protected] "\n" | 121 | [email protected] "\n" |
116 | [email protected] | 122 | [email protected] |
117 | [email protected] "#" | 123 | [email protected] "#" |
118 | [email protected] "!" | 124 | [email protected] "!" |
119 | [email protected] "[" | 125 | [email protected] "[" |
120 | [email protected] | 126 | [email protected] |
121 | [email protected] | 127 | [email protected] |
122 | [email protected] | 128 | [email protected] |
123 | [email protected] "enabled" | 129 | [email protected] |
124 | [email protected] | 130 | [email protected] "enabled" |
125 | [email protected] "(" | 131 | [email protected] |
126 | [email protected] "true" | 132 | [email protected] "(" |
127 | [email protected] ")" | 133 | [email protected] "true" |
134 | [email protected] ")" | ||
128 | [email protected] "]" | 135 | [email protected] "]" |
129 | [email protected] "\n" | 136 | [email protected] "\n" |
130 | [email protected] | 137 | [email protected] |
131 | [email protected] "#" | 138 | [email protected] "#" |
132 | [email protected] "!" | 139 | [email protected] "!" |
133 | [email protected] "[" | 140 | [email protected] "[" |
134 | [email protected] | 141 | [email protected] |
135 | [email protected] | 142 | [email protected] |
136 | [email protected] | 143 | [email protected] |
137 | [email protected] "attr" | 144 | [email protected] |
138 | [email protected] | 145 | [email protected] "attr" |
139 | [email protected] "(" | 146 | [email protected] |
140 | [email protected] "\"hello\"" | 147 | [email protected] "(" |
141 | [email protected] ")" | 148 | [email protected] "\"hello\"" |
149 | [email protected] ")" | ||
142 | [email protected] "]" | 150 | [email protected] "]" |
143 | [email protected] "\n" | 151 | [email protected] "\n" |
144 | [email protected] | 152 | [email protected] |
145 | [email protected] "#" | 153 | [email protected] "#" |
146 | [email protected] "!" | 154 | [email protected] "!" |
147 | [email protected] "[" | 155 | [email protected] "[" |
148 | [email protected] | 156 | [email protected] |
149 | [email protected] | 157 | [email protected] |
150 | [email protected] | 158 | [email protected] |
151 | [email protected] "repr" | 159 | [email protected] |
152 | [email protected] | 160 | [email protected] "repr" |
153 | [email protected] "(" | 161 | [email protected] |
154 | [email protected] "C" | 162 | [email protected] "(" |
155 | [email protected] "," | 163 | [email protected] "C" |
156 | [email protected] " " | 164 | [email protected] "," |
157 | [email protected] "align" | 165 | [email protected] " " |
158 | [email protected] " " | 166 | [email protected] "align" |
159 | [email protected] "=" | 167 | [email protected] " " |
160 | [email protected] " " | 168 | [email protected] "=" |
161 | [email protected] "4" | 169 | [email protected] " " |
162 | [email protected] ")" | 170 | [email protected] "4" |
171 | [email protected] ")" | ||
163 | [email protected] "]" | 172 | [email protected] "]" |
164 | [email protected] "\n" | 173 | [email protected] "\n" |
165 | [email protected] | 174 | [email protected] |
166 | [email protected] "#" | 175 | [email protected] "#" |
167 | [email protected] "!" | 176 | [email protected] "!" |
168 | [email protected] "[" | 177 | [email protected] "[" |
169 | [email protected] | 178 | [email protected] |
170 | [email protected] | 179 | [email protected] |
171 | [email protected] | 180 | [email protected] |
172 | [email protected] "repr" | 181 | [email protected] |
173 | [email protected] | 182 | [email protected] "repr" |
174 | [email protected] "(" | 183 | [email protected] |
175 | [email protected] "C" | 184 | [email protected] "(" |
176 | [email protected] "," | 185 | [email protected] "C" |
177 | [email protected] " " | 186 | [email protected] "," |
178 | [email protected] "align" | 187 | [email protected] " " |
179 | [email protected] | 188 | [email protected] "align" |
180 | [email protected] "(" | 189 | [email protected] |
181 | [email protected] "4" | 190 | [email protected] "(" |
182 | [email protected] ")" | 191 | [email protected] "4" |
183 | [email protected] ")" | 192 | [email protected] ")" |
193 | [email protected] ")" | ||
184 | [email protected] "]" | 194 | [email protected] "]" |
diff --git a/crates/syntax/test_data/parser/ok/0008_mod_item.rast b/crates/syntax/test_data/parser/ok/0008_mod_item.rast index b2c1d791f..8b1e0a52d 100644 --- a/crates/syntax/test_data/parser/ok/0008_mod_item.rast +++ b/crates/syntax/test_data/parser/ok/0008_mod_item.rast | |||
@@ -65,10 +65,11 @@ [email protected] | |||
65 | [email protected] "#" | 65 | [email protected] "#" |
66 | [email protected] "!" | 66 | [email protected] "!" |
67 | [email protected] "[" | 67 | [email protected] "[" |
68 | [email protected] | 68 | [email protected] |
69 | [email protected] | 69 | [email protected] |
70 | [email protected] | 70 | [email protected] |
71 | [email protected] "attr" | 71 | [email protected] |
72 | [email protected] "attr" | ||
72 | [email protected] "]" | 73 | [email protected] "]" |
73 | [email protected] "\n " | 74 | [email protected] "\n " |
74 | [email protected] | 75 | [email protected] |
diff --git a/crates/syntax/test_data/parser/ok/0011_outer_attribute.rast b/crates/syntax/test_data/parser/ok/0011_outer_attribute.rast index 478fdba75..ff5877a7b 100644 --- a/crates/syntax/test_data/parser/ok/0011_outer_attribute.rast +++ b/crates/syntax/test_data/parser/ok/0011_outer_attribute.rast | |||
@@ -3,23 +3,25 @@ [email protected] | |||
3 | [email protected] | 3 | [email protected] |
4 | [email protected] "#" | 4 | [email protected] "#" |
5 | [email protected] "[" | 5 | [email protected] "[" |
6 | [email protected] | 6 | [email protected] |
7 | [email protected] | 7 | [email protected] |
8 | [email protected] | 8 | [email protected] |
9 | [email protected] "cfg" | 9 | [email protected] |
10 | [email protected] | 10 | [email protected] "cfg" |
11 | [email protected] "(" | 11 | [email protected] |
12 | [email protected] "test" | 12 | [email protected] "(" |
13 | [email protected] ")" | 13 | [email protected] "test" |
14 | [email protected] ")" | ||
14 | [email protected] "]" | 15 | [email protected] "]" |
15 | [email protected] "\n" | 16 | [email protected] "\n" |
16 | [email protected] | 17 | [email protected] |
17 | [email protected] "#" | 18 | [email protected] "#" |
18 | [email protected] "[" | 19 | [email protected] "[" |
19 | [email protected] | 20 | [email protected] |
20 | [email protected] | 21 | [email protected] |
21 | [email protected] | 22 | [email protected] |
22 | [email protected] "ignore" | 23 | [email protected] |
24 | [email protected] "ignore" | ||
23 | [email protected] "]" | 25 | [email protected] "]" |
24 | [email protected] "\n" | 26 | [email protected] "\n" |
25 | [email protected] "fn" | 27 | [email protected] "fn" |
@@ -38,15 +40,16 @@ [email protected] | |||
38 | [email protected] | 40 | [email protected] |
39 | [email protected] "#" | 41 | [email protected] "#" |
40 | [email protected] "[" | 42 | [email protected] "[" |
41 | [email protected] | 43 | [email protected] |
42 | [email protected] | 44 | [email protected] |
43 | [email protected] | 45 | [email protected] |
44 | [email protected] "path" | 46 | [email protected] |
45 | [email protected] " " | 47 | [email protected] "path" |
46 | [email protected] "=" | 48 | [email protected] " " |
47 | [email protected] " " | 49 | [email protected] "=" |
48 | [email protected] | 50 | [email protected] " " |
49 | [email protected] "\"a.rs\"" | 51 | [email protected] |
52 | [email protected] "\"a.rs\"" | ||
50 | [email protected] "]" | 53 | [email protected] "]" |
51 | [email protected] "\n" | 54 | [email protected] "\n" |
52 | [email protected] "mod" | 55 | [email protected] "mod" |
diff --git a/crates/syntax/test_data/parser/ok/0017_attr_trailing_comma.rast b/crates/syntax/test_data/parser/ok/0017_attr_trailing_comma.rast index a3e091ad3..94791f771 100644 --- a/crates/syntax/test_data/parser/ok/0017_attr_trailing_comma.rast +++ b/crates/syntax/test_data/parser/ok/0017_attr_trailing_comma.rast | |||
@@ -3,15 +3,16 @@ [email protected] | |||
3 | [email protected] | 3 | [email protected] |
4 | [email protected] "#" | 4 | [email protected] "#" |
5 | [email protected] "[" | 5 | [email protected] "[" |
6 | [email protected] | 6 | [email protected] |
7 | [email protected] | 7 | [email protected] |
8 | [email protected] | 8 | [email protected] |
9 | [email protected] "foo" | 9 | [email protected] |
10 | [email protected] | 10 | [email protected] "foo" |
11 | [email protected] "(" | 11 | [email protected] |
12 | [email protected] "a" | 12 | [email protected] "(" |
13 | [email protected] "," | 13 | [email protected] "a" |
14 | [email protected] ")" | 14 | [email protected] "," |
15 | [email protected] ")" | ||
15 | [email protected] "]" | 16 | [email protected] "]" |
16 | [email protected] "\n" | 17 | [email protected] "\n" |
17 | [email protected] "fn" | 18 | [email protected] "fn" |
diff --git a/crates/syntax/test_data/parser/ok/0035_weird_exprs.rast b/crates/syntax/test_data/parser/ok/0035_weird_exprs.rast index 46b192dc1..20675dbf5 100644 --- a/crates/syntax/test_data/parser/ok/0035_weird_exprs.rast +++ b/crates/syntax/test_data/parser/ok/0035_weird_exprs.rast | |||
@@ -11,71 +11,76 @@ [email protected] | |||
11 | [email protected] "#" | 11 | [email protected] "#" |
12 | [email protected] "!" | 12 | [email protected] "!" |
13 | [email protected] "[" | 13 | [email protected] "[" |
14 | [email protected] | 14 | [email protected] |
15 | [email protected] | 15 | [email protected] |
16 | [email protected] | 16 | [email protected] |
17 | [email protected] "allow" | 17 | [email protected] |
18 | [email protected] | 18 | [email protected] "allow" |
19 | [email protected] "(" | 19 | [email protected] |
20 | [email protected] "non_camel_case_types" | 20 | [email protected] "(" |
21 | [email protected] ")" | 21 | [email protected] "non_camel_case_types" |
22 | [email protected] ")" | ||
22 | [email protected] "]" | 23 | [email protected] "]" |
23 | [email protected] "\n" | 24 | [email protected] "\n" |
24 | [email protected] | 25 | [email protected] |
25 | [email protected] "#" | 26 | [email protected] "#" |
26 | [email protected] "!" | 27 | [email protected] "!" |
27 | [email protected] "[" | 28 | [email protected] "[" |
28 | [email protected] | 29 | [email protected] |
29 | [email protected] | 30 | [email protected] |
30 | [email protected] | 31 | [email protected] |
31 | [email protected] "allow" | 32 | [email protected] |
32 | [email protected] | 33 | [email protected] "allow" |
33 | [email protected] "(" | 34 | [email protected] |
34 | [email protected] "dead_code" | 35 | [email protected] "(" |
35 | [email protected] ")" | 36 | [email protected] "dead_code" |
37 | [email protected] ")" | ||
36 | [email protected] "]" | 38 | [email protected] "]" |
37 | [email protected] "\n" | 39 | [email protected] "\n" |
38 | [email protected] | 40 | [email protected] |
39 | [email protected] "#" | 41 | [email protected] "#" |
40 | [email protected] "!" | 42 | [email protected] "!" |
41 | [email protected] "[" | 43 | [email protected] "[" |
42 | [email protected] | 44 | [email protected] |
43 | [email protected] | 45 | [email protected] |
44 | [email protected] | 46 | [email protected] |
45 | [email protected] "allow" | 47 | [email protected] |
46 | [email protected] | 48 | [email protected] "allow" |
47 | [email protected] "(" | 49 | [email protected] |
48 | [email protected] "unreachable_code" | 50 | [email protected] "(" |
49 | [email protected] ")" | 51 | [email protected] "unreachable_code" |
52 | [email protected] ")" | ||
50 | [email protected] "]" | 53 | [email protected] "]" |
51 | [email protected] "\n" | 54 | [email protected] "\n" |
52 | [email protected] | 55 | [email protected] |
53 | [email protected] "#" | 56 | [email protected] "#" |
54 | [email protected] "!" | 57 | [email protected] "!" |
55 | [email protected] "[" | 58 | [email protected] "[" |
56 | [email protected] | 59 | [email protected] |
57 | [email protected] | 60 | [email protected] |
58 | [email protected] | 61 | [email protected] |
59 | [email protected] "allow" | 62 | [email protected] |
60 | [email protected] | 63 | [email protected] "allow" |
61 | [email protected] "(" | 64 | [email protected] |
62 | [email protected] "unused_parens" | 65 | [email protected] "(" |
63 | [email protected] ")" | 66 | [email protected] "unused_parens" |
67 | [email protected] ")" | ||
64 | [email protected] "]" | 68 | [email protected] "]" |
65 | [email protected] "\n\n" | 69 | [email protected] "\n\n" |
66 | [email protected] | 70 | [email protected] |
67 | [email protected] "#" | 71 | [email protected] "#" |
68 | [email protected] "!" | 72 | [email protected] "!" |
69 | [email protected] "[" | 73 | [email protected] "[" |
70 | [email protected] | 74 | [email protected] |
71 | [email protected] | 75 | [email protected] |
72 | [email protected] | 76 | [email protected] |
73 | [email protected] "recursion_limit" | 77 | [email protected] |
74 | [email protected] " " | 78 | [email protected] "recursion_limit" |
75 | [email protected] "=" | 79 | [email protected] " " |
76 | [email protected] " " | 80 | [email protected] "=" |
77 | [email protected] | 81 | [email protected] " " |
78 | [email protected] "\"128\"" | 82 | [email protected] |
83 | [email protected] "\"128\"" | ||
79 | [email protected] "]" | 84 | [email protected] "]" |
80 | [email protected] "\n\n" | 85 | [email protected] "\n\n" |
81 | [email protected] | 86 | [email protected] |
diff --git a/crates/syntax/test_data/parser/ok/0044_let_attrs.rast b/crates/syntax/test_data/parser/ok/0044_let_attrs.rast index af44a4dbe..d0e7a1dbe 100644 --- a/crates/syntax/test_data/parser/ok/0044_let_attrs.rast +++ b/crates/syntax/test_data/parser/ok/0044_let_attrs.rast | |||
@@ -17,18 +17,19 @@ [email protected] | |||
17 | [email protected] | 17 | [email protected] |
18 | [email protected] "#" | 18 | [email protected] "#" |
19 | [email protected] "[" | 19 | [email protected] "[" |
20 | [email protected] | 20 | [email protected] |
21 | [email protected] | 21 | [email protected] |
22 | [email protected] | 22 | [email protected] |
23 | [email protected] "cfg" | 23 | [email protected] |
24 | [email protected] | 24 | [email protected] "cfg" |
25 | [email protected] "(" | 25 | [email protected] |
26 | [email protected] "feature" | 26 | [email protected] "(" |
27 | [email protected] " " | 27 | [email protected] "feature" |
28 | [email protected] "=" | 28 | [email protected] " " |
29 | [email protected] " " | 29 | [email protected] "=" |
30 | [email protected] "\"backtrace\"" | 30 | [email protected] " " |
31 | [email protected] ")" | 31 | [email protected] "\"backtrace\"" |
32 | [email protected] ")" | ||
32 | [email protected] "]" | 33 | [email protected] "]" |
33 | [email protected] "\n " | 34 | [email protected] "\n " |
34 | [email protected] "let" | 35 | [email protected] "let" |
diff --git a/crates/syntax/test_data/parser/ok/0045_block_attrs.rast b/crates/syntax/test_data/parser/ok/0045_block_attrs.rast index 5e50b4e0b..7fbd635d0 100644 --- a/crates/syntax/test_data/parser/ok/0045_block_attrs.rast +++ b/crates/syntax/test_data/parser/ok/0045_block_attrs.rast | |||
@@ -15,14 +15,15 @@ [email protected] | |||
15 | [email protected] "#" | 15 | [email protected] "#" |
16 | [email protected] "!" | 16 | [email protected] "!" |
17 | [email protected] "[" | 17 | [email protected] "[" |
18 | [email protected] | 18 | [email protected] |
19 | [email protected] | 19 | [email protected] |
20 | [email protected] | 20 | [email protected] |
21 | [email protected] "doc" | 21 | [email protected] |
22 | [email protected] | 22 | [email protected] "doc" |
23 | [email protected] "(" | 23 | [email protected] |
24 | [email protected] "\"Inner attributes all ..." | 24 | [email protected] "(" |
25 | [email protected] ")" | 25 | [email protected] "\"Inner attributes all ..." |
26 | [email protected] ")" | ||
26 | [email protected] "]" | 27 | [email protected] "]" |
27 | [email protected] "\n " | 28 | [email protected] "\n " |
28 | [email protected] "//! As are ModuleDoc ..." | 29 | [email protected] "//! As are ModuleDoc ..." |
@@ -35,28 +36,30 @@ [email protected] | |||
35 | [email protected] "#" | 36 | [email protected] "#" |
36 | [email protected] "!" | 37 | [email protected] "!" |
37 | [email protected] "[" | 38 | [email protected] "[" |
38 | [email protected] | 39 | [email protected] |
39 | [email protected] | 40 | [email protected] |
40 | [email protected] | 41 | [email protected] |
41 | [email protected] "doc" | 42 | [email protected] |
42 | [email protected] | 43 | [email protected] "doc" |
43 | [email protected] "(" | 44 | [email protected] |
44 | [email protected] "\"Inner attributes are ..." | 45 | [email protected] "(" |
45 | [email protected] ")" | 46 | [email protected] "\"Inner attributes are ..." |
47 | [email protected] ")" | ||
46 | [email protected] "]" | 48 | [email protected] "]" |
47 | [email protected] "\n " | 49 | [email protected] "\n " |
48 | [email protected] | 50 | [email protected] |
49 | [email protected] "#" | 51 | [email protected] "#" |
50 | [email protected] "!" | 52 | [email protected] "!" |
51 | [email protected] "[" | 53 | [email protected] "[" |
52 | [email protected] | 54 | [email protected] |
53 | [email protected] | 55 | [email protected] |
54 | [email protected] | 56 | [email protected] |
55 | [email protected] "doc" | 57 | [email protected] |
56 | [email protected] | 58 | [email protected] "doc" |
57 | [email protected] "(" | 59 | [email protected] |
58 | [email protected] "\"Being validated is n ..." | 60 | [email protected] "(" |
59 | [email protected] ")" | 61 | [email protected] "\"Being validated is n ..." |
62 | [email protected] ")" | ||
60 | [email protected] "]" | 63 | [email protected] "]" |
61 | [email protected] "\n " | 64 | [email protected] "\n " |
62 | [email protected] "//! As are ModuleDoc ..." | 65 | [email protected] "//! As are ModuleDoc ..." |
@@ -71,14 +74,15 @@ [email protected] | |||
71 | [email protected] "#" | 74 | [email protected] "#" |
72 | [email protected] "!" | 75 | [email protected] "!" |
73 | [email protected] "[" | 76 | [email protected] "[" |
74 | [email protected] | 77 | [email protected] |
75 | [email protected] | 78 | [email protected] |
76 | [email protected] | 79 | [email protected] |
77 | [email protected] "doc" | 80 | [email protected] |
78 | [email protected] | 81 | [email protected] "doc" |
79 | [email protected] "(" | 82 | [email protected] |
80 | [email protected] "\"Inner attributes are ..." | 83 | [email protected] "(" |
81 | [email protected] ")" | 84 | [email protected] "\"Inner attributes are ..." |
85 | [email protected] ")" | ||
82 | [email protected] "]" | 86 | [email protected] "]" |
83 | [email protected] "\n " | 87 | [email protected] "\n " |
84 | [email protected] "//! As are ModuleDoc ..." | 88 | [email protected] "//! As are ModuleDoc ..." |
@@ -111,14 +115,15 @@ [email protected] | |||
111 | [email protected] | 115 | [email protected] |
112 | [email protected] "#" | 116 | [email protected] "#" |
113 | [email protected] "[" | 117 | [email protected] "[" |
114 | [email protected] | 118 | [email protected] |
115 | [email protected] | 119 | [email protected] |
116 | [email protected] | 120 | [email protected] |
117 | [email protected] "doc" | 121 | [email protected] |
118 | [email protected] | 122 | [email protected] "doc" |
119 | [email protected] "(" | 123 | [email protected] |
120 | [email protected] "\"Outer attributes are ..." | 124 | [email protected] "(" |
121 | [email protected] ")" | 125 | [email protected] "\"Outer attributes are ..." |
126 | [email protected] ")" | ||
122 | [email protected] "]" | 127 | [email protected] "]" |
123 | [email protected] " " | 128 | [email protected] " " |
124 | [email protected] "{" | 129 | [email protected] "{" |
@@ -200,14 +205,15 @@ [email protected] | |||
200 | [email protected] "#" | 205 | [email protected] "#" |
201 | [email protected] "!" | 206 | [email protected] "!" |
202 | [email protected] "[" | 207 | [email protected] "[" |
203 | [email protected] | 208 | [email protected] |
204 | [email protected] | 209 | [email protected] |
205 | [email protected] | 210 | [email protected] |
206 | [email protected] "allow" | 211 | [email protected] |
207 | [email protected] | 212 | [email protected] "allow" |
208 | [email protected] "(" | 213 | [email protected] |
209 | [email protected] "unused_variables" | 214 | [email protected] "(" |
210 | [email protected] ")" | 215 | [email protected] "unused_variables" |
216 | [email protected] ")" | ||
211 | [email protected] "]" | 217 | [email protected] "]" |
212 | [email protected] " " | 218 | [email protected] " " |
213 | [email protected] "// this is `inner_at ..." | 219 | [email protected] "// this is `inner_at ..." |
diff --git a/crates/syntax/test_data/parser/ok/0046_extern_inner_attributes.rast b/crates/syntax/test_data/parser/ok/0046_extern_inner_attributes.rast index 37594769a..854ff9d56 100644 --- a/crates/syntax/test_data/parser/ok/0046_extern_inner_attributes.rast +++ b/crates/syntax/test_data/parser/ok/0046_extern_inner_attributes.rast | |||
@@ -14,14 +14,15 @@ [email protected] | |||
14 | [email protected] "#" | 14 | [email protected] "#" |
15 | [email protected] "!" | 15 | [email protected] "!" |
16 | [email protected] "[" | 16 | [email protected] "[" |
17 | [email protected] | 17 | [email protected] |
18 | [email protected] | 18 | [email protected] |
19 | [email protected] | 19 | [email protected] |
20 | [email protected] "doc" | 20 | [email protected] |
21 | [email protected] | 21 | [email protected] "doc" |
22 | [email protected] "(" | 22 | [email protected] |
23 | [email protected] "\"This is also a doc c ..." | 23 | [email protected] "(" |
24 | [email protected] ")" | 24 | [email protected] "\"This is also a doc c ..." |
25 | [email protected] ")" | ||
25 | [email protected] "]" | 26 | [email protected] "]" |
26 | [email protected] "\n" | 27 | [email protected] "\n" |
27 | [email protected] "}" | 28 | [email protected] "}" |
diff --git a/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast b/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast index 88470c41c..f935a0df5 100644 --- a/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast +++ b/crates/syntax/test_data/parser/ok/0051_parameter_attrs.rast | |||
@@ -10,19 +10,21 @@ [email protected] | |||
10 | [email protected] | 10 | [email protected] |
11 | [email protected] "#" | 11 | [email protected] "#" |
12 | [email protected] "[" | 12 | [email protected] "[" |
13 | [email protected] | 13 | [email protected] |
14 | [email protected] | 14 | [email protected] |
15 | [email protected] | 15 | [email protected] |
16 | [email protected] "attr1" | 16 | [email protected] |
17 | [email protected] "attr1" | ||
17 | [email protected] "]" | 18 | [email protected] "]" |
18 | [email protected] " " | 19 | [email protected] " " |
19 | [email protected] | 20 | [email protected] |
20 | [email protected] "#" | 21 | [email protected] "#" |
21 | [email protected] "[" | 22 | [email protected] "[" |
22 | [email protected] | 23 | [email protected] |
23 | [email protected] | 24 | [email protected] |
24 | [email protected] | 25 | [email protected] |
25 | [email protected] "attr2" | 26 | [email protected] |
27 | [email protected] "attr2" | ||
26 | [email protected] "]" | 28 | [email protected] "]" |
27 | [email protected] " " | 29 | [email protected] " " |
28 | [email protected] | 30 | [email protected] |
@@ -52,10 +54,11 @@ [email protected] | |||
52 | [email protected] | 54 | [email protected] |
53 | [email protected] "#" | 55 | [email protected] "#" |
54 | [email protected] "[" | 56 | [email protected] "[" |
55 | [email protected] | 57 | [email protected] |
56 | [email protected] | 58 | [email protected] |
57 | [email protected] | 59 | [email protected] |
58 | [email protected] "attr1" | 60 | [email protected] |
61 | [email protected] "attr1" | ||
59 | [email protected] "]" | 62 | [email protected] "]" |
60 | [email protected] " " | 63 | [email protected] " " |
61 | [email protected] | 64 | [email protected] |
@@ -111,10 +114,11 @@ [email protected] | |||
111 | [email protected] | 114 | [email protected] |
112 | [email protected] "#" | 115 | [email protected] "#" |
113 | [email protected] "[" | 116 | [email protected] "[" |
114 | [email protected] | 117 | [email protected] |
115 | [email protected] | 118 | [email protected] |
116 | [email protected] | 119 | [email protected] |
117 | [email protected] "attr" | 120 | [email protected] |
121 | [email protected] "attr" | ||
118 | [email protected] "]" | 122 | [email protected] "]" |
119 | [email protected] " " | 123 | [email protected] " " |
120 | [email protected] "..." | 124 | [email protected] "..." |
@@ -157,10 +161,11 @@ [email protected] | |||
157 | [email protected] | 161 | [email protected] |
158 | [email protected] "#" | 162 | [email protected] "#" |
159 | [email protected] "[" | 163 | [email protected] "[" |
160 | [email protected] | 164 | [email protected] |
161 | [email protected] | 165 | [email protected] |
162 | [email protected] | 166 | [email protected] |
163 | [email protected] "attr" | 167 | [email protected] |
168 | [email protected] "attr" | ||
164 | [email protected] "]" | 169 | [email protected] "]" |
165 | [email protected] " " | 170 | [email protected] " " |
166 | [email protected] | 171 | [email protected] |
@@ -207,10 +212,11 @@ [email protected] | |||
207 | [email protected] | 212 | [email protected] |
208 | [email protected] "#" | 213 | [email protected] "#" |
209 | [email protected] "[" | 214 | [email protected] "[" |
210 | [email protected] | 215 | [email protected] |
211 | [email protected] | 216 | [email protected] |
212 | [email protected] | 217 | [email protected] |
213 | [email protected] "attr" | 218 | [email protected] |
219 | [email protected] "attr" | ||
214 | [email protected] "]" | 220 | [email protected] "]" |
215 | [email protected] " " | 221 | [email protected] " " |
216 | [email protected] | 222 | [email protected] |
@@ -229,10 +235,11 @@ [email protected] | |||
229 | [email protected] "#" | 235 | [email protected] "#" |
230 | [email protected] " " | 236 | [email protected] " " |
231 | [email protected] "[" | 237 | [email protected] "[" |
232 | [email protected] | 238 | [email protected] |
233 | [email protected] | 239 | [email protected] |
234 | [email protected] | 240 | [email protected] |
235 | [email protected] "attr" | 241 | [email protected] |
242 | [email protected] "attr" | ||
236 | [email protected] "]" | 243 | [email protected] "]" |
237 | [email protected] " " | 244 | [email protected] " " |
238 | [email protected] | 245 | [email protected] |
@@ -275,10 +282,11 @@ [email protected] | |||
275 | [email protected] | 282 | [email protected] |
276 | [email protected] "#" | 283 | [email protected] "#" |
277 | [email protected] "[" | 284 | [email protected] "[" |
278 | [email protected] | 285 | [email protected] |
279 | [email protected] | 286 | [email protected] |
280 | [email protected] | 287 | [email protected] |
281 | [email protected] "must_use" | 288 | [email protected] |
289 | [email protected] "must_use" | ||
282 | [email protected] "]" | 290 | [email protected] "]" |
283 | [email protected] " " | 291 | [email protected] " " |
284 | [email protected] | 292 | [email protected] |
@@ -300,10 +308,11 @@ [email protected] | |||
300 | [email protected] | 308 | [email protected] |
301 | [email protected] "#" | 309 | [email protected] "#" |
302 | [email protected] "[" | 310 | [email protected] "[" |
303 | [email protected] | 311 | [email protected] |
304 | [email protected] | 312 | [email protected] |
305 | [email protected] | 313 | [email protected] |
306 | [email protected] "attr" | 314 | [email protected] |
315 | [email protected] "attr" | ||
307 | [email protected] "]" | 316 | [email protected] "]" |
308 | [email protected] " " | 317 | [email protected] " " |
309 | [email protected] | 318 | [email protected] |
@@ -325,10 +334,11 @@ [email protected] | |||
325 | [email protected] | 334 | [email protected] |
326 | [email protected] "#" | 335 | [email protected] "#" |
327 | [email protected] "[" | 336 | [email protected] "[" |
328 | [email protected] | 337 | [email protected] |
329 | [email protected] | 338 | [email protected] |
330 | [email protected] | 339 | [email protected] |
331 | [email protected] "attr" | 340 | [email protected] |
341 | [email protected] "attr" | ||
332 | [email protected] "]" | 342 | [email protected] "]" |
333 | [email protected] " " | 343 | [email protected] " " |
334 | [email protected] "&" | 344 | [email protected] "&" |
@@ -357,10 +367,11 @@ [email protected] | |||
357 | [email protected] | 367 | [email protected] |
358 | [email protected] "#" | 368 | [email protected] "#" |
359 | [email protected] "[" | 369 | [email protected] "[" |
360 | [email protected] | 370 | [email protected] |
361 | [email protected] | 371 | [email protected] |
362 | [email protected] | 372 | [email protected] |
363 | [email protected] "attr" | 373 | [email protected] |
374 | [email protected] "attr" | ||
364 | [email protected] "]" | 375 | [email protected] "]" |
365 | [email protected] " " | 376 | [email protected] " " |
366 | [email protected] "&" | 377 | [email protected] "&" |
@@ -391,10 +402,11 @@ [email protected] | |||
391 | [email protected] | 402 | [email protected] |
392 | [email protected] "#" | 403 | [email protected] "#" |
393 | [email protected] "[" | 404 | [email protected] "[" |
394 | [email protected] | 405 | [email protected] |
395 | [email protected] | 406 | [email protected] |
396 | [email protected] | 407 | [email protected] |
397 | [email protected] "attr" | 408 | [email protected] |
409 | [email protected] "attr" | ||
398 | [email protected] "]" | 410 | [email protected] "]" |
399 | [email protected] " " | 411 | [email protected] " " |
400 | [email protected] "&" | 412 | [email protected] "&" |
@@ -426,10 +438,11 @@ [email protected] | |||
426 | [email protected] | 438 | [email protected] |
427 | [email protected] "#" | 439 | [email protected] "#" |
428 | [email protected] "[" | 440 | [email protected] "[" |
429 | [email protected] | 441 | [email protected] |
430 | [email protected] | 442 | [email protected] |
431 | [email protected] | 443 | [email protected] |
432 | [email protected] "attr" | 444 | [email protected] |
445 | [email protected] "attr" | ||
433 | [email protected] "]" | 446 | [email protected] "]" |
434 | [email protected] " " | 447 | [email protected] " " |
435 | [email protected] "&" | 448 | [email protected] "&" |
@@ -457,10 +470,11 @@ [email protected] | |||
457 | [email protected] | 470 | [email protected] |
458 | [email protected] "#" | 471 | [email protected] "#" |
459 | [email protected] "[" | 472 | [email protected] "[" |
460 | [email protected] | 473 | [email protected] |
461 | [email protected] | 474 | [email protected] |
462 | [email protected] | 475 | [email protected] |
463 | [email protected] "attr" | 476 | [email protected] |
477 | [email protected] "attr" | ||
464 | [email protected] "]" | 478 | [email protected] "]" |
465 | [email protected] " " | 479 | [email protected] " " |
466 | [email protected] | 480 | [email protected] |
@@ -489,10 +503,11 @@ [email protected] | |||
489 | [email protected] | 503 | [email protected] |
490 | [email protected] "#" | 504 | [email protected] "#" |
491 | [email protected] "[" | 505 | [email protected] "[" |
492 | [email protected] | 506 | [email protected] |
493 | [email protected] | 507 | [email protected] |
494 | [email protected] | 508 | [email protected] |
495 | [email protected] "attr" | 509 | [email protected] |
510 | [email protected] "attr" | ||
496 | [email protected] "]" | 511 | [email protected] "]" |
497 | [email protected] " " | 512 | [email protected] " " |
498 | [email protected] | 513 | [email protected] |
diff --git a/crates/syntax/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast b/crates/syntax/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast index 87d8ebcba..97416f16a 100644 --- a/crates/syntax/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast +++ b/crates/syntax/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast | |||
@@ -5,10 +5,11 @@ [email protected] | |||
5 | [email protected] | 5 | [email protected] |
6 | [email protected] "#" | 6 | [email protected] "#" |
7 | [email protected] "[" | 7 | [email protected] "[" |
8 | [email protected] | 8 | [email protected] |
9 | [email protected] | 9 | [email protected] |
10 | [email protected] | 10 | [email protected] |
11 | [email protected] "macro_export" | 11 | [email protected] |
12 | [email protected] "macro_export" | ||
12 | [email protected] "]" | 13 | [email protected] "]" |
13 | [email protected] "\n" | 14 | [email protected] "\n" |
14 | [email protected] "macro_rules" | 15 | [email protected] "macro_rules" |
diff --git a/crates/syntax/test_data/parser/ok/0062_macro_2.0.rast b/crates/syntax/test_data/parser/ok/0062_macro_2.0.rast index 0c22c31a4..e75848f0f 100644 --- a/crates/syntax/test_data/parser/ok/0062_macro_2.0.rast +++ b/crates/syntax/test_data/parser/ok/0062_macro_2.0.rast | |||
@@ -55,10 +55,11 @@ [email protected] | |||
55 | [email protected] | 55 | [email protected] |
56 | [email protected] "#" | 56 | [email protected] "#" |
57 | [email protected] "[" | 57 | [email protected] "[" |
58 | [email protected] | 58 | [email protected] |
59 | [email protected] | 59 | [email protected] |
60 | [email protected] | 60 | [email protected] |
61 | [email protected] "test" | 61 | [email protected] |
62 | [email protected] "test" | ||
62 | [email protected] "]" | 63 | [email protected] "]" |
63 | [email protected] "\n" | 64 | [email protected] "\n" |
64 | [email protected] "fn" | 65 | [email protected] "fn" |
diff --git a/crates/syntax/test_data/parser/ok/0063_variadic_fun.rast b/crates/syntax/test_data/parser/ok/0063_variadic_fun.rast index f7c094898..dcc4105c9 100644 --- a/crates/syntax/test_data/parser/ok/0063_variadic_fun.rast +++ b/crates/syntax/test_data/parser/ok/0063_variadic_fun.rast | |||
@@ -96,14 +96,15 @@ [email protected] | |||
96 | [email protected] | 96 | [email protected] |
97 | [email protected] "#" | 97 | [email protected] "#" |
98 | [email protected] "[" | 98 | [email protected] "[" |
99 | [email protected] | 99 | [email protected] |
100 | [email protected] | 100 | [email protected] |
101 | [email protected] | 101 | [email protected] |
102 | [email protected] "cfg" | 102 | [email protected] |
103 | [email protected] | 103 | [email protected] "cfg" |
104 | [email protected] "(" | 104 | [email protected] |
105 | [email protected] "never" | 105 | [email protected] "(" |
106 | [email protected] ")" | 106 | [email protected] "never" |
107 | [email protected] ")" | ||
107 | [email protected] "]" | 108 | [email protected] "]" |
108 | [email protected] " " | 109 | [email protected] " " |
109 | [email protected] | 110 | [email protected] |
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index bd017567c..b2fe25f82 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs | |||
@@ -190,10 +190,21 @@ pub fn add_cursor(text: &str, offset: TextSize) -> String { | |||
190 | res | 190 | res |
191 | } | 191 | } |
192 | 192 | ||
193 | /// Extracts `//^ some text` annotations | 193 | /// Extracts `//^^^ some text` annotations. |
194 | /// | ||
195 | /// A run of `^^^` can be arbitrary long and points to the corresponding range | ||
196 | /// in the line above. | ||
197 | /// | ||
198 | /// The `// ^file text` syntax can be used to attach `text` to the entirety of | ||
199 | /// the file. | ||
200 | /// | ||
201 | /// Multiline string values are supported: | ||
202 | /// | ||
203 | /// // ^^^ first line | ||
204 | /// // | second line | ||
194 | pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> { | 205 | pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> { |
195 | let mut res = Vec::new(); | 206 | let mut res = Vec::new(); |
196 | let mut prev_line_start: Option<TextSize> = None; | 207 | let mut prev_line_start: Option<TextSize> = Some(0.into()); |
197 | let mut line_start: TextSize = 0.into(); | 208 | let mut line_start: TextSize = 0.into(); |
198 | let mut prev_line_annotations: Vec<(TextSize, usize)> = Vec::new(); | 209 | let mut prev_line_annotations: Vec<(TextSize, usize)> = Vec::new(); |
199 | for line in text.split_inclusive('\n') { | 210 | for line in text.split_inclusive('\n') { |
@@ -202,10 +213,15 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> { | |||
202 | let annotation_offset = TextSize::of(&line[..idx + "//".len()]); | 213 | let annotation_offset = TextSize::of(&line[..idx + "//".len()]); |
203 | for annotation in extract_line_annotations(&line[idx + "//".len()..]) { | 214 | for annotation in extract_line_annotations(&line[idx + "//".len()..]) { |
204 | match annotation { | 215 | match annotation { |
205 | LineAnnotation::Annotation { mut range, content } => { | 216 | LineAnnotation::Annotation { mut range, content, file } => { |
206 | range += annotation_offset; | 217 | range += annotation_offset; |
207 | this_line_annotations.push((range.end(), res.len())); | 218 | this_line_annotations.push((range.end(), res.len())); |
208 | res.push((range + prev_line_start.unwrap(), content)) | 219 | let range = if file { |
220 | TextRange::up_to(TextSize::of(text)) | ||
221 | } else { | ||
222 | range + prev_line_start.unwrap() | ||
223 | }; | ||
224 | res.push((range, content)) | ||
209 | } | 225 | } |
210 | LineAnnotation::Continuation { mut offset, content } => { | 226 | LineAnnotation::Continuation { mut offset, content } => { |
211 | offset += annotation_offset; | 227 | offset += annotation_offset; |
@@ -226,11 +242,12 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> { | |||
226 | 242 | ||
227 | prev_line_annotations = this_line_annotations; | 243 | prev_line_annotations = this_line_annotations; |
228 | } | 244 | } |
245 | |||
229 | res | 246 | res |
230 | } | 247 | } |
231 | 248 | ||
232 | enum LineAnnotation { | 249 | enum LineAnnotation { |
233 | Annotation { range: TextRange, content: String }, | 250 | Annotation { range: TextRange, content: String, file: bool }, |
234 | Continuation { offset: TextSize, content: String }, | 251 | Continuation { offset: TextSize, content: String }, |
235 | } | 252 | } |
236 | 253 | ||
@@ -238,14 +255,9 @@ fn extract_line_annotations(mut line: &str) -> Vec<LineAnnotation> { | |||
238 | let mut res = Vec::new(); | 255 | let mut res = Vec::new(); |
239 | let mut offset: TextSize = 0.into(); | 256 | let mut offset: TextSize = 0.into(); |
240 | let marker: fn(char) -> bool = if line.contains('^') { |c| c == '^' } else { |c| c == '|' }; | 257 | let marker: fn(char) -> bool = if line.contains('^') { |c| c == '^' } else { |c| c == '|' }; |
241 | loop { | 258 | while let Some(idx) = line.find(marker) { |
242 | match line.find(marker) { | 259 | offset += TextSize::try_from(idx).unwrap(); |
243 | Some(idx) => { | 260 | line = &line[idx..]; |
244 | offset += TextSize::try_from(idx).unwrap(); | ||
245 | line = &line[idx..]; | ||
246 | } | ||
247 | None => break, | ||
248 | }; | ||
249 | 261 | ||
250 | let mut len = line.chars().take_while(|&it| it == '^').count(); | 262 | let mut len = line.chars().take_while(|&it| it == '^').count(); |
251 | let mut continuation = false; | 263 | let mut continuation = false; |
@@ -256,12 +268,20 @@ fn extract_line_annotations(mut line: &str) -> Vec<LineAnnotation> { | |||
256 | } | 268 | } |
257 | let range = TextRange::at(offset, len.try_into().unwrap()); | 269 | let range = TextRange::at(offset, len.try_into().unwrap()); |
258 | let next = line[len..].find(marker).map_or(line.len(), |it| it + len); | 270 | let next = line[len..].find(marker).map_or(line.len(), |it| it + len); |
259 | let content = line[len..][..next - len].trim().to_string(); | 271 | let mut content = &line[len..][..next - len]; |
272 | |||
273 | let mut file = false; | ||
274 | if !continuation && content.starts_with("file") { | ||
275 | file = true; | ||
276 | content = &content["file".len()..] | ||
277 | } | ||
278 | |||
279 | let content = content.trim().to_string(); | ||
260 | 280 | ||
261 | let annotation = if continuation { | 281 | let annotation = if continuation { |
262 | LineAnnotation::Continuation { offset: range.end(), content } | 282 | LineAnnotation::Continuation { offset: range.end(), content } |
263 | } else { | 283 | } else { |
264 | LineAnnotation::Annotation { range, content } | 284 | LineAnnotation::Annotation { range, content, file } |
265 | }; | 285 | }; |
266 | res.push(annotation); | 286 | res.push(annotation); |
267 | 287 | ||
@@ -282,16 +302,20 @@ fn main() { | |||
282 | zoo + 1 | 302 | zoo + 1 |
283 | } //^^^ type: | 303 | } //^^^ type: |
284 | // | i32 | 304 | // | i32 |
305 | |||
306 | // ^file | ||
285 | "#, | 307 | "#, |
286 | ); | 308 | ); |
287 | let res = extract_annotations(&text) | 309 | let res = extract_annotations(&text) |
288 | .into_iter() | 310 | .into_iter() |
289 | .map(|(range, ann)| (&text[range], ann)) | 311 | .map(|(range, ann)| (&text[range], ann)) |
290 | .collect::<Vec<_>>(); | 312 | .collect::<Vec<_>>(); |
313 | |||
291 | assert_eq!( | 314 | assert_eq!( |
292 | res, | 315 | res[..3], |
293 | vec![("x", "def".into()), ("y", "def".into()), ("zoo", "type:\ni32\n".into()),] | 316 | [("x", "def".into()), ("y", "def".into()), ("zoo", "type:\ni32\n".into())] |
294 | ); | 317 | ); |
318 | assert_eq!(res[3].0.len(), 115); | ||
295 | } | 319 | } |
296 | 320 | ||
297 | /// Returns `false` if slow tests should not run, otherwise returns `true` and | 321 | /// Returns `false` if slow tests should not run, otherwise returns `true` and |
diff --git a/crates/tt/src/buffer.rs b/crates/tt/src/buffer.rs index e0021039a..dc577700f 100644 --- a/crates/tt/src/buffer.rs +++ b/crates/tt/src/buffer.rs | |||
@@ -133,7 +133,7 @@ impl<'a> TokenTreeRef<'a> { | |||
133 | } | 133 | } |
134 | } | 134 | } |
135 | 135 | ||
136 | /// A safe version of `Cursor` from `syn` crate https://github.com/dtolnay/syn/blob/6533607f91686545cb034d2838beea338d9d0742/src/buffer.rs#L125 | 136 | /// A safe version of `Cursor` from `syn` crate <https://github.com/dtolnay/syn/blob/6533607f91686545cb034d2838beea338d9d0742/src/buffer.rs#L125> |
137 | #[derive(Copy, Clone, Debug)] | 137 | #[derive(Copy, Clone, Debug)] |
138 | pub struct Cursor<'a> { | 138 | pub struct Cursor<'a> { |
139 | buffer: &'a TokenBuffer<'a>, | 139 | buffer: &'a TokenBuffer<'a>, |
diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs index bed44d600..b51ad5d0f 100644 --- a/crates/tt/src/lib.rs +++ b/crates/tt/src/lib.rs | |||
@@ -88,7 +88,7 @@ pub struct Ident { | |||
88 | } | 88 | } |
89 | 89 | ||
90 | fn print_debug_subtree(f: &mut fmt::Formatter<'_>, subtree: &Subtree, level: usize) -> fmt::Result { | 90 | fn print_debug_subtree(f: &mut fmt::Formatter<'_>, subtree: &Subtree, level: usize) -> fmt::Result { |
91 | let align = std::iter::repeat(" ").take(level).collect::<String>(); | 91 | let align = " ".repeat(level); |
92 | 92 | ||
93 | let aux = match subtree.delimiter.map(|it| (it.kind, it.id.0)) { | 93 | let aux = match subtree.delimiter.map(|it| (it.kind, it.id.0)) { |
94 | None => "$".to_string(), | 94 | None => "$".to_string(), |
@@ -113,7 +113,7 @@ fn print_debug_subtree(f: &mut fmt::Formatter<'_>, subtree: &Subtree, level: usi | |||
113 | } | 113 | } |
114 | 114 | ||
115 | fn print_debug_token(f: &mut fmt::Formatter<'_>, tkn: &TokenTree, level: usize) -> fmt::Result { | 115 | fn print_debug_token(f: &mut fmt::Formatter<'_>, tkn: &TokenTree, level: usize) -> fmt::Result { |
116 | let align = std::iter::repeat(" ").take(level).collect::<String>(); | 116 | let align = " ".repeat(level); |
117 | 117 | ||
118 | match tkn { | 118 | match tkn { |
119 | TokenTree::Leaf(leaf) => match leaf { | 119 | TokenTree::Leaf(leaf) => match leaf { |
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs index 0a4590c8d..0011f73c9 100644 --- a/crates/vfs/src/file_set.rs +++ b/crates/vfs/src/file_set.rs | |||
@@ -111,7 +111,7 @@ impl FileSetConfig { | |||
111 | let mut scratch_space = Vec::new(); | 111 | let mut scratch_space = Vec::new(); |
112 | let mut res = vec![FileSet::default(); self.len()]; | 112 | let mut res = vec![FileSet::default(); self.len()]; |
113 | for (file_id, path) in vfs.iter() { | 113 | for (file_id, path) in vfs.iter() { |
114 | let root = self.classify(&path, &mut scratch_space); | 114 | let root = self.classify(path, &mut scratch_space); |
115 | res[root].insert(file_id, path.clone()) | 115 | res[root].insert(file_id, path.clone()) |
116 | } | 116 | } |
117 | res | 117 | res |
diff --git a/docs/dev/README.md b/docs/dev/README.md index 16b23adc6..e81f1e74c 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md | |||
@@ -1,6 +1,6 @@ | |||
1 | # Contributing Quick Start | 1 | # Contributing Quick Start |
2 | 2 | ||
3 | Rust Analyzer is an ordinary Rust project, which is organized as a Cargo workspace, builds on stable and doesn't depend on C libraries. | 3 | rust-analyzer is an ordinary Rust project, which is organized as a Cargo workspace, builds on stable and doesn't depend on C libraries. |
4 | So, just | 4 | So, just |
5 | 5 | ||
6 | ``` | 6 | ``` |
@@ -9,18 +9,18 @@ $ cargo test | |||
9 | 9 | ||
10 | should be enough to get you started! | 10 | should be enough to get you started! |
11 | 11 | ||
12 | To learn more about how rust-analyzer works, see [./architecture.md](./architecture.md) document. | 12 | To learn more about how rust-analyzer works, see [./architecture.md](./architecture.md). |
13 | It also explains the high-level layout of the source code. | 13 | It also explains the high-level layout of the source code. |
14 | Do skim through that document. | 14 | Do skim through that document. |
15 | 15 | ||
16 | We also publish rustdoc docs to pages: https://rust-analyzer.github.io/rust-analyzer/ide/. | 16 | We also publish rustdoc docs to pages: https://rust-analyzer.github.io/rust-analyzer/ide/. |
17 | Note though, that internal documentation is very incomplete. | 17 | Note though, that the internal documentation is very incomplete. |
18 | 18 | ||
19 | Various organizational and process issues are discussed in this document. | 19 | Various organizational and process issues are discussed in this document. |
20 | 20 | ||
21 | # Getting in Touch | 21 | # Getting in Touch |
22 | 22 | ||
23 | Rust Analyzer is a part of [RLS-2.0 working | 23 | rust-analyzer is a part of the [RLS-2.0 working |
24 | group](https://github.com/rust-lang/compiler-team/tree/6a769c13656c0a6959ebc09e7b1f7c09b86fb9c0/working-groups/rls-2.0). | 24 | group](https://github.com/rust-lang/compiler-team/tree/6a769c13656c0a6959ebc09e7b1f7c09b86fb9c0/working-groups/rls-2.0). |
25 | Discussion happens in this Zulip stream: | 25 | Discussion happens in this Zulip stream: |
26 | 26 | ||
@@ -33,7 +33,7 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer | |||
33 | * [E-has-instructions](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-has-instructions) | 33 | * [E-has-instructions](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-has-instructions) |
34 | issues have links to the code in question and tests. | 34 | issues have links to the code in question and tests. |
35 | * [Broken Window](https://github.com/rust-analyzer/rust-analyzer/issues?q=is:issue+is:open+label:%22Broken+Window%22) | 35 | * [Broken Window](https://github.com/rust-analyzer/rust-analyzer/issues?q=is:issue+is:open+label:%22Broken+Window%22) |
36 | are issues which are not critical by themselves, but which should be fixed ASAP regardless, to avoid accumulation of technical debt. | 36 | are issues which are not necessarily critical by themselves, but which should be fixed ASAP regardless, to avoid accumulation of technical debt. |
37 | * [E-easy](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy), | 37 | * [E-easy](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy), |
38 | [E-medium](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-medium), | 38 | [E-medium](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-medium), |
39 | [E-hard](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-hard), | 39 | [E-hard](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-hard), |
@@ -42,7 +42,9 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer | |||
42 | * [S-actionable](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AS-actionable) and | 42 | * [S-actionable](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AS-actionable) and |
43 | [S-unactionable](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AS-unactionable) | 43 | [S-unactionable](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AS-unactionable) |
44 | specify if there are concrete steps to resolve or advance an issue. Roughly, actionable issues need only work to be fixed, | 44 | specify if there are concrete steps to resolve or advance an issue. Roughly, actionable issues need only work to be fixed, |
45 | while unactionable ones are effectively wont-fix. Each triaged issue should have one of these labels. | 45 | while unactionable ones are blocked either on user feedback (providing a reproducible example), or on larger architectural |
46 | work or decisions. This classification is descriptive, not prescriptive, and might be wrong: Any unactionable issue might have a simple fix that we missed. | ||
47 | Each triaged issue should have one of these labels. | ||
46 | * [fun](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3Afun) | 48 | * [fun](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3Afun) |
47 | is for cool, but probably hard stuff. | 49 | is for cool, but probably hard stuff. |
48 | * [Design](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%Design) | 50 | * [Design](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%Design) |
@@ -206,7 +208,7 @@ Release process is handled by `release`, `dist` and `promote` xtasks, `release` | |||
206 | ./rust-rust-analyzer # Note the name! | 208 | ./rust-rust-analyzer # Note the name! |
207 | ``` | 209 | ``` |
208 | 210 | ||
209 | Additionally, it assumes that remote for `rust-analyzer` is called `upstream` (I use `origin` to point to my fork). | 211 | Additionally, it assumes that the remote for `rust-analyzer` is called `upstream` (I use `origin` to point to my fork). |
210 | 212 | ||
211 | `release` calls the GitHub API calls to scrape pull request comments and categorize them in the changelog. | 213 | `release` calls the GitHub API calls to scrape pull request comments and categorize them in the changelog. |
212 | This step uses the `curl` and `jq` applications, which need to be available in `PATH`. | 214 | This step uses the `curl` and `jq` applications, which need to be available in `PATH`. |
diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md index 3de1b99a5..721c555ab 100644 --- a/docs/dev/architecture.md +++ b/docs/dev/architecture.md | |||
@@ -428,7 +428,7 @@ Rather than spawning futures or scheduling callbacks (open), the event loop acce | |||
428 | It's easy to see all the things that trigger rust-analyzer processing, together with their performance | 428 | It's easy to see all the things that trigger rust-analyzer processing, together with their performance |
429 | 429 | ||
430 | rust-analyzer includes a simple hierarchical profiler (`hprof`). | 430 | rust-analyzer includes a simple hierarchical profiler (`hprof`). |
431 | It is enabled with `RA_PROFILE='*>50` env var (log all (`*`) actions which take more than `50` ms) and produces output like: | 431 | It is enabled with `RA_PROFILE='*>50'` env var (log all (`*`) actions which take more than `50` ms) and produces output like: |
432 | 432 | ||
433 | ``` | 433 | ``` |
434 | 85ms - handle_completion | 434 | 85ms - handle_completion |
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index dbd9a3503..34a91486b 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc | |||
@@ -119,6 +119,7 @@ similar option. | |||
119 | + | 119 | + |
120 | -- | 120 | -- |
121 | Whether to add argument snippets when completing functions. | 121 | Whether to add argument snippets when completing functions. |
122 | Only applies when `#rust-analyzer.completion.addCallParenthesis#` is set. | ||
122 | -- | 123 | -- |
123 | [[rust-analyzer.completion.addCallParenthesis]]rust-analyzer.completion.addCallParenthesis (default: `true`):: | 124 | [[rust-analyzer.completion.addCallParenthesis]]rust-analyzer.completion.addCallParenthesis (default: `true`):: |
124 | + | 125 | + |
@@ -180,6 +181,11 @@ List of warnings that should be displayed with info severity. | |||
180 | The warnings will be indicated by a blue squiggly underline in code | 181 | The warnings will be indicated by a blue squiggly underline in code |
181 | and a blue icon in the `Problems Panel`. | 182 | and a blue icon in the `Problems Panel`. |
182 | -- | 183 | -- |
184 | [[rust-analyzer.experimental.procAttrMacros]]rust-analyzer.experimental.procAttrMacros (default: `false`):: | ||
185 | + | ||
186 | -- | ||
187 | Expand attribute macros. | ||
188 | -- | ||
183 | [[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`):: | 189 | [[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`):: |
184 | + | 190 | + |
185 | -- | 191 | -- |
@@ -222,6 +228,12 @@ Whether to show `Go to Type Definition` action. Only applies when | |||
222 | Whether to show `Implementations` action. Only applies when | 228 | Whether to show `Implementations` action. Only applies when |
223 | `#rust-analyzer.hoverActions.enable#` is set. | 229 | `#rust-analyzer.hoverActions.enable#` is set. |
224 | -- | 230 | -- |
231 | [[rust-analyzer.hoverActions.references]]rust-analyzer.hoverActions.references (default: `false`):: | ||
232 | + | ||
233 | -- | ||
234 | Whether to show `References` action. Only applies when | ||
235 | `#rust-analyzer.hoverActions.enable#` is set. | ||
236 | -- | ||
225 | [[rust-analyzer.hoverActions.run]]rust-analyzer.hoverActions.run (default: `true`):: | 237 | [[rust-analyzer.hoverActions.run]]rust-analyzer.hoverActions.run (default: `true`):: |
226 | + | 238 | + |
227 | -- | 239 | -- |
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index 1f95df56e..9a8d76700 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc | |||
@@ -187,6 +187,20 @@ Install it with pacman, for example: | |||
187 | $ pacman -S rust-analyzer | 187 | $ pacman -S rust-analyzer |
188 | ---- | 188 | ---- |
189 | 189 | ||
190 | ==== Gentoo Linux | ||
191 | |||
192 | `rust-analyzer` is available in the GURU repository: | ||
193 | |||
194 | - https://gitweb.gentoo.org/repo/proj/guru.git/tree/dev-util/rust-analyzer-bin/rust-analyzer-bin-9999.ebuild[`dev-util/rust-analyzer-bin-9999`] (the https://github.com/rust-analyzer/rust-analyzer/releases/latest[latest release] as a live binary ebuild) | ||
195 | |||
196 | If not already, GURU must be enabled (e.g. using `app-eselect/eselect-repository`) and sync'd before running `emerge`: | ||
197 | |||
198 | [source,bash] | ||
199 | ---- | ||
200 | $ eselect repository enable guru && emaint sync -r guru | ||
201 | $ emerge rust-analyzer-bin | ||
202 | ---- | ||
203 | |||
190 | === Emacs | 204 | === Emacs |
191 | 205 | ||
192 | Note this excellent https://robert.kra.hn/posts/2021-02-07_rust-with-emacs/[guide] from https://github.com/rksm[@rksm]. | 206 | Note this excellent https://robert.kra.hn/posts/2021-02-07_rust-with-emacs/[guide] from https://github.com/rksm[@rksm]. |
diff --git a/editors/code/package.json b/editors/code/package.json index 42a06e137..0f3ed48a0 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -553,7 +553,7 @@ | |||
553 | } | 553 | } |
554 | }, | 554 | }, |
555 | "rust-analyzer.completion.addCallArgumentSnippets": { | 555 | "rust-analyzer.completion.addCallArgumentSnippets": { |
556 | "markdownDescription": "Whether to add argument snippets when completing functions.", | 556 | "markdownDescription": "Whether to add argument snippets when completing functions.\nOnly applies when `#rust-analyzer.completion.addCallParenthesis#` is set.", |
557 | "default": true, | 557 | "default": true, |
558 | "type": "boolean" | 558 | "type": "boolean" |
559 | }, | 559 | }, |
@@ -617,6 +617,11 @@ | |||
617 | "type": "string" | 617 | "type": "string" |
618 | } | 618 | } |
619 | }, | 619 | }, |
620 | "rust-analyzer.experimental.procAttrMacros": { | ||
621 | "markdownDescription": "Expand attribute macros.", | ||
622 | "default": false, | ||
623 | "type": "boolean" | ||
624 | }, | ||
620 | "rust-analyzer.files.watcher": { | 625 | "rust-analyzer.files.watcher": { |
621 | "markdownDescription": "Controls file watching implementation.", | 626 | "markdownDescription": "Controls file watching implementation.", |
622 | "default": "client", | 627 | "default": "client", |
@@ -655,6 +660,11 @@ | |||
655 | "default": true, | 660 | "default": true, |
656 | "type": "boolean" | 661 | "type": "boolean" |
657 | }, | 662 | }, |
663 | "rust-analyzer.hoverActions.references": { | ||
664 | "markdownDescription": "Whether to show `References` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.", | ||
665 | "default": false, | ||
666 | "type": "boolean" | ||
667 | }, | ||
658 | "rust-analyzer.hoverActions.run": { | 668 | "rust-analyzer.hoverActions.run": { |
659 | "markdownDescription": "Whether to show `Run` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.", | 669 | "markdownDescription": "Whether to show `Run` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.", |
660 | "default": true, | 670 | "default": true, |
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 7200a26f7..2277eeb7e 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -165,6 +165,7 @@ export class Config { | |||
165 | return { | 165 | return { |
166 | enable: this.get<boolean>("hoverActions.enable"), | 166 | enable: this.get<boolean>("hoverActions.enable"), |
167 | implementations: this.get<boolean>("hoverActions.implementations"), | 167 | implementations: this.get<boolean>("hoverActions.implementations"), |
168 | references: this.get<boolean>("hoverActions.references"), | ||
168 | run: this.get<boolean>("hoverActions.run"), | 169 | run: this.get<boolean>("hoverActions.run"), |
169 | debug: this.get<boolean>("hoverActions.debug"), | 170 | debug: this.get<boolean>("hoverActions.debug"), |
170 | gotoTypeDef: this.get<boolean>("hoverActions.gotoTypeDef"), | 171 | gotoTypeDef: this.get<boolean>("hoverActions.gotoTypeDef"), |
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index cf67dd8cf..2ffd3be6f 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts | |||
@@ -39,6 +39,7 @@ export class Ctx { | |||
39 | extCtx.subscriptions.push(statusBar); | 39 | extCtx.subscriptions.push(statusBar); |
40 | statusBar.text = "rust-analyzer"; | 40 | statusBar.text = "rust-analyzer"; |
41 | statusBar.tooltip = "ready"; | 41 | statusBar.tooltip = "ready"; |
42 | statusBar.command = "rust-analyzer.analyzerStatus"; | ||
42 | statusBar.show(); | 43 | statusBar.show(); |
43 | 44 | ||
44 | const res = new Ctx(config, extCtx, client, serverPath, statusBar); | 45 | const res = new Ctx(config, extCtx, client, serverPath, statusBar); |
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 997770958..c4fc91386 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml | |||
@@ -11,7 +11,7 @@ anyhow = "1.0.26" | |||
11 | flate2 = "1.0" | 11 | flate2 = "1.0" |
12 | proc-macro2 = "1.0.8" | 12 | proc-macro2 = "1.0.8" |
13 | quote = "1.0.2" | 13 | quote = "1.0.2" |
14 | ungrammar = "=1.13" | 14 | ungrammar = "=1.14" |
15 | walkdir = "2.3.1" | 15 | walkdir = "2.3.1" |
16 | write-json = "0.1.0" | 16 | write-json = "0.1.0" |
17 | xshell = "0.1" | 17 | xshell = "0.1" |
diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs index 0fd1d13e6..fe37d0245 100644 --- a/xtask/src/ast_src.rs +++ b/xtask/src/ast_src.rs | |||
@@ -184,7 +184,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc { | |||
184 | "ITEM_LIST", | 184 | "ITEM_LIST", |
185 | "ASSOC_ITEM_LIST", | 185 | "ASSOC_ITEM_LIST", |
186 | "ATTR", | 186 | "ATTR", |
187 | "META_ITEM", // not an item actually | 187 | "META", |
188 | "USE_TREE", | 188 | "USE_TREE", |
189 | "USE_TREE_LIST", | 189 | "USE_TREE_LIST", |
190 | "PATH", | 190 | "PATH", |
diff --git a/xtask/src/codegen/gen_lint_completions.rs b/xtask/src/codegen/gen_lint_completions.rs index 24dbc6a39..54fcaa0e6 100644 --- a/xtask/src/codegen/gen_lint_completions.rs +++ b/xtask/src/codegen/gen_lint_completions.rs | |||
@@ -1,4 +1,5 @@ | |||
1 | //! Generates descriptors structure for unstable feature from Unstable Book | 1 | //! Generates descriptors structure for unstable feature from Unstable Book |
2 | use std::borrow::Cow; | ||
2 | use std::fmt::Write; | 3 | use std::fmt::Write; |
3 | use std::path::{Path, PathBuf}; | 4 | use std::path::{Path, PathBuf}; |
4 | 5 | ||
@@ -12,25 +13,75 @@ pub(crate) fn generate_lint_completions() -> Result<()> { | |||
12 | cmd!("git clone --depth=1 https://github.com/rust-lang/rust ./target/rust").run()?; | 13 | cmd!("git clone --depth=1 https://github.com/rust-lang/rust ./target/rust").run()?; |
13 | } | 14 | } |
14 | 15 | ||
15 | let mut contents = String::from("use crate::completions::attribute::LintCompletion;\n\n"); | 16 | let mut contents = String::from( |
16 | generate_descriptor(&mut contents, "./target/rust/src/doc/unstable-book/src".into())?; | 17 | r#"pub struct Lint { |
18 | pub label: &'static str, | ||
19 | pub description: &'static str, | ||
20 | } | ||
21 | |||
22 | "#, | ||
23 | ); | ||
24 | generate_lint_descriptor(&mut contents)?; | ||
25 | contents.push('\n'); | ||
26 | |||
27 | generate_feature_descriptor(&mut contents, "./target/rust/src/doc/unstable-book/src".into())?; | ||
17 | contents.push('\n'); | 28 | contents.push('\n'); |
18 | 29 | ||
19 | cmd!("curl http://rust-lang.github.io/rust-clippy/master/lints.json --output ./target/clippy_lints.json").run()?; | 30 | cmd!("curl https://rust-lang.github.io/rust-clippy/master/lints.json --output ./target/clippy_lints.json").run()?; |
20 | generate_descriptor_clippy(&mut contents, &Path::new("./target/clippy_lints.json"))?; | 31 | generate_descriptor_clippy(&mut contents, Path::new("./target/clippy_lints.json"))?; |
21 | let contents = reformat(&contents)?; | 32 | let contents = reformat(&contents)?; |
22 | 33 | ||
23 | let destination = | 34 | let destination = project_root().join("crates/ide_db/src/helpers/generated_lints.rs"); |
24 | project_root().join("crates/ide_completion/src/generated_lint_completions.rs"); | ||
25 | ensure_file_contents(destination.as_path(), &contents)?; | 35 | ensure_file_contents(destination.as_path(), &contents)?; |
26 | 36 | ||
27 | Ok(()) | 37 | Ok(()) |
28 | } | 38 | } |
29 | 39 | ||
30 | fn generate_descriptor(buf: &mut String, src_dir: PathBuf) -> Result<()> { | 40 | fn generate_lint_descriptor(buf: &mut String) -> Result<()> { |
31 | buf.push_str(r#"pub(super) const FEATURES: &[LintCompletion] = &["#); | 41 | let stdout = cmd!("rustc -W help").read()?; |
42 | let start_lints = | ||
43 | stdout.find("---- ------- -------").ok_or_else(|| anyhow::format_err!(""))?; | ||
44 | let start_lint_groups = | ||
45 | stdout.find("---- ---------").ok_or_else(|| anyhow::format_err!(""))?; | ||
46 | let end_lints = | ||
47 | stdout.find("Lint groups provided by rustc:").ok_or_else(|| anyhow::format_err!(""))?; | ||
48 | let end_lint_groups = stdout | ||
49 | .find("Lint tools like Clippy can provide additional lints and lint groups.") | ||
50 | .ok_or_else(|| anyhow::format_err!(""))?; | ||
51 | buf.push_str(r#"pub const DEFAULT_LINTS: &[Lint] = &["#); | ||
52 | buf.push('\n'); | ||
53 | let mut lints = stdout[start_lints..end_lints] | ||
54 | .lines() | ||
55 | .skip(1) | ||
56 | .filter(|l| !l.is_empty()) | ||
57 | .map(|line| { | ||
58 | let (name, rest) = line.trim().split_once(char::is_whitespace).unwrap(); | ||
59 | let (_default_level, description) = | ||
60 | rest.trim().split_once(char::is_whitespace).unwrap(); | ||
61 | (name.trim(), Cow::Borrowed(description.trim())) | ||
62 | }) | ||
63 | .collect::<Vec<_>>(); | ||
64 | lints.extend( | ||
65 | stdout[start_lint_groups..end_lint_groups].lines().skip(1).filter(|l| !l.is_empty()).map( | ||
66 | |line| { | ||
67 | let (name, lints) = line.trim().split_once(char::is_whitespace).unwrap(); | ||
68 | (name.trim(), format!("lint group for: {}", lints.trim()).into()) | ||
69 | }, | ||
70 | ), | ||
71 | ); | ||
72 | |||
73 | lints.sort_by(|(ident, _), (ident2, _)| ident.cmp(ident2)); | ||
74 | lints.into_iter().for_each(|(name, description)| { | ||
75 | push_lint_completion(buf, &name.replace("-", "_"), &description) | ||
76 | }); | ||
77 | buf.push_str("];\n"); | ||
78 | Ok(()) | ||
79 | } | ||
80 | |||
81 | fn generate_feature_descriptor(buf: &mut String, src_dir: PathBuf) -> Result<()> { | ||
82 | buf.push_str(r#"pub const FEATURES: &[Lint] = &["#); | ||
32 | buf.push('\n'); | 83 | buf.push('\n'); |
33 | ["language-features", "library-features"] | 84 | let mut vec = ["language-features", "library-features"] |
34 | .iter() | 85 | .iter() |
35 | .flat_map(|it| WalkDir::new(src_dir.join(it))) | 86 | .flat_map(|it| WalkDir::new(src_dir.join(it))) |
36 | .filter_map(|e| e.ok()) | 87 | .filter_map(|e| e.ok()) |
@@ -38,13 +89,16 @@ fn generate_descriptor(buf: &mut String, src_dir: PathBuf) -> Result<()> { | |||
38 | // Get all `.md ` files | 89 | // Get all `.md ` files |
39 | entry.file_type().is_file() && entry.path().extension().unwrap_or_default() == "md" | 90 | entry.file_type().is_file() && entry.path().extension().unwrap_or_default() == "md" |
40 | }) | 91 | }) |
41 | .for_each(|entry| { | 92 | .map(|entry| { |
42 | let path = entry.path(); | 93 | let path = entry.path(); |
43 | let feature_ident = path.file_stem().unwrap().to_str().unwrap().replace("-", "_"); | 94 | let feature_ident = path.file_stem().unwrap().to_str().unwrap().replace("-", "_"); |
44 | let doc = read_file(path).unwrap(); | 95 | let doc = read_file(path).unwrap(); |
45 | 96 | (feature_ident, doc) | |
46 | push_lint_completion(buf, &feature_ident, &doc); | 97 | }) |
47 | }); | 98 | .collect::<Vec<_>>(); |
99 | vec.sort_by(|(feature_ident, _), (feature_ident2, _)| feature_ident.cmp(feature_ident2)); | ||
100 | vec.into_iter() | ||
101 | .for_each(|(feature_ident, doc)| push_lint_completion(buf, &feature_ident, &doc)); | ||
48 | buf.push_str("];\n"); | 102 | buf.push_str("];\n"); |
49 | Ok(()) | 103 | Ok(()) |
50 | } | 104 | } |
@@ -55,6 +109,10 @@ struct ClippyLint { | |||
55 | id: String, | 109 | id: String, |
56 | } | 110 | } |
57 | 111 | ||
112 | fn unescape(s: &str) -> String { | ||
113 | s.replace(r#"\""#, "").replace(r#"\n"#, "\n").replace(r#"\r"#, "") | ||
114 | } | ||
115 | |||
58 | fn generate_descriptor_clippy(buf: &mut String, path: &Path) -> Result<()> { | 116 | fn generate_descriptor_clippy(buf: &mut String, path: &Path) -> Result<()> { |
59 | let file_content = read_file(path)?; | 117 | let file_content = read_file(path)?; |
60 | let mut clippy_lints: Vec<ClippyLint> = vec![]; | 118 | let mut clippy_lints: Vec<ClippyLint> = vec![]; |
@@ -81,12 +139,12 @@ fn generate_descriptor_clippy(buf: &mut String, path: &Path) -> Result<()> { | |||
81 | .strip_prefix(prefix_to_strip) | 139 | .strip_prefix(prefix_to_strip) |
82 | .expect("should be prefixed by what it does") | 140 | .expect("should be prefixed by what it does") |
83 | .strip_suffix(suffix_to_strip) | 141 | .strip_suffix(suffix_to_strip) |
84 | .expect("should be suffixed by comma") | 142 | .map(unescape) |
85 | .into(); | 143 | .expect("should be suffixed by comma"); |
86 | } | 144 | } |
87 | } | 145 | } |
88 | 146 | clippy_lints.sort_by(|lint, lint2| lint.id.cmp(&lint2.id)); | |
89 | buf.push_str(r#"pub(super) const CLIPPY_LINTS: &[LintCompletion] = &["#); | 147 | buf.push_str(r#"pub const CLIPPY_LINTS: &[Lint] = &["#); |
90 | buf.push('\n'); | 148 | buf.push('\n'); |
91 | clippy_lints.into_iter().for_each(|clippy_lint| { | 149 | clippy_lints.into_iter().for_each(|clippy_lint| { |
92 | let lint_ident = format!("clippy::{}", clippy_lint.id); | 150 | let lint_ident = format!("clippy::{}", clippy_lint.id); |
@@ -102,7 +160,7 @@ fn generate_descriptor_clippy(buf: &mut String, path: &Path) -> Result<()> { | |||
102 | fn push_lint_completion(buf: &mut String, label: &str, description: &str) { | 160 | fn push_lint_completion(buf: &mut String, label: &str, description: &str) { |
103 | writeln!( | 161 | writeln!( |
104 | buf, | 162 | buf, |
105 | r###" LintCompletion {{ | 163 | r###" Lint {{ |
106 | label: "{}", | 164 | label: "{}", |
107 | description: r##"{}"## | 165 | description: r##"{}"## |
108 | }},"###, | 166 | }},"###, |
diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs index ba4b24848..5435da76e 100644 --- a/xtask/src/codegen/gen_syntax.rs +++ b/xtask/src/codegen/gen_syntax.rs | |||
@@ -94,18 +94,16 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: &AstSrc) -> Result<String> { | |||
94 | support::children(&self.syntax) | 94 | support::children(&self.syntax) |
95 | } | 95 | } |
96 | } | 96 | } |
97 | } else { | 97 | } else if let Some(token_kind) = field.token_kind() { |
98 | if let Some(token_kind) = field.token_kind() { | 98 | quote! { |
99 | quote! { | 99 | pub fn #method_name(&self) -> Option<#ty> { |
100 | pub fn #method_name(&self) -> Option<#ty> { | 100 | support::token(&self.syntax, #token_kind) |
101 | support::token(&self.syntax, #token_kind) | ||
102 | } | ||
103 | } | 101 | } |
104 | } else { | 102 | } |
105 | quote! { | 103 | } else { |
106 | pub fn #method_name(&self) -> Option<#ty> { | 104 | quote! { |
107 | support::child(&self.syntax) | 105 | pub fn #method_name(&self) -> Option<#ty> { |
108 | } | 106 | support::child(&self.syntax) |
109 | } | 107 | } |
110 | } | 108 | } |
111 | } | 109 | } |
@@ -260,7 +258,7 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: &AstSrc) -> Result<String> { | |||
260 | for chunk in ast.split("# [pretty_doc_comment_placeholder_workaround] ") { | 258 | for chunk in ast.split("# [pretty_doc_comment_placeholder_workaround] ") { |
261 | res.push_str(chunk); | 259 | res.push_str(chunk); |
262 | if let Some(doc) = docs.next() { | 260 | if let Some(doc) = docs.next() { |
263 | write_doc_comment(&doc, &mut res); | 261 | write_doc_comment(doc, &mut res); |
264 | } | 262 | } |
265 | } | 263 | } |
266 | 264 | ||
@@ -296,14 +294,14 @@ fn generate_syntax_kinds(grammar: KindsSrc<'_>) -> Result<String> { | |||
296 | 294 | ||
297 | let full_keywords_values = &grammar.keywords; | 295 | let full_keywords_values = &grammar.keywords; |
298 | let full_keywords = | 296 | let full_keywords = |
299 | full_keywords_values.iter().map(|kw| format_ident!("{}_KW", to_upper_snake_case(&kw))); | 297 | full_keywords_values.iter().map(|kw| format_ident!("{}_KW", to_upper_snake_case(kw))); |
300 | 298 | ||
301 | let all_keywords_values = | 299 | let all_keywords_values = |
302 | grammar.keywords.iter().chain(grammar.contextual_keywords.iter()).collect::<Vec<_>>(); | 300 | grammar.keywords.iter().chain(grammar.contextual_keywords.iter()).collect::<Vec<_>>(); |
303 | let all_keywords_idents = all_keywords_values.iter().map(|kw| format_ident!("{}", kw)); | 301 | let all_keywords_idents = all_keywords_values.iter().map(|kw| format_ident!("{}", kw)); |
304 | let all_keywords = all_keywords_values | 302 | let all_keywords = all_keywords_values |
305 | .iter() | 303 | .iter() |
306 | .map(|name| format_ident!("{}_KW", to_upper_snake_case(&name))) | 304 | .map(|name| format_ident!("{}_KW", to_upper_snake_case(name))) |
307 | .collect::<Vec<_>>(); | 305 | .collect::<Vec<_>>(); |
308 | 306 | ||
309 | let literals = | 307 | let literals = |
diff --git a/xtask/src/main.rs b/xtask/src/main.rs index d0bef7b7a..063e11a5a 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | //! See https://github.com/matklad/cargo-xtask/. | 1 | //! See <https://github.com/matklad/cargo-xtask/>. |
2 | //! | 2 | //! |
3 | //! This binary defines various auxiliary build commands, which are not | 3 | //! This binary defines various auxiliary build commands, which are not |
4 | //! expressible with just `cargo`. Notably, it provides tests via `cargo test -p xtask` | 4 | //! expressible with just `cargo`. Notably, it provides tests via `cargo test -p xtask` |
diff --git a/xtask/src/metrics.rs b/xtask/src/metrics.rs index b0b76b8aa..7b190d425 100644 --- a/xtask/src/metrics.rs +++ b/xtask/src/metrics.rs | |||
@@ -71,7 +71,7 @@ impl Metrics { | |||
71 | Ok(()) | 71 | Ok(()) |
72 | } | 72 | } |
73 | fn measure_analysis_stats_self(&mut self) -> Result<()> { | 73 | fn measure_analysis_stats_self(&mut self) -> Result<()> { |
74 | self.measure_analysis_stats_path("self", &".") | 74 | self.measure_analysis_stats_path("self", ".") |
75 | } | 75 | } |
76 | fn measure_analysis_stats(&mut self, bench: &str) -> Result<()> { | 76 | fn measure_analysis_stats(&mut self, bench: &str) -> Result<()> { |
77 | self.measure_analysis_stats_path( | 77 | self.measure_analysis_stats_path( |
@@ -81,9 +81,8 @@ impl Metrics { | |||
81 | } | 81 | } |
82 | fn measure_analysis_stats_path(&mut self, name: &str, path: &str) -> Result<()> { | 82 | fn measure_analysis_stats_path(&mut self, name: &str, path: &str) -> Result<()> { |
83 | eprintln!("\nMeasuring analysis-stats/{}", name); | 83 | eprintln!("\nMeasuring analysis-stats/{}", name); |
84 | let output = | 84 | let output = cmd!("./target/release/rust-analyzer -q analysis-stats --memory-usage {path}") |
85 | cmd!("./target/release/rust-analyzer --quiet analysis-stats --memory-usage {path}") | 85 | .read()?; |
86 | .read()?; | ||
87 | for (metric, value, unit) in parse_metrics(&output) { | 86 | for (metric, value, unit) in parse_metrics(&output) { |
88 | self.report(&format!("analysis-stats/{}/{}", name, metric), value, unit.into()); | 87 | self.report(&format!("analysis-stats/{}/{}", name, metric), value, unit.into()); |
89 | } | 88 | } |
diff --git a/xtask/src/release/changelog.rs b/xtask/src/release/changelog.rs index ffcae2cf7..2384a746f 100644 --- a/xtask/src/release/changelog.rs +++ b/xtask/src/release/changelog.rs | |||
@@ -132,7 +132,7 @@ fn parse_changelog_line(s: &str) -> Option<PrInfo> { | |||
132 | return Some(PrInfo { kind, message: Some(message) }); | 132 | return Some(PrInfo { kind, message: Some(message) }); |
133 | } | 133 | } |
134 | }; | 134 | }; |
135 | let res = PrInfo { kind, message }; | 135 | let res = PrInfo { message, kind }; |
136 | Some(res) | 136 | Some(res) |
137 | } | 137 | } |
138 | 138 | ||
@@ -152,7 +152,7 @@ fn parse_title_line(s: &str) -> PrInfo { | |||
152 | PrKind::Skip => None, | 152 | PrKind::Skip => None, |
153 | _ => Some(s[prefix.len()..].to_string()), | 153 | _ => Some(s[prefix.len()..].to_string()), |
154 | }; | 154 | }; |
155 | return PrInfo { kind, message }; | 155 | return PrInfo { message, kind }; |
156 | } | 156 | } |
157 | } | 157 | } |
158 | PrInfo { kind: PrKind::Other, message: Some(s.to_string()) } | 158 | PrInfo { kind: PrKind::Other, message: Some(s.to_string()) } |
diff --git a/xtask/src/tidy.rs b/xtask/src/tidy.rs index 6f687a788..f2ba8efef 100644 --- a/xtask/src/tidy.rs +++ b/xtask/src/tidy.rs | |||
@@ -1,4 +1,7 @@ | |||
1 | use std::path::{Path, PathBuf}; | 1 | use std::{ |
2 | collections::HashSet, | ||
3 | path::{Path, PathBuf}, | ||
4 | }; | ||
2 | 5 | ||
3 | use xshell::{cmd, pushd, pushenv, read_file}; | 6 | use xshell::{cmd, pushd, pushenv, read_file}; |
4 | 7 | ||
@@ -33,7 +36,7 @@ fn check_code_formatting() { | |||
33 | let _e = pushenv("RUSTUP_TOOLCHAIN", "stable"); | 36 | let _e = pushenv("RUSTUP_TOOLCHAIN", "stable"); |
34 | crate::ensure_rustfmt().unwrap(); | 37 | crate::ensure_rustfmt().unwrap(); |
35 | let res = cmd!("cargo fmt -- --check").run(); | 38 | let res = cmd!("cargo fmt -- --check").run(); |
36 | if !res.is_ok() { | 39 | if res.is_err() { |
37 | let _ = cmd!("cargo fmt").run(); | 40 | let _ = cmd!("cargo fmt").run(); |
38 | } | 41 | } |
39 | res.unwrap() | 42 | res.unwrap() |
@@ -81,6 +84,7 @@ Please adjust docs/dev/lsp-extensions.md. | |||
81 | #[test] | 84 | #[test] |
82 | fn rust_files_are_tidy() { | 85 | fn rust_files_are_tidy() { |
83 | let mut tidy_docs = TidyDocs::default(); | 86 | let mut tidy_docs = TidyDocs::default(); |
87 | let mut tidy_marks = TidyMarks::default(); | ||
84 | for path in rust_files() { | 88 | for path in rust_files() { |
85 | let text = read_file(&path).unwrap(); | 89 | let text = read_file(&path).unwrap(); |
86 | check_todo(&path, &text); | 90 | check_todo(&path, &text); |
@@ -88,8 +92,10 @@ fn rust_files_are_tidy() { | |||
88 | check_trailing_ws(&path, &text); | 92 | check_trailing_ws(&path, &text); |
89 | deny_clippy(&path, &text); | 93 | deny_clippy(&path, &text); |
90 | tidy_docs.visit(&path, &text); | 94 | tidy_docs.visit(&path, &text); |
95 | tidy_marks.visit(&path, &text); | ||
91 | } | 96 | } |
92 | tidy_docs.finish(); | 97 | tidy_docs.finish(); |
98 | tidy_marks.finish(); | ||
93 | } | 99 | } |
94 | 100 | ||
95 | #[test] | 101 | #[test] |
@@ -193,7 +199,9 @@ https://github.blog/2015-06-08-how-to-undo-almost-anything-with-git/#redo-after- | |||
193 | fn deny_clippy(path: &Path, text: &str) { | 199 | fn deny_clippy(path: &Path, text: &str) { |
194 | let ignore = &[ | 200 | let ignore = &[ |
195 | // The documentation in string literals may contain anything for its own purposes | 201 | // The documentation in string literals may contain anything for its own purposes |
196 | "ide_completion/src/generated_lint_completions.rs", | 202 | "ide_db/src/helpers/generated_lints.rs", |
203 | // The tests test clippy lint hovers | ||
204 | "ide/src/hover.rs", | ||
197 | ]; | 205 | ]; |
198 | if ignore.iter().any(|p| path.ends_with(p)) { | 206 | if ignore.iter().any(|p| path.ends_with(p)) { |
199 | return; | 207 | return; |
@@ -244,19 +252,19 @@ Zlib OR Apache-2.0 OR MIT | |||
244 | .map(|it| it.trim()) | 252 | .map(|it| it.trim()) |
245 | .map(|it| it[r#""license":"#.len()..].trim_matches('"')) | 253 | .map(|it| it[r#""license":"#.len()..].trim_matches('"')) |
246 | .collect::<Vec<_>>(); | 254 | .collect::<Vec<_>>(); |
247 | licenses.sort(); | 255 | licenses.sort_unstable(); |
248 | licenses.dedup(); | 256 | licenses.dedup(); |
249 | if licenses != expected { | 257 | if licenses != expected { |
250 | let mut diff = String::new(); | 258 | let mut diff = String::new(); |
251 | 259 | ||
252 | diff += &format!("New Licenses:\n"); | 260 | diff.push_str("New Licenses:\n"); |
253 | for &l in licenses.iter() { | 261 | for &l in licenses.iter() { |
254 | if !expected.contains(&l) { | 262 | if !expected.contains(&l) { |
255 | diff += &format!(" {}\n", l) | 263 | diff += &format!(" {}\n", l) |
256 | } | 264 | } |
257 | } | 265 | } |
258 | 266 | ||
259 | diff += &format!("\nMissing Licenses:\n"); | 267 | diff.push_str("\nMissing Licenses:\n"); |
260 | for &l in expected.iter() { | 268 | for &l in expected.iter() { |
261 | if !licenses.contains(&l) { | 269 | if !licenses.contains(&l) { |
262 | diff += &format!(" {}\n", l) | 270 | diff += &format!(" {}\n", l) |
@@ -280,7 +288,7 @@ fn check_todo(path: &Path, text: &str) { | |||
280 | // `ast::make`. | 288 | // `ast::make`. |
281 | "ast/make.rs", | 289 | "ast/make.rs", |
282 | // The documentation in string literals may contain anything for its own purposes | 290 | // The documentation in string literals may contain anything for its own purposes |
283 | "ide_completion/src/generated_lint_completions.rs", | 291 | "ide_db/src/helpers/generated_lints.rs", |
284 | ]; | 292 | ]; |
285 | if need_todo.iter().any(|p| path.ends_with(p)) { | 293 | if need_todo.iter().any(|p| path.ends_with(p)) { |
286 | return; | 294 | return; |
@@ -310,7 +318,7 @@ fn check_dbg(path: &Path, text: &str) { | |||
310 | "ide_completion/src/completions/postfix.rs", | 318 | "ide_completion/src/completions/postfix.rs", |
311 | // The documentation in string literals may contain anything for its own purposes | 319 | // The documentation in string literals may contain anything for its own purposes |
312 | "ide_completion/src/lib.rs", | 320 | "ide_completion/src/lib.rs", |
313 | "ide_completion/src/generated_lint_completions.rs", | 321 | "ide_db/src/helpers/generated_lints.rs", |
314 | // test for doc test for remove_dbg | 322 | // test for doc test for remove_dbg |
315 | "src/tests/generated.rs", | 323 | "src/tests/generated.rs", |
316 | ]; | 324 | ]; |
@@ -364,7 +372,10 @@ impl TidyDocs { | |||
364 | self.contains_fixme.push(path.to_path_buf()); | 372 | self.contains_fixme.push(path.to_path_buf()); |
365 | } | 373 | } |
366 | } else { | 374 | } else { |
367 | if text.contains("// Feature:") || text.contains("// Assist:") { | 375 | if text.contains("// Feature:") |
376 | || text.contains("// Assist:") | ||
377 | || text.contains("// Diagnostic:") | ||
378 | { | ||
368 | return; | 379 | return; |
369 | } | 380 | } |
370 | self.missing_docs.push(path.display().to_string()); | 381 | self.missing_docs.push(path.display().to_string()); |
@@ -406,6 +417,39 @@ fn is_exclude_dir(p: &Path, dirs_to_exclude: &[&str]) -> bool { | |||
406 | .any(|it| dirs_to_exclude.contains(&it)) | 417 | .any(|it| dirs_to_exclude.contains(&it)) |
407 | } | 418 | } |
408 | 419 | ||
420 | #[derive(Default)] | ||
421 | struct TidyMarks { | ||
422 | hits: HashSet<String>, | ||
423 | checks: HashSet<String>, | ||
424 | } | ||
425 | |||
426 | impl TidyMarks { | ||
427 | fn visit(&mut self, _path: &Path, text: &str) { | ||
428 | for line in text.lines() { | ||
429 | if let Some(mark) = find_mark(line, "hit") { | ||
430 | self.hits.insert(mark.to_string()); | ||
431 | } | ||
432 | if let Some(mark) = find_mark(line, "check") { | ||
433 | self.checks.insert(mark.to_string()); | ||
434 | } | ||
435 | if let Some(mark) = find_mark(line, "check_count") { | ||
436 | self.checks.insert(mark.to_string()); | ||
437 | } | ||
438 | } | ||
439 | } | ||
440 | |||
441 | fn finish(self) { | ||
442 | assert!(!self.hits.is_empty()); | ||
443 | |||
444 | let diff: Vec<_> = | ||
445 | self.hits.symmetric_difference(&self.checks).map(|it| it.as_str()).collect(); | ||
446 | |||
447 | if !diff.is_empty() { | ||
448 | panic!("unpaired marks: {:?}", diff) | ||
449 | } | ||
450 | } | ||
451 | } | ||
452 | |||
409 | #[allow(deprecated)] | 453 | #[allow(deprecated)] |
410 | fn stable_hash(text: &str) -> u64 { | 454 | fn stable_hash(text: &str) -> u64 { |
411 | use std::hash::{Hash, Hasher, SipHasher}; | 455 | use std::hash::{Hash, Hasher, SipHasher}; |
@@ -415,3 +459,11 @@ fn stable_hash(text: &str) -> u64 { | |||
415 | text.hash(&mut hasher); | 459 | text.hash(&mut hasher); |
416 | hasher.finish() | 460 | hasher.finish() |
417 | } | 461 | } |
462 | |||
463 | fn find_mark<'a>(text: &'a str, mark: &'static str) -> Option<&'a str> { | ||
464 | let idx = text.find(mark)?; | ||
465 | let text = text[idx + mark.len()..].strip_prefix("!(")?; | ||
466 | let idx = text.find(|c: char| !(c.is_alphanumeric() || c == '_'))?; | ||
467 | let text = &text[..idx]; | ||
468 | Some(text) | ||
469 | } | ||