diff options
Diffstat (limited to 'crates')
247 files changed, 8558 insertions, 8462 deletions
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs index 69ceba735..d56b20b83 100644 --- a/crates/base_db/src/fixture.rs +++ b/crates/base_db/src/fixture.rs | |||
@@ -9,8 +9,8 @@ use test_utils::{ | |||
9 | use vfs::{file_set::FileSet, VfsPath}; | 9 | use vfs::{file_set::FileSet, VfsPath}; |
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | input::CrateName, Change, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, FileRange, | 12 | input::CrateName, Change, CrateDisplayName, CrateGraph, CrateId, Edition, Env, FileId, |
13 | SourceDatabaseExt, SourceRoot, SourceRootId, | 13 | FilePosition, FileRange, SourceDatabaseExt, SourceRoot, SourceRootId, |
14 | }; | 14 | }; |
15 | 15 | ||
16 | pub const WORKSPACE: SourceRootId = SourceRootId(0); | 16 | pub const WORKSPACE: SourceRootId = SourceRootId(0); |
@@ -24,6 +24,14 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { | |||
24 | (db, fixture.files[0]) | 24 | (db, fixture.files[0]) |
25 | } | 25 | } |
26 | 26 | ||
27 | fn with_many_files(ra_fixture: &str) -> (Self, Vec<FileId>) { | ||
28 | let fixture = ChangeFixture::parse(ra_fixture); | ||
29 | let mut db = Self::default(); | ||
30 | fixture.change.apply(&mut db); | ||
31 | assert!(fixture.file_position.is_none()); | ||
32 | (db, fixture.files) | ||
33 | } | ||
34 | |||
27 | fn with_files(ra_fixture: &str) -> Self { | 35 | fn with_files(ra_fixture: &str) -> Self { |
28 | let fixture = ChangeFixture::parse(ra_fixture); | 36 | let fixture = ChangeFixture::parse(ra_fixture); |
29 | let mut db = Self::default(); | 37 | let mut db = Self::default(); |
@@ -73,7 +81,7 @@ pub struct ChangeFixture { | |||
73 | 81 | ||
74 | impl ChangeFixture { | 82 | impl ChangeFixture { |
75 | pub fn parse(ra_fixture: &str) -> ChangeFixture { | 83 | pub fn parse(ra_fixture: &str) -> ChangeFixture { |
76 | let fixture = Fixture::parse(ra_fixture); | 84 | let (mini_core, fixture) = Fixture::parse(ra_fixture); |
77 | let mut change = Change::new(); | 85 | let mut change = Change::new(); |
78 | 86 | ||
79 | let mut files = Vec::new(); | 87 | let mut files = Vec::new(); |
@@ -158,6 +166,31 @@ impl ChangeFixture { | |||
158 | } | 166 | } |
159 | } | 167 | } |
160 | 168 | ||
169 | if let Some(mini_core) = mini_core { | ||
170 | let core_file = file_id; | ||
171 | file_id.0 += 1; | ||
172 | |||
173 | let mut fs = FileSet::default(); | ||
174 | fs.insert(core_file, VfsPath::new_virtual_path("/sysroot/core/lib.rs".to_string())); | ||
175 | roots.push(SourceRoot::new_library(fs)); | ||
176 | |||
177 | change.change_file(core_file, Some(Arc::new(mini_core.source_code()))); | ||
178 | |||
179 | let all_crates = crate_graph.crates_in_topological_order(); | ||
180 | |||
181 | let core_crate = crate_graph.add_crate_root( | ||
182 | core_file, | ||
183 | Edition::Edition2021, | ||
184 | Some(CrateDisplayName::from_canonical_name("core".to_string())), | ||
185 | CfgOptions::default(), | ||
186 | Env::default(), | ||
187 | Vec::new(), | ||
188 | ); | ||
189 | |||
190 | for krate in all_crates { | ||
191 | crate_graph.add_dep(krate, CrateName::new("core").unwrap(), core_crate).unwrap(); | ||
192 | } | ||
193 | } | ||
161 | roots.push(SourceRoot::new_local(mem::take(&mut file_set))); | 194 | roots.push(SourceRoot::new_local(mem::take(&mut file_set))); |
162 | change.set_roots(roots); | 195 | change.set_roots(roots); |
163 | change.set_crate_graph(crate_graph); | 196 | change.set_crate_graph(crate_graph); |
@@ -190,7 +223,7 @@ impl From<Fixture> for FileMeta { | |||
190 | edition: f | 223 | edition: f |
191 | .edition | 224 | .edition |
192 | .as_ref() | 225 | .as_ref() |
193 | .map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap()), | 226 | .map_or(Edition::Edition2018, |v| Edition::from_str(v).unwrap()), |
194 | env: f.env.into_iter().collect(), | 227 | env: f.env.into_iter().collect(), |
195 | introduce_new_source_root: f.introduce_new_source_root, | 228 | introduce_new_source_root: f.introduce_new_source_root, |
196 | } | 229 | } |
diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs index 54baa3a63..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, |
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/has_source.rs b/crates/hir/src/has_source.rs index dc10a4d0f..197149c5e 100644 --- a/crates/hir/src/has_source.rs +++ b/crates/hir/src/has_source.rs | |||
@@ -127,7 +127,7 @@ impl HasSource for Impl { | |||
127 | } | 127 | } |
128 | 128 | ||
129 | impl HasSource for TypeParam { | 129 | impl HasSource for TypeParam { |
130 | type Ast = Either<ast::Trait, ast::TypeParam>; | 130 | type Ast = Either<ast::TypeParam, ast::Trait>; |
131 | fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> { | 131 | fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> { |
132 | let child_source = self.id.parent.child_source(db.upcast()); | 132 | let child_source = self.id.parent.child_source(db.upcast()); |
133 | Some(child_source.map(|it| it[self.id.local_id].clone())) | 133 | Some(child_source.map(|it| it[self.id.local_id].clone())) |
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 0bb3767c1..b7eabaabb 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, |
@@ -60,8 +56,8 @@ use hir_ty::{ | |||
60 | autoderef, | 56 | autoderef, |
61 | consteval::ConstExt, | 57 | consteval::ConstExt, |
62 | could_unify, | 58 | could_unify, |
63 | diagnostics_sink::DiagnosticSink, | 59 | diagnostics::BodyValidationDiagnostic, |
64 | method_resolution::{self, def_crates, TyFingerprint}, | 60 | method_resolution::{self, TyFingerprint}, |
65 | primitive::UintTy, | 61 | primitive::UintTy, |
66 | subst_prefix, | 62 | subst_prefix, |
67 | traits::FnTrait, | 63 | traits::FnTrait, |
@@ -72,6 +68,7 @@ use hir_ty::{ | |||
72 | }; | 68 | }; |
73 | use itertools::Itertools; | 69 | use itertools::Itertools; |
74 | use nameres::diagnostics::DefDiagnosticKind; | 70 | use nameres::diagnostics::DefDiagnosticKind; |
71 | use once_cell::unsync::Lazy; | ||
75 | use rustc_hash::FxHashSet; | 72 | use rustc_hash::FxHashSet; |
76 | use stdx::{format_to, impl_from}; | 73 | use stdx::{format_to, impl_from}; |
77 | use syntax::{ | 74 | use syntax::{ |
@@ -84,6 +81,13 @@ use crate::db::{DefDatabase, HirDatabase}; | |||
84 | 81 | ||
85 | pub use crate::{ | 82 | pub use crate::{ |
86 | 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 | }, | ||
87 | has_source::HasSource, | 91 | has_source::HasSource, |
88 | semantics::{PathResolution, Semantics, SemanticsScope}, | 92 | semantics::{PathResolution, Semantics, SemanticsScope}, |
89 | }; | 93 | }; |
@@ -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,8 +546,7 @@ 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 | } |
@@ -541,73 +557,73 @@ impl Module { | |||
541 | || panic!("cannot find attribute #{}", invoc_attr_index), | 557 | || panic!("cannot find attribute #{}", invoc_attr_index), |
542 | ); | 558 | ); |
543 | ( | 559 | ( |
544 | ast_id.file_id, | 560 | ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&attr))), |
545 | SyntaxNodePtr::from(AstPtr::new(&attr)), | ||
546 | Some(attr_name.clone()), | 561 | Some(attr_name.clone()), |
547 | ) | 562 | ) |
548 | } | 563 | } |
549 | }; | 564 | }; |
550 | sink.push(UnresolvedProcMacro { | 565 | acc.push( |
551 | file, | 566 | UnresolvedProcMacro { node, precise_location, macro_name: name }.into(), |
552 | node: ast, | 567 | ); |
553 | precise_location, | ||
554 | macro_name: name, | ||
555 | }); | ||
556 | } | 568 | } |
557 | 569 | ||
558 | DefDiagnosticKind::UnresolvedMacroCall { ast, path } => { | 570 | DefDiagnosticKind::UnresolvedMacroCall { ast, path } => { |
559 | let node = ast.to_node(db.upcast()); | 571 | let node = ast.to_node(db.upcast()); |
560 | sink.push(UnresolvedMacroCall { | 572 | acc.push( |
561 | file: ast.file_id, | 573 | UnresolvedMacroCall { |
562 | node: AstPtr::new(&node), | 574 | macro_call: InFile::new(ast.file_id, AstPtr::new(&node)), |
563 | path: path.clone(), | 575 | path: path.clone(), |
564 | }); | 576 | } |
577 | .into(), | ||
578 | ); | ||
565 | } | 579 | } |
566 | 580 | ||
567 | DefDiagnosticKind::MacroError { ast, message } => { | 581 | DefDiagnosticKind::MacroError { ast, message } => { |
568 | let (file, ast) = match ast { | 582 | let node = match ast { |
569 | MacroCallKind::FnLike { ast_id, .. } => { | 583 | MacroCallKind::FnLike { ast_id, .. } => { |
570 | let node = ast_id.to_node(db.upcast()); | 584 | let node = ast_id.to_node(db.upcast()); |
571 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) | 585 | ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node))) |
572 | } | 586 | } |
573 | MacroCallKind::Derive { ast_id, .. } | 587 | MacroCallKind::Derive { ast_id, .. } |
574 | | MacroCallKind::Attr { ast_id, .. } => { | 588 | | MacroCallKind::Attr { ast_id, .. } => { |
575 | // FIXME: point to the attribute instead, this creates very large diagnostics | 589 | // FIXME: point to the attribute instead, this creates very large diagnostics |
576 | let node = ast_id.to_node(db.upcast()); | 590 | let node = ast_id.to_node(db.upcast()); |
577 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) | 591 | ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node))) |
578 | } | 592 | } |
579 | }; | 593 | }; |
580 | sink.push(MacroError { file, node: ast, message: message.clone() }); | 594 | acc.push(MacroError { node, message: message.clone() }.into()); |
581 | } | 595 | } |
582 | 596 | ||
583 | DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => { | 597 | DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => { |
584 | let node = ast.to_node(db.upcast()); | 598 | let node = ast.to_node(db.upcast()); |
585 | // Must have a name, otherwise we wouldn't emit it. | 599 | // Must have a name, otherwise we wouldn't emit it. |
586 | let name = node.name().expect("unimplemented builtin macro with no name"); | 600 | let name = node.name().expect("unimplemented builtin macro with no name"); |
587 | let ptr = SyntaxNodePtr::from(AstPtr::new(&name)); | 601 | acc.push( |
588 | 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 | ); | ||
589 | } | 607 | } |
590 | } | 608 | } |
591 | } | 609 | } |
592 | for decl in self.declarations(db) { | 610 | for decl in self.declarations(db) { |
593 | match decl { | 611 | match decl { |
594 | crate::ModuleDef::Function(f) => f.diagnostics(db, sink), | 612 | ModuleDef::Function(f) => f.diagnostics(db, acc), |
595 | crate::ModuleDef::Module(m) => { | 613 | ModuleDef::Module(m) => { |
596 | // Only add diagnostics from inline modules | 614 | // Only add diagnostics from inline modules |
597 | if def_map[m.id.local_id].origin.is_inline() { | 615 | if def_map[m.id.local_id].origin.is_inline() { |
598 | m.diagnostics(db, sink) | 616 | m.diagnostics(db, acc) |
599 | } | 617 | } |
600 | } | 618 | } |
601 | _ => { | 619 | _ => acc.extend(decl.diagnostics(db)), |
602 | decl.diagnostics(db, sink); | ||
603 | } | ||
604 | } | 620 | } |
605 | } | 621 | } |
606 | 622 | ||
607 | for impl_def in self.impl_defs(db) { | 623 | for impl_def in self.impl_defs(db) { |
608 | for item in impl_def.items(db) { | 624 | for item in impl_def.items(db) { |
609 | if let AssocItem::Function(f) = item { | 625 | if let AssocItem::Function(f) = item { |
610 | f.diagnostics(db, sink); | 626 | f.diagnostics(db, acc); |
611 | } | 627 | } |
612 | } | 628 | } |
613 | } | 629 | } |
@@ -1009,41 +1025,191 @@ impl Function { | |||
1009 | db.function_data(self.id).is_async() | 1025 | db.function_data(self.id).is_async() |
1010 | } | 1026 | } |
1011 | 1027 | ||
1012 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | 1028 | pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) { |
1013 | let krate = self.module(db).id.krate(); | 1029 | let krate = self.module(db).id.krate(); |
1014 | 1030 | ||
1015 | 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; |
1016 | for diag in source_map.diagnostics() { | 1032 | for diag in source_map.diagnostics() { |
1017 | match diag { | 1033 | match diag { |
1018 | BodyDiagnostic::InactiveCode { node, cfg, opts } => sink.push(InactiveCode { | 1034 | BodyDiagnostic::InactiveCode { node, cfg, opts } => acc.push( |
1019 | file: node.file_id, | 1035 | InactiveCode { node: node.clone(), cfg: cfg.clone(), opts: opts.clone() } |
1020 | node: node.value.clone(), | 1036 | .into(), |
1021 | cfg: cfg.clone(), | 1037 | ), |
1022 | opts: opts.clone(), | 1038 | BodyDiagnostic::MacroError { node, message } => acc.push( |
1023 | }), | 1039 | MacroError { |
1024 | BodyDiagnostic::MacroError { node, message } => sink.push(MacroError { | 1040 | node: node.clone().map(|it| it.into()), |
1025 | file: node.file_id, | 1041 | message: message.to_string(), |
1026 | node: node.value.clone().into(), | 1042 | } |
1027 | message: message.to_string(), | 1043 | .into(), |
1028 | }), | 1044 | ), |
1029 | BodyDiagnostic::UnresolvedProcMacro { node } => sink.push(UnresolvedProcMacro { | 1045 | BodyDiagnostic::UnresolvedProcMacro { node } => acc.push( |
1030 | file: node.file_id, | 1046 | UnresolvedProcMacro { |
1031 | node: node.value.clone().into(), | 1047 | node: node.clone().map(|it| it.into()), |
1032 | precise_location: None, | 1048 | precise_location: None, |
1033 | macro_name: None, | 1049 | macro_name: None, |
1034 | }), | 1050 | } |
1035 | BodyDiagnostic::UnresolvedMacroCall { node, path } => { | 1051 | .into(), |
1036 | sink.push(UnresolvedMacroCall { | 1052 | ), |
1037 | file: node.file_id, | 1053 | BodyDiagnostic::UnresolvedMacroCall { node, path } => acc.push( |
1038 | node: node.value.clone(), | 1054 | UnresolvedMacroCall { macro_call: node.clone(), path: path.clone() }.into(), |
1039 | path: path.clone(), | 1055 | ), |
1040 | }) | 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()) | ||
1041 | } | 1072 | } |
1042 | } | 1073 | } |
1043 | } | 1074 | } |
1044 | 1075 | ||
1045 | hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink); | 1076 | for expr in hir_ty::diagnostics::missing_unsafe(db, self.id.into()) { |
1046 | hir_ty::diagnostics::validate_body(db, self.id.into(), sink); | 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 | } | ||
1206 | } | ||
1207 | } | ||
1208 | } | ||
1209 | |||
1210 | for diag in hir_ty::diagnostics::validate_module_item(db, krate, self.id.into()) { | ||
1211 | acc.push(diag.into()) | ||
1212 | } | ||
1047 | } | 1213 | } |
1048 | 1214 | ||
1049 | /// Whether this function declaration has a definition. | 1215 | /// Whether this function declaration has a definition. |
@@ -1762,7 +1928,7 @@ impl Impl { | |||
1762 | } | 1928 | } |
1763 | 1929 | ||
1764 | 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> { |
1765 | let def_crates = match def_crates(db, &ty, krate) { | 1931 | let def_crates = match method_resolution::def_crates(db, &ty, krate) { |
1766 | Some(def_crates) => def_crates, | 1932 | Some(def_crates) => def_crates, |
1767 | None => return Vec::new(), | 1933 | None => return Vec::new(), |
1768 | }; | 1934 | }; |
@@ -2168,7 +2334,7 @@ impl Type { | |||
2168 | krate: Crate, | 2334 | krate: Crate, |
2169 | mut callback: impl FnMut(AssocItem) -> Option<T>, | 2335 | mut callback: impl FnMut(AssocItem) -> Option<T>, |
2170 | ) -> Option<T> { | 2336 | ) -> Option<T> { |
2171 | for krate in def_crates(db, &self.ty, krate.id)? { | 2337 | for krate in method_resolution::def_crates(db, &self.ty, krate.id)? { |
2172 | let impls = db.inherent_impls_in_crate(krate); | 2338 | let impls = db.inherent_impls_in_crate(krate); |
2173 | 2339 | ||
2174 | for impl_def in impls.for_self_ty(&self.ty) { | 2340 | for impl_def in impls.for_self_ty(&self.ty) { |
@@ -2345,13 +2511,13 @@ impl Type { | |||
2345 | match ty.kind(&Interner) { | 2511 | match ty.kind(&Interner) { |
2346 | TyKind::Adt(_, substs) => { | 2512 | TyKind::Adt(_, substs) => { |
2347 | cb(type_.derived(ty.clone())); | 2513 | cb(type_.derived(ty.clone())); |
2348 | walk_substs(db, type_, &substs, cb); | 2514 | walk_substs(db, type_, substs, cb); |
2349 | } | 2515 | } |
2350 | TyKind::AssociatedType(_, substs) => { | 2516 | TyKind::AssociatedType(_, substs) => { |
2351 | if let Some(_) = ty.associated_type_parent_trait(db) { | 2517 | if let Some(_) = ty.associated_type_parent_trait(db) { |
2352 | cb(type_.derived(ty.clone())); | 2518 | cb(type_.derived(ty.clone())); |
2353 | } | 2519 | } |
2354 | walk_substs(db, type_, &substs, cb); | 2520 | walk_substs(db, type_, substs, cb); |
2355 | } | 2521 | } |
2356 | TyKind::OpaqueType(_, subst) => { | 2522 | TyKind::OpaqueType(_, subst) => { |
2357 | if let Some(bounds) = ty.impl_trait_bounds(db) { | 2523 | if let Some(bounds) = ty.impl_trait_bounds(db) { |
@@ -2391,7 +2557,7 @@ impl Type { | |||
2391 | TyKind::FnDef(_, substs) | 2557 | TyKind::FnDef(_, substs) |
2392 | | TyKind::Tuple(_, substs) | 2558 | | TyKind::Tuple(_, substs) |
2393 | | TyKind::Closure(.., substs) => { | 2559 | | TyKind::Closure(.., substs) => { |
2394 | walk_substs(db, type_, &substs, cb); | 2560 | walk_substs(db, type_, substs, cb); |
2395 | } | 2561 | } |
2396 | TyKind::Function(hir_ty::FnPointer { substitution, .. }) => { | 2562 | TyKind::Function(hir_ty::FnPointer { substitution, .. }) => { |
2397 | walk_substs(db, type_, &substitution.0, cb); | 2563 | walk_substs(db, type_, &substitution.0, cb); |
@@ -2522,18 +2688,6 @@ impl ScopeDef { | |||
2522 | 2688 | ||
2523 | items | 2689 | items |
2524 | } | 2690 | } |
2525 | |||
2526 | pub fn is_value_def(&self) -> bool { | ||
2527 | matches!( | ||
2528 | self, | ||
2529 | ScopeDef::ModuleDef(ModuleDef::Function(_)) | ||
2530 | | ScopeDef::ModuleDef(ModuleDef::Variant(_)) | ||
2531 | | ScopeDef::ModuleDef(ModuleDef::Const(_)) | ||
2532 | | ScopeDef::ModuleDef(ModuleDef::Static(_)) | ||
2533 | | ScopeDef::GenericParam(GenericParam::ConstParam(_)) | ||
2534 | | ScopeDef::Local(_) | ||
2535 | ) | ||
2536 | } | ||
2537 | } | 2691 | } |
2538 | 2692 | ||
2539 | impl From<ItemInNs> for ScopeDef { | 2693 | impl From<ItemInNs> for ScopeDef { |
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index d522d5245..613266e07 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs | |||
@@ -192,7 +192,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
192 | node: &SyntaxNode, | 192 | node: &SyntaxNode, |
193 | offset: TextSize, | 193 | offset: TextSize, |
194 | ) -> Option<N> { | 194 | ) -> Option<N> { |
195 | if let Some(it) = find_node_at_offset(&node, offset) { | 195 | if let Some(it) = find_node_at_offset(node, offset) { |
196 | return Some(it); | 196 | return Some(it); |
197 | } | 197 | } |
198 | 198 | ||
@@ -744,7 +744,7 @@ impl<'db> SemanticsImpl<'db> { | |||
744 | return None; | 744 | return None; |
745 | } | 745 | } |
746 | 746 | ||
747 | 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)?; |
748 | let res = match func.self_param(self.db)?.access(self.db) { | 748 | let res = match func.self_param(self.db)?.access(self.db) { |
749 | Access::Shared | Access::Exclusive => true, | 749 | Access::Shared | Access::Exclusive => true, |
750 | Access::Owned => false, | 750 | Access::Owned => false, |
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 d9f9fadc1..d07adb084 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -583,13 +583,13 @@ impl AttrSourceMap { | |||
583 | .get(id.ast_index as usize) | 583 | .get(id.ast_index as usize) |
584 | .unwrap_or_else(|| panic!("cannot find doc comment at index {:?}", id)) | 584 | .unwrap_or_else(|| panic!("cannot find doc comment at index {:?}", id)) |
585 | .clone() | 585 | .clone() |
586 | .map(|attr| Either::Right(attr)) | 586 | .map(Either::Right) |
587 | } else { | 587 | } else { |
588 | self.attrs | 588 | self.attrs |
589 | .get(id.ast_index as usize) | 589 | .get(id.ast_index as usize) |
590 | .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", id)) | 590 | .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", id)) |
591 | .clone() | 591 | .clone() |
592 | .map(|attr| Either::Left(attr)) | 592 | .map(Either::Left) |
593 | } | 593 | } |
594 | } | 594 | } |
595 | } | 595 | } |
@@ -606,7 +606,7 @@ pub struct DocsRangeMap { | |||
606 | impl DocsRangeMap { | 606 | impl DocsRangeMap { |
607 | pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> { | 607 | pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> { |
608 | 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()?; |
609 | 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]; |
610 | if !line_docs_range.contains_range(range) { | 610 | if !line_docs_range.contains_range(range) { |
611 | return None; | 611 | return None; |
612 | } | 612 | } |
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/generics.rs b/crates/hir_def/src/generics.rs index 44d22b918..0f04b2bae 100644 --- a/crates/hir_def/src/generics.rs +++ b/crates/hir_def/src/generics.rs | |||
@@ -92,7 +92,7 @@ pub enum WherePredicateTypeTarget { | |||
92 | 92 | ||
93 | #[derive(Default)] | 93 | #[derive(Default)] |
94 | pub(crate) struct SourceMap { | 94 | pub(crate) struct SourceMap { |
95 | pub(crate) type_params: ArenaMap<LocalTypeParamId, Either<ast::Trait, ast::TypeParam>>, | 95 | pub(crate) type_params: ArenaMap<LocalTypeParamId, Either<ast::TypeParam, ast::Trait>>, |
96 | lifetime_params: ArenaMap<LocalLifetimeParamId, ast::LifetimeParam>, | 96 | lifetime_params: ArenaMap<LocalLifetimeParamId, ast::LifetimeParam>, |
97 | const_params: ArenaMap<LocalConstParamId, ast::ConstParam>, | 97 | const_params: ArenaMap<LocalConstParamId, ast::ConstParam>, |
98 | } | 98 | } |
@@ -199,7 +199,7 @@ impl GenericParams { | |||
199 | default: None, | 199 | default: None, |
200 | provenance: TypeParamProvenance::TraitSelf, | 200 | provenance: TypeParamProvenance::TraitSelf, |
201 | }); | 201 | }); |
202 | sm.type_params.insert(self_param_id, Either::Left(src.value.clone())); | 202 | sm.type_params.insert(self_param_id, Either::Right(src.value.clone())); |
203 | // add super traits as bounds on Self | 203 | // add super traits as bounds on Self |
204 | // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar | 204 | // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar |
205 | let self_param = TypeRef::Path(name![Self].into()); | 205 | let self_param = TypeRef::Path(name![Self].into()); |
@@ -277,10 +277,10 @@ impl GenericParams { | |||
277 | provenance: TypeParamProvenance::TypeParamList, | 277 | provenance: TypeParamProvenance::TypeParamList, |
278 | }; | 278 | }; |
279 | let param_id = self.types.alloc(param); | 279 | let param_id = self.types.alloc(param); |
280 | sm.type_params.insert(param_id, Either::Right(type_param.clone())); | 280 | sm.type_params.insert(param_id, Either::Left(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()); |
@@ -413,7 +413,7 @@ impl GenericParams { | |||
413 | } | 413 | } |
414 | 414 | ||
415 | impl HasChildSource<LocalTypeParamId> for GenericDefId { | 415 | impl HasChildSource<LocalTypeParamId> for GenericDefId { |
416 | type Value = Either<ast::Trait, ast::TypeParam>; | 416 | type Value = Either<ast::TypeParam, ast::Trait>; |
417 | fn child_source( | 417 | fn child_source( |
418 | &self, | 418 | &self, |
419 | db: &dyn DefDatabase, | 419 | db: &dyn DefDatabase, |
@@ -449,7 +449,7 @@ impl ChildBySource for GenericDefId { | |||
449 | let sm = sm.as_ref(); | 449 | let sm = sm.as_ref(); |
450 | for (local_id, src) in sm.value.type_params.iter() { | 450 | for (local_id, src) in sm.value.type_params.iter() { |
451 | let id = TypeParamId { parent: *self, local_id }; | 451 | let id = TypeParamId { parent: *self, local_id }; |
452 | if let Either::Right(type_param) = src { | 452 | if let Either::Left(type_param) = src { |
453 | res[keys::TYPE_PARAM].insert(sm.with_value(type_param.clone()), id) | 453 | res[keys::TYPE_PARAM].insert(sm.with_value(type_param.clone()), id) |
454 | } | 454 | } |
455 | } | 455 | } |
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs index 0f74f050d..08407ebfa 100644 --- a/crates/hir_def/src/item_scope.rs +++ b/crates/hir_def/src/item_scope.rs | |||
@@ -59,7 +59,7 @@ pub struct ItemScope { | |||
59 | pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { | 59 | pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { |
60 | BuiltinType::ALL | 60 | BuiltinType::ALL |
61 | .iter() | 61 | .iter() |
62 | .map(|(name, ty)| (name.clone(), PerNs::types(ty.clone().into(), Visibility::Public))) | 62 | .map(|(name, ty)| (name.clone(), PerNs::types((*ty).into(), Visibility::Public))) |
63 | .collect() | 63 | .collect() |
64 | }); | 64 | }); |
65 | 65 | ||
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index cfda7cb32..5b1386406 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -674,7 +674,7 @@ impl<'a> Ctx<'a> { | |||
674 | default: None, | 674 | default: None, |
675 | provenance: TypeParamProvenance::TraitSelf, | 675 | provenance: TypeParamProvenance::TraitSelf, |
676 | }); | 676 | }); |
677 | sm.type_params.insert(self_param_id, Either::Left(trait_def.clone())); | 677 | sm.type_params.insert(self_param_id, Either::Right(trait_def.clone())); |
678 | // add super traits as bounds on Self | 678 | // add super traits as bounds on Self |
679 | // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar | 679 | // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar |
680 | let self_param = TypeRef::Path(name![Self].into()); | 680 | let self_param = TypeRef::Path(name![Self].into()); |
@@ -823,7 +823,7 @@ fn is_intrinsic_fn_unsafe(name: &Name) -> bool { | |||
823 | known::type_name, | 823 | known::type_name, |
824 | known::variant_count, | 824 | known::variant_count, |
825 | ] | 825 | ] |
826 | .contains(&name) | 826 | .contains(name) |
827 | } | 827 | } |
828 | 828 | ||
829 | fn lower_abi(abi: ast::Abi) -> Interned<str> { | 829 | fn lower_abi(abi: ast::Abi) -> Interned<str> { |
@@ -855,7 +855,7 @@ impl UseTreeLowering<'_> { | |||
855 | // E.g. `use something::{inner}` (prefix is `None`, path is `something`) | 855 | // E.g. `use something::{inner}` (prefix is `None`, path is `something`) |
856 | // 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`) |
857 | Some(path) => { | 857 | Some(path) => { |
858 | match ModPath::from_src(self.db, path, &self.hygiene) { | 858 | match ModPath::from_src(self.db, path, self.hygiene) { |
859 | Some(it) => Some(it), | 859 | Some(it) => Some(it), |
860 | None => return None, // FIXME: report errors somewhere | 860 | None => return None, // FIXME: report errors somewhere |
861 | } | 861 | } |
@@ -874,7 +874,7 @@ impl UseTreeLowering<'_> { | |||
874 | } else { | 874 | } else { |
875 | let is_glob = tree.star_token().is_some(); | 875 | let is_glob = tree.star_token().is_some(); |
876 | let path = match tree.path() { | 876 | let path = match tree.path() { |
877 | Some(path) => Some(ModPath::from_src(self.db, path, &self.hygiene)?), | 877 | Some(path) => Some(ModPath::from_src(self.db, path, self.hygiene)?), |
878 | None => None, | 878 | None => None, |
879 | }; | 879 | }; |
880 | 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/lib.rs b/crates/hir_def/src/lib.rs index 987485acc..bb174aec8 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs | |||
@@ -112,6 +112,10 @@ impl ModuleId { | |||
112 | self.def_map(db).containing_module(self.local_id) | 112 | self.def_map(db).containing_module(self.local_id) |
113 | } | 113 | } |
114 | 114 | ||
115 | pub fn containing_block(&self) -> Option<BlockId> { | ||
116 | self.block | ||
117 | } | ||
118 | |||
115 | /// Returns `true` if this module represents a block expression. | 119 | /// Returns `true` if this module represents a block expression. |
116 | /// | 120 | /// |
117 | /// 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 |
@@ -581,6 +585,18 @@ impl HasModule for GenericDefId { | |||
581 | } | 585 | } |
582 | } | 586 | } |
583 | 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 | |||
584 | impl HasModule for StaticLoc { | 600 | impl HasModule for StaticLoc { |
585 | fn module(&self, _db: &dyn db::DefDatabase) -> ModuleId { | 601 | fn module(&self, _db: &dyn db::DefDatabase) -> ModuleId { |
586 | self.container | 602 | self.container |
@@ -731,13 +747,11 @@ fn macro_call_as_call_id( | |||
731 | ) | 747 | ) |
732 | .map(MacroCallId::from) | 748 | .map(MacroCallId::from) |
733 | } else { | 749 | } else { |
734 | Ok(def | 750 | Ok(def.as_lazy_macro( |
735 | .as_lazy_macro( | 751 | db.upcast(), |
736 | db.upcast(), | 752 | krate, |
737 | krate, | 753 | MacroCallKind::FnLike { ast_id: call.ast_id, fragment }, |
738 | MacroCallKind::FnLike { ast_id: call.ast_id, fragment }, | 754 | )) |
739 | ) | ||
740 | .into()) | ||
741 | }; | 755 | }; |
742 | Ok(res) | 756 | Ok(res) |
743 | } | 757 | } |
@@ -756,17 +770,15 @@ fn derive_macro_as_call_id( | |||
756 | .segments() | 770 | .segments() |
757 | .last() | 771 | .last() |
758 | .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?; | 772 | .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?; |
759 | let res = def | 773 | let res = def.as_lazy_macro( |
760 | .as_lazy_macro( | 774 | db.upcast(), |
761 | db.upcast(), | 775 | krate, |
762 | krate, | 776 | MacroCallKind::Derive { |
763 | MacroCallKind::Derive { | 777 | ast_id: item_attr.ast_id, |
764 | ast_id: item_attr.ast_id, | 778 | derive_name: last_segment.to_string(), |
765 | derive_name: last_segment.to_string(), | 779 | derive_attr_index: derive_attr.ast_index, |
766 | derive_attr_index: derive_attr.ast_index, | 780 | }, |
767 | }, | 781 | ); |
768 | ) | ||
769 | .into(); | ||
770 | Ok(res) | 782 | Ok(res) |
771 | } | 783 | } |
772 | 784 | ||
@@ -794,17 +806,15 @@ fn attr_macro_as_call_id( | |||
794 | // The parentheses are always disposed here. | 806 | // The parentheses are always disposed here. |
795 | arg.delimiter = None; | 807 | arg.delimiter = None; |
796 | 808 | ||
797 | let res = def | 809 | let res = def.as_lazy_macro( |
798 | .as_lazy_macro( | 810 | db.upcast(), |
799 | db.upcast(), | 811 | krate, |
800 | krate, | 812 | MacroCallKind::Attr { |
801 | MacroCallKind::Attr { | 813 | ast_id: item_attr.ast_id, |
802 | ast_id: item_attr.ast_id, | 814 | attr_name: last_segment.to_string(), |
803 | attr_name: last_segment.to_string(), | 815 | attr_args: arg, |
804 | attr_args: arg, | 816 | invoc_attr_index: macro_attr.id.ast_index, |
805 | invoc_attr_index: macro_attr.id.ast_index, | 817 | }, |
806 | }, | 818 | ); |
807 | ) | ||
808 | .into(); | ||
809 | Ok(res) | 819 | Ok(res) |
810 | } | 820 | } |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 93f30f23d..fc2c50fb8 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -500,7 +500,7 @@ impl DefCollector<'_> { | |||
500 | let (per_ns, _) = self.def_map.resolve_path( | 500 | let (per_ns, _) = self.def_map.resolve_path( |
501 | self.db, | 501 | self.db, |
502 | self.def_map.root, | 502 | self.def_map.root, |
503 | &path, | 503 | path, |
504 | BuiltinShadowMode::Other, | 504 | BuiltinShadowMode::Other, |
505 | ); | 505 | ); |
506 | 506 | ||
@@ -722,7 +722,7 @@ impl DefCollector<'_> { | |||
722 | if import.is_extern_crate { | 722 | if import.is_extern_crate { |
723 | let res = self.def_map.resolve_name_in_extern_prelude( | 723 | let res = self.def_map.resolve_name_in_extern_prelude( |
724 | self.db, | 724 | self.db, |
725 | &import | 725 | import |
726 | .path | 726 | .path |
727 | .as_ident() | 727 | .as_ident() |
728 | .expect("extern crate should have been desugared to one-element path"), | 728 | .expect("extern crate should have been desugared to one-element path"), |
@@ -1351,7 +1351,7 @@ impl ModCollector<'_, '_> { | |||
1351 | let imports = Import::from_use( | 1351 | let imports = Import::from_use( |
1352 | self.def_collector.db, | 1352 | self.def_collector.db, |
1353 | krate, | 1353 | krate, |
1354 | &self.item_tree, | 1354 | self.item_tree, |
1355 | ItemTreeId::new(self.file_id, import_id), | 1355 | ItemTreeId::new(self.file_id, import_id), |
1356 | ); | 1356 | ); |
1357 | self.def_collector.unresolved_imports.extend(imports.into_iter().map( | 1357 | self.def_collector.unresolved_imports.extend(imports.into_iter().map( |
@@ -1368,7 +1368,7 @@ impl ModCollector<'_, '_> { | |||
1368 | import: Import::from_extern_crate( | 1368 | import: Import::from_extern_crate( |
1369 | self.def_collector.db, | 1369 | self.def_collector.db, |
1370 | krate, | 1370 | krate, |
1371 | &self.item_tree, | 1371 | self.item_tree, |
1372 | ItemTreeId::new(self.file_id, import_id), | 1372 | ItemTreeId::new(self.file_id, import_id), |
1373 | ), | 1373 | ), |
1374 | status: PartialResolvedImport::Unresolved, | 1374 | status: PartialResolvedImport::Unresolved, |
@@ -1889,7 +1889,7 @@ impl ModCollector<'_, '_> { | |||
1889 | self.def_collector.def_map.with_ancestor_maps( | 1889 | self.def_collector.def_map.with_ancestor_maps( |
1890 | self.def_collector.db, | 1890 | self.def_collector.db, |
1891 | self.module_id, | 1891 | self.module_id, |
1892 | &mut |map, module| map[module].scope.get_legacy_macro(&name), | 1892 | &mut |map, module| map[module].scope.get_legacy_macro(name), |
1893 | ) | 1893 | ) |
1894 | }) | 1894 | }) |
1895 | }, | 1895 | }, |
@@ -1992,8 +1992,8 @@ mod tests { | |||
1992 | collector.def_map | 1992 | collector.def_map |
1993 | } | 1993 | } |
1994 | 1994 | ||
1995 | fn do_resolve(code: &str) -> DefMap { | 1995 | fn do_resolve(not_ra_fixture: &str) -> DefMap { |
1996 | let (db, _file_id) = TestDB::with_single_file(&code); | 1996 | let (db, _file_id) = TestDB::with_single_file(not_ra_fixture); |
1997 | let krate = db.test_crate(); | 1997 | let krate = db.test_crate(); |
1998 | 1998 | ||
1999 | let edition = db.crate_graph()[krate].edition; | 1999 | let edition = db.crate_graph()[krate].edition; |
@@ -2005,24 +2005,37 @@ mod tests { | |||
2005 | fn test_macro_expand_will_stop_1() { | 2005 | fn test_macro_expand_will_stop_1() { |
2006 | do_resolve( | 2006 | do_resolve( |
2007 | r#" | 2007 | r#" |
2008 | macro_rules! foo { | 2008 | macro_rules! foo { |
2009 | ($($ty:ty)*) => { foo!($($ty)*); } | 2009 | ($($ty:ty)*) => { foo!($($ty)*); } |
2010 | } | 2010 | } |
2011 | foo!(KABOOM); | 2011 | foo!(KABOOM); |
2012 | "#, | 2012 | "#, |
2013 | ); | ||
2014 | do_resolve( | ||
2015 | r#" | ||
2016 | macro_rules! foo { | ||
2017 | ($($ty:ty)*) => { foo!(() $($ty)*); } | ||
2018 | } | ||
2019 | foo!(KABOOM); | ||
2020 | "#, | ||
2013 | ); | 2021 | ); |
2014 | } | 2022 | } |
2015 | 2023 | ||
2016 | #[ignore] // this test does succeed, but takes quite a while :/ | 2024 | #[ignore] |
2017 | #[test] | 2025 | #[test] |
2018 | fn test_macro_expand_will_stop_2() { | 2026 | fn test_macro_expand_will_stop_2() { |
2027 | // FIXME: this test does succeed, but takes quite a while: 90 seconds in | ||
2028 | // the release mode. That's why the argument is not an ra_fixture -- | ||
2029 | // otherwise injection highlighting gets stuck. | ||
2030 | // | ||
2031 | // We need to find a way to fail this faster. | ||
2019 | do_resolve( | 2032 | do_resolve( |
2020 | r#" | 2033 | r#" |
2021 | macro_rules! foo { | 2034 | macro_rules! foo { |
2022 | ($($ty:ty)*) => { foo!($($ty)* $($ty)*); } | 2035 | ($($ty:ty)*) => { foo!($($ty)* $($ty)*); } |
2023 | } | 2036 | } |
2024 | foo!(KABOOM); | 2037 | foo!(KABOOM); |
2025 | "#, | 2038 | "#, |
2026 | ); | 2039 | ); |
2027 | } | 2040 | } |
2028 | } | 2041 | } |
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/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 b20b066e2..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 | }; |
@@ -245,145 +242,4 @@ impl TestDB { | |||
245 | }) | 242 | }) |
246 | .collect() | 243 | .collect() |
247 | } | 244 | } |
248 | |||
249 | pub(crate) fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> { | ||
250 | let mut files = Vec::new(); | ||
251 | let crate_graph = self.crate_graph(); | ||
252 | for krate in crate_graph.iter() { | ||
253 | let crate_def_map = self.crate_def_map(krate); | ||
254 | for (module_id, _) in crate_def_map.modules() { | ||
255 | let file_id = crate_def_map[module_id].origin.file_id(); | ||
256 | files.extend(file_id) | ||
257 | } | ||
258 | } | ||
259 | assert!(!files.is_empty()); | ||
260 | files | ||
261 | .into_iter() | ||
262 | .filter_map(|file_id| { | ||
263 | let text = self.file_text(file_id); | ||
264 | let annotations = extract_annotations(&text); | ||
265 | if annotations.is_empty() { | ||
266 | return None; | ||
267 | } | ||
268 | Some((file_id, annotations)) | ||
269 | }) | ||
270 | .collect() | ||
271 | } | ||
272 | |||
273 | pub(crate) fn diagnostics(&self, cb: &mut dyn FnMut(FileRange, String)) { | ||
274 | let crate_graph = self.crate_graph(); | ||
275 | for krate in crate_graph.iter() { | ||
276 | let crate_def_map = self.crate_def_map(krate); | ||
277 | |||
278 | for diag in crate_def_map.diagnostics() { | ||
279 | let (node, message): (InFile<SyntaxNode>, &str) = match &diag.kind { | ||
280 | DefDiagnosticKind::UnresolvedModule { ast, .. } => { | ||
281 | let node = ast.to_node(self.upcast()); | ||
282 | (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedModule") | ||
283 | } | ||
284 | DefDiagnosticKind::UnresolvedExternCrate { ast, .. } => { | ||
285 | let node = ast.to_node(self.upcast()); | ||
286 | (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedExternCrate") | ||
287 | } | ||
288 | DefDiagnosticKind::UnresolvedImport { id, .. } => { | ||
289 | let item_tree = id.item_tree(self.upcast()); | ||
290 | let import = &item_tree[id.value]; | ||
291 | let node = InFile::new(id.file_id(), import.ast_id).to_node(self.upcast()); | ||
292 | (InFile::new(id.file_id(), node.syntax().clone()), "UnresolvedImport") | ||
293 | } | ||
294 | DefDiagnosticKind::UnconfiguredCode { ast, .. } => { | ||
295 | let node = ast.to_node(self.upcast()); | ||
296 | (InFile::new(ast.file_id, node.syntax().clone()), "UnconfiguredCode") | ||
297 | } | ||
298 | DefDiagnosticKind::UnresolvedProcMacro { ast, .. } => { | ||
299 | (ast.to_node(self.upcast()), "UnresolvedProcMacro") | ||
300 | } | ||
301 | DefDiagnosticKind::UnresolvedMacroCall { ast, .. } => { | ||
302 | let node = ast.to_node(self.upcast()); | ||
303 | (InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedMacroCall") | ||
304 | } | ||
305 | DefDiagnosticKind::MacroError { ast, message } => { | ||
306 | (ast.to_node(self.upcast()), message.as_str()) | ||
307 | } | ||
308 | DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => { | ||
309 | let node = ast.to_node(self.upcast()); | ||
310 | ( | ||
311 | InFile::new(ast.file_id, node.syntax().clone()), | ||
312 | "UnimplementedBuiltinMacro", | ||
313 | ) | ||
314 | } | ||
315 | }; | ||
316 | |||
317 | let frange = node.as_ref().original_file_range(self); | ||
318 | cb(frange, message.to_string()) | ||
319 | } | ||
320 | |||
321 | for (_module_id, module) in crate_def_map.modules() { | ||
322 | for decl in module.scope.declarations() { | ||
323 | if let ModuleDefId::FunctionId(it) = decl { | ||
324 | let source_map = self.body_with_source_map(it.into()).1; | ||
325 | for diag in source_map.diagnostics() { | ||
326 | let (ptr, message): (InFile<SyntaxNodePtr>, &str) = match diag { | ||
327 | BodyDiagnostic::InactiveCode { node, .. } => { | ||
328 | (node.clone().map(|it| it.into()), "InactiveCode") | ||
329 | } | ||
330 | BodyDiagnostic::MacroError { node, message } => { | ||
331 | (node.clone().map(|it| it.into()), message.as_str()) | ||
332 | } | ||
333 | BodyDiagnostic::UnresolvedProcMacro { node } => { | ||
334 | (node.clone().map(|it| it.into()), "UnresolvedProcMacro") | ||
335 | } | ||
336 | BodyDiagnostic::UnresolvedMacroCall { node, .. } => { | ||
337 | (node.clone().map(|it| it.into()), "UnresolvedMacroCall") | ||
338 | } | ||
339 | }; | ||
340 | |||
341 | let root = self.parse_or_expand(ptr.file_id).unwrap(); | ||
342 | let node = ptr.map(|ptr| ptr.to_node(&root)); | ||
343 | let frange = node.as_ref().original_file_range(self); | ||
344 | cb(frange, message.to_string()) | ||
345 | } | ||
346 | } | ||
347 | } | ||
348 | } | ||
349 | } | ||
350 | } | ||
351 | |||
352 | pub(crate) fn check_diagnostics(&self) { | ||
353 | let db: &TestDB = self; | ||
354 | let annotations = db.extract_annotations(); | ||
355 | assert!(!annotations.is_empty()); | ||
356 | |||
357 | let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); | ||
358 | db.diagnostics(&mut |frange, message| { | ||
359 | actual.entry(frange.file_id).or_default().push((frange.range, message)); | ||
360 | }); | ||
361 | |||
362 | for (file_id, diags) in actual.iter_mut() { | ||
363 | diags.sort_by_key(|it| it.0.start()); | ||
364 | let text = db.file_text(*file_id); | ||
365 | // For multiline spans, place them on line start | ||
366 | for (range, content) in diags { | ||
367 | if text[*range].contains('\n') { | ||
368 | *range = TextRange::new(range.start(), range.start() + TextSize::from(1)); | ||
369 | *content = format!("... {}", content); | ||
370 | } | ||
371 | } | ||
372 | } | ||
373 | |||
374 | assert_eq!(annotations, actual); | ||
375 | } | ||
376 | |||
377 | pub(crate) fn check_no_diagnostics(&self) { | ||
378 | let db: &TestDB = self; | ||
379 | let annotations = db.extract_annotations(); | ||
380 | assert!(annotations.is_empty()); | ||
381 | |||
382 | let mut has_diagnostics = false; | ||
383 | db.diagnostics(&mut |_, _| { | ||
384 | has_diagnostics = true; | ||
385 | }); | ||
386 | |||
387 | assert!(!has_diagnostics); | ||
388 | } | ||
389 | } | 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_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 45e6e446a..66f44202b 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -57,7 +57,7 @@ impl TokenExpander { | |||
57 | // 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 |
58 | // some proc-macro implementation | 58 | // some proc-macro implementation |
59 | // See #4315 for details | 59 | // See #4315 for details |
60 | db.expand_proc_macro(id.into()).into() | 60 | db.expand_proc_macro(id).into() |
61 | } | 61 | } |
62 | } | 62 | } |
63 | } | 63 | } |
@@ -241,7 +241,7 @@ fn parse_macro_expansion( | |||
241 | } | 241 | } |
242 | }; | 242 | }; |
243 | if is_self_replicating(&node, &call_node.value) { | 243 | if is_self_replicating(&node, &call_node.value) { |
244 | return ExpandResult::only_err(err); | 244 | ExpandResult::only_err(err) |
245 | } else { | 245 | } else { |
246 | 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) } |
247 | } | 247 | } |
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs index 9093255f4..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,8 +214,7 @@ 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() |
diff --git a/crates/hir_expand/src/input.rs b/crates/hir_expand/src/input.rs index 82dc7f326..bc3ecc593 100644 --- a/crates/hir_expand/src/input.rs +++ b/crates/hir_expand/src/input.rs | |||
@@ -78,7 +78,7 @@ mod tests { | |||
78 | use super::*; | 78 | use super::*; |
79 | 79 | ||
80 | 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) { |
81 | let (db, file_id) = TestDB::with_single_file(&ra_fixture); | 81 | let (db, file_id) = TestDB::with_single_file(ra_fixture); |
82 | let parsed = db.parse(file_id); | 82 | let parsed = db.parse(file_id); |
83 | 83 | ||
84 | 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 623791b58..33107aa24 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs | |||
@@ -53,7 +53,7 @@ mod test_db; | |||
53 | /// 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 |
54 | /// finite (because everything bottoms out at the real `FileId`) and small | 54 | /// finite (because everything bottoms out at the real `FileId`) and small |
55 | /// (`MacroCallId` uses the location interning. You can check details here: | 55 | /// (`MacroCallId` uses the location interning. You can check details here: |
56 | /// https://en.wikipedia.org/wiki/String_interning). | 56 | /// <https://en.wikipedia.org/wiki/String_interning>). |
57 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 57 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
58 | pub struct HirFileId(HirFileIdRepr); | 58 | pub struct HirFileId(HirFileIdRepr); |
59 | 59 | ||
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs index dbe1b446e..025e10239 100644 --- a/crates/hir_expand/src/proc_macro.rs +++ b/crates/hir_expand/src/proc_macro.rs | |||
@@ -45,13 +45,12 @@ impl ProcMacroExpander { | |||
45 | let proc_macro = krate_graph[self.krate] | 45 | let proc_macro = krate_graph[self.krate] |
46 | .proc_macro | 46 | .proc_macro |
47 | .get(id.0 as usize) | 47 | .get(id.0 as usize) |
48 | .clone() | ||
49 | .ok_or_else(|| err!("No derive macro found."))?; | 48 | .ok_or_else(|| err!("No derive macro found."))?; |
50 | 49 | ||
51 | // 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. |
52 | let env = &krate_graph[calling_crate].env; | 51 | let env = &krate_graph[calling_crate].env; |
53 | 52 | ||
54 | proc_macro.expander.expand(&tt, attr_arg, &env).map_err(mbe::ExpandError::from) | 53 | proc_macro.expander.expand(tt, attr_arg, env).map_err(mbe::ExpandError::from) |
55 | } | 54 | } |
56 | None => Err(mbe::ExpandError::UnresolvedProcMacro), | 55 | None => Err(mbe::ExpandError::UnresolvedProcMacro), |
57 | } | 56 | } |
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml index 4b714c6d8..74129eb21 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" |
@@ -20,7 +20,7 @@ rustc-hash = "1.1.0" | |||
20 | scoped-tls = "1" | 20 | scoped-tls = "1" |
21 | chalk-solve = { version = "0.68", default-features = false } | 21 | chalk-solve = { version = "0.68", default-features = false } |
22 | chalk-ir = "0.68" | 22 | chalk-ir = "0.68" |
23 | chalk-recursive = "0.68" | 23 | chalk-recursive = { version = "0.68", default-features = false } |
24 | la-arena = { version = "0.2.0", path = "../../lib/arena" } | 24 | la-arena = { version = "0.2.0", path = "../../lib/arena" } |
25 | once_cell = { version = "1.5.0" } | 25 | once_cell = { version = "1.5.0" } |
26 | 26 | ||
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 a2a4d61db..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 | ||
@@ -382,20 +334,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
382 | // FIXME Report witnesses | 334 | // FIXME Report witnesses |
383 | // eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses); | 335 | // eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses); |
384 | if !witnesses.is_empty() { | 336 | if !witnesses.is_empty() { |
385 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | 337 | self.diagnostics.push(BodyValidationDiagnostic::MissingMatchArms { match_expr: id }); |
386 | let root = source_ptr.file_syntax(db.upcast()); | ||
387 | if let ast::Expr::MatchExpr(match_expr) = &source_ptr.value.to_node(&root) { | ||
388 | if let (Some(match_expr), Some(arms)) = | ||
389 | (match_expr.expr(), match_expr.match_arm_list()) | ||
390 | { | ||
391 | self.sink.push(MissingMatchArms { | ||
392 | file: source_ptr.file_id, | ||
393 | match_expr: AstPtr::new(&match_expr), | ||
394 | arms: AstPtr::new(&arms), | ||
395 | }) | ||
396 | } | ||
397 | } | ||
398 | } | ||
399 | } | 338 | } |
400 | } | 339 | } |
401 | 340 | ||
@@ -453,24 +392,12 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
453 | if params.len(&Interner) > 0 | 392 | if params.len(&Interner) > 0 |
454 | && params.at(&Interner, 0).ty(&Interner) == Some(&mismatch.actual) | 393 | && params.at(&Interner, 0).ty(&Interner) == Some(&mismatch.actual) |
455 | { | 394 | { |
456 | let (_, source_map) = db.body_with_source_map(self.owner); | 395 | self.diagnostics |
457 | 396 | .push(BodyValidationDiagnostic::MissingOkOrSomeInTailExpr { expr: id, required }); | |
458 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | ||
459 | self.sink.push(MissingOkOrSomeInTailExpr { | ||
460 | file: source_ptr.file_id, | ||
461 | expr: source_ptr.value, | ||
462 | required, | ||
463 | }); | ||
464 | } | ||
465 | } | 397 | } |
466 | } | 398 | } |
467 | 399 | ||
468 | fn validate_missing_tail_expr( | 400 | fn validate_missing_tail_expr(&mut self, body_id: ExprId, possible_tail_id: ExprId) { |
469 | &mut self, | ||
470 | body_id: ExprId, | ||
471 | possible_tail_id: ExprId, | ||
472 | db: &dyn HirDatabase, | ||
473 | ) { | ||
474 | let mismatch = match self.infer.type_mismatch_for_expr(body_id) { | 401 | let mismatch = match self.infer.type_mismatch_for_expr(body_id) { |
475 | Some(m) => m, | 402 | Some(m) => m, |
476 | None => return, | 403 | None => return, |
@@ -485,12 +412,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
485 | return; | 412 | return; |
486 | } | 413 | } |
487 | 414 | ||
488 | let (_, source_map) = db.body_with_source_map(self.owner); | 415 | self.diagnostics |
489 | 416 | .push(BodyValidationDiagnostic::RemoveThisSemicolon { expr: possible_tail_id }); | |
490 | if let Ok(source_ptr) = source_map.expr_syntax(possible_tail_id) { | ||
491 | self.sink | ||
492 | .push(RemoveThisSemicolon { file: source_ptr.file_id, expr: source_ptr.value }); | ||
493 | } | ||
494 | } | 417 | } |
495 | } | 418 | } |
496 | 419 | ||
@@ -568,258 +491,3 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul | |||
568 | walk(pat, body, infer, &mut has_type_mismatches); | 491 | walk(pat, body, infer, &mut has_type_mismatches); |
569 | !has_type_mismatches | 492 | !has_type_mismatches |
570 | } | 493 | } |
571 | |||
572 | #[cfg(test)] | ||
573 | mod tests { | ||
574 | use crate::diagnostics::tests::check_diagnostics; | ||
575 | |||
576 | #[test] | ||
577 | fn simple_free_fn_zero() { | ||
578 | check_diagnostics( | ||
579 | r#" | ||
580 | fn zero() {} | ||
581 | fn f() { zero(1); } | ||
582 | //^^^^^^^ Expected 0 arguments, found 1 | ||
583 | "#, | ||
584 | ); | ||
585 | |||
586 | check_diagnostics( | ||
587 | r#" | ||
588 | fn zero() {} | ||
589 | fn f() { zero(); } | ||
590 | "#, | ||
591 | ); | ||
592 | } | ||
593 | |||
594 | #[test] | ||
595 | fn simple_free_fn_one() { | ||
596 | check_diagnostics( | ||
597 | r#" | ||
598 | fn one(arg: u8) {} | ||
599 | fn f() { one(); } | ||
600 | //^^^^^ Expected 1 argument, found 0 | ||
601 | "#, | ||
602 | ); | ||
603 | |||
604 | check_diagnostics( | ||
605 | r#" | ||
606 | fn one(arg: u8) {} | ||
607 | fn f() { one(1); } | ||
608 | "#, | ||
609 | ); | ||
610 | } | ||
611 | |||
612 | #[test] | ||
613 | fn method_as_fn() { | ||
614 | check_diagnostics( | ||
615 | r#" | ||
616 | struct S; | ||
617 | impl S { fn method(&self) {} } | ||
618 | |||
619 | fn f() { | ||
620 | S::method(); | ||
621 | } //^^^^^^^^^^^ Expected 1 argument, found 0 | ||
622 | "#, | ||
623 | ); | ||
624 | |||
625 | check_diagnostics( | ||
626 | r#" | ||
627 | struct S; | ||
628 | impl S { fn method(&self) {} } | ||
629 | |||
630 | fn f() { | ||
631 | S::method(&S); | ||
632 | S.method(); | ||
633 | } | ||
634 | "#, | ||
635 | ); | ||
636 | } | ||
637 | |||
638 | #[test] | ||
639 | fn method_with_arg() { | ||
640 | check_diagnostics( | ||
641 | r#" | ||
642 | struct S; | ||
643 | impl S { fn method(&self, arg: u8) {} } | ||
644 | |||
645 | fn f() { | ||
646 | S.method(); | ||
647 | } //^^^^^^^^^^ Expected 1 argument, found 0 | ||
648 | "#, | ||
649 | ); | ||
650 | |||
651 | check_diagnostics( | ||
652 | r#" | ||
653 | struct S; | ||
654 | impl S { fn method(&self, arg: u8) {} } | ||
655 | |||
656 | fn f() { | ||
657 | S::method(&S, 0); | ||
658 | S.method(1); | ||
659 | } | ||
660 | "#, | ||
661 | ); | ||
662 | } | ||
663 | |||
664 | #[test] | ||
665 | fn method_unknown_receiver() { | ||
666 | // note: this is incorrect code, so there might be errors on this in the | ||
667 | // future, but we shouldn't emit an argument count diagnostic here | ||
668 | check_diagnostics( | ||
669 | r#" | ||
670 | trait Foo { fn method(&self, arg: usize) {} } | ||
671 | |||
672 | fn f() { | ||
673 | let x; | ||
674 | x.method(); | ||
675 | } | ||
676 | "#, | ||
677 | ); | ||
678 | } | ||
679 | |||
680 | #[test] | ||
681 | fn tuple_struct() { | ||
682 | check_diagnostics( | ||
683 | r#" | ||
684 | struct Tup(u8, u16); | ||
685 | fn f() { | ||
686 | Tup(0); | ||
687 | } //^^^^^^ Expected 2 arguments, found 1 | ||
688 | "#, | ||
689 | ) | ||
690 | } | ||
691 | |||
692 | #[test] | ||
693 | fn enum_variant() { | ||
694 | check_diagnostics( | ||
695 | r#" | ||
696 | enum En { Variant(u8, u16), } | ||
697 | fn f() { | ||
698 | En::Variant(0); | ||
699 | } //^^^^^^^^^^^^^^ Expected 2 arguments, found 1 | ||
700 | "#, | ||
701 | ) | ||
702 | } | ||
703 | |||
704 | #[test] | ||
705 | fn enum_variant_type_macro() { | ||
706 | check_diagnostics( | ||
707 | r#" | ||
708 | macro_rules! Type { | ||
709 | () => { u32 }; | ||
710 | } | ||
711 | enum Foo { | ||
712 | Bar(Type![]) | ||
713 | } | ||
714 | impl Foo { | ||
715 | fn new() { | ||
716 | Foo::Bar(0); | ||
717 | Foo::Bar(0, 1); | ||
718 | //^^^^^^^^^^^^^^ Expected 1 argument, found 2 | ||
719 | Foo::Bar(); | ||
720 | //^^^^^^^^^^ Expected 1 argument, found 0 | ||
721 | } | ||
722 | } | ||
723 | "#, | ||
724 | ); | ||
725 | } | ||
726 | |||
727 | #[test] | ||
728 | fn varargs() { | ||
729 | check_diagnostics( | ||
730 | r#" | ||
731 | extern "C" { | ||
732 | fn fixed(fixed: u8); | ||
733 | fn varargs(fixed: u8, ...); | ||
734 | fn varargs2(...); | ||
735 | } | ||
736 | |||
737 | fn f() { | ||
738 | unsafe { | ||
739 | fixed(0); | ||
740 | fixed(0, 1); | ||
741 | //^^^^^^^^^^^ Expected 1 argument, found 2 | ||
742 | varargs(0); | ||
743 | varargs(0, 1); | ||
744 | varargs2(); | ||
745 | varargs2(0); | ||
746 | varargs2(0, 1); | ||
747 | } | ||
748 | } | ||
749 | "#, | ||
750 | ) | ||
751 | } | ||
752 | |||
753 | #[test] | ||
754 | fn arg_count_lambda() { | ||
755 | check_diagnostics( | ||
756 | r#" | ||
757 | fn main() { | ||
758 | let f = |()| (); | ||
759 | f(); | ||
760 | //^^^ Expected 1 argument, found 0 | ||
761 | f(()); | ||
762 | f((), ()); | ||
763 | //^^^^^^^^^ Expected 1 argument, found 2 | ||
764 | } | ||
765 | "#, | ||
766 | ) | ||
767 | } | ||
768 | |||
769 | #[test] | ||
770 | fn cfgd_out_call_arguments() { | ||
771 | check_diagnostics( | ||
772 | r#" | ||
773 | struct C(#[cfg(FALSE)] ()); | ||
774 | impl C { | ||
775 | fn new() -> Self { | ||
776 | Self( | ||
777 | #[cfg(FALSE)] | ||
778 | (), | ||
779 | ) | ||
780 | } | ||
781 | |||
782 | fn method(&self) {} | ||
783 | } | ||
784 | |||
785 | fn main() { | ||
786 | C::new().method(#[cfg(FALSE)] 0); | ||
787 | } | ||
788 | "#, | ||
789 | ); | ||
790 | } | ||
791 | |||
792 | #[test] | ||
793 | fn cfgd_out_fn_params() { | ||
794 | check_diagnostics( | ||
795 | r#" | ||
796 | fn foo(#[cfg(NEVER)] x: ()) {} | ||
797 | |||
798 | struct S; | ||
799 | |||
800 | impl S { | ||
801 | fn method(#[cfg(NEVER)] self) {} | ||
802 | fn method2(#[cfg(NEVER)] self, arg: u8) {} | ||
803 | fn method3(self, #[cfg(NEVER)] arg: u8) {} | ||
804 | } | ||
805 | |||
806 | extern "C" { | ||
807 | fn fixed(fixed: u8, #[cfg(NEVER)] ...); | ||
808 | fn varargs(#[cfg(not(NEVER))] ...); | ||
809 | } | ||
810 | |||
811 | fn main() { | ||
812 | foo(); | ||
813 | S::method(); | ||
814 | S::method2(0); | ||
815 | S::method3(S); | ||
816 | S.method3(); | ||
817 | unsafe { | ||
818 | fixed(0); | ||
819 | varargs(1, 2, 3); | ||
820 | } | ||
821 | } | ||
822 | "#, | ||
823 | ) | ||
824 | } | ||
825 | } | ||
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs index c8e1b23de..a30e42699 100644 --- a/crates/hir_ty/src/diagnostics/match_check.rs +++ b/crates/hir_ty/src/diagnostics/match_check.rs | |||
@@ -364,960 +364,3 @@ impl PatternFoldable for PatKind { | |||
364 | } | 364 | } |
365 | } | 365 | } |
366 | } | 366 | } |
367 | |||
368 | #[cfg(test)] | ||
369 | pub(super) mod tests { | ||
370 | mod report { | ||
371 | use std::any::Any; | ||
372 | |||
373 | use hir_def::{expr::PatId, DefWithBodyId}; | ||
374 | use hir_expand::{HirFileId, InFile}; | ||
375 | use syntax::SyntaxNodePtr; | ||
376 | |||
377 | use crate::{ | ||
378 | db::HirDatabase, | ||
379 | diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink}, | ||
380 | }; | ||
381 | |||
382 | /// In tests, match check bails out loudly. | ||
383 | /// This helps to catch incorrect tests that pass due to false negatives. | ||
384 | pub(crate) fn report_bail_out( | ||
385 | db: &dyn HirDatabase, | ||
386 | def: DefWithBodyId, | ||
387 | pat: PatId, | ||
388 | sink: &mut DiagnosticSink, | ||
389 | ) { | ||
390 | let (_, source_map) = db.body_with_source_map(def); | ||
391 | if let Ok(source_ptr) = source_map.pat_syntax(pat) { | ||
392 | let pat_syntax_ptr = source_ptr.value.either(Into::into, Into::into); | ||
393 | sink.push(BailedOut { file: source_ptr.file_id, pat_syntax_ptr }); | ||
394 | } | ||
395 | } | ||
396 | |||
397 | #[derive(Debug)] | ||
398 | struct BailedOut { | ||
399 | file: HirFileId, | ||
400 | pat_syntax_ptr: SyntaxNodePtr, | ||
401 | } | ||
402 | |||
403 | impl Diagnostic for BailedOut { | ||
404 | fn code(&self) -> DiagnosticCode { | ||
405 | DiagnosticCode("internal:match-check-bailed-out") | ||
406 | } | ||
407 | fn message(&self) -> String { | ||
408 | format!("Internal: match check bailed out") | ||
409 | } | ||
410 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
411 | InFile { file_id: self.file, value: self.pat_syntax_ptr.clone() } | ||
412 | } | ||
413 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
414 | self | ||
415 | } | ||
416 | } | ||
417 | } | ||
418 | |||
419 | use crate::diagnostics::tests::check_diagnostics; | ||
420 | |||
421 | pub(crate) use self::report::report_bail_out; | ||
422 | |||
423 | #[test] | ||
424 | fn empty_tuple() { | ||
425 | check_diagnostics( | ||
426 | r#" | ||
427 | fn main() { | ||
428 | match () { } | ||
429 | //^^ Missing match arm | ||
430 | match (()) { } | ||
431 | //^^^^ Missing match arm | ||
432 | |||
433 | match () { _ => (), } | ||
434 | match () { () => (), } | ||
435 | match (()) { (()) => (), } | ||
436 | } | ||
437 | "#, | ||
438 | ); | ||
439 | } | ||
440 | |||
441 | #[test] | ||
442 | fn tuple_of_two_empty_tuple() { | ||
443 | check_diagnostics( | ||
444 | r#" | ||
445 | fn main() { | ||
446 | match ((), ()) { } | ||
447 | //^^^^^^^^ Missing match arm | ||
448 | |||
449 | match ((), ()) { ((), ()) => (), } | ||
450 | } | ||
451 | "#, | ||
452 | ); | ||
453 | } | ||
454 | |||
455 | #[test] | ||
456 | fn boolean() { | ||
457 | check_diagnostics( | ||
458 | r#" | ||
459 | fn test_main() { | ||
460 | match false { } | ||
461 | //^^^^^ Missing match arm | ||
462 | match false { true => (), } | ||
463 | //^^^^^ Missing match arm | ||
464 | match (false, true) {} | ||
465 | //^^^^^^^^^^^^^ Missing match arm | ||
466 | match (false, true) { (true, true) => (), } | ||
467 | //^^^^^^^^^^^^^ Missing match arm | ||
468 | match (false, true) { | ||
469 | //^^^^^^^^^^^^^ Missing match arm | ||
470 | (false, true) => (), | ||
471 | (false, false) => (), | ||
472 | (true, false) => (), | ||
473 | } | ||
474 | match (false, true) { (true, _x) => (), } | ||
475 | //^^^^^^^^^^^^^ Missing match arm | ||
476 | |||
477 | match false { true => (), false => (), } | ||
478 | match (false, true) { | ||
479 | (false, _) => (), | ||
480 | (true, false) => (), | ||
481 | (_, true) => (), | ||
482 | } | ||
483 | match (false, true) { | ||
484 | (true, true) => (), | ||
485 | (true, false) => (), | ||
486 | (false, true) => (), | ||
487 | (false, false) => (), | ||
488 | } | ||
489 | match (false, true) { | ||
490 | (true, _x) => (), | ||
491 | (false, true) => (), | ||
492 | (false, false) => (), | ||
493 | } | ||
494 | match (false, true, false) { | ||
495 | (false, ..) => (), | ||
496 | (true, ..) => (), | ||
497 | } | ||
498 | match (false, true, false) { | ||
499 | (.., false) => (), | ||
500 | (.., true) => (), | ||
501 | } | ||
502 | match (false, true, false) { (..) => (), } | ||
503 | } | ||
504 | "#, | ||
505 | ); | ||
506 | } | ||
507 | |||
508 | #[test] | ||
509 | fn tuple_of_tuple_and_bools() { | ||
510 | check_diagnostics( | ||
511 | r#" | ||
512 | fn main() { | ||
513 | match (false, ((), false)) {} | ||
514 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
515 | match (false, ((), false)) { (true, ((), true)) => (), } | ||
516 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
517 | match (false, ((), false)) { (true, _) => (), } | ||
518 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
519 | |||
520 | match (false, ((), false)) { | ||
521 | (true, ((), true)) => (), | ||
522 | (true, ((), false)) => (), | ||
523 | (false, ((), true)) => (), | ||
524 | (false, ((), false)) => (), | ||
525 | } | ||
526 | match (false, ((), false)) { | ||
527 | (true, ((), true)) => (), | ||
528 | (true, ((), false)) => (), | ||
529 | (false, _) => (), | ||
530 | } | ||
531 | } | ||
532 | "#, | ||
533 | ); | ||
534 | } | ||
535 | |||
536 | #[test] | ||
537 | fn enums() { | ||
538 | check_diagnostics( | ||
539 | r#" | ||
540 | enum Either { A, B, } | ||
541 | |||
542 | fn main() { | ||
543 | match Either::A { } | ||
544 | //^^^^^^^^^ Missing match arm | ||
545 | match Either::B { Either::A => (), } | ||
546 | //^^^^^^^^^ Missing match arm | ||
547 | |||
548 | match &Either::B { | ||
549 | //^^^^^^^^^^ Missing match arm | ||
550 | Either::A => (), | ||
551 | } | ||
552 | |||
553 | match Either::B { | ||
554 | Either::A => (), Either::B => (), | ||
555 | } | ||
556 | match &Either::B { | ||
557 | Either::A => (), Either::B => (), | ||
558 | } | ||
559 | } | ||
560 | "#, | ||
561 | ); | ||
562 | } | ||
563 | |||
564 | #[test] | ||
565 | fn enum_containing_bool() { | ||
566 | check_diagnostics( | ||
567 | r#" | ||
568 | enum Either { A(bool), B } | ||
569 | |||
570 | fn main() { | ||
571 | match Either::B { } | ||
572 | //^^^^^^^^^ Missing match arm | ||
573 | match Either::B { | ||
574 | //^^^^^^^^^ Missing match arm | ||
575 | Either::A(true) => (), Either::B => () | ||
576 | } | ||
577 | |||
578 | match Either::B { | ||
579 | Either::A(true) => (), | ||
580 | Either::A(false) => (), | ||
581 | Either::B => (), | ||
582 | } | ||
583 | match Either::B { | ||
584 | Either::B => (), | ||
585 | _ => (), | ||
586 | } | ||
587 | match Either::B { | ||
588 | Either::A(_) => (), | ||
589 | Either::B => (), | ||
590 | } | ||
591 | |||
592 | } | ||
593 | "#, | ||
594 | ); | ||
595 | } | ||
596 | |||
597 | #[test] | ||
598 | fn enum_different_sizes() { | ||
599 | check_diagnostics( | ||
600 | r#" | ||
601 | enum Either { A(bool), B(bool, bool) } | ||
602 | |||
603 | fn main() { | ||
604 | match Either::A(false) { | ||
605 | //^^^^^^^^^^^^^^^^ Missing match arm | ||
606 | Either::A(_) => (), | ||
607 | Either::B(false, _) => (), | ||
608 | } | ||
609 | |||
610 | match Either::A(false) { | ||
611 | Either::A(_) => (), | ||
612 | Either::B(true, _) => (), | ||
613 | Either::B(false, _) => (), | ||
614 | } | ||
615 | match Either::A(false) { | ||
616 | Either::A(true) | Either::A(false) => (), | ||
617 | Either::B(true, _) => (), | ||
618 | Either::B(false, _) => (), | ||
619 | } | ||
620 | } | ||
621 | "#, | ||
622 | ); | ||
623 | } | ||
624 | |||
625 | #[test] | ||
626 | fn tuple_of_enum_no_diagnostic() { | ||
627 | check_diagnostics( | ||
628 | r#" | ||
629 | enum Either { A(bool), B(bool, bool) } | ||
630 | enum Either2 { C, D } | ||
631 | |||
632 | fn main() { | ||
633 | match (Either::A(false), Either2::C) { | ||
634 | (Either::A(true), _) | (Either::A(false), _) => (), | ||
635 | (Either::B(true, _), Either2::C) => (), | ||
636 | (Either::B(false, _), Either2::C) => (), | ||
637 | (Either::B(_, _), Either2::D) => (), | ||
638 | } | ||
639 | } | ||
640 | "#, | ||
641 | ); | ||
642 | } | ||
643 | |||
644 | #[test] | ||
645 | fn or_pattern_no_diagnostic() { | ||
646 | check_diagnostics( | ||
647 | r#" | ||
648 | enum Either {A, B} | ||
649 | |||
650 | fn main() { | ||
651 | match (Either::A, Either::B) { | ||
652 | (Either::A | Either::B, _) => (), | ||
653 | } | ||
654 | }"#, | ||
655 | ) | ||
656 | } | ||
657 | |||
658 | #[test] | ||
659 | fn mismatched_types() { | ||
660 | // Match statements with arms that don't match the | ||
661 | // expression pattern do not fire this diagnostic. | ||
662 | check_diagnostics( | ||
663 | r#" | ||
664 | enum Either { A, B } | ||
665 | enum Either2 { C, D } | ||
666 | |||
667 | fn main() { | ||
668 | match Either::A { | ||
669 | Either2::C => (), | ||
670 | // ^^^^^^^^^^ Internal: match check bailed out | ||
671 | Either2::D => (), | ||
672 | } | ||
673 | match (true, false) { | ||
674 | (true, false, true) => (), | ||
675 | // ^^^^^^^^^^^^^^^^^^^ Internal: match check bailed out | ||
676 | (true) => (), | ||
677 | } | ||
678 | match (true, false) { (true,) => {} } | ||
679 | // ^^^^^^^ Internal: match check bailed out | ||
680 | match (0) { () => () } | ||
681 | // ^^ Internal: match check bailed out | ||
682 | match Unresolved::Bar { Unresolved::Baz => () } | ||
683 | } | ||
684 | "#, | ||
685 | ); | ||
686 | } | ||
687 | |||
688 | #[test] | ||
689 | fn mismatched_types_in_or_patterns() { | ||
690 | check_diagnostics( | ||
691 | r#" | ||
692 | fn main() { | ||
693 | match false { true | () => {} } | ||
694 | // ^^^^^^^^^ Internal: match check bailed out | ||
695 | match (false,) { (true | (),) => {} } | ||
696 | // ^^^^^^^^^^^^ Internal: match check bailed out | ||
697 | } | ||
698 | "#, | ||
699 | ); | ||
700 | } | ||
701 | |||
702 | #[test] | ||
703 | fn malformed_match_arm_tuple_enum_missing_pattern() { | ||
704 | // We are testing to be sure we don't panic here when the match | ||
705 | // arm `Either::B` is missing its pattern. | ||
706 | check_diagnostics( | ||
707 | r#" | ||
708 | enum Either { A, B(u32) } | ||
709 | |||
710 | fn main() { | ||
711 | match Either::A { | ||
712 | Either::A => (), | ||
713 | Either::B() => (), | ||
714 | } | ||
715 | } | ||
716 | "#, | ||
717 | ); | ||
718 | } | ||
719 | |||
720 | #[test] | ||
721 | fn malformed_match_arm_extra_fields() { | ||
722 | check_diagnostics( | ||
723 | r#" | ||
724 | enum A { B(isize, isize), C } | ||
725 | fn main() { | ||
726 | match A::B(1, 2) { | ||
727 | A::B(_, _, _) => (), | ||
728 | // ^^^^^^^^^^^^^ Internal: match check bailed out | ||
729 | } | ||
730 | match A::B(1, 2) { | ||
731 | A::C(_) => (), | ||
732 | // ^^^^^^^ Internal: match check bailed out | ||
733 | } | ||
734 | } | ||
735 | "#, | ||
736 | ); | ||
737 | } | ||
738 | |||
739 | #[test] | ||
740 | fn expr_diverges() { | ||
741 | check_diagnostics( | ||
742 | r#" | ||
743 | enum Either { A, B } | ||
744 | |||
745 | fn main() { | ||
746 | match loop {} { | ||
747 | Either::A => (), | ||
748 | // ^^^^^^^^^ Internal: match check bailed out | ||
749 | Either::B => (), | ||
750 | } | ||
751 | match loop {} { | ||
752 | Either::A => (), | ||
753 | // ^^^^^^^^^ Internal: match check bailed out | ||
754 | } | ||
755 | match loop { break Foo::A } { | ||
756 | //^^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
757 | Either::A => (), | ||
758 | } | ||
759 | match loop { break Foo::A } { | ||
760 | Either::A => (), | ||
761 | Either::B => (), | ||
762 | } | ||
763 | } | ||
764 | "#, | ||
765 | ); | ||
766 | } | ||
767 | |||
768 | #[test] | ||
769 | fn expr_partially_diverges() { | ||
770 | check_diagnostics( | ||
771 | r#" | ||
772 | enum Either<T> { A(T), B } | ||
773 | |||
774 | fn foo() -> Either<!> { Either::B } | ||
775 | fn main() -> u32 { | ||
776 | match foo() { | ||
777 | Either::A(val) => val, | ||
778 | Either::B => 0, | ||
779 | } | ||
780 | } | ||
781 | "#, | ||
782 | ); | ||
783 | } | ||
784 | |||
785 | #[test] | ||
786 | fn enum_record() { | ||
787 | check_diagnostics( | ||
788 | r#" | ||
789 | enum Either { A { foo: bool }, B } | ||
790 | |||
791 | fn main() { | ||
792 | let a = Either::A { foo: true }; | ||
793 | match a { } | ||
794 | //^ Missing match arm | ||
795 | match a { Either::A { foo: true } => () } | ||
796 | //^ Missing match arm | ||
797 | match a { | ||
798 | Either::A { } => (), | ||
799 | //^^^^^^^^^ Missing structure fields: | ||
800 | // | - foo | ||
801 | Either::B => (), | ||
802 | } | ||
803 | match a { | ||
804 | //^ Missing match arm | ||
805 | Either::A { } => (), | ||
806 | } //^^^^^^^^^ Missing structure fields: | ||
807 | // | - foo | ||
808 | |||
809 | match a { | ||
810 | Either::A { foo: true } => (), | ||
811 | Either::A { foo: false } => (), | ||
812 | Either::B => (), | ||
813 | } | ||
814 | match a { | ||
815 | Either::A { foo: _ } => (), | ||
816 | Either::B => (), | ||
817 | } | ||
818 | } | ||
819 | "#, | ||
820 | ); | ||
821 | } | ||
822 | |||
823 | #[test] | ||
824 | fn enum_record_fields_out_of_order() { | ||
825 | check_diagnostics( | ||
826 | r#" | ||
827 | enum Either { | ||
828 | A { foo: bool, bar: () }, | ||
829 | B, | ||
830 | } | ||
831 | |||
832 | fn main() { | ||
833 | let a = Either::A { foo: true, bar: () }; | ||
834 | match a { | ||
835 | //^ Missing match arm | ||
836 | Either::A { bar: (), foo: false } => (), | ||
837 | Either::A { foo: true, bar: () } => (), | ||
838 | } | ||
839 | |||
840 | match a { | ||
841 | Either::A { bar: (), foo: false } => (), | ||
842 | Either::A { foo: true, bar: () } => (), | ||
843 | Either::B => (), | ||
844 | } | ||
845 | } | ||
846 | "#, | ||
847 | ); | ||
848 | } | ||
849 | |||
850 | #[test] | ||
851 | fn enum_record_ellipsis() { | ||
852 | check_diagnostics( | ||
853 | r#" | ||
854 | enum Either { | ||
855 | A { foo: bool, bar: bool }, | ||
856 | B, | ||
857 | } | ||
858 | |||
859 | fn main() { | ||
860 | let a = Either::B; | ||
861 | match a { | ||
862 | //^ Missing match arm | ||
863 | Either::A { foo: true, .. } => (), | ||
864 | Either::B => (), | ||
865 | } | ||
866 | match a { | ||
867 | //^ Missing match arm | ||
868 | Either::A { .. } => (), | ||
869 | } | ||
870 | |||
871 | match a { | ||
872 | Either::A { foo: true, .. } => (), | ||
873 | Either::A { foo: false, .. } => (), | ||
874 | Either::B => (), | ||
875 | } | ||
876 | |||
877 | match a { | ||
878 | Either::A { .. } => (), | ||
879 | Either::B => (), | ||
880 | } | ||
881 | } | ||
882 | "#, | ||
883 | ); | ||
884 | } | ||
885 | |||
886 | #[test] | ||
887 | fn enum_tuple_partial_ellipsis() { | ||
888 | check_diagnostics( | ||
889 | r#" | ||
890 | enum Either { | ||
891 | A(bool, bool, bool, bool), | ||
892 | B, | ||
893 | } | ||
894 | |||
895 | fn main() { | ||
896 | match Either::B { | ||
897 | //^^^^^^^^^ Missing match arm | ||
898 | Either::A(true, .., true) => (), | ||
899 | Either::A(true, .., false) => (), | ||
900 | Either::A(false, .., false) => (), | ||
901 | Either::B => (), | ||
902 | } | ||
903 | match Either::B { | ||
904 | //^^^^^^^^^ Missing match arm | ||
905 | Either::A(true, .., true) => (), | ||
906 | Either::A(true, .., false) => (), | ||
907 | Either::A(.., true) => (), | ||
908 | Either::B => (), | ||
909 | } | ||
910 | |||
911 | match Either::B { | ||
912 | Either::A(true, .., true) => (), | ||
913 | Either::A(true, .., false) => (), | ||
914 | Either::A(false, .., true) => (), | ||
915 | Either::A(false, .., false) => (), | ||
916 | Either::B => (), | ||
917 | } | ||
918 | match Either::B { | ||
919 | Either::A(true, .., true) => (), | ||
920 | Either::A(true, .., false) => (), | ||
921 | Either::A(.., true) => (), | ||
922 | Either::A(.., false) => (), | ||
923 | Either::B => (), | ||
924 | } | ||
925 | } | ||
926 | "#, | ||
927 | ); | ||
928 | } | ||
929 | |||
930 | #[test] | ||
931 | fn never() { | ||
932 | check_diagnostics( | ||
933 | r#" | ||
934 | enum Never {} | ||
935 | |||
936 | fn enum_(never: Never) { | ||
937 | match never {} | ||
938 | } | ||
939 | fn enum_ref(never: &Never) { | ||
940 | match never {} | ||
941 | //^^^^^ Missing match arm | ||
942 | } | ||
943 | fn bang(never: !) { | ||
944 | match never {} | ||
945 | } | ||
946 | "#, | ||
947 | ); | ||
948 | } | ||
949 | |||
950 | #[test] | ||
951 | fn unknown_type() { | ||
952 | check_diagnostics( | ||
953 | r#" | ||
954 | enum Option<T> { Some(T), None } | ||
955 | |||
956 | fn main() { | ||
957 | // `Never` is deliberately not defined so that it's an uninferred type. | ||
958 | match Option::<Never>::None { | ||
959 | None => (), | ||
960 | Some(never) => match never {}, | ||
961 | // ^^^^^^^^^^^ Internal: match check bailed out | ||
962 | } | ||
963 | match Option::<Never>::None { | ||
964 | //^^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
965 | Option::Some(_never) => {}, | ||
966 | } | ||
967 | } | ||
968 | "#, | ||
969 | ); | ||
970 | } | ||
971 | |||
972 | #[test] | ||
973 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { | ||
974 | check_diagnostics( | ||
975 | r#" | ||
976 | fn main() { | ||
977 | match (false, true, false) { | ||
978 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
979 | (false, ..) => (), | ||
980 | } | ||
981 | }"#, | ||
982 | ); | ||
983 | } | ||
984 | |||
985 | #[test] | ||
986 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { | ||
987 | check_diagnostics( | ||
988 | r#" | ||
989 | fn main() { | ||
990 | match (false, true, false) { | ||
991 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
992 | (.., false) => (), | ||
993 | } | ||
994 | }"#, | ||
995 | ); | ||
996 | } | ||
997 | |||
998 | #[test] | ||
999 | fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() { | ||
1000 | check_diagnostics( | ||
1001 | r#" | ||
1002 | fn main() { | ||
1003 | match (false, true, false) { | ||
1004 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
1005 | (true, .., false) => (), | ||
1006 | } | ||
1007 | }"#, | ||
1008 | ); | ||
1009 | } | ||
1010 | |||
1011 | #[test] | ||
1012 | fn record_struct() { | ||
1013 | check_diagnostics( | ||
1014 | r#"struct Foo { a: bool } | ||
1015 | fn main(f: Foo) { | ||
1016 | match f {} | ||
1017 | //^ Missing match arm | ||
1018 | match f { Foo { a: true } => () } | ||
1019 | //^ Missing match arm | ||
1020 | match &f { Foo { a: true } => () } | ||
1021 | //^^ Missing match arm | ||
1022 | match f { Foo { a: _ } => () } | ||
1023 | match f { | ||
1024 | Foo { a: true } => (), | ||
1025 | Foo { a: false } => (), | ||
1026 | } | ||
1027 | match &f { | ||
1028 | Foo { a: true } => (), | ||
1029 | Foo { a: false } => (), | ||
1030 | } | ||
1031 | } | ||
1032 | "#, | ||
1033 | ); | ||
1034 | } | ||
1035 | |||
1036 | #[test] | ||
1037 | fn tuple_struct() { | ||
1038 | check_diagnostics( | ||
1039 | r#"struct Foo(bool); | ||
1040 | fn main(f: Foo) { | ||
1041 | match f {} | ||
1042 | //^ Missing match arm | ||
1043 | match f { Foo(true) => () } | ||
1044 | //^ Missing match arm | ||
1045 | match f { | ||
1046 | Foo(true) => (), | ||
1047 | Foo(false) => (), | ||
1048 | } | ||
1049 | } | ||
1050 | "#, | ||
1051 | ); | ||
1052 | } | ||
1053 | |||
1054 | #[test] | ||
1055 | fn unit_struct() { | ||
1056 | check_diagnostics( | ||
1057 | r#"struct Foo; | ||
1058 | fn main(f: Foo) { | ||
1059 | match f {} | ||
1060 | //^ Missing match arm | ||
1061 | match f { Foo => () } | ||
1062 | } | ||
1063 | "#, | ||
1064 | ); | ||
1065 | } | ||
1066 | |||
1067 | #[test] | ||
1068 | fn record_struct_ellipsis() { | ||
1069 | check_diagnostics( | ||
1070 | r#"struct Foo { foo: bool, bar: bool } | ||
1071 | fn main(f: Foo) { | ||
1072 | match f { Foo { foo: true, .. } => () } | ||
1073 | //^ Missing match arm | ||
1074 | match f { | ||
1075 | //^ Missing match arm | ||
1076 | Foo { foo: true, .. } => (), | ||
1077 | Foo { bar: false, .. } => () | ||
1078 | } | ||
1079 | match f { Foo { .. } => () } | ||
1080 | match f { | ||
1081 | Foo { foo: true, .. } => (), | ||
1082 | Foo { foo: false, .. } => () | ||
1083 | } | ||
1084 | } | ||
1085 | "#, | ||
1086 | ); | ||
1087 | } | ||
1088 | |||
1089 | #[test] | ||
1090 | fn internal_or() { | ||
1091 | check_diagnostics( | ||
1092 | r#" | ||
1093 | fn main() { | ||
1094 | enum Either { A(bool), B } | ||
1095 | match Either::B { | ||
1096 | //^^^^^^^^^ Missing match arm | ||
1097 | Either::A(true | false) => (), | ||
1098 | } | ||
1099 | } | ||
1100 | "#, | ||
1101 | ); | ||
1102 | } | ||
1103 | |||
1104 | #[test] | ||
1105 | fn no_panic_at_unimplemented_subpattern_type() { | ||
1106 | check_diagnostics( | ||
1107 | r#" | ||
1108 | struct S { a: char} | ||
1109 | fn main(v: S) { | ||
1110 | match v { S{ a } => {} } | ||
1111 | match v { S{ a: _x } => {} } | ||
1112 | match v { S{ a: 'a' } => {} } | ||
1113 | //^^^^^^^^^^^ Internal: match check bailed out | ||
1114 | match v { S{..} => {} } | ||
1115 | match v { _ => {} } | ||
1116 | match v { } | ||
1117 | //^ Missing match arm | ||
1118 | } | ||
1119 | "#, | ||
1120 | ); | ||
1121 | } | ||
1122 | |||
1123 | #[test] | ||
1124 | fn binding() { | ||
1125 | check_diagnostics( | ||
1126 | r#" | ||
1127 | fn main() { | ||
1128 | match true { | ||
1129 | _x @ true => {} | ||
1130 | false => {} | ||
1131 | } | ||
1132 | match true { _x @ true => {} } | ||
1133 | //^^^^ Missing match arm | ||
1134 | } | ||
1135 | "#, | ||
1136 | ); | ||
1137 | } | ||
1138 | |||
1139 | #[test] | ||
1140 | fn binding_ref_has_correct_type() { | ||
1141 | // Asserts `PatKind::Binding(ref _x): bool`, not &bool. | ||
1142 | // If that's not true match checking will panic with "incompatible constructors" | ||
1143 | // FIXME: make facilities to test this directly like `tests::check_infer(..)` | ||
1144 | check_diagnostics( | ||
1145 | r#" | ||
1146 | enum Foo { A } | ||
1147 | fn main() { | ||
1148 | // FIXME: this should not bail out but current behavior is such as the old algorithm. | ||
1149 | // ExprValidator::validate_match(..) checks types of top level patterns incorrecly. | ||
1150 | match Foo::A { | ||
1151 | ref _x => {} | ||
1152 | // ^^^^^^ Internal: match check bailed out | ||
1153 | Foo::A => {} | ||
1154 | } | ||
1155 | match (true,) { | ||
1156 | (ref _x,) => {} | ||
1157 | (true,) => {} | ||
1158 | } | ||
1159 | } | ||
1160 | "#, | ||
1161 | ); | ||
1162 | } | ||
1163 | |||
1164 | #[test] | ||
1165 | fn enum_non_exhaustive() { | ||
1166 | check_diagnostics( | ||
1167 | r#" | ||
1168 | //- /lib.rs crate:lib | ||
1169 | #[non_exhaustive] | ||
1170 | pub enum E { A, B } | ||
1171 | fn _local() { | ||
1172 | match E::A { _ => {} } | ||
1173 | match E::A { | ||
1174 | E::A => {} | ||
1175 | E::B => {} | ||
1176 | } | ||
1177 | match E::A { | ||
1178 | E::A | E::B => {} | ||
1179 | } | ||
1180 | } | ||
1181 | |||
1182 | //- /main.rs crate:main deps:lib | ||
1183 | use lib::E; | ||
1184 | fn main() { | ||
1185 | match E::A { _ => {} } | ||
1186 | match E::A { | ||
1187 | //^^^^ Missing match arm | ||
1188 | E::A => {} | ||
1189 | E::B => {} | ||
1190 | } | ||
1191 | match E::A { | ||
1192 | //^^^^ Missing match arm | ||
1193 | E::A | E::B => {} | ||
1194 | } | ||
1195 | } | ||
1196 | "#, | ||
1197 | ); | ||
1198 | } | ||
1199 | |||
1200 | #[test] | ||
1201 | fn match_guard() { | ||
1202 | check_diagnostics( | ||
1203 | r#" | ||
1204 | fn main() { | ||
1205 | match true { | ||
1206 | true if false => {} | ||
1207 | true => {} | ||
1208 | false => {} | ||
1209 | } | ||
1210 | match true { | ||
1211 | //^^^^ Missing match arm | ||
1212 | true if false => {} | ||
1213 | false => {} | ||
1214 | } | ||
1215 | "#, | ||
1216 | ); | ||
1217 | } | ||
1218 | |||
1219 | #[test] | ||
1220 | fn pattern_type_is_of_substitution() { | ||
1221 | cov_mark::check!(match_check_wildcard_expanded_to_substitutions); | ||
1222 | check_diagnostics( | ||
1223 | r#" | ||
1224 | struct Foo<T>(T); | ||
1225 | struct Bar; | ||
1226 | fn main() { | ||
1227 | match Foo(Bar) { | ||
1228 | _ | Foo(Bar) => {} | ||
1229 | } | ||
1230 | } | ||
1231 | "#, | ||
1232 | ); | ||
1233 | } | ||
1234 | |||
1235 | #[test] | ||
1236 | fn record_struct_no_such_field() { | ||
1237 | check_diagnostics( | ||
1238 | r#" | ||
1239 | struct Foo { } | ||
1240 | fn main(f: Foo) { | ||
1241 | match f { Foo { bar } => () } | ||
1242 | // ^^^^^^^^^^^ Internal: match check bailed out | ||
1243 | } | ||
1244 | "#, | ||
1245 | ); | ||
1246 | } | ||
1247 | |||
1248 | #[test] | ||
1249 | fn match_ergonomics_issue_9095() { | ||
1250 | check_diagnostics( | ||
1251 | r#" | ||
1252 | enum Foo<T> { A(T) } | ||
1253 | fn main() { | ||
1254 | match &Foo::A(true) { | ||
1255 | _ => {} | ||
1256 | Foo::A(_) => {} | ||
1257 | } | ||
1258 | } | ||
1259 | "#, | ||
1260 | ); | ||
1261 | } | ||
1262 | |||
1263 | mod false_negatives { | ||
1264 | //! The implementation of match checking here is a work in progress. As we roll this out, we | ||
1265 | //! prefer false negatives to false positives (ideally there would be no false positives). This | ||
1266 | //! test module should document known false negatives. Eventually we will have a complete | ||
1267 | //! implementation of match checking and this module will be empty. | ||
1268 | //! | ||
1269 | //! The reasons for documenting known false negatives: | ||
1270 | //! | ||
1271 | //! 1. It acts as a backlog of work that can be done to improve the behavior of the system. | ||
1272 | //! 2. It ensures the code doesn't panic when handling these cases. | ||
1273 | use super::*; | ||
1274 | |||
1275 | #[test] | ||
1276 | fn integers() { | ||
1277 | // We don't currently check integer exhaustiveness. | ||
1278 | check_diagnostics( | ||
1279 | r#" | ||
1280 | fn main() { | ||
1281 | match 5 { | ||
1282 | 10 => (), | ||
1283 | // ^^ Internal: match check bailed out | ||
1284 | 11..20 => (), | ||
1285 | } | ||
1286 | } | ||
1287 | "#, | ||
1288 | ); | ||
1289 | } | ||
1290 | |||
1291 | #[test] | ||
1292 | fn reference_patterns_at_top_level() { | ||
1293 | check_diagnostics( | ||
1294 | r#" | ||
1295 | fn main() { | ||
1296 | match &false { | ||
1297 | &true => {} | ||
1298 | // ^^^^^ Internal: match check bailed out | ||
1299 | } | ||
1300 | } | ||
1301 | "#, | ||
1302 | ); | ||
1303 | } | ||
1304 | |||
1305 | #[test] | ||
1306 | fn reference_patterns_in_fields() { | ||
1307 | check_diagnostics( | ||
1308 | r#" | ||
1309 | fn main() { | ||
1310 | match (&false,) { | ||
1311 | (true,) => {} | ||
1312 | // ^^^^^^^ Internal: match check bailed out | ||
1313 | } | ||
1314 | match (&false,) { | ||
1315 | (&true,) => {} | ||
1316 | // ^^^^^^^^ Internal: match check bailed out | ||
1317 | } | ||
1318 | } | ||
1319 | "#, | ||
1320 | ); | ||
1321 | } | ||
1322 | } | ||
1323 | } | ||
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 bd76a606c..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 | //! |
@@ -645,7 +645,7 @@ impl SubPatSet { | |||
645 | (Seq { subpats: s_set }, Seq { subpats: mut o_set }) => { | 645 | (Seq { subpats: s_set }, Seq { subpats: mut o_set }) => { |
646 | s_set.retain(|i, s_sub_set| { | 646 | s_set.retain(|i, s_sub_set| { |
647 | // Missing entries count as full. | 647 | // Missing entries count as full. |
648 | let o_sub_set = o_set.remove(&i).unwrap_or(Full); | 648 | let o_sub_set = o_set.remove(i).unwrap_or(Full); |
649 | s_sub_set.union(o_sub_set); | 649 | s_sub_set.union(o_sub_set); |
650 | // We drop full entries. | 650 | // We drop full entries. |
651 | !s_sub_set.is_full() | 651 | !s_sub_set.is_full() |
@@ -656,7 +656,7 @@ impl SubPatSet { | |||
656 | (Alt { subpats: s_set, .. }, Alt { subpats: mut o_set, .. }) => { | 656 | (Alt { subpats: s_set, .. }, Alt { subpats: mut o_set, .. }) => { |
657 | s_set.retain(|i, s_sub_set| { | 657 | s_set.retain(|i, s_sub_set| { |
658 | // Missing entries count as empty. | 658 | // Missing entries count as empty. |
659 | let o_sub_set = o_set.remove(&i).unwrap_or(Empty); | 659 | let o_sub_set = o_set.remove(i).unwrap_or(Empty); |
660 | s_sub_set.union(o_sub_set); | 660 | s_sub_set.union(o_sub_set); |
661 | // We drop empty entries. | 661 | // We drop empty entries. |
662 | !s_sub_set.is_empty() | 662 | !s_sub_set.is_empty() |
@@ -898,7 +898,7 @@ impl Usefulness { | |||
898 | } else { | 898 | } else { |
899 | witnesses | 899 | witnesses |
900 | .into_iter() | 900 | .into_iter() |
901 | .map(|witness| witness.apply_constructor(pcx, &ctor, ctor_wild_subpatterns)) | 901 | .map(|witness| witness.apply_constructor(pcx, ctor, ctor_wild_subpatterns)) |
902 | .collect() | 902 | .collect() |
903 | }; | 903 | }; |
904 | 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 0e9f777da..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 | /// |
@@ -191,14 +195,6 @@ impl InferenceResult { | |||
191 | _ => None, | 195 | _ => None, |
192 | }) | 196 | }) |
193 | } | 197 | } |
194 | pub fn add_diagnostics( | ||
195 | &self, | ||
196 | db: &dyn HirDatabase, | ||
197 | owner: DefWithBodyId, | ||
198 | sink: &mut DiagnosticSink, | ||
199 | ) { | ||
200 | self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink)) | ||
201 | } | ||
202 | } | 198 | } |
203 | 199 | ||
204 | impl Index<ExprId> for InferenceResult { | 200 | impl Index<ExprId> for InferenceResult { |
@@ -765,6 +761,38 @@ impl Expectation { | |||
765 | Expectation::RValueLikeUnsized(_) | Expectation::None => None, | 761 | Expectation::RValueLikeUnsized(_) | Expectation::None => None, |
766 | } | 762 | } |
767 | } | 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 | } | ||
768 | } | 796 | } |
769 | 797 | ||
770 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] | 798 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] |
@@ -804,43 +832,3 @@ impl std::ops::BitOrAssign for Diverges { | |||
804 | *self = *self | other; | 832 | *self = *self | other; |
805 | } | 833 | } |
806 | } | 834 | } |
807 | |||
808 | mod diagnostics { | ||
809 | use hir_def::{expr::ExprId, DefWithBodyId}; | ||
810 | |||
811 | use crate::{ | ||
812 | db::HirDatabase, | ||
813 | diagnostics::{BreakOutsideOfLoop, NoSuchField}, | ||
814 | diagnostics_sink::DiagnosticSink, | ||
815 | }; | ||
816 | |||
817 | #[derive(Debug, PartialEq, Eq, Clone)] | ||
818 | pub(super) enum InferenceDiagnostic { | ||
819 | NoSuchField { expr: ExprId }, | ||
820 | BreakOutsideOfLoop { expr: ExprId }, | ||
821 | } | ||
822 | |||
823 | impl InferenceDiagnostic { | ||
824 | pub(super) fn add_to( | ||
825 | &self, | ||
826 | db: &dyn HirDatabase, | ||
827 | owner: DefWithBodyId, | ||
828 | sink: &mut DiagnosticSink, | ||
829 | ) { | ||
830 | match self { | ||
831 | InferenceDiagnostic::NoSuchField { expr } => { | ||
832 | let (_, source_map) = db.body_with_source_map(owner); | ||
833 | let field = source_map.field_syntax(*expr); | ||
834 | sink.push(NoSuchField { file: field.file_id, field: field.value }) | ||
835 | } | ||
836 | InferenceDiagnostic::BreakOutsideOfLoop { expr } => { | ||
837 | let (_, source_map) = db.body_with_source_map(owner); | ||
838 | let ptr = source_map | ||
839 | .expr_syntax(*expr) | ||
840 | .expect("break outside of loop in synthetic syntax"); | ||
841 | sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value }) | ||
842 | } | ||
843 | } | ||
844 | } | ||
845 | } | ||
846 | } | ||
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 25dff7e49..035f4ded6 100644 --- a/crates/hir_ty/src/infer/pat.rs +++ b/crates/hir_ty/src/infer/pat.rs | |||
@@ -192,7 +192,7 @@ impl<'a> InferenceContext<'a> { | |||
192 | Pat::Path(path) => { | 192 | Pat::Path(path) => { |
193 | // FIXME use correct resolver for the surrounding expression | 193 | // FIXME use correct resolver for the surrounding expression |
194 | let resolver = self.resolver.clone(); | 194 | let resolver = self.resolver.clone(); |
195 | 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()) |
196 | } | 196 | } |
197 | Pat::Bind { mode, name: _, subpat } => { | 197 | Pat::Bind { mode, name: _, subpat } => { |
198 | let mode = if mode == &BindingAnnotation::Unannotated { | 198 | let mode = if mode == &BindingAnnotation::Unannotated { |
@@ -275,7 +275,7 @@ impl<'a> InferenceContext<'a> { | |||
275 | if !self.unify(&ty, &expected) { | 275 | if !self.unify(&ty, &expected) { |
276 | self.result | 276 | self.result |
277 | .type_mismatches | 277 | .type_mismatches |
278 | .insert(pat.into(), TypeMismatch { expected: expected, actual: ty.clone() }); | 278 | .insert(pat.into(), TypeMismatch { expected, actual: ty.clone() }); |
279 | } | 279 | } |
280 | self.write_pat_ty(pat, ty.clone()); | 280 | self.write_pat_ty(pat, ty.clone()); |
281 | ty | 281 | ty |
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 a23527f7d..3d233b1e2 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs | |||
@@ -8,7 +8,7 @@ use arrayvec::ArrayVec; | |||
8 | use base_db::{CrateId, Edition}; | 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, |
@@ -870,7 +882,7 @@ fn transform_receiver_ty( | |||
870 | .fill_with_unknown() | 882 | .fill_with_unknown() |
871 | .build(), | 883 | .build(), |
872 | AssocContainerId::ImplId(impl_id) => { | 884 | AssocContainerId::ImplId(impl_id) => { |
873 | 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)?; |
874 | TyBuilder::subst_for_def(db, function_id) | 886 | TyBuilder::subst_for_def(db, function_id) |
875 | .use_parent_substs(&impl_substs) | 887 | .use_parent_substs(&impl_substs) |
876 | .fill_with_unknown() | 888 | .fill_with_unknown() |
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..713b74165 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() { |
@@ -23,38 +23,29 @@ fn infer_block_expr_type_mismatch() { | |||
23 | fn coerce_places() { | 23 | fn coerce_places() { |
24 | check_infer( | 24 | check_infer( |
25 | r#" | 25 | r#" |
26 | struct S<T> { a: T } | 26 | //- minicore: coerce_unsized |
27 | struct S<T> { a: T } | ||
27 | 28 | ||
28 | fn f<T>(_: &[T]) -> T { loop {} } | 29 | fn f<T>(_: &[T]) -> T { loop {} } |
29 | fn g<T>(_: S<&[T]>) -> T { loop {} } | 30 | fn g<T>(_: S<&[T]>) -> T { loop {} } |
30 | 31 | ||
31 | fn gen<T>() -> *mut [T; 2] { loop {} } | 32 | fn gen<T>() -> *mut [T; 2] { loop {} } |
32 | fn test1<U>() -> *mut [U] { | 33 | fn test1<U>() -> *mut [U] { |
33 | gen() | 34 | gen() |
34 | } | 35 | } |
35 | |||
36 | fn test2() { | ||
37 | let arr: &[u8; 1] = &[1]; | ||
38 | |||
39 | let a: &[_] = arr; | ||
40 | let b = f(arr); | ||
41 | let c: &[_] = { arr }; | ||
42 | let d = g(S { a: arr }); | ||
43 | let e: [&[_]; 1] = [arr]; | ||
44 | let f: [&[_]; 2] = [arr; 2]; | ||
45 | let g: (&[_], &[_]) = (arr, arr); | ||
46 | } | ||
47 | 36 | ||
48 | #[lang = "sized"] | 37 | fn test2() { |
49 | pub trait Sized {} | 38 | let arr: &[u8; 1] = &[1]; |
50 | #[lang = "unsize"] | ||
51 | pub trait Unsize<T: ?Sized> {} | ||
52 | #[lang = "coerce_unsized"] | ||
53 | pub trait CoerceUnsized<T> {} | ||
54 | 39 | ||
55 | impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} | 40 | let a: &[_] = arr; |
56 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} | 41 | let b = f(arr); |
57 | "#, | 42 | let c: &[_] = { arr }; |
43 | let d = g(S { a: arr }); | ||
44 | let e: [&[_]; 1] = [arr]; | ||
45 | let f: [&[_]; 2] = [arr; 2]; | ||
46 | let g: (&[_], &[_]) = (arr, arr); | ||
47 | } | ||
48 | "#, | ||
58 | expect![[r#" | 49 | expect![[r#" |
59 | 30..31 '_': &[T] | 50 | 30..31 '_': &[T] |
60 | 44..55 '{ loop {} }': T | 51 | 44..55 '{ loop {} }': T |
@@ -131,60 +122,52 @@ fn infer_let_stmt_coerce() { | |||
131 | fn infer_custom_coerce_unsized() { | 122 | fn infer_custom_coerce_unsized() { |
132 | check_infer( | 123 | check_infer( |
133 | r#" | 124 | r#" |
134 | struct A<T: ?Sized>(*const T); | 125 | //- minicore: coerce_unsized |
135 | struct B<T: ?Sized>(*const T); | 126 | use core::{marker::Unsize, ops::CoerceUnsized}; |
136 | struct C<T: ?Sized> { inner: *const T } | ||
137 | 127 | ||
138 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<B<U>> for B<T> {} | 128 | struct A<T: ?Sized>(*const T); |
139 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<C<U>> for C<T> {} | 129 | struct B<T: ?Sized>(*const T); |
130 | struct C<T: ?Sized> { inner: *const T } | ||
140 | 131 | ||
141 | fn foo1<T>(x: A<[T]>) -> A<[T]> { x } | 132 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<B<U>> for B<T> {} |
142 | fn foo2<T>(x: B<[T]>) -> B<[T]> { x } | 133 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<C<U>> for C<T> {} |
143 | fn foo3<T>(x: C<[T]>) -> C<[T]> { x } | ||
144 | |||
145 | fn test(a: A<[u8; 2]>, b: B<[u8; 2]>, c: C<[u8; 2]>) { | ||
146 | let d = foo1(a); | ||
147 | let e = foo2(b); | ||
148 | let f = foo3(c); | ||
149 | } | ||
150 | 134 | ||
135 | fn foo1<T>(x: A<[T]>) -> A<[T]> { x } | ||
136 | fn foo2<T>(x: B<[T]>) -> B<[T]> { x } | ||
137 | fn foo3<T>(x: C<[T]>) -> C<[T]> { x } | ||
151 | 138 | ||
152 | #[lang = "sized"] | 139 | fn test(a: A<[u8; 2]>, b: B<[u8; 2]>, c: C<[u8; 2]>) { |
153 | pub trait Sized {} | 140 | let d = foo1(a); |
154 | #[lang = "unsize"] | 141 | let e = foo2(b); |
155 | pub trait Unsize<T: ?Sized> {} | 142 | let f = foo3(c); |
156 | #[lang = "coerce_unsized"] | 143 | } |
157 | pub trait CoerceUnsized<T> {} | 144 | "#, |
158 | |||
159 | impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} | ||
160 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} | ||
161 | "#, | ||
162 | expect![[r#" | 145 | expect![[r#" |
163 | 257..258 'x': A<[T]> | 146 | 306..307 'x': A<[T]> |
164 | 278..283 '{ x }': A<[T]> | 147 | 327..332 '{ x }': A<[T]> |
165 | 280..281 'x': A<[T]> | 148 | 329..330 'x': A<[T]> |
166 | 295..296 'x': B<[T]> | 149 | 344..345 'x': B<[T]> |
167 | 316..321 '{ x }': B<[T]> | 150 | 365..370 '{ x }': B<[T]> |
168 | 318..319 'x': B<[T]> | 151 | 367..368 'x': B<[T]> |
169 | 333..334 'x': C<[T]> | 152 | 382..383 'x': C<[T]> |
170 | 354..359 '{ x }': C<[T]> | 153 | 403..408 '{ x }': C<[T]> |
171 | 356..357 'x': C<[T]> | 154 | 405..406 'x': C<[T]> |
172 | 369..370 'a': A<[u8; 2]> | 155 | 418..419 'a': A<[u8; 2]> |
173 | 384..385 'b': B<[u8; 2]> | 156 | 433..434 'b': B<[u8; 2]> |
174 | 399..400 'c': C<[u8; 2]> | 157 | 448..449 'c': C<[u8; 2]> |
175 | 414..480 '{ ...(c); }': () | 158 | 463..529 '{ ...(c); }': () |
176 | 424..425 'd': A<[{unknown}]> | 159 | 473..474 'd': A<[{unknown}]> |
177 | 428..432 'foo1': fn foo1<{unknown}>(A<[{unknown}]>) -> A<[{unknown}]> | 160 | 477..481 'foo1': fn foo1<{unknown}>(A<[{unknown}]>) -> A<[{unknown}]> |
178 | 428..435 'foo1(a)': A<[{unknown}]> | 161 | 477..484 'foo1(a)': A<[{unknown}]> |
179 | 433..434 'a': A<[u8; 2]> | 162 | 482..483 'a': A<[u8; 2]> |
180 | 445..446 'e': B<[u8]> | 163 | 494..495 'e': B<[u8]> |
181 | 449..453 'foo2': fn foo2<u8>(B<[u8]>) -> B<[u8]> | 164 | 498..502 'foo2': fn foo2<u8>(B<[u8]>) -> B<[u8]> |
182 | 449..456 'foo2(b)': B<[u8]> | 165 | 498..505 'foo2(b)': B<[u8]> |
183 | 454..455 'b': B<[u8; 2]> | 166 | 503..504 'b': B<[u8; 2]> |
184 | 466..467 'f': C<[u8]> | 167 | 515..516 'f': C<[u8]> |
185 | 470..474 'foo3': fn foo3<u8>(C<[u8]>) -> C<[u8]> | 168 | 519..523 'foo3': fn foo3<u8>(C<[u8]>) -> C<[u8]> |
186 | 470..477 'foo3(c)': C<[u8]> | 169 | 519..526 'foo3(c)': C<[u8]> |
187 | 475..476 'c': C<[u8; 2]> | 170 | 524..525 'c': C<[u8; 2]> |
188 | "#]], | 171 | "#]], |
189 | ); | 172 | ); |
190 | } | 173 | } |
@@ -193,21 +176,16 @@ fn infer_custom_coerce_unsized() { | |||
193 | fn infer_if_coerce() { | 176 | fn infer_if_coerce() { |
194 | check_infer( | 177 | check_infer( |
195 | r#" | 178 | r#" |
196 | fn foo<T>(x: &[T]) -> &[T] { loop {} } | 179 | //- minicore: unsize |
197 | fn test() { | 180 | fn foo<T>(x: &[T]) -> &[T] { loop {} } |
198 | let x = if true { | 181 | fn test() { |
199 | foo(&[1]) | 182 | let x = if true { |
200 | } else { | 183 | foo(&[1]) |
201 | &[1] | 184 | } else { |
202 | }; | 185 | &[1] |
203 | } | 186 | }; |
204 | 187 | } | |
205 | 188 | "#, | |
206 | #[lang = "sized"] | ||
207 | pub trait Sized {} | ||
208 | #[lang = "unsize"] | ||
209 | pub trait Unsize<T: ?Sized> {} | ||
210 | "#, | ||
211 | expect![[r#" | 189 | expect![[r#" |
212 | 10..11 'x': &[T] | 190 | 10..11 'x': &[T] |
213 | 27..38 '{ loop {} }': &[T] | 191 | 27..38 '{ loop {} }': &[T] |
@@ -235,25 +213,16 @@ fn infer_if_coerce() { | |||
235 | fn infer_if_else_coerce() { | 213 | fn infer_if_else_coerce() { |
236 | check_infer( | 214 | check_infer( |
237 | r#" | 215 | r#" |
238 | fn foo<T>(x: &[T]) -> &[T] { loop {} } | 216 | //- minicore: coerce_unsized |
239 | fn test() { | 217 | fn foo<T>(x: &[T]) -> &[T] { loop {} } |
240 | let x = if true { | 218 | fn test() { |
241 | &[1] | 219 | let x = if true { |
242 | } else { | 220 | &[1] |
243 | foo(&[1]) | 221 | } else { |
244 | }; | 222 | foo(&[1]) |
245 | } | 223 | }; |
246 | 224 | } | |
247 | #[lang = "sized"] | 225 | "#, |
248 | pub trait Sized {} | ||
249 | #[lang = "unsize"] | ||
250 | pub trait Unsize<T: ?Sized> {} | ||
251 | #[lang = "coerce_unsized"] | ||
252 | pub trait CoerceUnsized<T> {} | ||
253 | |||
254 | impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} | ||
255 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} | ||
256 | "#, | ||
257 | expect![[r#" | 226 | expect![[r#" |
258 | 10..11 'x': &[T] | 227 | 10..11 'x': &[T] |
259 | 27..38 '{ loop {} }': &[T] | 228 | 27..38 '{ loop {} }': &[T] |
@@ -281,20 +250,16 @@ fn infer_if_else_coerce() { | |||
281 | fn infer_match_first_coerce() { | 250 | fn infer_match_first_coerce() { |
282 | check_infer( | 251 | check_infer( |
283 | r#" | 252 | r#" |
284 | fn foo<T>(x: &[T]) -> &[T] { loop {} } | 253 | //- minicore: unsize |
285 | fn test(i: i32) { | 254 | fn foo<T>(x: &[T]) -> &[T] { loop {} } |
286 | let x = match i { | 255 | fn test(i: i32) { |
287 | 2 => foo(&[2]), | 256 | let x = match i { |
288 | 1 => &[1], | 257 | 2 => foo(&[2]), |
289 | _ => &[3], | 258 | 1 => &[1], |
290 | }; | 259 | _ => &[3], |
291 | } | 260 | }; |
292 | 261 | } | |
293 | #[lang = "sized"] | 262 | "#, |
294 | pub trait Sized {} | ||
295 | #[lang = "unsize"] | ||
296 | pub trait Unsize<T: ?Sized> {} | ||
297 | "#, | ||
298 | expect![[r#" | 263 | expect![[r#" |
299 | 10..11 'x': &[T] | 264 | 10..11 'x': &[T] |
300 | 27..38 '{ loop {} }': &[T] | 265 | 27..38 '{ loop {} }': &[T] |
@@ -329,25 +294,16 @@ fn infer_match_first_coerce() { | |||
329 | fn infer_match_second_coerce() { | 294 | fn infer_match_second_coerce() { |
330 | check_infer( | 295 | check_infer( |
331 | r#" | 296 | r#" |
332 | fn foo<T>(x: &[T]) -> &[T] { loop {} } | 297 | //- minicore: coerce_unsized |
333 | fn test(i: i32) { | 298 | fn foo<T>(x: &[T]) -> &[T] { loop {} } |
334 | let x = match i { | 299 | fn test(i: i32) { |
335 | 1 => &[1], | 300 | let x = match i { |
336 | 2 => foo(&[2]), | 301 | 1 => &[1], |
337 | _ => &[3], | 302 | 2 => foo(&[2]), |
338 | }; | 303 | _ => &[3], |
339 | } | 304 | }; |
340 | 305 | } | |
341 | #[lang = "sized"] | 306 | "#, |
342 | pub trait Sized {} | ||
343 | #[lang = "unsize"] | ||
344 | pub trait Unsize<T: ?Sized> {} | ||
345 | #[lang = "coerce_unsized"] | ||
346 | pub trait CoerceUnsized<T> {} | ||
347 | |||
348 | impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} | ||
349 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} | ||
350 | "#, | ||
351 | expect![[r#" | 307 | expect![[r#" |
352 | 10..11 'x': &[T] | 308 | 10..11 'x': &[T] |
353 | 27..38 '{ loop {} }': &[T] | 309 | 27..38 '{ loop {} }': &[T] |
@@ -470,15 +426,15 @@ fn coerce_autoderef() { | |||
470 | #[test] | 426 | #[test] |
471 | fn coerce_autoderef_generic() { | 427 | fn coerce_autoderef_generic() { |
472 | check_infer_with_mismatches( | 428 | check_infer_with_mismatches( |
473 | r" | 429 | r#" |
474 | struct Foo; | 430 | struct Foo; |
475 | fn takes_ref<T>(x: &T) -> T { *x } | 431 | fn takes_ref<T>(x: &T) -> T { *x } |
476 | fn test() { | 432 | fn test() { |
477 | takes_ref(&Foo); | 433 | takes_ref(&Foo); |
478 | takes_ref(&&Foo); | 434 | takes_ref(&&Foo); |
479 | takes_ref(&&&Foo); | 435 | takes_ref(&&&Foo); |
480 | } | 436 | } |
481 | ", | 437 | "#, |
482 | expect![[r" | 438 | expect![[r" |
483 | 28..29 'x': &T | 439 | 28..29 'x': &T |
484 | 40..46 '{ *x }': T | 440 | 40..46 '{ *x }': T |
@@ -508,30 +464,29 @@ fn coerce_autoderef_generic() { | |||
508 | fn coerce_autoderef_block() { | 464 | fn coerce_autoderef_block() { |
509 | check_infer_with_mismatches( | 465 | check_infer_with_mismatches( |
510 | r#" | 466 | r#" |
511 | struct String {} | 467 | //- minicore: deref |
512 | #[lang = "deref"] | 468 | struct String {} |
513 | trait Deref { type Target; } | 469 | impl core::ops::Deref for String { type Target = str; } |
514 | impl Deref for String { type Target = str; } | 470 | fn takes_ref_str(x: &str) {} |
515 | fn takes_ref_str(x: &str) {} | 471 | fn returns_string() -> String { loop {} } |
516 | fn returns_string() -> String { loop {} } | 472 | fn test() { |
517 | fn test() { | 473 | takes_ref_str(&{ returns_string() }); |
518 | takes_ref_str(&{ returns_string() }); | 474 | } |
519 | } | 475 | "#, |
520 | "#, | 476 | expect![[r#" |
521 | expect![[r" | 477 | 90..91 'x': &str |
522 | 126..127 'x': &str | 478 | 99..101 '{}': () |
523 | 135..137 '{}': () | 479 | 132..143 '{ loop {} }': String |
524 | 168..179 '{ loop {} }': String | 480 | 134..141 'loop {}': ! |
525 | 170..177 'loop {}': ! | 481 | 139..141 '{}': () |
526 | 175..177 '{}': () | 482 | 154..199 '{ ... }); }': () |
527 | 190..235 '{ ... }); }': () | 483 | 160..173 'takes_ref_str': fn takes_ref_str(&str) |
528 | 196..209 'takes_ref_str': fn takes_ref_str(&str) | 484 | 160..196 'takes_...g() })': () |
529 | 196..232 'takes_...g() })': () | 485 | 174..195 '&{ ret...ng() }': &String |
530 | 210..231 '&{ ret...ng() }': &String | 486 | 175..195 '{ retu...ng() }': String |
531 | 211..231 '{ retu...ng() }': String | 487 | 177..191 'returns_string': fn returns_string() -> String |
532 | 213..227 'returns_string': fn returns_string() -> String | 488 | 177..193 'return...ring()': String |
533 | 213..229 'return...ring()': String | 489 | "#]], |
534 | "]], | ||
535 | ); | 490 | ); |
536 | } | 491 | } |
537 | 492 | ||
@@ -674,25 +629,19 @@ fn coerce_placeholder_ref() { | |||
674 | fn coerce_unsize_array() { | 629 | fn coerce_unsize_array() { |
675 | check_infer_with_mismatches( | 630 | check_infer_with_mismatches( |
676 | r#" | 631 | r#" |
677 | #[lang = "unsize"] | 632 | //- minicore: coerce_unsized |
678 | pub trait Unsize<T> {} | 633 | fn test() { |
679 | #[lang = "coerce_unsized"] | 634 | let f: &[usize] = &[1, 2, 3]; |
680 | pub trait CoerceUnsized<T> {} | 635 | } |
681 | |||
682 | impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {} | ||
683 | |||
684 | fn test() { | ||
685 | let f: &[usize] = &[1, 2, 3]; | ||
686 | } | ||
687 | "#, | 636 | "#, |
688 | expect![[r#" | 637 | expect![[r#" |
689 | 161..198 '{ ... 3]; }': () | 638 | 10..47 '{ ... 3]; }': () |
690 | 171..172 'f': &[usize] | 639 | 20..21 'f': &[usize] |
691 | 185..195 '&[1, 2, 3]': &[usize; 3] | 640 | 34..44 '&[1, 2, 3]': &[usize; 3] |
692 | 186..195 '[1, 2, 3]': [usize; 3] | 641 | 35..44 '[1, 2, 3]': [usize; 3] |
693 | 187..188 '1': usize | 642 | 36..37 '1': usize |
694 | 190..191 '2': usize | 643 | 39..40 '2': usize |
695 | 193..194 '3': usize | 644 | 42..43 '3': usize |
696 | "#]], | 645 | "#]], |
697 | ); | 646 | ); |
698 | } | 647 | } |
@@ -701,93 +650,94 @@ fn coerce_unsize_array() { | |||
701 | fn coerce_unsize_trait_object_simple() { | 650 | fn coerce_unsize_trait_object_simple() { |
702 | check_infer_with_mismatches( | 651 | check_infer_with_mismatches( |
703 | r#" | 652 | r#" |
704 | #[lang = "sized"] | 653 | //- minicore: coerce_unsized |
705 | pub trait Sized {} | 654 | trait Foo<T, U> {} |
706 | #[lang = "unsize"] | 655 | trait Bar<U, T, X>: Foo<T, U> {} |
707 | pub trait Unsize<T> {} | 656 | trait Baz<T, X>: Bar<usize, T, X> {} |
708 | #[lang = "coerce_unsized"] | 657 | |
709 | pub trait CoerceUnsized<T> {} | 658 | struct S<T, X>; |
710 | 659 | impl<T, X> Foo<T, usize> for S<T, X> {} | |
711 | impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {} | 660 | impl<T, X> Bar<usize, T, X> for S<T, X> {} |
712 | 661 | impl<T, X> Baz<T, X> for S<T, X> {} | |
713 | trait Foo<T, U> {} | 662 | |
714 | trait Bar<U, T, X>: Foo<T, U> {} | 663 | fn test() { |
715 | trait Baz<T, X>: Bar<usize, T, X> {} | 664 | let obj: &dyn Baz<i8, i16> = &S; |
716 | 665 | let obj: &dyn Bar<_, i8, i16> = &S; | |
717 | struct S<T, X>; | 666 | let obj: &dyn Foo<i8, _> = &S; |
718 | impl<T, X> Foo<T, usize> for S<T, X> {} | 667 | } |
719 | impl<T, X> Bar<usize, T, X> for S<T, X> {} | 668 | "#, |
720 | impl<T, X> Baz<T, X> for S<T, X> {} | 669 | expect![[r#" |
721 | 670 | 236..351 '{ ... &S; }': () | |
722 | fn test() { | 671 | 246..249 'obj': &dyn Baz<i8, i16> |
723 | let obj: &dyn Baz<i8, i16> = &S; | 672 | 271..273 '&S': &S<i8, i16> |
724 | let obj: &dyn Bar<_, i8, i16> = &S; | 673 | 272..273 'S': S<i8, i16> |
725 | let obj: &dyn Foo<i8, _> = &S; | 674 | 283..286 'obj': &dyn Bar<usize, i8, i16> |
726 | } | 675 | 311..313 '&S': &S<i8, i16> |
727 | "#, | 676 | 312..313 'S': S<i8, i16> |
728 | expect![[r" | 677 | 323..326 'obj': &dyn Foo<i8, usize> |
729 | 424..539 '{ ... &S; }': () | 678 | 346..348 '&S': &S<i8, {unknown}> |
730 | 434..437 'obj': &dyn Baz<i8, i16> | 679 | 347..348 'S': S<i8, {unknown}> |
731 | 459..461 '&S': &S<i8, i16> | 680 | "#]], |
732 | 460..461 'S': S<i8, i16> | ||
733 | 471..474 'obj': &dyn Bar<usize, i8, i16> | ||
734 | 499..501 '&S': &S<i8, i16> | ||
735 | 500..501 'S': S<i8, i16> | ||
736 | 511..514 'obj': &dyn Foo<i8, usize> | ||
737 | 534..536 '&S': &S<i8, {unknown}> | ||
738 | 535..536 'S': S<i8, {unknown}> | ||
739 | "]], | ||
740 | ); | 681 | ); |
741 | } | 682 | } |
742 | 683 | ||
743 | #[test] | 684 | #[test] |
744 | // The rust reference says this should be possible, but rustc doesn't implement | ||
745 | // it. We used to support it, but Chalk doesn't. | ||
746 | #[ignore] | ||
747 | fn coerce_unsize_trait_object_to_trait_object() { | 685 | fn coerce_unsize_trait_object_to_trait_object() { |
686 | // FIXME: The rust reference says this should be possible, but rustc doesn't | ||
687 | // implement it. We used to support it, but Chalk doesn't. Here's the | ||
688 | // correct expect: | ||
689 | // | ||
690 | // 424..609 '{ ...bj2; }': () | ||
691 | // 434..437 'obj': &dyn Baz<i8, i16> | ||
692 | // 459..461 '&S': &S<i8, i16> | ||
693 | // 460..461 'S': S<i8, i16> | ||
694 | // 471..474 'obj': &dyn Bar<usize, i8, i16> | ||
695 | // 496..499 'obj': &dyn Baz<i8, i16> | ||
696 | // 509..512 'obj': &dyn Foo<i8, usize> | ||
697 | // 531..534 'obj': &dyn Bar<usize, i8, i16> | ||
698 | // 544..548 'obj2': &dyn Baz<i8, i16> | ||
699 | // 570..572 '&S': &S<i8, i16> | ||
700 | // 571..572 'S': S<i8, i16> | ||
701 | // 582..583 '_': &dyn Foo<i8, usize> | ||
702 | // 602..606 'obj2': &dyn Baz<i8, i16> | ||
748 | check_infer_with_mismatches( | 703 | check_infer_with_mismatches( |
749 | r#" | 704 | r#" |
750 | #[lang = "sized"] | 705 | //- minicore: coerce_unsized |
751 | pub trait Sized {} | 706 | trait Foo<T, U> {} |
752 | #[lang = "unsize"] | 707 | trait Bar<U, T, X>: Foo<T, U> {} |
753 | pub trait Unsize<T> {} | 708 | trait Baz<T, X>: Bar<usize, T, X> {} |
754 | #[lang = "coerce_unsized"] | 709 | |
755 | pub trait CoerceUnsized<T> {} | 710 | struct S<T, X>; |
756 | 711 | impl<T, X> Foo<T, usize> for S<T, X> {} | |
757 | impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {} | 712 | impl<T, X> Bar<usize, T, X> for S<T, X> {} |
758 | 713 | impl<T, X> Baz<T, X> for S<T, X> {} | |
759 | trait Foo<T, U> {} | 714 | |
760 | trait Bar<U, T, X>: Foo<T, U> {} | 715 | fn test() { |
761 | trait Baz<T, X>: Bar<usize, T, X> {} | 716 | let obj: &dyn Baz<i8, i16> = &S; |
762 | 717 | let obj: &dyn Bar<_, _, _> = obj; | |
763 | struct S<T, X>; | 718 | let obj: &dyn Foo<_, _> = obj; |
764 | impl<T, X> Foo<T, usize> for S<T, X> {} | 719 | let obj2: &dyn Baz<i8, i16> = &S; |
765 | impl<T, X> Bar<usize, T, X> for S<T, X> {} | 720 | let _: &dyn Foo<_, _> = obj2; |
766 | impl<T, X> Baz<T, X> for S<T, X> {} | 721 | } |
767 | 722 | "#, | |
768 | fn test() { | 723 | expect![[r#" |
769 | let obj: &dyn Baz<i8, i16> = &S; | 724 | 236..421 '{ ...bj2; }': () |
770 | let obj: &dyn Bar<_, _, _> = obj; | 725 | 246..249 'obj': &dyn Baz<i8, i16> |
771 | let obj: &dyn Foo<_, _> = obj; | 726 | 271..273 '&S': &S<i8, i16> |
772 | let obj2: &dyn Baz<i8, i16> = &S; | 727 | 272..273 'S': S<i8, i16> |
773 | let _: &dyn Foo<_, _> = obj2; | 728 | 283..286 'obj': &dyn Bar<{unknown}, {unknown}, {unknown}> |
774 | } | 729 | 308..311 'obj': &dyn Baz<i8, i16> |
775 | "#, | 730 | 321..324 'obj': &dyn Foo<{unknown}, {unknown}> |
776 | expect![[r" | 731 | 343..346 'obj': &dyn Bar<{unknown}, {unknown}, {unknown}> |
777 | 424..609 '{ ...bj2; }': () | 732 | 356..360 'obj2': &dyn Baz<i8, i16> |
778 | 434..437 'obj': &dyn Baz<i8, i16> | 733 | 382..384 '&S': &S<i8, i16> |
779 | 459..461 '&S': &S<i8, i16> | 734 | 383..384 'S': S<i8, i16> |
780 | 460..461 'S': S<i8, i16> | 735 | 394..395 '_': &dyn Foo<{unknown}, {unknown}> |
781 | 471..474 'obj': &dyn Bar<usize, i8, i16> | 736 | 414..418 'obj2': &dyn Baz<i8, i16> |
782 | 496..499 'obj': &dyn Baz<i8, i16> | 737 | 308..311: expected &dyn Bar<{unknown}, {unknown}, {unknown}>, got &dyn Baz<i8, i16> |
783 | 509..512 'obj': &dyn Foo<i8, usize> | 738 | 343..346: expected &dyn Foo<{unknown}, {unknown}>, got &dyn Bar<{unknown}, {unknown}, {unknown}> |
784 | 531..534 'obj': &dyn Bar<usize, i8, i16> | 739 | 414..418: expected &dyn Foo<{unknown}, {unknown}>, got &dyn Baz<i8, i16> |
785 | 544..548 'obj2': &dyn Baz<i8, i16> | 740 | "#]], |
786 | 570..572 '&S': &S<i8, i16> | ||
787 | 571..572 'S': S<i8, i16> | ||
788 | 582..583 '_': &dyn Foo<i8, usize> | ||
789 | 602..606 'obj2': &dyn Baz<i8, i16> | ||
790 | "]], | ||
791 | ); | 741 | ); |
792 | } | 742 | } |
793 | 743 | ||
@@ -795,40 +745,32 @@ fn coerce_unsize_trait_object_to_trait_object() { | |||
795 | fn coerce_unsize_super_trait_cycle() { | 745 | fn coerce_unsize_super_trait_cycle() { |
796 | check_infer_with_mismatches( | 746 | check_infer_with_mismatches( |
797 | r#" | 747 | r#" |
798 | #[lang = "sized"] | 748 | //- minicore: coerce_unsized |
799 | pub trait Sized {} | 749 | trait A {} |
800 | #[lang = "unsize"] | 750 | trait B: C + A {} |
801 | pub trait Unsize<T> {} | 751 | trait C: B {} |
802 | #[lang = "coerce_unsized"] | 752 | trait D: C |
803 | pub trait CoerceUnsized<T> {} | 753 | |
804 | 754 | struct S; | |
805 | impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {} | 755 | impl A for S {} |
806 | 756 | impl B for S {} | |
807 | trait A {} | 757 | impl C for S {} |
808 | trait B: C + A {} | 758 | impl D for S {} |
809 | trait C: B {} | 759 | |
810 | trait D: C | 760 | fn test() { |
811 | 761 | let obj: &dyn D = &S; | |
812 | struct S; | 762 | let obj: &dyn A = &S; |
813 | impl A for S {} | 763 | } |
814 | impl B for S {} | 764 | "#, |
815 | impl C for S {} | 765 | expect![[r#" |
816 | impl D for S {} | 766 | 140..195 '{ ... &S; }': () |
817 | 767 | 150..153 'obj': &dyn D | |
818 | fn test() { | 768 | 164..166 '&S': &S |
819 | let obj: &dyn D = &S; | 769 | 165..166 'S': S |
820 | let obj: &dyn A = &S; | 770 | 176..179 'obj': &dyn A |
821 | } | 771 | 190..192 '&S': &S |
822 | "#, | 772 | 191..192 'S': S |
823 | expect![[r" | 773 | "#]], |
824 | 328..383 '{ ... &S; }': () | ||
825 | 338..341 'obj': &dyn D | ||
826 | 352..354 '&S': &S | ||
827 | 353..354 'S': S | ||
828 | 364..367 'obj': &dyn A | ||
829 | 378..380 '&S': &S | ||
830 | 379..380 'S': S | ||
831 | "]], | ||
832 | ); | 774 | ); |
833 | } | 775 | } |
834 | 776 | ||
@@ -837,41 +779,35 @@ fn coerce_unsize_generic() { | |||
837 | // FIXME: fix the type mismatches here | 779 | // FIXME: fix the type mismatches here |
838 | check_infer_with_mismatches( | 780 | check_infer_with_mismatches( |
839 | r#" | 781 | r#" |
840 | #[lang = "unsize"] | 782 | //- minicore: coerce_unsized |
841 | pub trait Unsize<T> {} | 783 | struct Foo<T> { t: T }; |
842 | #[lang = "coerce_unsized"] | 784 | struct Bar<T>(Foo<T>); |
843 | pub trait CoerceUnsized<T> {} | ||
844 | |||
845 | impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {} | ||
846 | 785 | ||
847 | struct Foo<T> { t: T }; | 786 | fn test() { |
848 | struct Bar<T>(Foo<T>); | 787 | let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] }; |
849 | 788 | let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] }); | |
850 | fn test() { | 789 | } |
851 | let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] }; | 790 | "#, |
852 | let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] }); | ||
853 | } | ||
854 | "#, | ||
855 | expect![[r#" | 791 | expect![[r#" |
856 | 209..317 '{ ... }); }': () | 792 | 58..166 '{ ... }); }': () |
857 | 219..220 '_': &Foo<[usize]> | 793 | 68..69 '_': &Foo<[usize]> |
858 | 238..259 '&Foo {..., 3] }': &Foo<[usize]> | 794 | 87..108 '&Foo {..., 3] }': &Foo<[usize]> |
859 | 239..259 'Foo { ..., 3] }': Foo<[usize]> | 795 | 88..108 'Foo { ..., 3] }': Foo<[usize]> |
860 | 248..257 '[1, 2, 3]': [usize; 3] | 796 | 97..106 '[1, 2, 3]': [usize; 3] |
861 | 249..250 '1': usize | 797 | 98..99 '1': usize |
862 | 252..253 '2': usize | 798 | 101..102 '2': usize |
863 | 255..256 '3': usize | 799 | 104..105 '3': usize |
864 | 269..270 '_': &Bar<[usize]> | 800 | 118..119 '_': &Bar<[usize]> |
865 | 288..314 '&Bar(F... 3] })': &Bar<[i32; 3]> | 801 | 137..163 '&Bar(F... 3] })': &Bar<[i32; 3]> |
866 | 289..292 'Bar': Bar<[i32; 3]>(Foo<[i32; 3]>) -> Bar<[i32; 3]> | 802 | 138..141 'Bar': Bar<[i32; 3]>(Foo<[i32; 3]>) -> Bar<[i32; 3]> |
867 | 289..314 'Bar(Fo... 3] })': Bar<[i32; 3]> | 803 | 138..163 'Bar(Fo... 3] })': Bar<[i32; 3]> |
868 | 293..313 'Foo { ..., 3] }': Foo<[i32; 3]> | 804 | 142..162 'Foo { ..., 3] }': Foo<[i32; 3]> |
869 | 302..311 '[1, 2, 3]': [i32; 3] | 805 | 151..160 '[1, 2, 3]': [i32; 3] |
870 | 303..304 '1': i32 | 806 | 152..153 '1': i32 |
871 | 306..307 '2': i32 | 807 | 155..156 '2': i32 |
872 | 309..310 '3': i32 | 808 | 158..159 '3': i32 |
873 | 248..257: expected [usize], got [usize; 3] | 809 | 97..106: expected [usize], got [usize; 3] |
874 | 288..314: expected &Bar<[usize]>, got &Bar<[i32; 3]> | 810 | 137..163: expected &Bar<[usize]>, got &Bar<[i32; 3]> |
875 | "#]], | 811 | "#]], |
876 | ); | 812 | ); |
877 | } | 813 | } |
@@ -881,15 +817,7 @@ fn coerce_unsize_apit() { | |||
881 | // FIXME: #8984 | 817 | // FIXME: #8984 |
882 | check_infer_with_mismatches( | 818 | check_infer_with_mismatches( |
883 | r#" | 819 | r#" |
884 | #[lang = "sized"] | 820 | //- minicore: coerce_unsized |
885 | pub trait Sized {} | ||
886 | #[lang = "unsize"] | ||
887 | pub trait Unsize<T> {} | ||
888 | #[lang = "coerce_unsized"] | ||
889 | pub trait CoerceUnsized<T> {} | ||
890 | |||
891 | impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {} | ||
892 | |||
893 | trait Foo {} | 821 | trait Foo {} |
894 | 822 | ||
895 | fn test(f: impl Foo) { | 823 | fn test(f: impl Foo) { |
@@ -897,12 +825,12 @@ fn test(f: impl Foo) { | |||
897 | } | 825 | } |
898 | "#, | 826 | "#, |
899 | expect![[r#" | 827 | expect![[r#" |
900 | 210..211 'f': impl Foo | 828 | 22..23 'f': impl Foo |
901 | 223..252 '{ ... &f; }': () | 829 | 35..64 '{ ... &f; }': () |
902 | 233..234 '_': &dyn Foo | 830 | 45..46 '_': &dyn Foo |
903 | 247..249 '&f': &impl Foo | 831 | 59..61 '&f': &impl Foo |
904 | 248..249 'f': impl Foo | 832 | 60..61 'f': impl Foo |
905 | 247..249: expected &dyn Foo, got &impl Foo | 833 | 59..61: expected &dyn Foo, got &impl Foo |
906 | "#]], | 834 | "#]], |
907 | ); | 835 | ); |
908 | } | 836 | } |
@@ -963,7 +891,7 @@ fn test() -> i32 { | |||
963 | 891 | ||
964 | #[test] | 892 | #[test] |
965 | fn panic_macro() { | 893 | fn panic_macro() { |
966 | check_infer_with_mismatches( | 894 | check_no_mismatches( |
967 | r#" | 895 | r#" |
968 | mod panic { | 896 | mod panic { |
969 | #[macro_export] | 897 | #[macro_export] |
@@ -991,15 +919,26 @@ fn main() { | |||
991 | panic!() | 919 | panic!() |
992 | } | 920 | } |
993 | "#, | 921 | "#, |
994 | expect![[r#" | 922 | ); |
995 | 174..185 '{ loop {} }': ! | 923 | } |
996 | 176..183 'loop {}': ! | 924 | |
997 | 181..183 '{}': () | 925 | #[test] |
998 | !0..24 '$crate...:panic': fn panic() -> ! | 926 | fn coerce_unsize_expected_type() { |
999 | !0..26 '$crate...anic()': ! | 927 | check_no_mismatches( |
1000 | !0..26 '$crate...anic()': ! | 928 | r#" |
1001 | !0..28 '$crate...015!()': ! | 929 | //- minicore: coerce_unsized |
1002 | 454..470 '{ ...c!() }': () | 930 | fn main() { |
1003 | "#]], | 931 | let foo: &[u32] = &[1, 2]; |
932 | let foo: &[u32] = match true { | ||
933 | true => &[1, 2], | ||
934 | false => &[1, 2, 3], | ||
935 | }; | ||
936 | let foo: &[u32] = if true { | ||
937 | &[1, 2] | ||
938 | } else { | ||
939 | &[1, 2, 3] | ||
940 | }; | ||
941 | } | ||
942 | "#, | ||
1004 | ); | 943 | ); |
1005 | } | 944 | } |
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs index f26b2c8a7..d9b5ee9cf 100644 --- a/crates/hir_ty/src/tests/method_resolution.rs +++ b/crates/hir_ty/src/tests/method_resolution.rs | |||
@@ -780,10 +780,7 @@ fn test() { (&S).foo(); } | |||
780 | fn method_resolution_unsize_array() { | 780 | fn method_resolution_unsize_array() { |
781 | check_types( | 781 | check_types( |
782 | r#" | 782 | r#" |
783 | #[lang = "slice"] | 783 | //- minicore: slice |
784 | impl<T> [T] { | ||
785 | fn len(&self) -> usize { loop {} } | ||
786 | } | ||
787 | fn test() { | 784 | fn test() { |
788 | let a = [1, 2, 3]; | 785 | let a = [1, 2, 3]; |
789 | a.len(); | 786 | a.len(); |
@@ -1178,11 +1175,7 @@ fn main() { | |||
1178 | fn autoderef_visibility_field() { | 1175 | fn autoderef_visibility_field() { |
1179 | check_infer( | 1176 | check_infer( |
1180 | r#" | 1177 | r#" |
1181 | #[lang = "deref"] | 1178 | //- minicore: deref |
1182 | pub trait Deref { | ||
1183 | type Target; | ||
1184 | fn deref(&self) -> &Self::Target; | ||
1185 | } | ||
1186 | mod a { | 1179 | mod a { |
1187 | pub struct Foo(pub char); | 1180 | pub struct Foo(pub char); |
1188 | pub struct Bar(i32); | 1181 | pub struct Bar(i32); |
@@ -1191,7 +1184,7 @@ mod a { | |||
1191 | Self(0) | 1184 | Self(0) |
1192 | } | 1185 | } |
1193 | } | 1186 | } |
1194 | impl super::Deref for Bar { | 1187 | impl core::ops::Deref for Bar { |
1195 | type Target = Foo; | 1188 | type Target = Foo; |
1196 | fn deref(&self) -> &Foo { | 1189 | fn deref(&self) -> &Foo { |
1197 | &Foo('z') | 1190 | &Foo('z') |
@@ -1205,22 +1198,21 @@ mod b { | |||
1205 | } | 1198 | } |
1206 | "#, | 1199 | "#, |
1207 | expect![[r#" | 1200 | expect![[r#" |
1208 | 67..71 'self': &Self | 1201 | 107..138 '{ ... }': Bar |
1209 | 200..231 '{ ... }': Bar | 1202 | 121..125 'Self': Bar(i32) -> Bar |
1210 | 214..218 'Self': Bar(i32) -> Bar | 1203 | 121..128 'Self(0)': Bar |
1211 | 214..221 'Self(0)': Bar | 1204 | 126..127 '0': i32 |
1212 | 219..220 '0': i32 | 1205 | 226..230 'self': &Bar |
1213 | 315..319 'self': &Bar | 1206 | 240..273 '{ ... }': &Foo |
1214 | 329..362 '{ ... }': &Foo | 1207 | 254..263 '&Foo('z')': &Foo |
1215 | 343..352 '&Foo('z')': &Foo | 1208 | 255..258 'Foo': Foo(char) -> Foo |
1216 | 344..347 'Foo': Foo(char) -> Foo | 1209 | 255..263 'Foo('z')': Foo |
1217 | 344..352 'Foo('z')': Foo | 1210 | 259..262 ''z'': char |
1218 | 348..351 ''z'': char | 1211 | 303..350 '{ ... }': () |
1219 | 392..439 '{ ... }': () | 1212 | 317..318 'x': char |
1220 | 406..407 'x': char | 1213 | 321..339 'super:...r::new': fn new() -> Bar |
1221 | 410..428 'super:...r::new': fn new() -> Bar | 1214 | 321..341 'super:...:new()': Bar |
1222 | 410..430 'super:...:new()': Bar | 1215 | 321..343 'super:...ew().0': char |
1223 | 410..432 'super:...ew().0': char | ||
1224 | "#]], | 1216 | "#]], |
1225 | ) | 1217 | ) |
1226 | } | 1218 | } |
@@ -1230,11 +1222,7 @@ fn autoderef_visibility_method() { | |||
1230 | cov_mark::check!(autoderef_candidate_not_visible); | 1222 | cov_mark::check!(autoderef_candidate_not_visible); |
1231 | check_infer( | 1223 | check_infer( |
1232 | r#" | 1224 | r#" |
1233 | #[lang = "deref"] | 1225 | //- minicore: deref |
1234 | pub trait Deref { | ||
1235 | type Target; | ||
1236 | fn deref(&self) -> &Self::Target; | ||
1237 | } | ||
1238 | mod a { | 1226 | mod a { |
1239 | pub struct Foo(pub char); | 1227 | pub struct Foo(pub char); |
1240 | impl Foo { | 1228 | impl Foo { |
@@ -1251,7 +1239,7 @@ mod a { | |||
1251 | self.0 | 1239 | self.0 |
1252 | } | 1240 | } |
1253 | } | 1241 | } |
1254 | impl super::Deref for Bar { | 1242 | impl core::ops::Deref for Bar { |
1255 | type Target = Foo; | 1243 | type Target = Foo; |
1256 | fn deref(&self) -> &Foo { | 1244 | fn deref(&self) -> &Foo { |
1257 | &Foo('z') | 1245 | &Foo('z') |
@@ -1265,30 +1253,29 @@ mod b { | |||
1265 | } | 1253 | } |
1266 | "#, | 1254 | "#, |
1267 | expect![[r#" | 1255 | expect![[r#" |
1268 | 67..71 'self': &Self | 1256 | 75..79 'self': &Foo |
1269 | 168..172 'self': &Foo | 1257 | 89..119 '{ ... }': char |
1270 | 182..212 '{ ... }': char | 1258 | 103..107 'self': &Foo |
1271 | 196..200 'self': &Foo | 1259 | 103..109 'self.0': char |
1272 | 196..202 'self.0': char | 1260 | 195..226 '{ ... }': Bar |
1273 | 288..319 '{ ... }': Bar | 1261 | 209..213 'Self': Bar(i32) -> Bar |
1274 | 302..306 'Self': Bar(i32) -> Bar | 1262 | 209..216 'Self(0)': Bar |
1275 | 302..309 'Self(0)': Bar | 1263 | 214..215 '0': i32 |
1276 | 307..308 '0': i32 | 1264 | 245..249 'self': &Bar |
1277 | 338..342 'self': &Bar | 1265 | 258..288 '{ ... }': i32 |
1278 | 351..381 '{ ... }': i32 | 1266 | 272..276 'self': &Bar |
1279 | 365..369 'self': &Bar | 1267 | 272..278 'self.0': i32 |
1280 | 365..371 'self.0': i32 | 1268 | 376..380 'self': &Bar |
1281 | 465..469 'self': &Bar | 1269 | 390..423 '{ ... }': &Foo |
1282 | 479..512 '{ ... }': &Foo | 1270 | 404..413 '&Foo('z')': &Foo |
1283 | 493..502 '&Foo('z')': &Foo | 1271 | 405..408 'Foo': Foo(char) -> Foo |
1284 | 494..497 'Foo': Foo(char) -> Foo | 1272 | 405..413 'Foo('z')': Foo |
1285 | 494..502 'Foo('z')': Foo | 1273 | 409..412 ''z'': char |
1286 | 498..501 ''z'': char | 1274 | 453..506 '{ ... }': () |
1287 | 542..595 '{ ... }': () | 1275 | 467..468 'x': char |
1288 | 556..557 'x': char | 1276 | 471..489 'super:...r::new': fn new() -> Bar |
1289 | 560..578 'super:...r::new': fn new() -> Bar | 1277 | 471..491 'super:...:new()': Bar |
1290 | 560..580 'super:...:new()': Bar | 1278 | 471..499 'super:...ango()': char |
1291 | 560..588 'super:...ango()': char | ||
1292 | "#]], | 1279 | "#]], |
1293 | ) | 1280 | ) |
1294 | } | 1281 | } |
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs index 7d00cee9b..5adbe9c45 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() { |
@@ -518,47 +518,24 @@ fn infer_generics_in_patterns() { | |||
518 | 518 | ||
519 | #[test] | 519 | #[test] |
520 | fn infer_const_pattern() { | 520 | fn infer_const_pattern() { |
521 | check_infer_with_mismatches( | 521 | check_mismatches( |
522 | r#" | 522 | r#" |
523 | enum Option<T> { None } | 523 | enum Option<T> { None } |
524 | use Option::None; | 524 | use Option::None; |
525 | struct Foo; | 525 | struct Foo; |
526 | const Bar: usize = 1; | 526 | const Bar: usize = 1; |
527 | 527 | ||
528 | fn test() { | 528 | fn test() { |
529 | let a: Option<u32> = None; | 529 | let a: Option<u32> = None; |
530 | let b: Option<i64> = match a { | 530 | let b: Option<i64> = match a { |
531 | None => None, | 531 | None => None, |
532 | }; | 532 | }; |
533 | let _: () = match () { Foo => Foo }; // Expected mismatch | 533 | let _: () = match () { Foo => () }; |
534 | let _: () = match () { Bar => Bar }; // Expected mismatch | 534 | // ^^^ expected (), got Foo |
535 | } | 535 | let _: () = match () { Bar => () }; |
536 | // ^^^ expected (), got usize | ||
537 | } | ||
536 | "#, | 538 | "#, |
537 | expect![[r#" | ||
538 | 73..74 '1': usize | ||
539 | 87..309 '{ ...atch }': () | ||
540 | 97..98 'a': Option<u32> | ||
541 | 114..118 'None': Option<u32> | ||
542 | 128..129 'b': Option<i64> | ||
543 | 145..182 'match ... }': Option<i64> | ||
544 | 151..152 'a': Option<u32> | ||
545 | 163..167 'None': Option<u32> | ||
546 | 171..175 'None': Option<i64> | ||
547 | 192..193 '_': () | ||
548 | 200..223 'match ... Foo }': Foo | ||
549 | 206..208 '()': () | ||
550 | 211..214 'Foo': Foo | ||
551 | 218..221 'Foo': Foo | ||
552 | 254..255 '_': () | ||
553 | 262..285 'match ... Bar }': usize | ||
554 | 268..270 '()': () | ||
555 | 273..276 'Bar': usize | ||
556 | 280..283 'Bar': usize | ||
557 | 200..223: expected (), got Foo | ||
558 | 211..214: expected (), got Foo | ||
559 | 262..285: expected (), got usize | ||
560 | 273..276: expected (), got usize | ||
561 | "#]], | ||
562 | ); | 539 | ); |
563 | } | 540 | } |
564 | 541 | ||
@@ -594,48 +571,44 @@ fn main() { | |||
594 | fn match_ergonomics_in_closure_params() { | 571 | fn match_ergonomics_in_closure_params() { |
595 | check_infer( | 572 | check_infer( |
596 | r#" | 573 | r#" |
597 | #[lang = "fn_once"] | 574 | //- minicore: fn |
598 | trait FnOnce<Args> { | 575 | fn foo<T, U, F: FnOnce(T) -> U>(t: T, f: F) -> U { loop {} } |
599 | type Output; | ||
600 | } | ||
601 | |||
602 | fn foo<T, U, F: FnOnce(T) -> U>(t: T, f: F) -> U { loop {} } | ||
603 | 576 | ||
604 | fn test() { | 577 | fn test() { |
605 | foo(&(1, "a"), |&(x, y)| x); // normal, no match ergonomics | 578 | foo(&(1, "a"), |&(x, y)| x); // normal, no match ergonomics |
606 | foo(&(1, "a"), |(x, y)| x); | 579 | foo(&(1, "a"), |(x, y)| x); |
607 | } | 580 | } |
608 | "#, | 581 | "#, |
609 | expect![[r#" | 582 | expect![[r#" |
610 | 93..94 't': T | 583 | 32..33 't': T |
611 | 99..100 'f': F | 584 | 38..39 'f': F |
612 | 110..121 '{ loop {} }': U | 585 | 49..60 '{ loop {} }': U |
613 | 112..119 'loop {}': ! | 586 | 51..58 'loop {}': ! |
614 | 117..119 '{}': () | 587 | 56..58 '{}': () |
615 | 133..232 '{ ... x); }': () | 588 | 72..171 '{ ... x); }': () |
616 | 139..142 'foo': fn foo<&(i32, &str), i32, |&(i32, &str)| -> i32>(&(i32, &str), |&(i32, &str)| -> i32) -> i32 | 589 | 78..81 'foo': fn foo<&(i32, &str), i32, |&(i32, &str)| -> i32>(&(i32, &str), |&(i32, &str)| -> i32) -> i32 |
617 | 139..166 'foo(&(...y)| x)': i32 | 590 | 78..105 'foo(&(...y)| x)': i32 |
618 | 143..152 '&(1, "a")': &(i32, &str) | 591 | 82..91 '&(1, "a")': &(i32, &str) |
619 | 144..152 '(1, "a")': (i32, &str) | 592 | 83..91 '(1, "a")': (i32, &str) |
620 | 145..146 '1': i32 | 593 | 84..85 '1': i32 |
621 | 148..151 '"a"': &str | 594 | 87..90 '"a"': &str |
622 | 154..165 '|&(x, y)| x': |&(i32, &str)| -> i32 | 595 | 93..104 '|&(x, y)| x': |&(i32, &str)| -> i32 |
623 | 155..162 '&(x, y)': &(i32, &str) | 596 | 94..101 '&(x, y)': &(i32, &str) |
624 | 156..162 '(x, y)': (i32, &str) | 597 | 95..101 '(x, y)': (i32, &str) |
625 | 157..158 'x': i32 | 598 | 96..97 'x': i32 |
626 | 160..161 'y': &str | 599 | 99..100 'y': &str |
627 | 164..165 'x': i32 | 600 | 103..104 'x': i32 |
628 | 203..206 'foo': fn foo<&(i32, &str), &i32, |&(i32, &str)| -> &i32>(&(i32, &str), |&(i32, &str)| -> &i32) -> &i32 | 601 | 142..145 'foo': fn foo<&(i32, &str), &i32, |&(i32, &str)| -> &i32>(&(i32, &str), |&(i32, &str)| -> &i32) -> &i32 |
629 | 203..229 'foo(&(...y)| x)': &i32 | 602 | 142..168 'foo(&(...y)| x)': &i32 |
630 | 207..216 '&(1, "a")': &(i32, &str) | 603 | 146..155 '&(1, "a")': &(i32, &str) |
631 | 208..216 '(1, "a")': (i32, &str) | 604 | 147..155 '(1, "a")': (i32, &str) |
632 | 209..210 '1': i32 | 605 | 148..149 '1': i32 |
633 | 212..215 '"a"': &str | 606 | 151..154 '"a"': &str |
634 | 218..228 '|(x, y)| x': |&(i32, &str)| -> &i32 | 607 | 157..167 '|(x, y)| x': |&(i32, &str)| -> &i32 |
635 | 219..225 '(x, y)': (i32, &str) | 608 | 158..164 '(x, y)': (i32, &str) |
636 | 220..221 'x': &i32 | 609 | 159..160 'x': &i32 |
637 | 223..224 'y': &&str | 610 | 162..163 'y': &&str |
638 | 227..228 'x': &i32 | 611 | 166..167 'x': &i32 |
639 | "#]], | 612 | "#]], |
640 | ); | 613 | ); |
641 | } | 614 | } |
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs index 1019e783b..1edec1615 100644 --- a/crates/hir_ty/src/tests/regression.rs +++ b/crates/hir_ty/src/tests/regression.rs | |||
@@ -884,41 +884,37 @@ fn issue_4966() { | |||
884 | fn issue_6628() { | 884 | fn issue_6628() { |
885 | check_infer( | 885 | check_infer( |
886 | r#" | 886 | r#" |
887 | #[lang = "fn_once"] | 887 | //- minicore: fn |
888 | pub trait FnOnce<Args> { | 888 | struct S<T>(); |
889 | type Output; | 889 | impl<T> S<T> { |
890 | } | 890 | fn f(&self, _t: T) {} |
891 | 891 | fn g<F: FnOnce(&T)>(&self, _f: F) {} | |
892 | struct S<T>(); | 892 | } |
893 | impl<T> S<T> { | 893 | fn main() { |
894 | fn f(&self, _t: T) {} | 894 | let s = S(); |
895 | fn g<F: FnOnce(&T)>(&self, _f: F) {} | 895 | s.g(|_x| {}); |
896 | } | 896 | s.f(10); |
897 | fn main() { | 897 | } |
898 | let s = S(); | 898 | "#, |
899 | s.g(|_x| {}); | ||
900 | s.f(10); | ||
901 | } | ||
902 | "#, | ||
903 | expect![[r#" | 899 | expect![[r#" |
904 | 105..109 'self': &S<T> | 900 | 40..44 'self': &S<T> |
905 | 111..113 '_t': T | 901 | 46..48 '_t': T |
906 | 118..120 '{}': () | 902 | 53..55 '{}': () |
907 | 146..150 'self': &S<T> | 903 | 81..85 'self': &S<T> |
908 | 152..154 '_f': F | 904 | 87..89 '_f': F |
909 | 159..161 '{}': () | 905 | 94..96 '{}': () |
910 | 174..225 '{ ...10); }': () | 906 | 109..160 '{ ...10); }': () |
911 | 184..185 's': S<i32> | 907 | 119..120 's': S<i32> |
912 | 188..189 'S': S<i32>() -> S<i32> | 908 | 123..124 'S': S<i32>() -> S<i32> |
913 | 188..191 'S()': S<i32> | 909 | 123..126 'S()': S<i32> |
914 | 197..198 's': S<i32> | 910 | 132..133 's': S<i32> |
915 | 197..209 's.g(|_x| {})': () | 911 | 132..144 's.g(|_x| {})': () |
916 | 201..208 '|_x| {}': |&i32| -> () | 912 | 136..143 '|_x| {}': |&i32| -> () |
917 | 202..204 '_x': &i32 | 913 | 137..139 '_x': &i32 |
918 | 206..208 '{}': () | 914 | 141..143 '{}': () |
919 | 215..216 's': S<i32> | 915 | 150..151 's': S<i32> |
920 | 215..222 's.f(10)': () | 916 | 150..157 's.f(10)': () |
921 | 219..221 '10': i32 | 917 | 154..156 '10': i32 |
922 | "#]], | 918 | "#]], |
923 | ); | 919 | ); |
924 | } | 920 | } |
@@ -927,35 +923,33 @@ fn issue_6628() { | |||
927 | fn issue_6852() { | 923 | fn issue_6852() { |
928 | check_infer( | 924 | check_infer( |
929 | r#" | 925 | r#" |
930 | #[lang = "deref"] | 926 | //- minicore: deref |
931 | pub trait Deref { | 927 | use core::ops::Deref; |
932 | type Target; | ||
933 | } | ||
934 | 928 | ||
935 | struct BufWriter {} | 929 | struct BufWriter {} |
936 | 930 | ||
937 | struct Mutex<T> {} | 931 | struct Mutex<T> {} |
938 | struct MutexGuard<'a, T> {} | 932 | struct MutexGuard<'a, T> {} |
939 | impl<T> Mutex<T> { | 933 | impl<T> Mutex<T> { |
940 | fn lock(&self) -> MutexGuard<'_, T> {} | 934 | fn lock(&self) -> MutexGuard<'_, T> {} |
941 | } | 935 | } |
942 | impl<'a, T: 'a> Deref for MutexGuard<'a, T> { | 936 | impl<'a, T: 'a> Deref for MutexGuard<'a, T> { |
943 | type Target = T; | 937 | type Target = T; |
944 | } | 938 | } |
945 | fn flush(&self) { | 939 | fn flush(&self) { |
946 | let w: &Mutex<BufWriter>; | 940 | let w: &Mutex<BufWriter>; |
947 | *(w.lock()); | 941 | *(w.lock()); |
948 | } | 942 | } |
949 | "#, | 943 | "#, |
950 | expect![[r#" | 944 | expect![[r#" |
951 | 156..160 'self': &Mutex<T> | 945 | 123..127 'self': &Mutex<T> |
952 | 183..185 '{}': () | 946 | 150..152 '{}': () |
953 | 267..271 'self': &{unknown} | 947 | 234..238 'self': &{unknown} |
954 | 273..323 '{ ...()); }': () | 948 | 240..290 '{ ...()); }': () |
955 | 283..284 'w': &Mutex<BufWriter> | 949 | 250..251 'w': &Mutex<BufWriter> |
956 | 309..320 '*(w.lock())': BufWriter | 950 | 276..287 '*(w.lock())': BufWriter |
957 | 311..312 'w': &Mutex<BufWriter> | 951 | 278..279 'w': &Mutex<BufWriter> |
958 | 311..319 'w.lock()': MutexGuard<BufWriter> | 952 | 278..286 'w.lock()': MutexGuard<BufWriter> |
959 | "#]], | 953 | "#]], |
960 | ); | 954 | ); |
961 | } | 955 | } |
@@ -977,37 +971,33 @@ fn param_overrides_fn() { | |||
977 | fn lifetime_from_chalk_during_deref() { | 971 | fn lifetime_from_chalk_during_deref() { |
978 | check_types( | 972 | check_types( |
979 | r#" | 973 | r#" |
980 | #[lang = "deref"] | 974 | //- minicore: deref |
981 | pub trait Deref { | 975 | struct Box<T: ?Sized> {} |
982 | type Target; | 976 | impl<T> core::ops::Deref for Box<T> { |
983 | } | 977 | type Target = T; |
984 | |||
985 | struct Box<T: ?Sized> {} | ||
986 | impl<T> Deref for Box<T> { | ||
987 | type Target = T; | ||
988 | 978 | ||
989 | fn deref(&self) -> &Self::Target { | 979 | fn deref(&self) -> &Self::Target { |
990 | loop {} | 980 | loop {} |
991 | } | 981 | } |
992 | } | 982 | } |
993 | 983 | ||
994 | trait Iterator { | 984 | trait Iterator { |
995 | type Item; | 985 | type Item; |
996 | } | 986 | } |
997 | 987 | ||
998 | pub struct Iter<'a, T: 'a> { | 988 | pub struct Iter<'a, T: 'a> { |
999 | inner: Box<dyn IterTrait<'a, T, Item = &'a T> + 'a>, | 989 | inner: Box<dyn IterTrait<'a, T, Item = &'a T> + 'a>, |
1000 | } | 990 | } |
1001 | 991 | ||
1002 | trait IterTrait<'a, T: 'a>: Iterator<Item = &'a T> { | 992 | trait IterTrait<'a, T: 'a>: Iterator<Item = &'a T> { |
1003 | fn clone_box(&self); | 993 | fn clone_box(&self); |
1004 | } | 994 | } |
1005 | 995 | ||
1006 | fn clone_iter<T>(s: Iter<T>) { | 996 | fn clone_iter<T>(s: Iter<T>) { |
1007 | s.inner.clone_box(); | 997 | s.inner.clone_box(); |
1008 | //^^^^^^^^^^^^^^^^^^^ () | 998 | //^^^^^^^^^^^^^^^^^^^ () |
1009 | } | 999 | } |
1010 | "#, | 1000 | "#, |
1011 | ) | 1001 | ) |
1012 | } | 1002 | } |
1013 | 1003 | ||
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs index 3418ed21e..68776f3c0 100644 --- a/crates/hir_ty/src/tests/simple.rs +++ b/crates/hir_ty/src/tests/simple.rs | |||
@@ -113,7 +113,7 @@ fn type_alias_in_struct_lit() { | |||
113 | fn infer_ranges() { | 113 | fn infer_ranges() { |
114 | check_types( | 114 | check_types( |
115 | r#" | 115 | r#" |
116 | //- /main.rs crate:main deps:core | 116 | //- minicore: range |
117 | fn test() { | 117 | fn test() { |
118 | let a = ..; | 118 | let a = ..; |
119 | let b = 1..; | 119 | let b = 1..; |
@@ -125,32 +125,6 @@ fn test() { | |||
125 | let t = (a, b, c, d, e, f); | 125 | let t = (a, b, c, d, e, f); |
126 | t; | 126 | t; |
127 | } //^ (RangeFull, RangeFrom<i32>, RangeTo<u32>, Range<usize>, RangeToInclusive<i32>, RangeInclusive<char>) | 127 | } //^ (RangeFull, RangeFrom<i32>, RangeTo<u32>, Range<usize>, RangeToInclusive<i32>, RangeInclusive<char>) |
128 | |||
129 | //- /core.rs crate:core | ||
130 | #[prelude_import] use prelude::*; | ||
131 | mod prelude {} | ||
132 | |||
133 | pub mod ops { | ||
134 | pub struct Range<Idx> { | ||
135 | pub start: Idx, | ||
136 | pub end: Idx, | ||
137 | } | ||
138 | pub struct RangeFrom<Idx> { | ||
139 | pub start: Idx, | ||
140 | } | ||
141 | struct RangeFull; | ||
142 | pub struct RangeInclusive<Idx> { | ||
143 | start: Idx, | ||
144 | end: Idx, | ||
145 | is_empty: u8, | ||
146 | } | ||
147 | pub struct RangeTo<Idx> { | ||
148 | pub end: Idx, | ||
149 | } | ||
150 | pub struct RangeToInclusive<Idx> { | ||
151 | pub end: Idx, | ||
152 | } | ||
153 | } | ||
154 | "#, | 128 | "#, |
155 | ); | 129 | ); |
156 | } | 130 | } |
@@ -175,16 +149,17 @@ fn test() { | |||
175 | fn infer_basics() { | 149 | fn infer_basics() { |
176 | check_infer( | 150 | check_infer( |
177 | r#" | 151 | r#" |
178 | fn test(a: u32, b: isize, c: !, d: &str) { | 152 | fn test(a: u32, b: isize, c: !, d: &str) { |
179 | a; | 153 | a; |
180 | b; | 154 | b; |
181 | c; | 155 | c; |
182 | d; | 156 | d; |
183 | 1usize; | 157 | 1usize; |
184 | 1isize; | 158 | 1isize; |
185 | "test"; | 159 | "test"; |
186 | 1.0f32; | 160 | 1.0f32; |
187 | }"#, | 161 | } |
162 | "#, | ||
188 | expect![[r#" | 163 | expect![[r#" |
189 | 8..9 'a': u32 | 164 | 8..9 'a': u32 |
190 | 16..17 'b': isize | 165 | 16..17 'b': isize |
@@ -207,15 +182,15 @@ fn infer_basics() { | |||
207 | fn infer_let() { | 182 | fn infer_let() { |
208 | check_infer( | 183 | check_infer( |
209 | r#" | 184 | r#" |
210 | fn test() { | 185 | fn test() { |
211 | let a = 1isize; | 186 | let a = 1isize; |
212 | let b: usize = 1; | 187 | let b: usize = 1; |
213 | let c = b; | 188 | let c = b; |
214 | let d: u32; | 189 | let d: u32; |
215 | let e; | 190 | let e; |
216 | let f: i32 = e; | 191 | let f: i32 = e; |
217 | } | 192 | } |
218 | "#, | 193 | "#, |
219 | expect![[r#" | 194 | expect![[r#" |
220 | 10..117 '{ ...= e; }': () | 195 | 10..117 '{ ...= e; }': () |
221 | 20..21 'a': isize | 196 | 20..21 'a': isize |
@@ -236,17 +211,17 @@ fn infer_let() { | |||
236 | fn infer_paths() { | 211 | fn infer_paths() { |
237 | check_infer( | 212 | check_infer( |
238 | r#" | 213 | r#" |
239 | fn a() -> u32 { 1 } | 214 | fn a() -> u32 { 1 } |
240 | 215 | ||
241 | mod b { | 216 | mod b { |
242 | fn c() -> u32 { 1 } | 217 | fn c() -> u32 { 1 } |
243 | } | 218 | } |
244 | 219 | ||
245 | fn test() { | 220 | fn test() { |
246 | a(); | 221 | a(); |
247 | b::c(); | 222 | b::c(); |
248 | } | 223 | } |
249 | "#, | 224 | "#, |
250 | expect![[r#" | 225 | expect![[r#" |
251 | 14..19 '{ 1 }': u32 | 226 | 14..19 '{ 1 }': u32 |
252 | 16..17 '1': u32 | 227 | 16..17 '1': u32 |
@@ -265,17 +240,17 @@ fn infer_paths() { | |||
265 | fn infer_path_type() { | 240 | fn infer_path_type() { |
266 | check_infer( | 241 | check_infer( |
267 | r#" | 242 | r#" |
268 | struct S; | 243 | struct S; |
269 | 244 | ||
270 | impl S { | 245 | impl S { |
271 | fn foo() -> i32 { 1 } | 246 | fn foo() -> i32 { 1 } |
272 | } | 247 | } |
273 | 248 | ||
274 | fn test() { | 249 | fn test() { |
275 | S::foo(); | 250 | S::foo(); |
276 | <S>::foo(); | 251 | <S>::foo(); |
277 | } | 252 | } |
278 | "#, | 253 | "#, |
279 | expect![[r#" | 254 | expect![[r#" |
280 | 40..45 '{ 1 }': i32 | 255 | 40..45 '{ 1 }': i32 |
281 | 42..43 '1': i32 | 256 | 42..43 '1': i32 |
@@ -292,21 +267,21 @@ fn infer_path_type() { | |||
292 | fn infer_struct() { | 267 | fn infer_struct() { |
293 | check_infer( | 268 | check_infer( |
294 | r#" | 269 | r#" |
295 | struct A { | 270 | struct A { |
296 | b: B, | 271 | b: B, |
297 | c: C, | 272 | c: C, |
298 | } | 273 | } |
299 | struct B; | 274 | struct B; |
300 | struct C(usize); | 275 | struct C(usize); |
301 | 276 | ||
302 | fn test() { | 277 | fn test() { |
303 | let c = C(1); | 278 | let c = C(1); |
304 | B; | 279 | B; |
305 | let a: A = A { b: B, c: C(1) }; | 280 | let a: A = A { b: B, c: C(1) }; |
306 | a.b; | 281 | a.b; |
307 | a.c; | 282 | a.c; |
308 | } | 283 | } |
309 | "#, | 284 | "#, |
310 | expect![[r#" | 285 | expect![[r#" |
311 | 71..153 '{ ...a.c; }': () | 286 | 71..153 '{ ...a.c; }': () |
312 | 81..82 'c': C | 287 | 81..82 'c': C |
@@ -332,14 +307,15 @@ fn infer_struct() { | |||
332 | fn infer_enum() { | 307 | fn infer_enum() { |
333 | check_infer( | 308 | check_infer( |
334 | r#" | 309 | r#" |
335 | enum E { | 310 | enum E { |
336 | V1 { field: u32 }, | 311 | V1 { field: u32 }, |
337 | V2 | 312 | V2 |
338 | } | 313 | } |
339 | fn test() { | 314 | fn test() { |
340 | E::V1 { field: 1 }; | 315 | E::V1 { field: 1 }; |
341 | E::V2; | 316 | E::V2; |
342 | }"#, | 317 | } |
318 | "#, | ||
343 | expect![[r#" | 319 | expect![[r#" |
344 | 51..89 '{ ...:V2; }': () | 320 | 51..89 '{ ...:V2; }': () |
345 | 57..75 'E::V1 ...d: 1 }': E | 321 | 57..75 'E::V1 ...d: 1 }': E |
@@ -353,23 +329,23 @@ fn infer_enum() { | |||
353 | fn infer_union() { | 329 | fn infer_union() { |
354 | check_infer( | 330 | check_infer( |
355 | r#" | 331 | r#" |
356 | union MyUnion { | 332 | union MyUnion { |
357 | foo: u32, | 333 | foo: u32, |
358 | bar: f32, | 334 | bar: f32, |
359 | } | 335 | } |
360 | 336 | ||
361 | fn test() { | 337 | fn test() { |
362 | let u = MyUnion { foo: 0 }; | 338 | let u = MyUnion { foo: 0 }; |
363 | unsafe { baz(u); } | 339 | unsafe { baz(u); } |
364 | let u = MyUnion { bar: 0.0 }; | 340 | let u = MyUnion { bar: 0.0 }; |
365 | unsafe { baz(u); } | 341 | unsafe { baz(u); } |
366 | } | 342 | } |
367 | 343 | ||
368 | unsafe fn baz(u: MyUnion) { | 344 | unsafe fn baz(u: MyUnion) { |
369 | let inner = u.foo; | 345 | let inner = u.foo; |
370 | let inner = u.bar; | 346 | let inner = u.bar; |
371 | } | 347 | } |
372 | "#, | 348 | "#, |
373 | expect![[r#" | 349 | expect![[r#" |
374 | 57..172 '{ ...); } }': () | 350 | 57..172 '{ ...); } }': () |
375 | 67..68 'u': MyUnion | 351 | 67..68 'u': MyUnion |
@@ -404,19 +380,19 @@ fn infer_union() { | |||
404 | fn infer_refs() { | 380 | fn infer_refs() { |
405 | check_infer( | 381 | check_infer( |
406 | r#" | 382 | r#" |
407 | fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) { | 383 | fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) { |
408 | a; | 384 | a; |
409 | *a; | 385 | *a; |
410 | &a; | 386 | &a; |
411 | &mut a; | 387 | &mut a; |
412 | b; | 388 | b; |
413 | *b; | 389 | *b; |
414 | &b; | 390 | &b; |
415 | c; | 391 | c; |
416 | *c; | 392 | *c; |
417 | d; | 393 | d; |
418 | *d; | 394 | *d; |
419 | } | 395 | } |
420 | "#, | 396 | "#, |
421 | expect![[r#" | 397 | expect![[r#" |
422 | 8..9 'a': &u32 | 398 | 8..9 'a': &u32 |
@@ -450,11 +426,11 @@ fn infer_refs() { | |||
450 | fn infer_raw_ref() { | 426 | fn infer_raw_ref() { |
451 | check_infer( | 427 | check_infer( |
452 | r#" | 428 | r#" |
453 | fn test(a: i32) { | 429 | fn test(a: i32) { |
454 | &raw mut a; | 430 | &raw mut a; |
455 | &raw const a; | 431 | &raw const a; |
456 | } | 432 | } |
457 | "#, | 433 | "#, |
458 | expect![[r#" | 434 | expect![[r#" |
459 | 8..9 'a': i32 | 435 | 8..9 'a': i32 |
460 | 16..53 '{ ...t a; }': () | 436 | 16..53 '{ ...t a; }': () |
@@ -524,26 +500,26 @@ h"; | |||
524 | fn infer_unary_op() { | 500 | fn infer_unary_op() { |
525 | check_infer( | 501 | check_infer( |
526 | r#" | 502 | r#" |
527 | enum SomeType {} | 503 | enum SomeType {} |
528 | 504 | ||
529 | fn test(x: SomeType) { | 505 | fn test(x: SomeType) { |
530 | let b = false; | 506 | let b = false; |
531 | let c = !b; | 507 | let c = !b; |
532 | let a = 100; | 508 | let a = 100; |
533 | let d: i128 = -a; | 509 | let d: i128 = -a; |
534 | let e = -100; | 510 | let e = -100; |
535 | let f = !!!true; | 511 | let f = !!!true; |
536 | let g = !42; | 512 | let g = !42; |
537 | let h = !10u32; | 513 | let h = !10u32; |
538 | let j = !a; | 514 | let j = !a; |
539 | -3.14; | 515 | -3.14; |
540 | !3; | 516 | !3; |
541 | -x; | 517 | -x; |
542 | !x; | 518 | !x; |
543 | -"hello"; | 519 | -"hello"; |
544 | !"hello"; | 520 | !"hello"; |
545 | } | 521 | } |
546 | "#, | 522 | "#, |
547 | expect![[r#" | 523 | expect![[r#" |
548 | 26..27 'x': SomeType | 524 | 26..27 'x': SomeType |
549 | 39..271 '{ ...lo"; }': () | 525 | 39..271 '{ ...lo"; }': () |
@@ -594,19 +570,19 @@ fn infer_unary_op() { | |||
594 | fn infer_backwards() { | 570 | fn infer_backwards() { |
595 | check_infer( | 571 | check_infer( |
596 | r#" | 572 | r#" |
597 | fn takes_u32(x: u32) {} | 573 | fn takes_u32(x: u32) {} |
598 | 574 | ||
599 | struct S { i32_field: i32 } | 575 | struct S { i32_field: i32 } |
600 | 576 | ||
601 | fn test() -> &mut &f64 { | 577 | fn test() -> &mut &f64 { |
602 | let a = unknown_function(); | 578 | let a = unknown_function(); |
603 | takes_u32(a); | 579 | takes_u32(a); |
604 | let b = unknown_function(); | 580 | let b = unknown_function(); |
605 | S { i32_field: b }; | 581 | S { i32_field: b }; |
606 | let c = unknown_function(); | 582 | let c = unknown_function(); |
607 | &mut &c | 583 | &mut &c |
608 | } | 584 | } |
609 | "#, | 585 | "#, |
610 | expect![[r#" | 586 | expect![[r#" |
611 | 13..14 'x': u32 | 587 | 13..14 'x': u32 |
612 | 21..23 '{}': () | 588 | 21..23 '{}': () |
@@ -636,23 +612,23 @@ fn infer_backwards() { | |||
636 | fn infer_self() { | 612 | fn infer_self() { |
637 | check_infer( | 613 | check_infer( |
638 | r#" | 614 | r#" |
639 | struct S; | 615 | struct S; |
640 | 616 | ||
641 | impl S { | 617 | impl S { |
642 | fn test(&self) { | 618 | fn test(&self) { |
643 | self; | 619 | self; |
644 | } | 620 | } |
645 | fn test2(self: &Self) { | 621 | fn test2(self: &Self) { |
646 | self; | 622 | self; |
647 | } | 623 | } |
648 | fn test3() -> Self { | 624 | fn test3() -> Self { |
649 | S {} | 625 | S {} |
650 | } | 626 | } |
651 | fn test4() -> Self { | 627 | fn test4() -> Self { |
652 | Self {} | 628 | Self {} |
653 | } | 629 | } |
654 | } | 630 | } |
655 | "#, | 631 | "#, |
656 | expect![[r#" | 632 | expect![[r#" |
657 | 33..37 'self': &S | 633 | 33..37 'self': &S |
658 | 39..60 '{ ... }': () | 634 | 39..60 '{ ... }': () |
@@ -672,30 +648,30 @@ fn infer_self() { | |||
672 | fn infer_self_as_path() { | 648 | fn infer_self_as_path() { |
673 | check_infer( | 649 | check_infer( |
674 | r#" | 650 | r#" |
675 | struct S1; | 651 | struct S1; |
676 | struct S2(isize); | 652 | struct S2(isize); |
677 | enum E { | 653 | enum E { |
678 | V1, | 654 | V1, |
679 | V2(u32), | 655 | V2(u32), |
680 | } | 656 | } |
681 | 657 | ||
682 | impl S1 { | 658 | impl S1 { |
683 | fn test() { | 659 | fn test() { |
684 | Self; | 660 | Self; |
685 | } | 661 | } |
686 | } | 662 | } |
687 | impl S2 { | 663 | impl S2 { |
688 | fn test() { | 664 | fn test() { |
689 | Self(1); | 665 | Self(1); |
690 | } | 666 | } |
691 | } | 667 | } |
692 | impl E { | 668 | impl E { |
693 | fn test() { | 669 | fn test() { |
694 | Self::V1; | 670 | Self::V1; |
695 | Self::V2(1); | 671 | Self::V2(1); |
696 | } | 672 | } |
697 | } | 673 | } |
698 | "#, | 674 | "#, |
699 | expect![[r#" | 675 | expect![[r#" |
700 | 86..107 '{ ... }': () | 676 | 86..107 '{ ... }': () |
701 | 96..100 'Self': S1 | 677 | 96..100 'Self': S1 |
@@ -716,26 +692,26 @@ fn infer_self_as_path() { | |||
716 | fn infer_binary_op() { | 692 | fn infer_binary_op() { |
717 | check_infer( | 693 | check_infer( |
718 | r#" | 694 | r#" |
719 | fn f(x: bool) -> i32 { | 695 | fn f(x: bool) -> i32 { |
720 | 0i32 | 696 | 0i32 |
721 | } | 697 | } |
722 | 698 | ||
723 | fn test() -> bool { | 699 | fn test() -> bool { |
724 | let x = a && b; | 700 | let x = a && b; |
725 | let y = true || false; | 701 | let y = true || false; |
726 | let z = x == y; | 702 | let z = x == y; |
727 | let t = x != y; | 703 | let t = x != y; |
728 | let minus_forty: isize = -40isize; | 704 | let minus_forty: isize = -40isize; |
729 | let h = minus_forty <= CONST_2; | 705 | let h = minus_forty <= CONST_2; |
730 | let c = f(z || y) + 5; | 706 | let c = f(z || y) + 5; |
731 | let d = b; | 707 | let d = b; |
732 | let g = minus_forty ^= i; | 708 | let g = minus_forty ^= i; |
733 | let ten: usize = 10; | 709 | let ten: usize = 10; |
734 | let ten_is_eleven = ten == some_num; | 710 | let ten_is_eleven = ten == some_num; |
735 | 711 | ||
736 | ten < 3 | 712 | ten < 3 |
737 | } | 713 | } |
738 | "#, | 714 | "#, |
739 | expect![[r#" | 715 | expect![[r#" |
740 | 5..6 'x': bool | 716 | 5..6 'x': bool |
741 | 21..33 '{ 0i32 }': i32 | 717 | 21..33 '{ 0i32 }': i32 |
@@ -795,11 +771,11 @@ fn infer_binary_op() { | |||
795 | fn infer_shift_op() { | 771 | fn infer_shift_op() { |
796 | check_infer( | 772 | check_infer( |
797 | r#" | 773 | r#" |
798 | fn test() { | 774 | fn test() { |
799 | 1u32 << 5u8; | 775 | 1u32 << 5u8; |
800 | 1u32 >> 5u8; | 776 | 1u32 >> 5u8; |
801 | } | 777 | } |
802 | "#, | 778 | "#, |
803 | expect![[r#" | 779 | expect![[r#" |
804 | 10..47 '{ ...5u8; }': () | 780 | 10..47 '{ ...5u8; }': () |
805 | 16..20 '1u32': u32 | 781 | 16..20 '1u32': u32 |
@@ -816,29 +792,29 @@ fn infer_shift_op() { | |||
816 | fn infer_field_autoderef() { | 792 | fn infer_field_autoderef() { |
817 | check_infer( | 793 | check_infer( |
818 | r#" | 794 | r#" |
819 | struct A { | 795 | struct A { |
820 | b: B, | 796 | b: B, |
821 | } | 797 | } |
822 | struct B; | 798 | struct B; |
823 | |||
824 | fn test1(a: A) { | ||
825 | let a1 = a; | ||
826 | a1.b; | ||
827 | let a2 = &a; | ||
828 | a2.b; | ||
829 | let a3 = &mut a; | ||
830 | a3.b; | ||
831 | let a4 = &&&&&&&a; | ||
832 | a4.b; | ||
833 | let a5 = &mut &&mut &&mut a; | ||
834 | a5.b; | ||
835 | } | ||
836 | 799 | ||
837 | fn test2(a1: *const A, a2: *mut A) { | 800 | fn test1(a: A) { |
838 | a1.b; | 801 | let a1 = a; |
839 | a2.b; | 802 | a1.b; |
840 | } | 803 | let a2 = &a; |
841 | "#, | 804 | a2.b; |
805 | let a3 = &mut a; | ||
806 | a3.b; | ||
807 | let a4 = &&&&&&&a; | ||
808 | a4.b; | ||
809 | let a5 = &mut &&mut &&mut a; | ||
810 | a5.b; | ||
811 | } | ||
812 | |||
813 | fn test2(a1: *const A, a2: *mut A) { | ||
814 | a1.b; | ||
815 | a2.b; | ||
816 | } | ||
817 | "#, | ||
842 | expect![[r#" | 818 | expect![[r#" |
843 | 43..44 'a': A | 819 | 43..44 'a': A |
844 | 49..212 '{ ...5.b; }': () | 820 | 49..212 '{ ...5.b; }': () |
@@ -891,58 +867,53 @@ fn infer_field_autoderef() { | |||
891 | fn infer_argument_autoderef() { | 867 | fn infer_argument_autoderef() { |
892 | check_infer( | 868 | check_infer( |
893 | r#" | 869 | r#" |
894 | #[lang = "deref"] | 870 | //- minicore: deref |
895 | pub trait Deref { | 871 | use core::ops::Deref; |
896 | type Target; | 872 | struct A<T>(T); |
897 | fn deref(&self) -> &Self::Target; | ||
898 | } | ||
899 | |||
900 | struct A<T>(T); | ||
901 | 873 | ||
902 | impl<T> A<T> { | 874 | impl<T> A<T> { |
903 | fn foo(&self) -> &T { | 875 | fn foo(&self) -> &T { |
904 | &self.0 | 876 | &self.0 |
905 | } | 877 | } |
906 | } | 878 | } |
907 | 879 | ||
908 | struct B<T>(T); | 880 | struct B<T>(T); |
909 | 881 | ||
910 | impl<T> Deref for B<T> { | 882 | impl<T> Deref for B<T> { |
911 | type Target = T; | 883 | type Target = T; |
912 | fn deref(&self) -> &Self::Target { | 884 | fn deref(&self) -> &Self::Target { |
913 | &self.0 | 885 | &self.0 |
914 | } | 886 | } |
915 | } | 887 | } |
916 | 888 | ||
917 | fn test() { | 889 | fn test() { |
918 | let t = A::foo(&&B(B(A(42)))); | 890 | let t = A::foo(&&B(B(A(42)))); |
919 | } | 891 | } |
920 | "#, | 892 | "#, |
921 | expect![[r#" | 893 | expect![[r#" |
922 | 67..71 'self': &Self | 894 | 66..70 'self': &A<T> |
923 | 138..142 'self': &A<T> | 895 | 78..101 '{ ... }': &T |
924 | 150..173 '{ ... }': &T | 896 | 88..95 '&self.0': &T |
925 | 160..167 '&self.0': &T | 897 | 89..93 'self': &A<T> |
926 | 161..165 'self': &A<T> | 898 | 89..95 'self.0': T |
927 | 161..167 'self.0': T | 899 | 182..186 'self': &B<T> |
928 | 254..258 'self': &B<T> | 900 | 205..228 '{ ... }': &T |
929 | 277..300 '{ ... }': &T | 901 | 215..222 '&self.0': &T |
930 | 287..294 '&self.0': &T | 902 | 216..220 'self': &B<T> |
931 | 288..292 'self': &B<T> | 903 | 216..222 'self.0': T |
932 | 288..294 'self.0': T | 904 | 242..280 '{ ...))); }': () |
933 | 314..352 '{ ...))); }': () | 905 | 252..253 't': &i32 |
934 | 324..325 't': &i32 | 906 | 256..262 'A::foo': fn foo<i32>(&A<i32>) -> &i32 |
935 | 328..334 'A::foo': fn foo<i32>(&A<i32>) -> &i32 | 907 | 256..277 'A::foo...42))))': &i32 |
936 | 328..349 'A::foo...42))))': &i32 | 908 | 263..276 '&&B(B(A(42)))': &&B<B<A<i32>>> |
937 | 335..348 '&&B(B(A(42)))': &&B<B<A<i32>>> | 909 | 264..276 '&B(B(A(42)))': &B<B<A<i32>>> |
938 | 336..348 '&B(B(A(42)))': &B<B<A<i32>>> | 910 | 265..266 'B': B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>> |
939 | 337..338 'B': B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>> | 911 | 265..276 'B(B(A(42)))': B<B<A<i32>>> |
940 | 337..348 'B(B(A(42)))': B<B<A<i32>>> | 912 | 267..268 'B': B<A<i32>>(A<i32>) -> B<A<i32>> |
941 | 339..340 'B': B<A<i32>>(A<i32>) -> B<A<i32>> | 913 | 267..275 'B(A(42))': B<A<i32>> |
942 | 339..347 'B(A(42))': B<A<i32>> | 914 | 269..270 'A': A<i32>(i32) -> A<i32> |
943 | 341..342 'A': A<i32>(i32) -> A<i32> | 915 | 269..274 'A(42)': A<i32> |
944 | 341..346 'A(42)': A<i32> | 916 | 271..273 '42': i32 |
945 | 343..345 '42': i32 | ||
946 | "#]], | 917 | "#]], |
947 | ); | 918 | ); |
948 | } | 919 | } |
@@ -951,62 +922,57 @@ fn infer_argument_autoderef() { | |||
951 | fn infer_method_argument_autoderef() { | 922 | fn infer_method_argument_autoderef() { |
952 | check_infer( | 923 | check_infer( |
953 | r#" | 924 | r#" |
954 | #[lang = "deref"] | 925 | //- minicore: deref |
955 | pub trait Deref { | 926 | use core::ops::Deref; |
956 | type Target; | 927 | struct A<T>(*mut T); |
957 | fn deref(&self) -> &Self::Target; | ||
958 | } | ||
959 | 928 | ||
960 | struct A<T>(*mut T); | 929 | impl<T> A<T> { |
961 | 930 | fn foo(&self, x: &A<T>) -> &T { | |
962 | impl<T> A<T> { | 931 | &*x.0 |
963 | fn foo(&self, x: &A<T>) -> &T { | 932 | } |
964 | &*x.0 | 933 | } |
965 | } | ||
966 | } | ||
967 | 934 | ||
968 | struct B<T>(T); | 935 | struct B<T>(T); |
969 | 936 | ||
970 | impl<T> Deref for B<T> { | 937 | impl<T> Deref for B<T> { |
971 | type Target = T; | 938 | type Target = T; |
972 | fn deref(&self) -> &Self::Target { | 939 | fn deref(&self) -> &Self::Target { |
973 | &self.0 | 940 | &self.0 |
974 | } | 941 | } |
975 | } | 942 | } |
976 | 943 | ||
977 | fn test(a: A<i32>) { | 944 | fn test(a: A<i32>) { |
978 | let t = A(0 as *mut _).foo(&&B(B(a))); | 945 | let t = A(0 as *mut _).foo(&&B(B(a))); |
979 | } | 946 | } |
980 | "#, | 947 | "#, |
981 | expect![[r#" | 948 | expect![[r#" |
982 | 67..71 'self': &Self | 949 | 71..75 'self': &A<T> |
983 | 143..147 'self': &A<T> | 950 | 77..78 'x': &A<T> |
984 | 149..150 'x': &A<T> | 951 | 93..114 '{ ... }': &T |
985 | 165..186 '{ ... }': &T | 952 | 103..108 '&*x.0': &T |
986 | 175..180 '&*x.0': &T | 953 | 104..108 '*x.0': T |
987 | 176..180 '*x.0': T | 954 | 105..106 'x': &A<T> |
988 | 177..178 'x': &A<T> | 955 | 105..108 'x.0': *mut T |
989 | 177..180 'x.0': *mut T | 956 | 195..199 'self': &B<T> |
990 | 267..271 'self': &B<T> | 957 | 218..241 '{ ... }': &T |
991 | 290..313 '{ ... }': &T | 958 | 228..235 '&self.0': &T |
992 | 300..307 '&self.0': &T | 959 | 229..233 'self': &B<T> |
993 | 301..305 'self': &B<T> | 960 | 229..235 'self.0': T |
994 | 301..307 'self.0': T | 961 | 253..254 'a': A<i32> |
995 | 325..326 'a': A<i32> | 962 | 264..310 '{ ...))); }': () |
996 | 336..382 '{ ...))); }': () | 963 | 274..275 't': &i32 |
997 | 346..347 't': &i32 | 964 | 278..279 'A': A<i32>(*mut i32) -> A<i32> |
998 | 350..351 'A': A<i32>(*mut i32) -> A<i32> | 965 | 278..292 'A(0 as *mut _)': A<i32> |
999 | 350..364 'A(0 as *mut _)': A<i32> | 966 | 278..307 'A(0 as...B(a)))': &i32 |
1000 | 350..379 'A(0 as...B(a)))': &i32 | 967 | 280..281 '0': i32 |
1001 | 352..353 '0': i32 | 968 | 280..291 '0 as *mut _': *mut i32 |
1002 | 352..363 '0 as *mut _': *mut i32 | 969 | 297..306 '&&B(B(a))': &&B<B<A<i32>>> |
1003 | 369..378 '&&B(B(a))': &&B<B<A<i32>>> | 970 | 298..306 '&B(B(a))': &B<B<A<i32>>> |
1004 | 370..378 '&B(B(a))': &B<B<A<i32>>> | 971 | 299..300 'B': B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>> |
1005 | 371..372 'B': B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>> | 972 | 299..306 'B(B(a))': B<B<A<i32>>> |
1006 | 371..378 'B(B(a))': B<B<A<i32>>> | 973 | 301..302 'B': B<A<i32>>(A<i32>) -> B<A<i32>> |
1007 | 373..374 'B': B<A<i32>>(A<i32>) -> B<A<i32>> | 974 | 301..305 'B(a)': B<A<i32>> |
1008 | 373..377 'B(a)': B<A<i32>> | 975 | 303..304 'a': A<i32> |
1009 | 375..376 'a': A<i32> | ||
1010 | "#]], | 976 | "#]], |
1011 | ); | 977 | ); |
1012 | } | 978 | } |
@@ -1015,15 +981,15 @@ fn infer_method_argument_autoderef() { | |||
1015 | fn infer_in_elseif() { | 981 | fn infer_in_elseif() { |
1016 | check_infer( | 982 | check_infer( |
1017 | r#" | 983 | r#" |
1018 | struct Foo { field: i32 } | 984 | struct Foo { field: i32 } |
1019 | fn main(foo: Foo) { | 985 | fn main(foo: Foo) { |
1020 | if true { | 986 | if true { |
1021 | 987 | ||
1022 | } else if false { | 988 | } else if false { |
1023 | foo.field | 989 | foo.field |
1024 | } | 990 | } |
1025 | } | 991 | } |
1026 | "#, | 992 | "#, |
1027 | expect![[r#" | 993 | expect![[r#" |
1028 | 34..37 'foo': Foo | 994 | 34..37 'foo': Foo |
1029 | 44..108 '{ ... } }': () | 995 | 44..108 '{ ... } }': () |
@@ -1043,28 +1009,29 @@ fn infer_in_elseif() { | |||
1043 | fn infer_if_match_with_return() { | 1009 | fn infer_if_match_with_return() { |
1044 | check_infer( | 1010 | check_infer( |
1045 | r#" | 1011 | r#" |
1046 | fn foo() { | 1012 | fn foo() { |
1047 | let _x1 = if true { | 1013 | let _x1 = if true { |
1048 | 1 | 1014 | 1 |
1049 | } else { | 1015 | } else { |
1050 | return; | 1016 | return; |
1051 | }; | 1017 | }; |
1052 | let _x2 = if true { | 1018 | let _x2 = if true { |
1053 | 2 | 1019 | 2 |
1054 | } else { | 1020 | } else { |
1055 | return | 1021 | return |
1056 | }; | 1022 | }; |
1057 | let _x3 = match true { | 1023 | let _x3 = match true { |
1058 | true => 3, | 1024 | true => 3, |
1059 | _ => { | 1025 | _ => { |
1060 | return; | 1026 | return; |
1061 | } | 1027 | } |
1062 | }; | 1028 | }; |
1063 | let _x4 = match true { | 1029 | let _x4 = match true { |
1064 | true => 4, | 1030 | true => 4, |
1065 | _ => return | 1031 | _ => return |
1066 | }; | 1032 | }; |
1067 | }"#, | 1033 | } |
1034 | "#, | ||
1068 | expect![[r#" | 1035 | expect![[r#" |
1069 | 9..322 '{ ... }; }': () | 1036 | 9..322 '{ ... }; }': () |
1070 | 19..22 '_x1': i32 | 1037 | 19..22 '_x1': i32 |
@@ -2639,11 +2606,8 @@ fn f() { | |||
2639 | fn infer_boxed_self_receiver() { | 2606 | fn infer_boxed_self_receiver() { |
2640 | check_infer( | 2607 | check_infer( |
2641 | r#" | 2608 | r#" |
2642 | #[lang = "deref"] | 2609 | //- minicore: deref |
2643 | pub trait Deref { | 2610 | use core::ops::Deref; |
2644 | type Target; | ||
2645 | fn deref(&self) -> &Self::Target; | ||
2646 | } | ||
2647 | 2611 | ||
2648 | struct Box<T>(T); | 2612 | struct Box<T>(T); |
2649 | 2613 | ||
@@ -2675,40 +2639,39 @@ fn main() { | |||
2675 | } | 2639 | } |
2676 | "#, | 2640 | "#, |
2677 | expect![[r#" | 2641 | expect![[r#" |
2678 | 67..71 'self': &Self | 2642 | 104..108 'self': &Box<T> |
2679 | 175..179 'self': &Box<T> | 2643 | 188..192 'self': &Box<Foo<T>> |
2680 | 259..263 'self': &Box<Foo<T>> | 2644 | 218..220 '{}': () |
2681 | 289..291 '{}': () | 2645 | 242..246 'self': &Box<Foo<T>> |
2682 | 313..317 'self': &Box<Foo<T>> | 2646 | 275..277 '{}': () |
2683 | 346..348 '{}': () | 2647 | 297..301 'self': Box<Foo<T>> |
2684 | 368..372 'self': Box<Foo<T>> | 2648 | 322..324 '{}': () |
2685 | 393..395 '{}': () | 2649 | 338..559 '{ ...r(); }': () |
2686 | 409..630 '{ ...r(); }': () | 2650 | 348..353 'boxed': Box<Foo<i32>> |
2687 | 419..424 'boxed': Box<Foo<i32>> | 2651 | 356..359 'Box': Box<Foo<i32>>(Foo<i32>) -> Box<Foo<i32>> |
2688 | 427..430 'Box': Box<Foo<i32>>(Foo<i32>) -> Box<Foo<i32>> | 2652 | 356..371 'Box(Foo(0_i32))': Box<Foo<i32>> |
2689 | 427..442 'Box(Foo(0_i32))': Box<Foo<i32>> | 2653 | 360..363 'Foo': Foo<i32>(i32) -> Foo<i32> |
2690 | 431..434 'Foo': Foo<i32>(i32) -> Foo<i32> | 2654 | 360..370 'Foo(0_i32)': Foo<i32> |
2691 | 431..441 'Foo(0_i32)': Foo<i32> | 2655 | 364..369 '0_i32': i32 |
2692 | 435..440 '0_i32': i32 | 2656 | 382..386 'bad1': &i32 |
2693 | 453..457 'bad1': &i32 | 2657 | 389..394 'boxed': Box<Foo<i32>> |
2694 | 460..465 'boxed': Box<Foo<i32>> | 2658 | 389..406 'boxed....nner()': &i32 |
2695 | 460..477 'boxed....nner()': &i32 | 2659 | 416..421 'good1': &i32 |
2696 | 487..492 'good1': &i32 | 2660 | 424..438 'Foo::get_inner': fn get_inner<i32>(&Box<Foo<i32>>) -> &i32 |
2697 | 495..509 'Foo::get_inner': fn get_inner<i32>(&Box<Foo<i32>>) -> &i32 | 2661 | 424..446 'Foo::g...boxed)': &i32 |
2698 | 495..517 'Foo::g...boxed)': &i32 | 2662 | 439..445 '&boxed': &Box<Foo<i32>> |
2699 | 510..516 '&boxed': &Box<Foo<i32>> | 2663 | 440..445 'boxed': Box<Foo<i32>> |
2700 | 511..516 'boxed': Box<Foo<i32>> | 2664 | 457..461 'bad2': &Foo<i32> |
2701 | 528..532 'bad2': &Foo<i32> | 2665 | 464..469 'boxed': Box<Foo<i32>> |
2702 | 535..540 'boxed': Box<Foo<i32>> | 2666 | 464..480 'boxed....self()': &Foo<i32> |
2703 | 535..551 'boxed....self()': &Foo<i32> | 2667 | 490..495 'good2': &Foo<i32> |
2704 | 561..566 'good2': &Foo<i32> | 2668 | 498..511 'Foo::get_self': fn get_self<i32>(&Box<Foo<i32>>) -> &Foo<i32> |
2705 | 569..582 'Foo::get_self': fn get_self<i32>(&Box<Foo<i32>>) -> &Foo<i32> | 2669 | 498..519 'Foo::g...boxed)': &Foo<i32> |
2706 | 569..590 'Foo::g...boxed)': &Foo<i32> | 2670 | 512..518 '&boxed': &Box<Foo<i32>> |
2707 | 583..589 '&boxed': &Box<Foo<i32>> | 2671 | 513..518 'boxed': Box<Foo<i32>> |
2708 | 584..589 'boxed': Box<Foo<i32>> | 2672 | 530..535 'inner': Foo<i32> |
2709 | 601..606 'inner': Foo<i32> | 2673 | 538..543 'boxed': Box<Foo<i32>> |
2710 | 609..614 'boxed': Box<Foo<i32>> | 2674 | 538..556 'boxed....nner()': Foo<i32> |
2711 | 609..627 'boxed....nner()': Foo<i32> | ||
2712 | "#]], | 2675 | "#]], |
2713 | ); | 2676 | ); |
2714 | } | 2677 | } |
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index 588f0d1d4..065cca74f 100644 --- a/crates/hir_ty/src/tests/traits.rs +++ b/crates/hir_ty/src/tests/traits.rs | |||
@@ -6,10 +6,10 @@ use super::{check_infer, check_infer_with_mismatches, check_types}; | |||
6 | fn infer_await() { | 6 | fn infer_await() { |
7 | check_types( | 7 | check_types( |
8 | r#" | 8 | r#" |
9 | //- /main.rs crate:main deps:core | 9 | //- minicore: future |
10 | struct IntFuture; | 10 | struct IntFuture; |
11 | 11 | ||
12 | impl Future for IntFuture { | 12 | impl core::future::Future for IntFuture { |
13 | type Output = u64; | 13 | type Output = u64; |
14 | } | 14 | } |
15 | 15 | ||
@@ -18,16 +18,6 @@ fn test() { | |||
18 | let v = r.await; | 18 | let v = r.await; |
19 | v; | 19 | v; |
20 | } //^ u64 | 20 | } //^ u64 |
21 | |||
22 | //- /core.rs crate:core | ||
23 | pub mod prelude { | ||
24 | pub mod rust_2018 { | ||
25 | #[lang = "future_trait"] | ||
26 | pub trait Future { | ||
27 | type Output; | ||
28 | } | ||
29 | } | ||
30 | } | ||
31 | "#, | 21 | "#, |
32 | ); | 22 | ); |
33 | } | 23 | } |
@@ -36,25 +26,14 @@ pub mod prelude { | |||
36 | fn infer_async() { | 26 | fn infer_async() { |
37 | check_types( | 27 | check_types( |
38 | r#" | 28 | r#" |
39 | //- /main.rs crate:main deps:core | 29 | //- minicore: future |
40 | async fn foo() -> u64 { | 30 | async fn foo() -> u64 { 128 } |
41 | 128 | ||
42 | } | ||
43 | 31 | ||
44 | fn test() { | 32 | fn test() { |
45 | let r = foo(); | 33 | let r = foo(); |
46 | let v = r.await; | 34 | let v = r.await; |
47 | v; | 35 | v; |
48 | } //^ u64 | 36 | } //^ u64 |
49 | |||
50 | //- /core.rs crate:core | ||
51 | #[prelude_import] use future::*; | ||
52 | mod future { | ||
53 | #[lang = "future_trait"] | ||
54 | trait Future { | ||
55 | type Output; | ||
56 | } | ||
57 | } | ||
58 | "#, | 37 | "#, |
59 | ); | 38 | ); |
60 | } | 39 | } |
@@ -63,24 +42,13 @@ mod future { | |||
63 | fn infer_desugar_async() { | 42 | fn infer_desugar_async() { |
64 | check_types( | 43 | check_types( |
65 | r#" | 44 | r#" |
66 | //- /main.rs crate:main deps:core | 45 | //- minicore: future |
67 | async fn foo() -> u64 { | 46 | async fn foo() -> u64 { 128 } |
68 | 128 | ||
69 | } | ||
70 | 47 | ||
71 | fn test() { | 48 | fn test() { |
72 | let r = foo(); | 49 | let r = foo(); |
73 | r; | 50 | r; |
74 | } //^ impl Future<Output = u64> | 51 | } //^ impl Future<Output = u64> |
75 | |||
76 | //- /core.rs crate:core | ||
77 | #[prelude_import] use future::*; | ||
78 | mod future { | ||
79 | trait Future { | ||
80 | type Output; | ||
81 | } | ||
82 | } | ||
83 | |||
84 | "#, | 52 | "#, |
85 | ); | 53 | ); |
86 | } | 54 | } |
@@ -89,7 +57,7 @@ mod future { | |||
89 | fn infer_async_block() { | 57 | fn infer_async_block() { |
90 | check_types( | 58 | check_types( |
91 | r#" | 59 | r#" |
92 | //- /main.rs crate:main deps:core | 60 | //- minicore: future, option |
93 | async fn test() { | 61 | async fn test() { |
94 | let a = async { 42 }; | 62 | let a = async { 42 }; |
95 | a; | 63 | a; |
@@ -101,7 +69,7 @@ async fn test() { | |||
101 | b; | 69 | b; |
102 | // ^ () | 70 | // ^ () |
103 | let c = async { | 71 | let c = async { |
104 | let y = Option::None; | 72 | let y = None; |
105 | y | 73 | y |
106 | // ^ Option<u64> | 74 | // ^ Option<u64> |
107 | }; | 75 | }; |
@@ -109,18 +77,6 @@ async fn test() { | |||
109 | c; | 77 | c; |
110 | // ^ impl Future<Output = Option<u64>> | 78 | // ^ impl Future<Output = Option<u64>> |
111 | } | 79 | } |
112 | |||
113 | enum Option<T> { None, Some(T) } | ||
114 | |||
115 | //- /core.rs crate:core | ||
116 | #[prelude_import] use future::*; | ||
117 | mod future { | ||
118 | #[lang = "future_trait"] | ||
119 | trait Future { | ||
120 | type Output; | ||
121 | } | ||
122 | } | ||
123 | |||
124 | "#, | 80 | "#, |
125 | ); | 81 | ); |
126 | } | 82 | } |
@@ -704,14 +660,9 @@ mod ops { | |||
704 | fn deref_trait() { | 660 | fn deref_trait() { |
705 | check_types( | 661 | check_types( |
706 | r#" | 662 | r#" |
707 | #[lang = "deref"] | 663 | //- minicore: deref |
708 | trait Deref { | ||
709 | type Target; | ||
710 | fn deref(&self) -> &Self::Target; | ||
711 | } | ||
712 | |||
713 | struct Arc<T>; | 664 | struct Arc<T>; |
714 | impl<T> Deref for Arc<T> { | 665 | impl<T> core::ops::Deref for Arc<T> { |
715 | type Target = T; | 666 | type Target = T; |
716 | } | 667 | } |
717 | 668 | ||
@@ -731,16 +682,10 @@ fn test(s: Arc<S>) { | |||
731 | fn deref_trait_with_inference_var() { | 682 | fn deref_trait_with_inference_var() { |
732 | check_types( | 683 | check_types( |
733 | r#" | 684 | r#" |
734 | //- /main.rs | 685 | //- minicore: deref |
735 | #[lang = "deref"] | ||
736 | trait Deref { | ||
737 | type Target; | ||
738 | fn deref(&self) -> &Self::Target; | ||
739 | } | ||
740 | |||
741 | struct Arc<T>; | 686 | struct Arc<T>; |
742 | fn new_arc<T>() -> Arc<T> {} | 687 | fn new_arc<T>() -> Arc<T> {} |
743 | impl<T> Deref for Arc<T> { | 688 | impl<T> core::ops::Deref for Arc<T> { |
744 | type Target = T; | 689 | type Target = T; |
745 | } | 690 | } |
746 | 691 | ||
@@ -761,15 +706,10 @@ fn test() { | |||
761 | fn deref_trait_infinite_recursion() { | 706 | fn deref_trait_infinite_recursion() { |
762 | check_types( | 707 | check_types( |
763 | r#" | 708 | r#" |
764 | #[lang = "deref"] | 709 | //- minicore: deref |
765 | trait Deref { | ||
766 | type Target; | ||
767 | fn deref(&self) -> &Self::Target; | ||
768 | } | ||
769 | |||
770 | struct S; | 710 | struct S; |
771 | 711 | ||
772 | impl Deref for S { | 712 | impl core::ops::Deref for S { |
773 | type Target = S; | 713 | type Target = S; |
774 | } | 714 | } |
775 | 715 | ||
@@ -784,14 +724,9 @@ fn test(s: S) { | |||
784 | fn deref_trait_with_question_mark_size() { | 724 | fn deref_trait_with_question_mark_size() { |
785 | check_types( | 725 | check_types( |
786 | r#" | 726 | r#" |
787 | #[lang = "deref"] | 727 | //- minicore: deref |
788 | trait Deref { | ||
789 | type Target; | ||
790 | fn deref(&self) -> &Self::Target; | ||
791 | } | ||
792 | |||
793 | struct Arc<T>; | 728 | struct Arc<T>; |
794 | impl<T> Deref for Arc<T> { | 729 | impl<T: ?Sized> core::ops::Deref for Arc<T> { |
795 | type Target = T; | 730 | type Target = T; |
796 | } | 731 | } |
797 | 732 | ||
@@ -1475,7 +1410,6 @@ fn test( | |||
1475 | } | 1410 | } |
1476 | 1411 | ||
1477 | #[test] | 1412 | #[test] |
1478 | #[ignore] | ||
1479 | fn error_bound_chalk() { | 1413 | fn error_bound_chalk() { |
1480 | check_types( | 1414 | check_types( |
1481 | r#" | 1415 | r#" |
@@ -1912,11 +1846,7 @@ fn test() { | |||
1912 | fn closure_1() { | 1846 | fn closure_1() { |
1913 | check_infer_with_mismatches( | 1847 | check_infer_with_mismatches( |
1914 | r#" | 1848 | r#" |
1915 | #[lang = "fn_once"] | 1849 | //- minicore: fn |
1916 | trait FnOnce<Args> { | ||
1917 | type Output; | ||
1918 | } | ||
1919 | |||
1920 | enum Option<T> { Some(T), None } | 1850 | enum Option<T> { Some(T), None } |
1921 | impl<T> Option<T> { | 1851 | impl<T> Option<T> { |
1922 | fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> { loop {} } | 1852 | fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> { loop {} } |
@@ -1929,34 +1859,34 @@ fn test() { | |||
1929 | let y: Option<i64> = x.map(|_v| 1); | 1859 | let y: Option<i64> = x.map(|_v| 1); |
1930 | }"#, | 1860 | }"#, |
1931 | expect![[r#" | 1861 | expect![[r#" |
1932 | 147..151 'self': Option<T> | 1862 | 86..90 'self': Option<T> |
1933 | 153..154 'f': F | 1863 | 92..93 'f': F |
1934 | 172..183 '{ loop {} }': Option<U> | 1864 | 111..122 '{ loop {} }': Option<U> |
1935 | 174..181 'loop {}': ! | 1865 | 113..120 'loop {}': ! |
1936 | 179..181 '{}': () | 1866 | 118..120 '{}': () |
1937 | 197..316 '{ ... 1); }': () | 1867 | 136..255 '{ ... 1); }': () |
1938 | 207..208 'x': Option<u32> | 1868 | 146..147 'x': Option<u32> |
1939 | 211..223 'Option::Some': Some<u32>(u32) -> Option<u32> | 1869 | 150..162 'Option::Some': Some<u32>(u32) -> Option<u32> |
1940 | 211..229 'Option...(1u32)': Option<u32> | 1870 | 150..168 'Option...(1u32)': Option<u32> |
1941 | 224..228 '1u32': u32 | 1871 | 163..167 '1u32': u32 |
1942 | 235..236 'x': Option<u32> | 1872 | 174..175 'x': Option<u32> |
1943 | 235..251 'x.map(...v + 1)': Option<u32> | 1873 | 174..190 'x.map(...v + 1)': Option<u32> |
1944 | 241..250 '|v| v + 1': |u32| -> u32 | 1874 | 180..189 '|v| v + 1': |u32| -> u32 |
1945 | 242..243 'v': u32 | 1875 | 181..182 'v': u32 |
1946 | 245..246 'v': u32 | 1876 | 184..185 'v': u32 |
1947 | 245..250 'v + 1': u32 | 1877 | 184..189 'v + 1': u32 |
1948 | 249..250 '1': u32 | 1878 | 188..189 '1': u32 |
1949 | 257..258 'x': Option<u32> | 1879 | 196..197 'x': Option<u32> |
1950 | 257..273 'x.map(... 1u64)': Option<u64> | 1880 | 196..212 'x.map(... 1u64)': Option<u64> |
1951 | 263..272 '|_v| 1u64': |u32| -> u64 | 1881 | 202..211 '|_v| 1u64': |u32| -> u64 |
1952 | 264..266 '_v': u32 | 1882 | 203..205 '_v': u32 |
1953 | 268..272 '1u64': u64 | 1883 | 207..211 '1u64': u64 |
1954 | 283..284 'y': Option<i64> | 1884 | 222..223 'y': Option<i64> |
1955 | 300..301 'x': Option<u32> | 1885 | 239..240 'x': Option<u32> |
1956 | 300..313 'x.map(|_v| 1)': Option<i64> | 1886 | 239..252 'x.map(|_v| 1)': Option<i64> |
1957 | 306..312 '|_v| 1': |u32| -> i64 | 1887 | 245..251 '|_v| 1': |u32| -> i64 |
1958 | 307..309 '_v': u32 | 1888 | 246..248 '_v': u32 |
1959 | 311..312 '1': i64 | 1889 | 250..251 '1': i64 |
1960 | "#]], | 1890 | "#]], |
1961 | ); | 1891 | ); |
1962 | } | 1892 | } |
@@ -2030,11 +1960,7 @@ fn test<F: FnOnce(u32) -> u64>(f: F) { | |||
2030 | fn closure_as_argument_inference_order() { | 1960 | fn closure_as_argument_inference_order() { |
2031 | check_infer_with_mismatches( | 1961 | check_infer_with_mismatches( |
2032 | r#" | 1962 | r#" |
2033 | #[lang = "fn_once"] | 1963 | //- minicore: fn |
2034 | trait FnOnce<Args> { | ||
2035 | type Output; | ||
2036 | } | ||
2037 | |||
2038 | fn foo1<T, U, F: FnOnce(T) -> U>(x: T, f: F) -> U { loop {} } | 1964 | fn foo1<T, U, F: FnOnce(T) -> U>(x: T, f: F) -> U { loop {} } |
2039 | fn foo2<T, U, F: FnOnce(T) -> U>(f: F, x: T) -> U { loop {} } | 1965 | fn foo2<T, U, F: FnOnce(T) -> U>(f: F, x: T) -> U { loop {} } |
2040 | 1966 | ||
@@ -2053,62 +1979,62 @@ fn test() { | |||
2053 | let x4 = S.foo2(|s| s.method(), S); | 1979 | let x4 = S.foo2(|s| s.method(), S); |
2054 | }"#, | 1980 | }"#, |
2055 | expect![[r#" | 1981 | expect![[r#" |
2056 | 94..95 'x': T | 1982 | 33..34 'x': T |
2057 | 100..101 'f': F | 1983 | 39..40 'f': F |
2058 | 111..122 '{ loop {} }': U | 1984 | 50..61 '{ loop {} }': U |
2059 | 113..120 'loop {}': ! | 1985 | 52..59 'loop {}': ! |
2060 | 118..120 '{}': () | 1986 | 57..59 '{}': () |
2061 | 156..157 'f': F | 1987 | 95..96 'f': F |
2062 | 162..163 'x': T | 1988 | 101..102 'x': T |
2063 | 173..184 '{ loop {} }': U | 1989 | 112..123 '{ loop {} }': U |
2064 | 175..182 'loop {}': ! | 1990 | 114..121 'loop {}': ! |
2065 | 180..182 '{}': () | 1991 | 119..121 '{}': () |
2066 | 219..223 'self': S | 1992 | 158..162 'self': S |
2067 | 271..275 'self': S | 1993 | 210..214 'self': S |
2068 | 277..278 'x': T | 1994 | 216..217 'x': T |
2069 | 283..284 'f': F | 1995 | 222..223 'f': F |
2070 | 294..305 '{ loop {} }': U | 1996 | 233..244 '{ loop {} }': U |
2071 | 296..303 'loop {}': ! | 1997 | 235..242 'loop {}': ! |
2072 | 301..303 '{}': () | 1998 | 240..242 '{}': () |
2073 | 343..347 'self': S | 1999 | 282..286 'self': S |
2074 | 349..350 'f': F | 2000 | 288..289 'f': F |
2075 | 355..356 'x': T | 2001 | 294..295 'x': T |
2076 | 366..377 '{ loop {} }': U | 2002 | 305..316 '{ loop {} }': U |
2077 | 368..375 'loop {}': ! | 2003 | 307..314 'loop {}': ! |
2078 | 373..375 '{}': () | 2004 | 312..314 '{}': () |
2079 | 391..550 '{ ... S); }': () | 2005 | 330..489 '{ ... S); }': () |
2080 | 401..403 'x1': u64 | 2006 | 340..342 'x1': u64 |
2081 | 406..410 'foo1': fn foo1<S, u64, |S| -> u64>(S, |S| -> u64) -> u64 | 2007 | 345..349 'foo1': fn foo1<S, u64, |S| -> u64>(S, |S| -> u64) -> u64 |
2082 | 406..429 'foo1(S...hod())': u64 | 2008 | 345..368 'foo1(S...hod())': u64 |
2083 | 411..412 'S': S | 2009 | 350..351 'S': S |
2084 | 414..428 '|s| s.method()': |S| -> u64 | 2010 | 353..367 '|s| s.method()': |S| -> u64 |
2085 | 415..416 's': S | 2011 | 354..355 's': S |
2086 | 418..419 's': S | 2012 | 357..358 's': S |
2087 | 418..428 's.method()': u64 | 2013 | 357..367 's.method()': u64 |
2088 | 439..441 'x2': u64 | 2014 | 378..380 'x2': u64 |
2089 | 444..448 'foo2': fn foo2<S, u64, |S| -> u64>(|S| -> u64, S) -> u64 | 2015 | 383..387 'foo2': fn foo2<S, u64, |S| -> u64>(|S| -> u64, S) -> u64 |
2090 | 444..467 'foo2(|...(), S)': u64 | 2016 | 383..406 'foo2(|...(), S)': u64 |
2091 | 449..463 '|s| s.method()': |S| -> u64 | 2017 | 388..402 '|s| s.method()': |S| -> u64 |
2092 | 450..451 's': S | 2018 | 389..390 's': S |
2093 | 453..454 's': S | 2019 | 392..393 's': S |
2094 | 453..463 's.method()': u64 | 2020 | 392..402 's.method()': u64 |
2095 | 465..466 'S': S | 2021 | 404..405 'S': S |
2096 | 477..479 'x3': u64 | 2022 | 416..418 'x3': u64 |
2097 | 482..483 'S': S | 2023 | 421..422 'S': S |
2098 | 482..507 'S.foo1...hod())': u64 | 2024 | 421..446 'S.foo1...hod())': u64 |
2099 | 489..490 'S': S | 2025 | 428..429 'S': S |
2100 | 492..506 '|s| s.method()': |S| -> u64 | 2026 | 431..445 '|s| s.method()': |S| -> u64 |
2101 | 493..494 's': S | 2027 | 432..433 's': S |
2102 | 496..497 's': S | 2028 | 435..436 's': S |
2103 | 496..506 's.method()': u64 | 2029 | 435..445 's.method()': u64 |
2104 | 517..519 'x4': u64 | 2030 | 456..458 'x4': u64 |
2105 | 522..523 'S': S | 2031 | 461..462 'S': S |
2106 | 522..547 'S.foo2...(), S)': u64 | 2032 | 461..486 'S.foo2...(), S)': u64 |
2107 | 529..543 '|s| s.method()': |S| -> u64 | 2033 | 468..482 '|s| s.method()': |S| -> u64 |
2108 | 530..531 's': S | 2034 | 469..470 's': S |
2109 | 533..534 's': S | 2035 | 472..473 's': S |
2110 | 533..543 's.method()': u64 | 2036 | 472..482 's.method()': u64 |
2111 | 545..546 'S': S | 2037 | 484..485 'S': S |
2112 | "#]], | 2038 | "#]], |
2113 | ); | 2039 | ); |
2114 | } | 2040 | } |
@@ -2117,11 +2043,7 @@ fn test() { | |||
2117 | fn fn_item_fn_trait() { | 2043 | fn fn_item_fn_trait() { |
2118 | check_types( | 2044 | check_types( |
2119 | r#" | 2045 | r#" |
2120 | #[lang = "fn_once"] | 2046 | //- minicore: fn |
2121 | trait FnOnce<Args> { | ||
2122 | type Output; | ||
2123 | } | ||
2124 | |||
2125 | struct S; | 2047 | struct S; |
2126 | 2048 | ||
2127 | fn foo() -> S {} | 2049 | fn foo() -> S {} |
@@ -2560,12 +2482,7 @@ fn test() -> impl Trait<i32> { | |||
2560 | fn assoc_types_from_bounds() { | 2482 | fn assoc_types_from_bounds() { |
2561 | check_infer( | 2483 | check_infer( |
2562 | r#" | 2484 | r#" |
2563 | //- /main.rs | 2485 | //- minicore: fn |
2564 | #[lang = "fn_once"] | ||
2565 | trait FnOnce<Args> { | ||
2566 | type Output; | ||
2567 | } | ||
2568 | |||
2569 | trait T { | 2486 | trait T { |
2570 | type O; | 2487 | type O; |
2571 | } | 2488 | } |
@@ -2584,15 +2501,15 @@ fn main() { | |||
2584 | f::<(), _>(|z| { z; }); | 2501 | f::<(), _>(|z| { z; }); |
2585 | }"#, | 2502 | }"#, |
2586 | expect![[r#" | 2503 | expect![[r#" |
2587 | 133..135 '_v': F | 2504 | 72..74 '_v': F |
2588 | 178..181 '{ }': () | 2505 | 117..120 '{ }': () |
2589 | 193..224 '{ ... }); }': () | 2506 | 132..163 '{ ... }); }': () |
2590 | 199..209 'f::<(), _>': fn f<(), |&()| -> ()>(|&()| -> ()) | 2507 | 138..148 'f::<(), _>': fn f<(), |&()| -> ()>(|&()| -> ()) |
2591 | 199..221 'f::<()... z; })': () | 2508 | 138..160 'f::<()... z; })': () |
2592 | 210..220 '|z| { z; }': |&()| -> () | 2509 | 149..159 '|z| { z; }': |&()| -> () |
2593 | 211..212 'z': &() | 2510 | 150..151 'z': &() |
2594 | 214..220 '{ z; }': () | 2511 | 153..159 '{ z; }': () |
2595 | 216..217 'z': &() | 2512 | 155..156 'z': &() |
2596 | "#]], | 2513 | "#]], |
2597 | ); | 2514 | ); |
2598 | } | 2515 | } |
@@ -2626,12 +2543,9 @@ fn test<T: Trait>() { | |||
2626 | fn dyn_trait_through_chalk() { | 2543 | fn dyn_trait_through_chalk() { |
2627 | check_types( | 2544 | check_types( |
2628 | r#" | 2545 | r#" |
2546 | //- minicore: deref | ||
2629 | struct Box<T> {} | 2547 | struct Box<T> {} |
2630 | #[lang = "deref"] | 2548 | impl<T> core::ops::Deref for Box<T> { |
2631 | trait Deref { | ||
2632 | type Target; | ||
2633 | } | ||
2634 | impl<T> Deref for Box<T> { | ||
2635 | type Target = T; | 2549 | type Target = T; |
2636 | } | 2550 | } |
2637 | trait Trait { | 2551 | trait Trait { |
@@ -2668,17 +2582,7 @@ fn test() { | |||
2668 | fn iterator_chain() { | 2582 | fn iterator_chain() { |
2669 | check_infer_with_mismatches( | 2583 | check_infer_with_mismatches( |
2670 | r#" | 2584 | r#" |
2671 | //- /main.rs | 2585 | //- minicore: fn, option |
2672 | #[lang = "fn_once"] | ||
2673 | trait FnOnce<Args> { | ||
2674 | type Output; | ||
2675 | } | ||
2676 | #[lang = "fn_mut"] | ||
2677 | trait FnMut<Args>: FnOnce<Args> { } | ||
2678 | |||
2679 | enum Option<T> { Some(T), None } | ||
2680 | use Option::*; | ||
2681 | |||
2682 | pub trait Iterator { | 2586 | pub trait Iterator { |
2683 | type Item; | 2587 | type Item; |
2684 | 2588 | ||
@@ -2738,46 +2642,46 @@ fn main() { | |||
2738 | .for_each(|y| { y; }); | 2642 | .for_each(|y| { y; }); |
2739 | }"#, | 2643 | }"#, |
2740 | expect![[r#" | 2644 | expect![[r#" |
2741 | 226..230 'self': Self | 2645 | 61..65 'self': Self |
2742 | 232..233 'f': F | 2646 | 67..68 'f': F |
2743 | 317..328 '{ loop {} }': FilterMap<Self, F> | 2647 | 152..163 '{ loop {} }': FilterMap<Self, F> |
2744 | 319..326 'loop {}': ! | 2648 | 154..161 'loop {}': ! |
2745 | 324..326 '{}': () | 2649 | 159..161 '{}': () |
2746 | 349..353 'self': Self | 2650 | 184..188 'self': Self |
2747 | 355..356 'f': F | 2651 | 190..191 'f': F |
2748 | 405..416 '{ loop {} }': () | 2652 | 240..251 '{ loop {} }': () |
2749 | 407..414 'loop {}': ! | 2653 | 242..249 'loop {}': ! |
2750 | 412..414 '{}': () | 2654 | 247..249 '{}': () |
2751 | 525..529 'self': Self | 2655 | 360..364 'self': Self |
2752 | 854..858 'self': I | 2656 | 689..693 'self': I |
2753 | 865..885 '{ ... }': I | 2657 | 700..720 '{ ... }': I |
2754 | 875..879 'self': I | 2658 | 710..714 'self': I |
2755 | 944..955 '{ loop {} }': Vec<T> | 2659 | 779..790 '{ loop {} }': Vec<T> |
2756 | 946..953 'loop {}': ! | 2660 | 781..788 'loop {}': ! |
2757 | 951..953 '{}': () | 2661 | 786..788 '{}': () |
2758 | 1142..1269 '{ ... }); }': () | 2662 | 977..1104 '{ ... }); }': () |
2759 | 1148..1163 'Vec::<i32>::new': fn new<i32>() -> Vec<i32> | 2663 | 983..998 'Vec::<i32>::new': fn new<i32>() -> Vec<i32> |
2760 | 1148..1165 'Vec::<...:new()': Vec<i32> | 2664 | 983..1000 'Vec::<...:new()': Vec<i32> |
2761 | 1148..1177 'Vec::<...iter()': IntoIter<i32> | 2665 | 983..1012 'Vec::<...iter()': IntoIter<i32> |
2762 | 1148..1240 'Vec::<...one })': FilterMap<IntoIter<i32>, |i32| -> Option<u32>> | 2666 | 983..1075 'Vec::<...one })': FilterMap<IntoIter<i32>, |i32| -> Option<u32>> |
2763 | 1148..1266 'Vec::<... y; })': () | 2667 | 983..1101 'Vec::<... y; })': () |
2764 | 1194..1239 '|x| if...None }': |i32| -> Option<u32> | 2668 | 1029..1074 '|x| if...None }': |i32| -> Option<u32> |
2765 | 1195..1196 'x': i32 | 2669 | 1030..1031 'x': i32 |
2766 | 1198..1239 'if x >...None }': Option<u32> | 2670 | 1033..1074 'if x >...None }': Option<u32> |
2767 | 1201..1202 'x': i32 | 2671 | 1036..1037 'x': i32 |
2768 | 1201..1206 'x > 0': bool | 2672 | 1036..1041 'x > 0': bool |
2769 | 1205..1206 '0': i32 | 2673 | 1040..1041 '0': i32 |
2770 | 1207..1225 '{ Some...u32) }': Option<u32> | 2674 | 1042..1060 '{ Some...u32) }': Option<u32> |
2771 | 1209..1213 'Some': Some<u32>(u32) -> Option<u32> | 2675 | 1044..1048 'Some': Some<u32>(u32) -> Option<u32> |
2772 | 1209..1223 'Some(x as u32)': Option<u32> | 2676 | 1044..1058 'Some(x as u32)': Option<u32> |
2773 | 1214..1215 'x': i32 | 2677 | 1049..1050 'x': i32 |
2774 | 1214..1222 'x as u32': u32 | 2678 | 1049..1057 'x as u32': u32 |
2775 | 1231..1239 '{ None }': Option<u32> | 2679 | 1066..1074 '{ None }': Option<u32> |
2776 | 1233..1237 'None': Option<u32> | 2680 | 1068..1072 'None': Option<u32> |
2777 | 1255..1265 '|y| { y; }': |u32| -> () | 2681 | 1090..1100 '|y| { y; }': |u32| -> () |
2778 | 1256..1257 'y': u32 | 2682 | 1091..1092 'y': u32 |
2779 | 1259..1265 '{ y; }': () | 2683 | 1094..1100 '{ y; }': () |
2780 | 1261..1262 'y': u32 | 2684 | 1096..1097 'y': u32 |
2781 | "#]], | 2685 | "#]], |
2782 | ); | 2686 | ); |
2783 | } | 2687 | } |
@@ -3064,45 +2968,23 @@ fn foo() { | |||
3064 | fn infer_fn_trait_arg() { | 2968 | fn infer_fn_trait_arg() { |
3065 | check_infer_with_mismatches( | 2969 | check_infer_with_mismatches( |
3066 | r#" | 2970 | r#" |
3067 | //- /lib.rs deps:std | 2971 | //- minicore: fn, option |
3068 | 2972 | fn foo<F, T>(f: F) -> T | |
3069 | #[lang = "fn_once"] | 2973 | where |
3070 | pub trait FnOnce<Args> { | 2974 | F: Fn(Option<i32>) -> T, |
3071 | type Output; | 2975 | { |
3072 | 2976 | let s = None; | |
3073 | extern "rust-call" fn call_once(&self, args: Args) -> Self::Output; | 2977 | f(s) |
3074 | } | 2978 | } |
3075 | 2979 | "#, | |
3076 | #[lang = "fn"] | ||
3077 | pub trait Fn<Args>:FnOnce<Args> { | ||
3078 | extern "rust-call" fn call(&self, args: Args) -> Self::Output; | ||
3079 | } | ||
3080 | |||
3081 | enum Option<T> { | ||
3082 | None, | ||
3083 | Some(T) | ||
3084 | } | ||
3085 | |||
3086 | fn foo<F, T>(f: F) -> T | ||
3087 | where | ||
3088 | F: Fn(Option<i32>) -> T, | ||
3089 | { | ||
3090 | let s = None; | ||
3091 | f(s) | ||
3092 | } | ||
3093 | "#, | ||
3094 | expect![[r#" | 2980 | expect![[r#" |
3095 | 101..105 'self': &Self | 2981 | 13..14 'f': F |
3096 | 107..111 'args': Args | 2982 | 59..89 '{ ...f(s) }': T |
3097 | 220..224 'self': &Self | 2983 | 69..70 's': Option<i32> |
3098 | 226..230 'args': Args | 2984 | 73..77 'None': Option<i32> |
3099 | 313..314 'f': F | 2985 | 83..84 'f': F |
3100 | 359..389 '{ ...f(s) }': T | 2986 | 83..87 'f(s)': T |
3101 | 369..370 's': Option<i32> | 2987 | 85..86 's': Option<i32> |
3102 | 373..377 'None': Option<i32> | ||
3103 | 383..384 'f': F | ||
3104 | 383..387 'f(s)': T | ||
3105 | 385..386 's': Option<i32> | ||
3106 | "#]], | 2988 | "#]], |
3107 | ); | 2989 | ); |
3108 | } | 2990 | } |
@@ -3181,17 +3063,7 @@ fn foo() { | |||
3181 | fn infer_dyn_fn_output() { | 3063 | fn infer_dyn_fn_output() { |
3182 | check_types( | 3064 | check_types( |
3183 | r#" | 3065 | r#" |
3184 | #[lang = "fn_once"] | 3066 | //- minicore: fn |
3185 | pub trait FnOnce<Args> { | ||
3186 | type Output; | ||
3187 | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; | ||
3188 | } | ||
3189 | |||
3190 | #[lang = "fn"] | ||
3191 | pub trait Fn<Args>: FnOnce<Args> { | ||
3192 | extern "rust-call" fn call(&self, args: Args) -> Self::Output; | ||
3193 | } | ||
3194 | |||
3195 | fn foo() { | 3067 | fn foo() { |
3196 | let f: &dyn Fn() -> i32; | 3068 | let f: &dyn Fn() -> i32; |
3197 | f(); | 3069 | f(); |
@@ -3204,12 +3076,7 @@ fn foo() { | |||
3204 | fn infer_dyn_fn_once_output() { | 3076 | fn infer_dyn_fn_once_output() { |
3205 | check_types( | 3077 | check_types( |
3206 | r#" | 3078 | r#" |
3207 | #[lang = "fn_once"] | 3079 | //- minicore: fn |
3208 | pub trait FnOnce<Args> { | ||
3209 | type Output; | ||
3210 | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; | ||
3211 | } | ||
3212 | |||
3213 | fn foo() { | 3080 | fn foo() { |
3214 | let f: dyn FnOnce() -> i32; | 3081 | let f: dyn FnOnce() -> i32; |
3215 | f(); | 3082 | f(); |
@@ -3644,20 +3511,16 @@ fn main() { | |||
3644 | fn fn_returning_unit() { | 3511 | fn fn_returning_unit() { |
3645 | check_infer_with_mismatches( | 3512 | check_infer_with_mismatches( |
3646 | r#" | 3513 | r#" |
3647 | #[lang = "fn_once"] | 3514 | //- minicore: fn |
3648 | trait FnOnce<Args> { | ||
3649 | type Output; | ||
3650 | } | ||
3651 | |||
3652 | fn test<F: FnOnce()>(f: F) { | 3515 | fn test<F: FnOnce()>(f: F) { |
3653 | let _: () = f(); | 3516 | let _: () = f(); |
3654 | }"#, | 3517 | }"#, |
3655 | expect![[r#" | 3518 | expect![[r#" |
3656 | 82..83 'f': F | 3519 | 21..22 'f': F |
3657 | 88..112 '{ ...f(); }': () | 3520 | 27..51 '{ ...f(); }': () |
3658 | 98..99 '_': () | 3521 | 37..38 '_': () |
3659 | 106..107 'f': F | 3522 | 45..46 'f': F |
3660 | 106..109 'f()': () | 3523 | 45..48 'f()': () |
3661 | "#]], | 3524 | "#]], |
3662 | ); | 3525 | ); |
3663 | } | 3526 | } |
@@ -3696,16 +3559,7 @@ impl foo::Foo for u32 { | |||
3696 | fn infer_async_ret_type() { | 3559 | fn infer_async_ret_type() { |
3697 | check_types( | 3560 | check_types( |
3698 | r#" | 3561 | r#" |
3699 | //- /main.rs crate:main deps:core | 3562 | //- minicore: future, result |
3700 | |||
3701 | enum Result<T, E> { | ||
3702 | Ok(T), | ||
3703 | Err(E), | ||
3704 | } | ||
3705 | |||
3706 | use Result::*; | ||
3707 | |||
3708 | |||
3709 | struct Fooey; | 3563 | struct Fooey; |
3710 | 3564 | ||
3711 | impl Fooey { | 3565 | impl Fooey { |
@@ -3728,13 +3582,71 @@ async fn get_accounts() -> Result<u32, ()> { | |||
3728 | // ^ u32 | 3582 | // ^ u32 |
3729 | Ok(ret) | 3583 | Ok(ret) |
3730 | } | 3584 | } |
3585 | "#, | ||
3586 | ); | ||
3587 | } | ||
3731 | 3588 | ||
3732 | //- /core.rs crate:core | 3589 | #[test] |
3733 | #[prelude_import] use future::*; | 3590 | fn local_impl_1() { |
3734 | mod future { | 3591 | check_types( |
3735 | #[lang = "future_trait"] | 3592 | r#" |
3736 | trait Future { | 3593 | trait Trait<T> { |
3737 | type Output; | 3594 | fn foo(&self) -> T; |
3595 | } | ||
3596 | |||
3597 | fn test() { | ||
3598 | struct S; | ||
3599 | impl Trait<u32> for S { | ||
3600 | fn foo(&self) { 0 } | ||
3601 | } | ||
3602 | |||
3603 | S.foo(); | ||
3604 | // ^^^^^^^ u32 | ||
3605 | } | ||
3606 | "#, | ||
3607 | ); | ||
3608 | } | ||
3609 | |||
3610 | #[test] | ||
3611 | fn local_impl_2() { | ||
3612 | check_types( | ||
3613 | r#" | ||
3614 | struct S; | ||
3615 | |||
3616 | fn test() { | ||
3617 | trait Trait<T> { | ||
3618 | fn foo(&self) -> T; | ||
3619 | } | ||
3620 | impl Trait<u32> for S { | ||
3621 | fn foo(&self) { 0 } | ||
3622 | } | ||
3623 | |||
3624 | S.foo(); | ||
3625 | // ^^^^^^^ u32 | ||
3626 | } | ||
3627 | "#, | ||
3628 | ); | ||
3629 | } | ||
3630 | |||
3631 | #[test] | ||
3632 | fn local_impl_3() { | ||
3633 | check_types( | ||
3634 | r#" | ||
3635 | trait Trait<T> { | ||
3636 | fn foo(&self) -> T; | ||
3637 | } | ||
3638 | |||
3639 | fn test() { | ||
3640 | struct S1; | ||
3641 | { | ||
3642 | struct S2; | ||
3643 | |||
3644 | impl Trait<S1> for S2 { | ||
3645 | fn foo(&self) { S1 } | ||
3646 | } | ||
3647 | |||
3648 | S2.foo(); | ||
3649 | // ^^^^^^^^ S1 | ||
3738 | } | 3650 | } |
3739 | } | 3651 | } |
3740 | "#, | 3652 | "#, |
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml index 88f3d09d3..0e8447394 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" |
@@ -29,6 +29,7 @@ ide_db = { path = "../ide_db", version = "0.0.0" } | |||
29 | cfg = { path = "../cfg", version = "0.0.0" } | 29 | cfg = { path = "../cfg", version = "0.0.0" } |
30 | profile = { path = "../profile", version = "0.0.0" } | 30 | profile = { path = "../profile", version = "0.0.0" } |
31 | ide_assists = { path = "../ide_assists", version = "0.0.0" } | 31 | ide_assists = { path = "../ide_assists", version = "0.0.0" } |
32 | ide_diagnostics = { path = "../ide_diagnostics", version = "0.0.0" } | ||
32 | ide_ssr = { path = "../ide_ssr", version = "0.0.0" } | 33 | ide_ssr = { path = "../ide_ssr", version = "0.0.0" } |
33 | ide_completion = { path = "../ide_completion", version = "0.0.0" } | 34 | ide_completion = { path = "../ide_completion", version = "0.0.0" } |
34 | 35 | ||
@@ -39,4 +40,3 @@ hir = { path = "../hir", version = "0.0.0" } | |||
39 | [dev-dependencies] | 40 | [dev-dependencies] |
40 | test_utils = { path = "../test_utils" } | 41 | test_utils = { path = "../test_utils" } |
41 | expect-test = "1.1" | 42 | expect-test = "1.1" |
42 | cov-mark = "1.1.0" | ||
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs deleted file mode 100644 index d5c954b8b..000000000 --- a/crates/ide/src/diagnostics.rs +++ /dev/null | |||
@@ -1,722 +0,0 @@ | |||
1 | //! Collects diagnostics & fixits for a single file. | ||
2 | //! | ||
3 | //! The tricky bit here is that diagnostics are produced by hir 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. | ||
6 | |||
7 | mod fixes; | ||
8 | mod field_shorthand; | ||
9 | mod unlinked_file; | ||
10 | |||
11 | use std::cell::RefCell; | ||
12 | |||
13 | use hir::{ | ||
14 | db::AstDatabase, | ||
15 | diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder}, | ||
16 | InFile, Semantics, | ||
17 | }; | ||
18 | use ide_assists::AssistResolveStrategy; | ||
19 | use ide_db::{base_db::SourceDatabase, RootDatabase}; | ||
20 | use itertools::Itertools; | ||
21 | use rustc_hash::FxHashSet; | ||
22 | use syntax::{ | ||
23 | ast::{self, AstNode}, | ||
24 | SyntaxNode, SyntaxNodePtr, TextRange, TextSize, | ||
25 | }; | ||
26 | use text_edit::TextEdit; | ||
27 | use unlinked_file::UnlinkedFile; | ||
28 | |||
29 | use crate::{Assist, AssistId, AssistKind, FileId, Label, SourceChange}; | ||
30 | |||
31 | use self::fixes::DiagnosticWithFixes; | ||
32 | |||
33 | #[derive(Debug)] | ||
34 | pub struct Diagnostic { | ||
35 | // pub name: Option<String>, | ||
36 | pub message: String, | ||
37 | pub range: TextRange, | ||
38 | pub severity: Severity, | ||
39 | pub fixes: Option<Vec<Assist>>, | ||
40 | pub unused: bool, | ||
41 | pub code: Option<DiagnosticCode>, | ||
42 | } | ||
43 | |||
44 | impl Diagnostic { | ||
45 | fn error(range: TextRange, message: String) -> Self { | ||
46 | Self { message, range, severity: Severity::Error, fixes: None, unused: false, code: None } | ||
47 | } | ||
48 | |||
49 | fn hint(range: TextRange, message: String) -> Self { | ||
50 | Self { | ||
51 | message, | ||
52 | range, | ||
53 | severity: Severity::WeakWarning, | ||
54 | fixes: None, | ||
55 | unused: false, | ||
56 | code: None, | ||
57 | } | ||
58 | } | ||
59 | |||
60 | fn with_fixes(self, fixes: Option<Vec<Assist>>) -> Self { | ||
61 | Self { fixes, ..self } | ||
62 | } | ||
63 | |||
64 | fn with_unused(self, unused: bool) -> Self { | ||
65 | Self { unused, ..self } | ||
66 | } | ||
67 | |||
68 | fn with_code(self, code: Option<DiagnosticCode>) -> Self { | ||
69 | Self { code, ..self } | ||
70 | } | ||
71 | } | ||
72 | |||
73 | #[derive(Debug, Copy, Clone)] | ||
74 | pub enum Severity { | ||
75 | Error, | ||
76 | WeakWarning, | ||
77 | } | ||
78 | |||
79 | #[derive(Default, Debug, Clone)] | ||
80 | pub struct DiagnosticsConfig { | ||
81 | pub disable_experimental: bool, | ||
82 | pub disabled: FxHashSet<String>, | ||
83 | } | ||
84 | |||
85 | pub(crate) fn diagnostics( | ||
86 | db: &RootDatabase, | ||
87 | config: &DiagnosticsConfig, | ||
88 | resolve: &AssistResolveStrategy, | ||
89 | file_id: FileId, | ||
90 | ) -> Vec<Diagnostic> { | ||
91 | let _p = profile::span("diagnostics"); | ||
92 | let sema = Semantics::new(db); | ||
93 | let parse = db.parse(file_id); | ||
94 | let mut res = Vec::new(); | ||
95 | |||
96 | // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily. | ||
97 | res.extend( | ||
98 | parse | ||
99 | .errors() | ||
100 | .iter() | ||
101 | .take(128) | ||
102 | .map(|err| Diagnostic::error(err.range(), format!("Syntax Error: {}", err))), | ||
103 | ); | ||
104 | |||
105 | for node in parse.tree().syntax().descendants() { | ||
106 | check_unnecessary_braces_in_use_statement(&mut res, file_id, &node); | ||
107 | field_shorthand::check(&mut res, file_id, &node); | ||
108 | } | ||
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 | // Override severity and mark as unused. | ||
139 | res.borrow_mut().push( | ||
140 | Diagnostic::hint( | ||
141 | sema.diagnostics_display_range(d.display_source()).range, | ||
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 | } | ||
213 | |||
214 | drop(sink); | ||
215 | res.into_inner() | ||
216 | } | ||
217 | |||
218 | fn diagnostic_with_fix<D: DiagnosticWithFixes>( | ||
219 | d: &D, | ||
220 | sema: &Semantics<RootDatabase>, | ||
221 | resolve: &AssistResolveStrategy, | ||
222 | ) -> Diagnostic { | ||
223 | Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message()) | ||
224 | .with_fixes(d.fixes(&sema, resolve)) | ||
225 | .with_code(Some(d.code())) | ||
226 | } | ||
227 | |||
228 | fn warning_with_fix<D: DiagnosticWithFixes>( | ||
229 | d: &D, | ||
230 | sema: &Semantics<RootDatabase>, | ||
231 | resolve: &AssistResolveStrategy, | ||
232 | ) -> Diagnostic { | ||
233 | Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message()) | ||
234 | .with_fixes(d.fixes(&sema, resolve)) | ||
235 | .with_code(Some(d.code())) | ||
236 | } | ||
237 | |||
238 | fn check_unnecessary_braces_in_use_statement( | ||
239 | acc: &mut Vec<Diagnostic>, | ||
240 | file_id: FileId, | ||
241 | node: &SyntaxNode, | ||
242 | ) -> Option<()> { | ||
243 | let use_tree_list = ast::UseTreeList::cast(node.clone())?; | ||
244 | if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() { | ||
245 | // If there is a comment inside the bracketed `use`, | ||
246 | // assume it is a commented out module path and don't show diagnostic. | ||
247 | if use_tree_list.has_inner_comment() { | ||
248 | return Some(()); | ||
249 | } | ||
250 | |||
251 | let use_range = use_tree_list.syntax().text_range(); | ||
252 | let edit = | ||
253 | text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(&single_use_tree) | ||
254 | .unwrap_or_else(|| { | ||
255 | let to_replace = single_use_tree.syntax().text().to_string(); | ||
256 | let mut edit_builder = TextEdit::builder(); | ||
257 | edit_builder.delete(use_range); | ||
258 | edit_builder.insert(use_range.start(), to_replace); | ||
259 | edit_builder.finish() | ||
260 | }); | ||
261 | |||
262 | acc.push( | ||
263 | Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string()) | ||
264 | .with_fixes(Some(vec![fix( | ||
265 | "remove_braces", | ||
266 | "Remove unnecessary braces", | ||
267 | SourceChange::from_text_edit(file_id, edit), | ||
268 | use_range, | ||
269 | )])), | ||
270 | ); | ||
271 | } | ||
272 | |||
273 | Some(()) | ||
274 | } | ||
275 | |||
276 | fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement( | ||
277 | single_use_tree: &ast::UseTree, | ||
278 | ) -> Option<TextEdit> { | ||
279 | let use_tree_list_node = single_use_tree.syntax().parent()?; | ||
280 | if single_use_tree.path()?.segment()?.self_token().is_some() { | ||
281 | let start = use_tree_list_node.prev_sibling_or_token()?.text_range().start(); | ||
282 | let end = use_tree_list_node.text_range().end(); | ||
283 | return Some(TextEdit::delete(TextRange::new(start, end))); | ||
284 | } | ||
285 | None | ||
286 | } | ||
287 | |||
288 | fn fix(id: &'static str, label: &str, source_change: SourceChange, target: TextRange) -> Assist { | ||
289 | let mut res = unresolved_fix(id, label, target); | ||
290 | res.source_change = Some(source_change); | ||
291 | res | ||
292 | } | ||
293 | |||
294 | fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist { | ||
295 | assert!(!id.contains(' ')); | ||
296 | Assist { | ||
297 | id: AssistId(id, AssistKind::QuickFix), | ||
298 | label: Label::new(label), | ||
299 | group: None, | ||
300 | target, | ||
301 | source_change: None, | ||
302 | } | ||
303 | } | ||
304 | |||
305 | #[cfg(test)] | ||
306 | mod tests { | ||
307 | use expect_test::Expect; | ||
308 | use ide_assists::AssistResolveStrategy; | ||
309 | use stdx::trim_indent; | ||
310 | use test_utils::{assert_eq_text, extract_annotations}; | ||
311 | |||
312 | use crate::{fixture, DiagnosticsConfig}; | ||
313 | |||
314 | /// Takes a multi-file input fixture with annotated cursor positions, | ||
315 | /// and checks that: | ||
316 | /// * a diagnostic is produced | ||
317 | /// * the first diagnostic fix trigger range touches the input cursor position | ||
318 | /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied | ||
319 | #[track_caller] | ||
320 | pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { | ||
321 | check_nth_fix(0, ra_fixture_before, ra_fixture_after); | ||
322 | } | ||
323 | /// Takes a multi-file input fixture with annotated cursor positions, | ||
324 | /// and checks that: | ||
325 | /// * a diagnostic is produced | ||
326 | /// * every diagnostic fixes trigger range touches the input cursor position | ||
327 | /// * that the contents of the file containing the cursor match `after` after each diagnostic fix is applied | ||
328 | pub(crate) fn check_fixes(ra_fixture_before: &str, ra_fixtures_after: Vec<&str>) { | ||
329 | for (i, ra_fixture_after) in ra_fixtures_after.iter().enumerate() { | ||
330 | check_nth_fix(i, ra_fixture_before, ra_fixture_after) | ||
331 | } | ||
332 | } | ||
333 | |||
334 | #[track_caller] | ||
335 | fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) { | ||
336 | let after = trim_indent(ra_fixture_after); | ||
337 | |||
338 | let (analysis, file_position) = fixture::position(ra_fixture_before); | ||
339 | let diagnostic = analysis | ||
340 | .diagnostics( | ||
341 | &DiagnosticsConfig::default(), | ||
342 | AssistResolveStrategy::All, | ||
343 | file_position.file_id, | ||
344 | ) | ||
345 | .unwrap() | ||
346 | .pop() | ||
347 | .unwrap(); | ||
348 | let fix = &diagnostic.fixes.unwrap()[nth]; | ||
349 | let actual = { | ||
350 | let source_change = fix.source_change.as_ref().unwrap(); | ||
351 | let file_id = *source_change.source_file_edits.keys().next().unwrap(); | ||
352 | let mut actual = analysis.file_text(file_id).unwrap().to_string(); | ||
353 | |||
354 | for edit in source_change.source_file_edits.values() { | ||
355 | edit.apply(&mut actual); | ||
356 | } | ||
357 | actual | ||
358 | }; | ||
359 | |||
360 | assert_eq_text!(&after, &actual); | ||
361 | assert!( | ||
362 | fix.target.contains_inclusive(file_position.offset), | ||
363 | "diagnostic fix range {:?} does not touch cursor position {:?}", | ||
364 | fix.target, | ||
365 | file_position.offset | ||
366 | ); | ||
367 | } | ||
368 | /// Checks that there's a diagnostic *without* fix at `$0`. | ||
369 | fn check_no_fix(ra_fixture: &str) { | ||
370 | let (analysis, file_position) = fixture::position(ra_fixture); | ||
371 | let diagnostic = analysis | ||
372 | .diagnostics( | ||
373 | &DiagnosticsConfig::default(), | ||
374 | AssistResolveStrategy::All, | ||
375 | file_position.file_id, | ||
376 | ) | ||
377 | .unwrap() | ||
378 | .pop() | ||
379 | .unwrap(); | ||
380 | assert!(diagnostic.fixes.is_none(), "got a fix when none was expected: {:?}", diagnostic); | ||
381 | } | ||
382 | |||
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) { | ||
399 | let (analysis, file_id) = fixture::file(ra_fixture); | ||
400 | let diagnostics = analysis | ||
401 | .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id) | ||
402 | .unwrap(); | ||
403 | expect.assert_debug_eq(&diagnostics) | ||
404 | } | ||
405 | |||
406 | pub(crate) fn check_diagnostics(ra_fixture: &str) { | ||
407 | let (analysis, file_id) = fixture::file(ra_fixture); | ||
408 | let diagnostics = analysis | ||
409 | .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id) | ||
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 | } | ||
453 | |||
454 | #[test] | ||
455 | fn range_mapping_out_of_macros() { | ||
456 | // FIXME: this is very wrong, but somewhat tricky to fix. | ||
457 | check_fix( | ||
458 | r#" | ||
459 | fn some() {} | ||
460 | fn items() {} | ||
461 | fn here() {} | ||
462 | |||
463 | macro_rules! id { ($($tt:tt)*) => { $($tt)*}; } | ||
464 | |||
465 | fn main() { | ||
466 | let _x = id![Foo { a: $042 }]; | ||
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 | } | ||
486 | |||
487 | #[test] | ||
488 | fn test_check_unnecessary_braces_in_use_statement() { | ||
489 | check_no_diagnostics( | ||
490 | r#" | ||
491 | use a; | ||
492 | use a::{c, d::e}; | ||
493 | |||
494 | mod a { | ||
495 | mod c {} | ||
496 | mod d { | ||
497 | mod e {} | ||
498 | } | ||
499 | } | ||
500 | "#, | ||
501 | ); | ||
502 | check_no_diagnostics( | ||
503 | r#" | ||
504 | use a; | ||
505 | use a::{ | ||
506 | c, | ||
507 | // d::e | ||
508 | }; | ||
509 | |||
510 | mod a { | ||
511 | mod c {} | ||
512 | mod d { | ||
513 | mod e {} | ||
514 | } | ||
515 | } | ||
516 | "#, | ||
517 | ); | ||
518 | check_fix( | ||
519 | r" | ||
520 | mod b {} | ||
521 | use {$0b}; | ||
522 | ", | ||
523 | r" | ||
524 | mod b {} | ||
525 | use b; | ||
526 | ", | ||
527 | ); | ||
528 | check_fix( | ||
529 | r" | ||
530 | mod b {} | ||
531 | use {b$0}; | ||
532 | ", | ||
533 | r" | ||
534 | mod b {} | ||
535 | use b; | ||
536 | ", | ||
537 | ); | ||
538 | check_fix( | ||
539 | r" | ||
540 | mod a { mod c {} } | ||
541 | use a::{c$0}; | ||
542 | ", | ||
543 | r" | ||
544 | mod a { mod c {} } | ||
545 | use a::c; | ||
546 | ", | ||
547 | ); | ||
548 | check_fix( | ||
549 | r" | ||
550 | mod a {} | ||
551 | use a::{self$0}; | ||
552 | ", | ||
553 | r" | ||
554 | mod a {} | ||
555 | use a; | ||
556 | ", | ||
557 | ); | ||
558 | check_fix( | ||
559 | r" | ||
560 | mod a { mod c {} mod d { mod e {} } } | ||
561 | use a::{c, d::{e$0}}; | ||
562 | ", | ||
563 | r" | ||
564 | mod a { mod c {} mod d { mod e {} } } | ||
565 | use a::{c, d::e}; | ||
566 | ", | ||
567 | ); | ||
568 | } | ||
569 | |||
570 | #[test] | ||
571 | fn test_disabled_diagnostics() { | ||
572 | let mut config = DiagnosticsConfig::default(); | ||
573 | config.disabled.insert("unresolved-module".into()); | ||
574 | |||
575 | let (analysis, file_id) = fixture::file(r#"mod foo;"#); | ||
576 | |||
577 | let diagnostics = | ||
578 | analysis.diagnostics(&config, AssistResolveStrategy::All, file_id).unwrap(); | ||
579 | assert!(diagnostics.is_empty()); | ||
580 | |||
581 | let diagnostics = analysis | ||
582 | .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id) | ||
583 | .unwrap(); | ||
584 | assert!(!diagnostics.is_empty()); | ||
585 | } | ||
586 | |||
587 | #[test] | ||
588 | fn unlinked_file_prepend_first_item() { | ||
589 | cov_mark::check!(unlinked_file_prepend_before_first_item); | ||
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 | |||
634 | mod preexisting; | ||
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#" | ||
690 | mod foo; | ||
691 | "#, | ||
692 | ); | ||
693 | } | ||
694 | |||
695 | #[test] | ||
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 | |||
704 | //- /foo.rs | ||
705 | $0 | ||
706 | "#, | ||
707 | ); | ||
708 | } | ||
709 | |||
710 | #[test] | ||
711 | fn unlinked_file_with_cfg_on() { | ||
712 | check_no_diagnostics( | ||
713 | r#" | ||
714 | //- /main.rs | ||
715 | #[cfg(not(never))] | ||
716 | mod foo; | ||
717 | |||
718 | //- /foo.rs | ||
719 | "#, | ||
720 | ); | ||
721 | } | ||
722 | } | ||
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/unlinked_file.rs b/crates/ide/src/diagnostics/unlinked_file.rs deleted file mode 100644 index 51fe0f360..000000000 --- a/crates/ide/src/diagnostics/unlinked_file.rs +++ /dev/null | |||
@@ -1,183 +0,0 @@ | |||
1 | //! Diagnostic emitted for files that aren't part of any crate. | ||
2 | |||
3 | use hir::{ | ||
4 | db::DefDatabase, | ||
5 | diagnostics::{Diagnostic, DiagnosticCode}, | ||
6 | InFile, | ||
7 | }; | ||
8 | use ide_assists::AssistResolveStrategy; | ||
9 | use ide_db::{ | ||
10 | base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt}, | ||
11 | source_change::SourceChange, | ||
12 | RootDatabase, | ||
13 | }; | ||
14 | use syntax::{ | ||
15 | ast::{self, ModuleItemOwner, NameOwner}, | ||
16 | AstNode, SyntaxNodePtr, | ||
17 | }; | ||
18 | use text_edit::TextEdit; | ||
19 | |||
20 | use crate::{ | ||
21 | diagnostics::{fix, fixes::DiagnosticWithFixes}, | ||
22 | Assist, | ||
23 | }; | ||
24 | |||
25 | // Diagnostic: unlinked-file | ||
26 | // | ||
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. | ||
29 | #[derive(Debug)] | ||
30 | pub(crate) struct UnlinkedFile { | ||
31 | pub(crate) file_id: FileId, | ||
32 | pub(crate) node: SyntaxNodePtr, | ||
33 | } | ||
34 | |||
35 | impl Diagnostic for UnlinkedFile { | ||
36 | fn code(&self) -> DiagnosticCode { | ||
37 | DiagnosticCode("unlinked-file") | ||
38 | } | ||
39 | |||
40 | fn message(&self) -> String { | ||
41 | "file not included in module tree".to_string() | ||
42 | } | ||
43 | |||
44 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
45 | InFile::new(self.file_id.into(), self.node.clone()) | ||
46 | } | ||
47 | |||
48 | fn as_any(&self) -> &(dyn std::any::Any + Send + 'static) { | ||
49 | self | ||
50 | } | ||
51 | } | ||
52 | |||
53 | impl DiagnosticWithFixes for UnlinkedFile { | ||
54 | fn fixes( | ||
55 | &self, | ||
56 | sema: &hir::Semantics<RootDatabase>, | ||
57 | _resolve: &AssistResolveStrategy, | ||
58 | ) -> Option<Vec<Assist>> { | ||
59 | // If there's an existing module that could add `mod` or `pub mod` items to include the unlinked file, | ||
60 | // suggest that as a fix. | ||
61 | |||
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 | |||
82 | for path in &paths { | ||
83 | if let Some(parent_id) = source_root.file_for_path(path) { | ||
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 | } | ||
96 | } | ||
97 | } | ||
98 | } | ||
99 | |||
100 | None | ||
101 | } | ||
102 | } | ||
103 | |||
104 | fn make_fixes( | ||
105 | db: &RootDatabase, | ||
106 | parent_file_id: FileId, | ||
107 | new_mod_name: &str, | ||
108 | added_file_id: FileId, | ||
109 | ) -> Option<Vec<Assist>> { | ||
110 | fn is_outline_mod(item: &ast::Item) -> bool { | ||
111 | matches!(item, ast::Item::Module(m) if m.item_list().is_none()) | ||
112 | } | ||
113 | |||
114 | let mod_decl = format!("mod {};", new_mod_name); | ||
115 | let pub_mod_decl = format!("pub mod {};", new_mod_name); | ||
116 | |||
117 | let ast: ast::SourceFile = db.parse(parent_file_id).tree(); | ||
118 | |||
119 | let mut mod_decl_builder = TextEdit::builder(); | ||
120 | let mut pub_mod_decl_builder = TextEdit::builder(); | ||
121 | |||
122 | // If there's an existing `mod m;` statement matching the new one, don't emit a fix (it's | ||
123 | // probably `#[cfg]`d out). | ||
124 | for item in ast.items() { | ||
125 | if let ast::Item::Module(m) = item { | ||
126 | if let Some(name) = m.name() { | ||
127 | if m.item_list().is_none() && name.to_string() == new_mod_name { | ||
128 | cov_mark::hit!(unlinked_file_skip_fix_when_mod_already_exists); | ||
129 | return None; | ||
130 | } | ||
131 | } | ||
132 | } | ||
133 | } | ||
134 | |||
135 | // If there are existing `mod m;` items, append after them (after the first group of them, rather). | ||
136 | match ast | ||
137 | .items() | ||
138 | .skip_while(|item| !is_outline_mod(item)) | ||
139 | .take_while(|item| is_outline_mod(item)) | ||
140 | .last() | ||
141 | { | ||
142 | Some(last) => { | ||
143 | cov_mark::hit!(unlinked_file_append_to_existing_mods); | ||
144 | let offset = last.syntax().text_range().end(); | ||
145 | mod_decl_builder.insert(offset, format!("\n{}", mod_decl)); | ||
146 | pub_mod_decl_builder.insert(offset, format!("\n{}", pub_mod_decl)); | ||
147 | } | ||
148 | None => { | ||
149 | // Prepend before the first item in the file. | ||
150 | match ast.items().next() { | ||
151 | Some(item) => { | ||
152 | cov_mark::hit!(unlinked_file_prepend_before_first_item); | ||
153 | let offset = item.syntax().text_range().start(); | ||
154 | mod_decl_builder.insert(offset, format!("{}\n\n", mod_decl)); | ||
155 | pub_mod_decl_builder.insert(offset, format!("{}\n\n", pub_mod_decl)); | ||
156 | } | ||
157 | None => { | ||
158 | // No items in the file, so just append at the end. | ||
159 | cov_mark::hit!(unlinked_file_empty_file); | ||
160 | let offset = ast.syntax().text_range().end(); | ||
161 | mod_decl_builder.insert(offset, format!("{}\n", mod_decl)); | ||
162 | pub_mod_decl_builder.insert(offset, format!("{}\n", pub_mod_decl)); | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | } | ||
167 | |||
168 | let trigger_range = db.parse(added_file_id).tree().syntax().text_range(); | ||
169 | Some(vec![ | ||
170 | fix( | ||
171 | "add_mod_declaration", | ||
172 | &format!("Insert `{}`", mod_decl), | ||
173 | SourceChange::from_text_edit(parent_file_id, mod_decl_builder.finish()), | ||
174 | trigger_range, | ||
175 | ), | ||
176 | fix( | ||
177 | "add_pub_mod_declaration", | ||
178 | &format!("Insert `{}`", pub_mod_decl), | ||
179 | SourceChange::from_text_edit(parent_file_id, pub_mod_decl_builder.finish()), | ||
180 | trigger_range, | ||
181 | ), | ||
182 | ]) | ||
183 | } | ||
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs index b75ec411c..455b32456 100644 --- a/crates/ide/src/display/navigation_target.rs +++ b/crates/ide/src/display/navigation_target.rs | |||
@@ -442,10 +442,10 @@ impl TryToNav for hir::TypeParam { | |||
442 | fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { | 442 | fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { |
443 | let src = self.source(db)?; | 443 | let src = self.source(db)?; |
444 | let full_range = match &src.value { | 444 | let full_range = match &src.value { |
445 | Either::Left(it) => it | 445 | Either::Left(type_param) => type_param.syntax().text_range(), |
446 | Either::Right(trait_) => trait_ | ||
446 | .name() | 447 | .name() |
447 | .map_or_else(|| it.syntax().text_range(), |name| name.syntax().text_range()), | 448 | .map_or_else(|| trait_.syntax().text_range(), |name| name.syntax().text_range()), |
448 | Either::Right(it) => it.syntax().text_range(), | ||
449 | }; | 449 | }; |
450 | let focus_range = match &src.value { | 450 | let focus_range = match &src.value { |
451 | Either::Left(it) => it.name(), | 451 | Either::Left(it) => it.name(), |
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index ec3828ab2..7ac0118fe 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 | } |
@@ -241,6 +241,10 @@ fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> { | |||
241 | Definition::ModuleDef(ModuleDef::Module(module)) => module.krate(), | 241 | Definition::ModuleDef(ModuleDef::Module(module)) => module.krate(), |
242 | _ => definition.module(db)?.krate(), | 242 | _ => definition.module(db)?.krate(), |
243 | }; | 243 | }; |
244 | // FIXME: using import map doesn't make sense here. What we want here is | ||
245 | // canonical path. What import map returns is the shortest path suitable for | ||
246 | // import. See this test: | ||
247 | cov_mark::hit!(test_reexport_order); | ||
244 | let import_map = db.import_map(krate.into()); | 248 | let import_map = db.import_map(krate.into()); |
245 | 249 | ||
246 | let mut base = krate.display_name(db)?.to_string(); | 250 | let mut base = krate.display_name(db)?.to_string(); |
@@ -642,13 +646,15 @@ pub mod foo { | |||
642 | ) | 646 | ) |
643 | } | 647 | } |
644 | 648 | ||
645 | // FIXME: ImportMap will return re-export paths instead of public module | ||
646 | // paths. The correct path to documentation will never be a re-export. | ||
647 | // This problem stops us from resolving stdlib items included in the prelude | ||
648 | // such as `Option::Some` correctly. | ||
649 | #[ignore = "ImportMap may return re-exports"] | ||
650 | #[test] | 649 | #[test] |
651 | fn test_reexport_order() { | 650 | fn test_reexport_order() { |
651 | cov_mark::check!(test_reexport_order); | ||
652 | // FIXME: This should return | ||
653 | // | ||
654 | // https://docs.rs/test/*/test/wrapper/modulestruct.Item.html | ||
655 | // | ||
656 | // That is, we should point inside the module, rather than at the | ||
657 | // re-export. | ||
652 | check( | 658 | check( |
653 | r#" | 659 | r#" |
654 | pub mod wrapper { | 660 | pub mod wrapper { |
@@ -663,7 +669,7 @@ fn foo() { | |||
663 | let bar: wrapper::It$0em; | 669 | let bar: wrapper::It$0em; |
664 | } | 670 | } |
665 | "#, | 671 | "#, |
666 | expect![[r#"https://docs.rs/test/*/test/wrapper/module/struct.Item.html"#]], | 672 | expect![[r#"https://docs.rs/test/*/test/wrapper/struct.Item.html"#]], |
667 | ) | 673 | ) |
668 | } | 674 | } |
669 | } | 675 | } |
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..cf679edd3 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}; |
@@ -13,14 +12,6 @@ pub(crate) fn file(ra_fixture: &str) -> (Analysis, FileId) { | |||
13 | (host.analysis(), change_fixture.files[0]) | 12 | (host.analysis(), change_fixture.files[0]) |
14 | } | 13 | } |
15 | 14 | ||
16 | /// Creates analysis for many files. | ||
17 | pub(crate) fn files(ra_fixture: &str) -> (Analysis, Vec<FileId>) { | ||
18 | let mut host = AnalysisHost::default(); | ||
19 | let change_fixture = ChangeFixture::parse(ra_fixture); | ||
20 | host.db.apply_change(change_fixture.change); | ||
21 | (host.analysis(), change_fixture.files) | ||
22 | } | ||
23 | |||
24 | /// Creates analysis from a multi-file fixture, returns positions marked with $0. | 15 | /// Creates analysis from a multi-file fixture, returns positions marked with $0. |
25 | pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) { | 16 | pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) { |
26 | let mut host = AnalysisHost::default(); | 17 | let mut host = AnalysisHost::default(); |
@@ -63,15 +54,8 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil | |||
63 | 54 | ||
64 | pub(crate) fn nav_target_annotation(ra_fixture: &str) -> (Analysis, FilePosition, FileRange) { | 55 | pub(crate) fn nav_target_annotation(ra_fixture: &str) -> (Analysis, FilePosition, FileRange) { |
65 | let (analysis, position, mut annotations) = annotations(ra_fixture); | 56 | let (analysis, position, mut annotations) = annotations(ra_fixture); |
66 | let (mut expected, data) = annotations.pop().unwrap(); | 57 | let (expected, data) = annotations.pop().unwrap(); |
67 | assert!(annotations.is_empty()); | 58 | assert!(annotations.is_empty()); |
68 | match data.as_str() { | 59 | 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) | 60 | (analysis, position, expected) |
77 | } | 61 | } |
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 2d36c34e9..d8e0dc4d5 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -43,7 +43,7 @@ pub(crate) fn goto_definition( | |||
43 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; | 43 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; |
44 | let (_, link, ns) = | 44 | let (_, link, ns) = |
45 | extract_definitions_from_markdown(docs.as_str()).into_iter().find(|(range, ..)| { | 45 | extract_definitions_from_markdown(docs.as_str()).into_iter().find(|(range, ..)| { |
46 | 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 }| { |
47 | file_id == position.file_id.into() && range.contains(position.offset) | 47 | file_id == position.file_id.into() && range.contains(position.offset) |
48 | }) | 48 | }) |
49 | })?; | 49 | })?; |
@@ -57,7 +57,7 @@ pub(crate) fn goto_definition( | |||
57 | }, | 57 | }, |
58 | ast::Name(name) => { | 58 | ast::Name(name) => { |
59 | let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); | 59 | let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); |
60 | try_find_trait_item_definition(&sema.db, &def) | 60 | try_find_trait_item_definition(sema.db, &def) |
61 | .or_else(|| def.try_to_nav(sema.db)) | 61 | .or_else(|| def.try_to_nav(sema.db)) |
62 | }, | 62 | }, |
63 | 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, <) { |
@@ -185,7 +185,7 @@ mod tests { | |||
185 | extern crate std$0; | 185 | extern crate std$0; |
186 | //- /std/lib.rs crate:std | 186 | //- /std/lib.rs crate:std |
187 | // empty | 187 | // empty |
188 | //^ file | 188 | //^file |
189 | "#, | 189 | "#, |
190 | ) | 190 | ) |
191 | } | 191 | } |
@@ -198,7 +198,7 @@ extern crate std$0; | |||
198 | extern crate std as abc$0; | 198 | extern crate std as abc$0; |
199 | //- /std/lib.rs crate:std | 199 | //- /std/lib.rs crate:std |
200 | // empty | 200 | // empty |
201 | //^ file | 201 | //^file |
202 | "#, | 202 | "#, |
203 | ) | 203 | ) |
204 | } | 204 | } |
@@ -253,7 +253,7 @@ mod $0foo; | |||
253 | 253 | ||
254 | //- /foo.rs | 254 | //- /foo.rs |
255 | // empty | 255 | // empty |
256 | //^ file | 256 | //^file |
257 | "#, | 257 | "#, |
258 | ); | 258 | ); |
259 | 259 | ||
@@ -264,7 +264,7 @@ mod $0foo; | |||
264 | 264 | ||
265 | //- /foo/mod.rs | 265 | //- /foo/mod.rs |
266 | // empty | 266 | // empty |
267 | //^ file | 267 | //^file |
268 | "#, | 268 | "#, |
269 | ); | 269 | ); |
270 | } | 270 | } |
@@ -395,7 +395,7 @@ use foo as bar$0; | |||
395 | 395 | ||
396 | //- /foo/lib.rs crate:foo | 396 | //- /foo/lib.rs crate:foo |
397 | // empty | 397 | // empty |
398 | //^ file | 398 | //^file |
399 | "#, | 399 | "#, |
400 | ); | 400 | ); |
401 | } | 401 | } |
@@ -1130,15 +1130,15 @@ fn foo<'foobar>(_: &'foobar ()) { | |||
1130 | } | 1130 | } |
1131 | 1131 | ||
1132 | #[test] | 1132 | #[test] |
1133 | #[ignore] // requires the HIR to somehow track these hrtb lifetimes | ||
1134 | fn goto_lifetime_hrtb() { | 1133 | fn goto_lifetime_hrtb() { |
1135 | check( | 1134 | // FIXME: requires the HIR to somehow track these hrtb lifetimes |
1135 | check_unresolved( | ||
1136 | r#"trait Foo<T> {} | 1136 | r#"trait Foo<T> {} |
1137 | fn foo<T>() where for<'a> T: Foo<&'a$0 (u8, u16)>, {} | 1137 | fn foo<T>() where for<'a> T: Foo<&'a$0 (u8, u16)>, {} |
1138 | //^^ | 1138 | //^^ |
1139 | "#, | 1139 | "#, |
1140 | ); | 1140 | ); |
1141 | check( | 1141 | check_unresolved( |
1142 | r#"trait Foo<T> {} | 1142 | r#"trait Foo<T> {} |
1143 | fn foo<T>() where for<'a$0> T: Foo<&'a (u8, u16)>, {} | 1143 | fn foo<T>() where for<'a$0> T: Foo<&'a (u8, u16)>, {} |
1144 | //^^ | 1144 | //^^ |
@@ -1147,9 +1147,9 @@ fn foo<T>() where for<'a$0> T: Foo<&'a (u8, u16)>, {} | |||
1147 | } | 1147 | } |
1148 | 1148 | ||
1149 | #[test] | 1149 | #[test] |
1150 | #[ignore] // requires ForTypes to be implemented | ||
1151 | fn goto_lifetime_hrtb_for_type() { | 1150 | fn goto_lifetime_hrtb_for_type() { |
1152 | check( | 1151 | // FIXME: requires ForTypes to be implemented |
1152 | check_unresolved( | ||
1153 | r#"trait Foo<T> {} | 1153 | r#"trait Foo<T> {} |
1154 | fn foo<T>() where T: for<'a> Foo<&'a$0 (u8, u16)>, {} | 1154 | fn foo<T>() where T: for<'a> Foo<&'a$0 (u8, u16)>, {} |
1155 | //^^ | 1155 | //^^ |
@@ -1287,7 +1287,7 @@ fn main() { | |||
1287 | } | 1287 | } |
1288 | //- /foo.txt | 1288 | //- /foo.txt |
1289 | // empty | 1289 | // empty |
1290 | //^ file | 1290 | //^file |
1291 | "#, | 1291 | "#, |
1292 | ); | 1292 | ); |
1293 | } | 1293 | } |
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index 95fd39850..0013820b4 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs | |||
@@ -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/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs index 004d9cb68..ca3c02bf6 100644 --- a/crates/ide/src/goto_type_definition.rs +++ b/crates/ide/src/goto_type_definition.rs | |||
@@ -25,7 +25,7 @@ pub(crate) fn goto_type_definition( | |||
25 | let token: SyntaxToken = pick_best(file.syntax().token_at_offset(position.offset))?; | 25 | let token: SyntaxToken = pick_best(file.syntax().token_at_offset(position.offset))?; |
26 | let token: SyntaxToken = sema.descend_into_macros(token); | 26 | let token: SyntaxToken = sema.descend_into_macros(token); |
27 | 27 | ||
28 | let (ty, node) = sema.token_ancestors_with_macros(token).find_map(|node| { | 28 | let (ty, node) = sema.token_ancestors_with_macros(token.clone()).find_map(|node| { |
29 | let ty = match_ast! { | 29 | let ty = match_ast! { |
30 | match node { | 30 | match node { |
31 | ast::Expr(it) => sema.type_of_expr(&it)?, | 31 | ast::Expr(it) => sema.type_of_expr(&it)?, |
@@ -33,13 +33,23 @@ pub(crate) fn goto_type_definition( | |||
33 | ast::SelfParam(it) => sema.type_of_self(&it)?, | 33 | ast::SelfParam(it) => sema.type_of_self(&it)?, |
34 | ast::Type(it) => sema.resolve_type(&it)?, | 34 | ast::Type(it) => sema.resolve_type(&it)?, |
35 | ast::RecordField(it) => sema.to_def(&it).map(|d| d.ty(db.upcast()))?, | 35 | ast::RecordField(it) => sema.to_def(&it).map(|d| d.ty(db.upcast()))?, |
36 | ast::RecordField(it) => sema.to_def(&it).map(|d| d.ty(db.upcast()))?, | ||
37 | // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise | ||
38 | ast::NameRef(it) => { | ||
39 | if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) { | ||
40 | let (_, _, ty) = sema.resolve_record_field(&record_field)?; | ||
41 | ty | ||
42 | } else { | ||
43 | let record_field = ast::RecordPatField::for_field_name_ref(&it)?; | ||
44 | sema.resolve_record_pat_field(&record_field)?.ty(db) | ||
45 | } | ||
46 | }, | ||
36 | _ => return None, | 47 | _ => return None, |
37 | } | 48 | } |
38 | }; | 49 | }; |
39 | 50 | ||
40 | Some((ty, node)) | 51 | Some((ty, node)) |
41 | })?; | 52 | })?; |
42 | |||
43 | let adt_def = ty.autoderef(db).filter_map(|ty| ty.as_adt()).last()?; | 53 | let adt_def = ty.autoderef(db).filter_map(|ty| ty.as_adt()).last()?; |
44 | 54 | ||
45 | let nav = adt_def.try_to_nav(db)?; | 55 | let nav = adt_def.try_to_nav(db)?; |
@@ -88,6 +98,54 @@ fn foo() { | |||
88 | } | 98 | } |
89 | 99 | ||
90 | #[test] | 100 | #[test] |
101 | fn goto_type_definition_record_expr_field() { | ||
102 | check( | ||
103 | r#" | ||
104 | struct Bar; | ||
105 | // ^^^ | ||
106 | struct Foo { foo: Bar } | ||
107 | fn foo() { | ||
108 | Foo { foo$0 } | ||
109 | } | ||
110 | "#, | ||
111 | ); | ||
112 | check( | ||
113 | r#" | ||
114 | struct Bar; | ||
115 | // ^^^ | ||
116 | struct Foo { foo: Bar } | ||
117 | fn foo() { | ||
118 | Foo { foo$0: Bar } | ||
119 | } | ||
120 | "#, | ||
121 | ); | ||
122 | } | ||
123 | |||
124 | #[test] | ||
125 | fn goto_type_definition_record_pat_field() { | ||
126 | check( | ||
127 | r#" | ||
128 | struct Bar; | ||
129 | // ^^^ | ||
130 | struct Foo { foo: Bar } | ||
131 | fn foo() { | ||
132 | let Foo { foo$0 }; | ||
133 | } | ||
134 | "#, | ||
135 | ); | ||
136 | check( | ||
137 | r#" | ||
138 | struct Bar; | ||
139 | // ^^^ | ||
140 | struct Foo { foo: Bar } | ||
141 | fn foo() { | ||
142 | let Foo { foo$0: bar }; | ||
143 | } | ||
144 | "#, | ||
145 | ); | ||
146 | } | ||
147 | |||
148 | #[test] | ||
91 | fn goto_type_definition_works_simple_ref() { | 149 | fn goto_type_definition_works_simple_ref() { |
92 | check( | 150 | check( |
93 | r#" | 151 | r#" |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 1c6d36939..529cf5f33 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -131,7 +131,7 @@ pub(crate) fn hover( | |||
131 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; | 131 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; |
132 | let (idl_range, link, ns) = | 132 | let (idl_range, link, ns) = |
133 | 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)| { |
134 | let InFile { file_id, value: range } = doc_mapping.map(range.clone())?; | 134 | let InFile { file_id, value: range } = doc_mapping.map(range)?; |
135 | if file_id == position.file_id.into() && range.contains(position.offset) { | 135 | if file_id == position.file_id.into() && range.contains(position.offset) { |
136 | Some((range, link, ns)) | 136 | Some((range, link, ns)) |
137 | } else { | 137 | } else { |
@@ -288,7 +288,7 @@ fn runnable_action( | |||
288 | ) -> Option<HoverAction> { | 288 | ) -> Option<HoverAction> { |
289 | match def { | 289 | match def { |
290 | Definition::ModuleDef(it) => match it { | 290 | Definition::ModuleDef(it) => match it { |
291 | ModuleDef::Module(it) => runnable_mod(&sema, it).map(|it| HoverAction::Runnable(it)), | 291 | ModuleDef::Module(it) => runnable_mod(sema, it).map(HoverAction::Runnable), |
292 | ModuleDef::Function(func) => { | 292 | ModuleDef::Function(func) => { |
293 | let src = func.source(sema.db)?; | 293 | let src = func.source(sema.db)?; |
294 | if src.file_id != file_id.into() { | 294 | if src.file_id != file_id.into() { |
@@ -297,7 +297,7 @@ fn runnable_action( | |||
297 | return None; | 297 | return None; |
298 | } | 298 | } |
299 | 299 | ||
300 | runnable_fn(&sema, func).map(HoverAction::Runnable) | 300 | runnable_fn(sema, func).map(HoverAction::Runnable) |
301 | } | 301 | } |
302 | _ => None, | 302 | _ => None, |
303 | }, | 303 | }, |
@@ -432,7 +432,7 @@ fn hover_for_definition( | |||
432 | return match def { | 432 | return match def { |
433 | Definition::Macro(it) => match &it.source(db)?.value { | 433 | Definition::Macro(it) => match &it.source(db)?.value { |
434 | Either::Left(mac) => { | 434 | Either::Left(mac) => { |
435 | let label = macro_label(&mac); | 435 | let label = macro_label(mac); |
436 | from_def_source_labeled(db, it, Some(label), mod_path) | 436 | from_def_source_labeled(db, it, Some(label), mod_path) |
437 | } | 437 | } |
438 | Either::Right(_) => { | 438 | Either::Right(_) => { |
@@ -516,7 +516,7 @@ fn hover_for_keyword( | |||
516 | if !token.kind().is_keyword() { | 516 | if !token.kind().is_keyword() { |
517 | return None; | 517 | return None; |
518 | } | 518 | } |
519 | let famous_defs = FamousDefs(&sema, sema.scope(&token.parent()?).krate()); | 519 | let famous_defs = FamousDefs(sema, sema.scope(&token.parent()?).krate()); |
520 | // 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 |
521 | let keyword_mod = format!("{}_keyword", token.text()); | 521 | let keyword_mod = format!("{}_keyword", token.text()); |
522 | let doc_owner = find_std_module(&famous_defs, &keyword_mod)?; | 522 | let doc_owner = find_std_module(&famous_defs, &keyword_mod)?; |
@@ -1821,9 +1821,10 @@ pub struct B$0ar | |||
1821 | ); | 1821 | ); |
1822 | } | 1822 | } |
1823 | 1823 | ||
1824 | #[ignore = "path based links currently only support documentation on ModuleDef items"] | ||
1825 | #[test] | 1824 | #[test] |
1826 | fn test_hover_path_link_field() { | 1825 | fn test_hover_path_link_field() { |
1826 | // FIXME: Should be | ||
1827 | // [Foo](https://docs.rs/test/*/test/struct.Foo.html) | ||
1827 | check( | 1828 | check( |
1828 | r#" | 1829 | r#" |
1829 | pub struct Foo; | 1830 | pub struct Foo; |
@@ -1845,7 +1846,7 @@ pub struct Bar { | |||
1845 | 1846 | ||
1846 | --- | 1847 | --- |
1847 | 1848 | ||
1848 | [Foo](https://docs.rs/test/*/test/struct.Foo.html) | 1849 | [Foo](struct.Foo.html) |
1849 | "#]], | 1850 | "#]], |
1850 | ); | 1851 | ); |
1851 | } | 1852 | } |
@@ -2999,29 +3000,24 @@ fn foo(ar$0g: &impl Foo + Bar<S>) {} | |||
2999 | fn test_hover_async_block_impl_trait_has_goto_type_action() { | 3000 | fn test_hover_async_block_impl_trait_has_goto_type_action() { |
3000 | check_actions( | 3001 | check_actions( |
3001 | r#" | 3002 | r#" |
3003 | //- minicore: future | ||
3002 | struct S; | 3004 | struct S; |
3003 | fn foo() { | 3005 | fn foo() { |
3004 | let fo$0o = async { S }; | 3006 | let fo$0o = async { S }; |
3005 | } | 3007 | } |
3006 | |||
3007 | #[prelude_import] use future::*; | ||
3008 | mod future { | ||
3009 | #[lang = "future_trait"] | ||
3010 | pub trait Future { type Output; } | ||
3011 | } | ||
3012 | "#, | 3008 | "#, |
3013 | expect![[r#" | 3009 | expect![[r#" |
3014 | [ | 3010 | [ |
3015 | GoToType( | 3011 | GoToType( |
3016 | [ | 3012 | [ |
3017 | HoverGotoTypeData { | 3013 | HoverGotoTypeData { |
3018 | mod_path: "test::future::Future", | 3014 | mod_path: "core::future::Future", |
3019 | nav: NavigationTarget { | 3015 | nav: NavigationTarget { |
3020 | file_id: FileId( | 3016 | file_id: FileId( |
3021 | 0, | 3017 | 1, |
3022 | ), | 3018 | ), |
3023 | full_range: 101..163, | 3019 | full_range: 245..427, |
3024 | focus_range: 140..146, | 3020 | focus_range: 284..290, |
3025 | name: "Future", | 3021 | name: "Future", |
3026 | kind: Trait, | 3022 | kind: Trait, |
3027 | description: "pub trait Future", | 3023 | description: "pub trait Future", |
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 821c61403..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() |
@@ -165,7 +165,7 @@ fn get_param_name_hints( | |||
165 | }; | 165 | }; |
166 | Some((param_name, arg)) | 166 | Some((param_name, arg)) |
167 | }) | 167 | }) |
168 | .filter(|(param_name, arg)| !should_hide_param_name_hint(sema, &callable, param_name, &arg)) | 168 | .filter(|(param_name, arg)| !should_hide_param_name_hint(sema, &callable, param_name, arg)) |
169 | .map(|(param_name, arg)| InlayHint { | 169 | .map(|(param_name, arg)| InlayHint { |
170 | range: arg.syntax().text_range(), | 170 | range: arg.syntax().text_range(), |
171 | kind: InlayKind::ParameterHint, | 171 | kind: InlayKind::ParameterHint, |
@@ -187,7 +187,7 @@ fn get_bind_pat_hints( | |||
187 | } | 187 | } |
188 | 188 | ||
189 | let krate = sema.scope(pat.syntax()).module().map(|it| it.krate()); | 189 | let krate = sema.scope(pat.syntax()).module().map(|it| it.krate()); |
190 | let famous_defs = FamousDefs(&sema, krate); | 190 | let famous_defs = FamousDefs(sema, krate); |
191 | 191 | ||
192 | let ty = sema.type_of_pat(&pat.clone().into())?; | 192 | let ty = sema.type_of_pat(&pat.clone().into())?; |
193 | 193 | ||
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..4bd073cc3 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -24,7 +24,6 @@ mod display; | |||
24 | 24 | ||
25 | mod annotations; | 25 | mod annotations; |
26 | mod call_hierarchy; | 26 | mod call_hierarchy; |
27 | mod diagnostics; | ||
28 | mod expand_macro; | 27 | mod expand_macro; |
29 | mod extend_selection; | 28 | mod extend_selection; |
30 | mod file_structure; | 29 | mod file_structure; |
@@ -40,6 +39,7 @@ mod matching_brace; | |||
40 | mod move_item; | 39 | mod move_item; |
41 | mod parent_module; | 40 | mod parent_module; |
42 | mod references; | 41 | mod references; |
42 | mod rename; | ||
43 | mod fn_references; | 43 | mod fn_references; |
44 | mod runnables; | 44 | mod runnables; |
45 | mod ssr; | 45 | mod ssr; |
@@ -71,7 +71,6 @@ use crate::display::ToNav; | |||
71 | pub use crate::{ | 71 | pub use crate::{ |
72 | annotations::{Annotation, AnnotationConfig, AnnotationKind}, | 72 | annotations::{Annotation, AnnotationConfig, AnnotationKind}, |
73 | call_hierarchy::CallItem, | 73 | call_hierarchy::CallItem, |
74 | diagnostics::{Diagnostic, DiagnosticsConfig, Severity}, | ||
75 | display::navigation_target::NavigationTarget, | 74 | display::navigation_target::NavigationTarget, |
76 | expand_macro::ExpandedMacro, | 75 | expand_macro::ExpandedMacro, |
77 | file_structure::{StructureNode, StructureNodeKind}, | 76 | file_structure::{StructureNode, StructureNodeKind}, |
@@ -81,7 +80,8 @@ pub use crate::{ | |||
81 | markup::Markup, | 80 | markup::Markup, |
82 | move_item::Direction, | 81 | move_item::Direction, |
83 | prime_caches::PrimeCachesProgress, | 82 | prime_caches::PrimeCachesProgress, |
84 | references::{rename::RenameError, ReferenceSearchResult}, | 83 | references::ReferenceSearchResult, |
84 | rename::RenameError, | ||
85 | runnables::{Runnable, RunnableKind, TestId}, | 85 | runnables::{Runnable, RunnableKind, TestId}, |
86 | syntax_highlighting::{ | 86 | syntax_highlighting::{ |
87 | tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag}, | 87 | tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag}, |
@@ -109,6 +109,7 @@ pub use ide_db::{ | |||
109 | symbol_index::Query, | 109 | symbol_index::Query, |
110 | RootDatabase, SymbolKind, | 110 | RootDatabase, SymbolKind, |
111 | }; | 111 | }; |
112 | pub use ide_diagnostics::{Diagnostic, DiagnosticsConfig, Severity}; | ||
112 | pub use ide_ssr::SsrError; | 113 | pub use ide_ssr::SsrError; |
113 | pub use syntax::{TextRange, TextSize}; | 114 | pub use syntax::{TextRange, TextSize}; |
114 | pub use text_edit::{Indel, TextEdit}; | 115 | pub use text_edit::{Indel, TextEdit}; |
@@ -282,20 +283,20 @@ impl Analysis { | |||
282 | file_id: FileId, | 283 | file_id: FileId, |
283 | text_range: Option<TextRange>, | 284 | text_range: Option<TextRange>, |
284 | ) -> Cancellable<String> { | 285 | ) -> Cancellable<String> { |
285 | self.with_db(|db| syntax_tree::syntax_tree(&db, file_id, text_range)) | 286 | self.with_db(|db| syntax_tree::syntax_tree(db, file_id, text_range)) |
286 | } | 287 | } |
287 | 288 | ||
288 | pub fn view_hir(&self, position: FilePosition) -> Cancellable<String> { | 289 | pub fn view_hir(&self, position: FilePosition) -> Cancellable<String> { |
289 | self.with_db(|db| view_hir::view_hir(&db, position)) | 290 | self.with_db(|db| view_hir::view_hir(db, position)) |
290 | } | 291 | } |
291 | 292 | ||
292 | pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> { | 293 | 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)) | 294 | self.with_db(|db| view_item_tree::view_item_tree(db, file_id)) |
294 | } | 295 | } |
295 | 296 | ||
296 | /// Renders the crate graph to GraphViz "dot" syntax. | 297 | /// Renders the crate graph to GraphViz "dot" syntax. |
297 | pub fn view_crate_graph(&self) -> Cancellable<Result<String, String>> { | 298 | pub fn view_crate_graph(&self) -> Cancellable<Result<String, String>> { |
298 | self.with_db(|db| view_crate_graph::view_crate_graph(&db)) | 299 | self.with_db(|db| view_crate_graph::view_crate_graph(db)) |
299 | } | 300 | } |
300 | 301 | ||
301 | pub fn expand_macro(&self, position: FilePosition) -> Cancellable<Option<ExpandedMacro>> { | 302 | pub fn expand_macro(&self, position: FilePosition) -> Cancellable<Option<ExpandedMacro>> { |
@@ -315,7 +316,7 @@ impl Analysis { | |||
315 | /// up minor stuff like continuing the comment. | 316 | /// up minor stuff like continuing the comment. |
316 | /// The edit will be a snippet (with `$0`). | 317 | /// The edit will be a snippet (with `$0`). |
317 | pub fn on_enter(&self, position: FilePosition) -> Cancellable<Option<TextEdit>> { | 318 | pub fn on_enter(&self, position: FilePosition) -> Cancellable<Option<TextEdit>> { |
318 | self.with_db(|db| typing::on_enter(&db, position)) | 319 | self.with_db(|db| typing::on_enter(db, position)) |
319 | } | 320 | } |
320 | 321 | ||
321 | /// Returns an edit which should be applied after a character was typed. | 322 | /// Returns an edit which should be applied after a character was typed. |
@@ -331,7 +332,7 @@ impl Analysis { | |||
331 | if !typing::TRIGGER_CHARS.contains(char_typed) { | 332 | if !typing::TRIGGER_CHARS.contains(char_typed) { |
332 | return Ok(None); | 333 | return Ok(None); |
333 | } | 334 | } |
334 | self.with_db(|db| typing::on_char_typed(&db, position, char_typed)) | 335 | self.with_db(|db| typing::on_char_typed(db, position, char_typed)) |
335 | } | 336 | } |
336 | 337 | ||
337 | /// Returns a tree representation of symbols in the file. Useful to draw a | 338 | /// Returns a tree representation of symbols in the file. Useful to draw a |
@@ -536,7 +537,7 @@ impl Analysis { | |||
536 | ) -> Cancellable<Vec<Assist>> { | 537 | ) -> Cancellable<Vec<Assist>> { |
537 | self.with_db(|db| { | 538 | self.with_db(|db| { |
538 | let ssr_assists = ssr::ssr_assists(db, &resolve, frange); | 539 | let ssr_assists = ssr::ssr_assists(db, &resolve, frange); |
539 | let mut acc = Assist::get(db, config, resolve, frange); | 540 | let mut acc = ide_assists::assists(db, config, resolve, frange); |
540 | acc.extend(ssr_assists.into_iter()); | 541 | acc.extend(ssr_assists.into_iter()); |
541 | acc | 542 | acc |
542 | }) | 543 | }) |
@@ -549,7 +550,7 @@ impl Analysis { | |||
549 | resolve: AssistResolveStrategy, | 550 | resolve: AssistResolveStrategy, |
550 | file_id: FileId, | 551 | file_id: FileId, |
551 | ) -> Cancellable<Vec<Diagnostic>> { | 552 | ) -> Cancellable<Vec<Diagnostic>> { |
552 | self.with_db(|db| diagnostics::diagnostics(db, config, &resolve, file_id)) | 553 | self.with_db(|db| ide_diagnostics::diagnostics(db, config, &resolve, file_id)) |
553 | } | 554 | } |
554 | 555 | ||
555 | /// Convenience function to return assists + quick fixes for diagnostics | 556 | /// Convenience function to return assists + quick fixes for diagnostics |
@@ -566,9 +567,8 @@ impl Analysis { | |||
566 | }; | 567 | }; |
567 | 568 | ||
568 | self.with_db(|db| { | 569 | self.with_db(|db| { |
569 | let ssr_assists = ssr::ssr_assists(db, &resolve, frange); | ||
570 | let diagnostic_assists = if include_fixes { | 570 | let diagnostic_assists = if include_fixes { |
571 | diagnostics::diagnostics(db, diagnostics_config, &resolve, frange.file_id) | 571 | ide_diagnostics::diagnostics(db, diagnostics_config, &resolve, frange.file_id) |
572 | .into_iter() | 572 | .into_iter() |
573 | .flat_map(|it| it.fixes.unwrap_or_default()) | 573 | .flat_map(|it| it.fixes.unwrap_or_default()) |
574 | .filter(|it| it.target.intersect(frange.range).is_some()) | 574 | .filter(|it| it.target.intersect(frange.range).is_some()) |
@@ -576,10 +576,12 @@ impl Analysis { | |||
576 | } else { | 576 | } else { |
577 | Vec::new() | 577 | Vec::new() |
578 | }; | 578 | }; |
579 | let ssr_assists = ssr::ssr_assists(db, &resolve, frange); | ||
580 | let assists = ide_assists::assists(db, assist_config, resolve, frange); | ||
579 | 581 | ||
580 | let mut res = Assist::get(db, assist_config, resolve, frange); | 582 | let mut res = diagnostic_assists; |
581 | res.extend(ssr_assists.into_iter()); | 583 | res.extend(ssr_assists.into_iter()); |
582 | res.extend(diagnostic_assists.into_iter()); | 584 | res.extend(assists.into_iter()); |
583 | 585 | ||
584 | res | 586 | res |
585 | }) | 587 | }) |
@@ -592,14 +594,14 @@ impl Analysis { | |||
592 | position: FilePosition, | 594 | position: FilePosition, |
593 | new_name: &str, | 595 | new_name: &str, |
594 | ) -> Cancellable<Result<SourceChange, RenameError>> { | 596 | ) -> Cancellable<Result<SourceChange, RenameError>> { |
595 | self.with_db(|db| references::rename::rename(db, position, new_name)) | 597 | self.with_db(|db| rename::rename(db, position, new_name)) |
596 | } | 598 | } |
597 | 599 | ||
598 | pub fn prepare_rename( | 600 | pub fn prepare_rename( |
599 | &self, | 601 | &self, |
600 | position: FilePosition, | 602 | position: FilePosition, |
601 | ) -> Cancellable<Result<RangeInfo<()>, RenameError>> { | 603 | ) -> Cancellable<Result<RangeInfo<()>, RenameError>> { |
602 | self.with_db(|db| references::rename::prepare_rename(db, position)) | 604 | self.with_db(|db| rename::prepare_rename(db, position)) |
603 | } | 605 | } |
604 | 606 | ||
605 | pub fn will_rename_file( | 607 | pub fn will_rename_file( |
@@ -607,7 +609,7 @@ impl Analysis { | |||
607 | file_id: FileId, | 609 | file_id: FileId, |
608 | new_name_stem: &str, | 610 | new_name_stem: &str, |
609 | ) -> Cancellable<Option<SourceChange>> { | 611 | ) -> Cancellable<Option<SourceChange>> { |
610 | self.with_db(|db| references::rename::will_rename_file(db, file_id, new_name_stem)) | 612 | self.with_db(|db| rename::will_rename_file(db, file_id, new_name_stem)) |
611 | } | 613 | } |
612 | 614 | ||
613 | pub fn structural_search_replace( | 615 | pub fn structural_search_replace( |
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index f8b64a669..945c9b9e1 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -9,8 +9,6 @@ | |||
9 | //! at the index that the match starts at and its tree parent is | 9 | //! at the index that the match starts at and its tree parent is |
10 | //! resolved to the search element definition, we get a reference. | 10 | //! resolved to the search element definition, we get a reference. |
11 | 11 | ||
12 | pub(crate) mod rename; | ||
13 | |||
14 | use hir::{PathResolution, Semantics}; | 12 | use hir::{PathResolution, Semantics}; |
15 | use ide_db::{ | 13 | use ide_db::{ |
16 | base_db::FileId, | 14 | base_db::FileId, |
@@ -62,7 +60,7 @@ pub(crate) fn find_all_refs( | |||
62 | if let Some(name) = get_name_of_item_declaration(&syntax, position) { | 60 | if let Some(name) = get_name_of_item_declaration(&syntax, position) { |
63 | (NameClass::classify(sema, &name)?.referenced_or_defined(sema.db), true) | 61 | (NameClass::classify(sema, &name)?.referenced_or_defined(sema.db), true) |
64 | } else { | 62 | } else { |
65 | (find_def(&sema, &syntax, position)?, false) | 63 | (find_def(sema, &syntax, position)?, false) |
66 | }; | 64 | }; |
67 | 65 | ||
68 | let mut usages = def.usages(sema).set_scope(search_scope).include_self_refs().all(); | 66 | let mut usages = def.usages(sema).set_scope(search_scope).include_self_refs().all(); |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/rename.rs index 7dfc5043e..8096dfa0e 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/rename.rs | |||
@@ -1,45 +1,25 @@ | |||
1 | //! Renaming functionality | 1 | //! Renaming functionality. |
2 | //! | 2 | //! |
3 | //! All reference and file rename requests go through here where the corresponding [`SourceChange`]s | 3 | //! This is mostly front-end for [`ide_db::rename`], but it also includes the |
4 | //! will be calculated. | 4 | //! tests. This module also implements a couple of magic tricks, like renaming |
5 | use std::fmt::{self, Display}; | 5 | //! `self` and to `self` (to switch between associated function and method). |
6 | 6 | use hir::{AsAssocItem, InFile, Semantics}; | |
7 | use either::Either; | ||
8 | use hir::{AsAssocItem, InFile, Module, ModuleDef, ModuleSource, Semantics}; | ||
9 | use ide_db::{ | 7 | use ide_db::{ |
10 | base_db::{AnchoredPathBuf, FileId}, | 8 | base_db::FileId, |
11 | defs::{Definition, NameClass, NameRefClass}, | 9 | defs::{Definition, NameClass, NameRefClass}, |
12 | search::FileReference, | 10 | rename::{bail, format_err, source_edit_from_references, IdentifierKind}, |
13 | RootDatabase, | 11 | RootDatabase, |
14 | }; | 12 | }; |
15 | use stdx::never; | 13 | use stdx::never; |
16 | use syntax::{ | 14 | use syntax::{ast, AstNode, SyntaxNode}; |
17 | ast::{self, NameOwner}, | ||
18 | lex_single_syntax_kind, AstNode, SyntaxKind, SyntaxNode, T, | ||
19 | }; | ||
20 | 15 | ||
21 | use text_edit::TextEdit; | 16 | use text_edit::TextEdit; |
22 | 17 | ||
23 | use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange}; | 18 | use crate::{FilePosition, RangeInfo, SourceChange}; |
24 | |||
25 | type RenameResult<T> = Result<T, RenameError>; | ||
26 | #[derive(Debug)] | ||
27 | pub struct RenameError(String); | ||
28 | |||
29 | impl fmt::Display for RenameError { | ||
30 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
31 | Display::fmt(&self.0, f) | ||
32 | } | ||
33 | } | ||
34 | 19 | ||
35 | macro_rules! format_err { | 20 | pub use ide_db::rename::RenameError; |
36 | ($fmt:expr) => {RenameError(format!($fmt))}; | ||
37 | ($fmt:expr, $($arg:tt)+) => {RenameError(format!($fmt, $($arg)+))} | ||
38 | } | ||
39 | 21 | ||
40 | macro_rules! bail { | 22 | type RenameResult<T> = Result<T, RenameError>; |
41 | ($($tokens:tt)*) => {return Err(format_err!($($tokens)*))} | ||
42 | } | ||
43 | 23 | ||
44 | /// Prepares a rename. The sole job of this function is to return the TextRange of the thing that is | 24 | /// Prepares a rename. The sole job of this function is to return the TextRange of the thing that is |
45 | /// being targeted for a rename. | 25 | /// being targeted for a rename. |
@@ -52,26 +32,10 @@ pub(crate) fn prepare_rename( | |||
52 | let syntax = source_file.syntax(); | 32 | let syntax = source_file.syntax(); |
53 | 33 | ||
54 | let def = find_definition(&sema, syntax, position)?; | 34 | let def = find_definition(&sema, syntax, position)?; |
55 | match def { | 35 | let frange = def |
56 | Definition::SelfType(_) => bail!("Cannot rename `Self`"), | 36 | .range_for_rename(&sema) |
57 | Definition::ModuleDef(ModuleDef::BuiltinType(_)) => bail!("Cannot rename builtin type"), | ||
58 | Definition::ModuleDef(ModuleDef::Module(_)) => (), | ||
59 | _ => { | ||
60 | let nav = def | ||
61 | .try_to_nav(sema.db) | ||
62 | .ok_or_else(|| format_err!("No references found at position"))?; | ||
63 | nav.focus_range.ok_or_else(|| format_err!("No identifier available to rename"))?; | ||
64 | } | ||
65 | }; | ||
66 | let name_like = sema | ||
67 | .find_node_at_offset_with_descend(&syntax, position.offset) | ||
68 | .ok_or_else(|| format_err!("No references found at position"))?; | 37 | .ok_or_else(|| format_err!("No references found at position"))?; |
69 | let node = match &name_like { | 38 | Ok(RangeInfo::new(frange.range, ())) |
70 | ast::NameLike::Name(it) => it.syntax(), | ||
71 | ast::NameLike::NameRef(it) => it.syntax(), | ||
72 | ast::NameLike::Lifetime(it) => it.syntax(), | ||
73 | }; | ||
74 | Ok(RangeInfo::new(sema.original_range(node).range, ())) | ||
75 | } | 39 | } |
76 | 40 | ||
77 | // Feature: Rename | 41 | // Feature: Rename |
@@ -103,12 +67,19 @@ pub(crate) fn rename_with_semantics( | |||
103 | let syntax = source_file.syntax(); | 67 | let syntax = source_file.syntax(); |
104 | 68 | ||
105 | let def = find_definition(sema, syntax, position)?; | 69 | let def = find_definition(sema, syntax, position)?; |
106 | match def { | 70 | |
107 | Definition::ModuleDef(ModuleDef::Module(module)) => rename_mod(&sema, module, new_name), | 71 | if let Definition::Local(local) = def { |
108 | Definition::SelfType(_) => bail!("Cannot rename `Self`"), | 72 | if let Some(self_param) = local.as_self_param(sema.db) { |
109 | Definition::ModuleDef(ModuleDef::BuiltinType(_)) => bail!("Cannot rename builtin type"), | 73 | cov_mark::hit!(rename_self_to_param); |
110 | def => rename_reference(sema, def, new_name), | 74 | return rename_self_to_param(sema, local, self_param, new_name); |
75 | } | ||
76 | if new_name == "self" { | ||
77 | cov_mark::hit!(rename_to_self); | ||
78 | return rename_to_self(sema, local); | ||
79 | } | ||
111 | } | 80 | } |
81 | |||
82 | def.rename(sema, new_name) | ||
112 | } | 83 | } |
113 | 84 | ||
114 | /// Called by the client when it is about to rename a file. | 85 | /// Called by the client when it is about to rename a file. |
@@ -119,38 +90,12 @@ pub(crate) fn will_rename_file( | |||
119 | ) -> Option<SourceChange> { | 90 | ) -> Option<SourceChange> { |
120 | let sema = Semantics::new(db); | 91 | let sema = Semantics::new(db); |
121 | let module = sema.to_module_def(file_id)?; | 92 | let module = sema.to_module_def(file_id)?; |
122 | let mut change = rename_mod(&sema, module, new_name_stem).ok()?; | 93 | let def = Definition::ModuleDef(module.into()); |
94 | let mut change = def.rename(&sema, new_name_stem).ok()?; | ||
123 | change.file_system_edits.clear(); | 95 | change.file_system_edits.clear(); |
124 | Some(change) | 96 | Some(change) |
125 | } | 97 | } |
126 | 98 | ||
127 | #[derive(Copy, Clone, Debug, PartialEq)] | ||
128 | enum IdentifierKind { | ||
129 | Ident, | ||
130 | Lifetime, | ||
131 | ToSelf, | ||
132 | Underscore, | ||
133 | } | ||
134 | |||
135 | fn check_identifier(new_name: &str) -> RenameResult<IdentifierKind> { | ||
136 | match lex_single_syntax_kind(new_name) { | ||
137 | Some(res) => match res { | ||
138 | (SyntaxKind::IDENT, _) => Ok(IdentifierKind::Ident), | ||
139 | (T![_], _) => Ok(IdentifierKind::Underscore), | ||
140 | (T![self], _) => Ok(IdentifierKind::ToSelf), | ||
141 | (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => { | ||
142 | Ok(IdentifierKind::Lifetime) | ||
143 | } | ||
144 | (SyntaxKind::LIFETIME_IDENT, _) => { | ||
145 | bail!("Invalid name `{}`: not a lifetime identifier", new_name) | ||
146 | } | ||
147 | (_, Some(syntax_error)) => bail!("Invalid name `{}`: {}", new_name, syntax_error), | ||
148 | (_, None) => bail!("Invalid name `{}`: not an identifier", new_name), | ||
149 | }, | ||
150 | None => bail!("Invalid name `{}`: not an identifier", new_name), | ||
151 | } | ||
152 | } | ||
153 | |||
154 | fn find_definition( | 99 | fn find_definition( |
155 | sema: &Semantics<RootDatabase>, | 100 | sema: &Semantics<RootDatabase>, |
156 | syntax: &SyntaxNode, | 101 | syntax: &SyntaxNode, |
@@ -192,145 +137,6 @@ fn find_definition( | |||
192 | .ok_or_else(|| format_err!("No references found at position")) | 137 | .ok_or_else(|| format_err!("No references found at position")) |
193 | } | 138 | } |
194 | 139 | ||
195 | fn rename_mod( | ||
196 | sema: &Semantics<RootDatabase>, | ||
197 | module: Module, | ||
198 | new_name: &str, | ||
199 | ) -> RenameResult<SourceChange> { | ||
200 | if IdentifierKind::Ident != check_identifier(new_name)? { | ||
201 | bail!("Invalid name `{0}`: cannot rename module to {0}", new_name); | ||
202 | } | ||
203 | |||
204 | let mut source_change = SourceChange::default(); | ||
205 | |||
206 | let InFile { file_id, value: def_source } = module.definition_source(sema.db); | ||
207 | let file_id = file_id.original_file(sema.db); | ||
208 | if let ModuleSource::SourceFile(..) = def_source { | ||
209 | // mod is defined in path/to/dir/mod.rs | ||
210 | let path = if module.is_mod_rs(sema.db) { | ||
211 | format!("../{}/mod.rs", new_name) | ||
212 | } else { | ||
213 | format!("{}.rs", new_name) | ||
214 | }; | ||
215 | let dst = AnchoredPathBuf { anchor: file_id, path }; | ||
216 | let move_file = FileSystemEdit::MoveFile { src: file_id, dst }; | ||
217 | source_change.push_file_system_edit(move_file); | ||
218 | } | ||
219 | |||
220 | if let Some(InFile { file_id, value: decl_source }) = module.declaration_source(sema.db) { | ||
221 | let file_id = file_id.original_file(sema.db); | ||
222 | match decl_source.name() { | ||
223 | Some(name) => source_change.insert_source_edit( | ||
224 | file_id, | ||
225 | TextEdit::replace(name.syntax().text_range(), new_name.to_string()), | ||
226 | ), | ||
227 | _ => never!("Module source node is missing a name"), | ||
228 | } | ||
229 | } | ||
230 | let def = Definition::ModuleDef(ModuleDef::Module(module)); | ||
231 | let usages = def.usages(sema).all(); | ||
232 | let ref_edits = usages.iter().map(|(&file_id, references)| { | ||
233 | (file_id, source_edit_from_references(references, def, new_name)) | ||
234 | }); | ||
235 | source_change.extend(ref_edits); | ||
236 | |||
237 | Ok(source_change) | ||
238 | } | ||
239 | |||
240 | fn rename_reference( | ||
241 | sema: &Semantics<RootDatabase>, | ||
242 | mut def: Definition, | ||
243 | new_name: &str, | ||
244 | ) -> RenameResult<SourceChange> { | ||
245 | let ident_kind = check_identifier(new_name)?; | ||
246 | |||
247 | if matches!( | ||
248 | def, // is target a lifetime? | ||
249 | Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_) | ||
250 | ) { | ||
251 | match ident_kind { | ||
252 | IdentifierKind::Ident | IdentifierKind::ToSelf | IdentifierKind::Underscore => { | ||
253 | cov_mark::hit!(rename_not_a_lifetime_ident_ref); | ||
254 | bail!("Invalid name `{}`: not a lifetime identifier", new_name); | ||
255 | } | ||
256 | IdentifierKind::Lifetime => cov_mark::hit!(rename_lifetime), | ||
257 | } | ||
258 | } else { | ||
259 | match (ident_kind, def) { | ||
260 | (IdentifierKind::Lifetime, _) => { | ||
261 | cov_mark::hit!(rename_not_an_ident_ref); | ||
262 | bail!("Invalid name `{}`: not an identifier", new_name); | ||
263 | } | ||
264 | (IdentifierKind::ToSelf, Definition::Local(local)) => { | ||
265 | if local.is_self(sema.db) { | ||
266 | // no-op | ||
267 | cov_mark::hit!(rename_self_to_self); | ||
268 | return Ok(SourceChange::default()); | ||
269 | } else { | ||
270 | cov_mark::hit!(rename_to_self); | ||
271 | return rename_to_self(sema, local); | ||
272 | } | ||
273 | } | ||
274 | (ident_kind, Definition::Local(local)) => { | ||
275 | if let Some(self_param) = local.as_self_param(sema.db) { | ||
276 | cov_mark::hit!(rename_self_to_param); | ||
277 | return rename_self_to_param(sema, local, self_param, new_name, ident_kind); | ||
278 | } else { | ||
279 | cov_mark::hit!(rename_local); | ||
280 | } | ||
281 | } | ||
282 | (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name), | ||
283 | (IdentifierKind::Ident, _) => cov_mark::hit!(rename_non_local), | ||
284 | (IdentifierKind::Underscore, _) => (), | ||
285 | } | ||
286 | } | ||
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 | }; | ||
318 | let usages = def.usages(sema).all(); | ||
319 | |||
320 | if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { | ||
321 | cov_mark::hit!(rename_underscore_multiple); | ||
322 | bail!("Cannot rename reference to `_` as it is being referenced multiple times"); | ||
323 | } | ||
324 | let mut source_change = SourceChange::default(); | ||
325 | source_change.extend(usages.iter().map(|(&file_id, references)| { | ||
326 | (file_id, source_edit_from_references(&references, def, new_name)) | ||
327 | })); | ||
328 | |||
329 | let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; | ||
330 | source_change.insert_source_edit(file_id, edit); | ||
331 | Ok(source_change) | ||
332 | } | ||
333 | |||
334 | fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameResult<SourceChange> { | 140 | fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameResult<SourceChange> { |
335 | if never!(local.is_self(sema.db)) { | 141 | if never!(local.is_self(sema.db)) { |
336 | bail!("rename_to_self invoked on self"); | 142 | bail!("rename_to_self invoked on self"); |
@@ -398,8 +204,15 @@ fn rename_self_to_param( | |||
398 | local: hir::Local, | 204 | local: hir::Local, |
399 | self_param: hir::SelfParam, | 205 | self_param: hir::SelfParam, |
400 | new_name: &str, | 206 | new_name: &str, |
401 | identifier_kind: IdentifierKind, | ||
402 | ) -> RenameResult<SourceChange> { | 207 | ) -> RenameResult<SourceChange> { |
208 | if new_name == "self" { | ||
209 | // Let's do nothing rather than complain. | ||
210 | cov_mark::hit!(rename_self_to_self); | ||
211 | return Ok(SourceChange::default()); | ||
212 | } | ||
213 | |||
214 | let identifier_kind = IdentifierKind::classify(new_name)?; | ||
215 | |||
403 | let InFile { file_id, value: self_param } = | 216 | let InFile { file_id, value: self_param } = |
404 | self_param.source(sema.db).ok_or_else(|| format_err!("cannot find function source"))?; | 217 | self_param.source(sema.db).ok_or_else(|| format_err!("cannot find function source"))?; |
405 | 218 | ||
@@ -413,7 +226,7 @@ fn rename_self_to_param( | |||
413 | let mut source_change = SourceChange::default(); | 226 | let mut source_change = SourceChange::default(); |
414 | source_change.insert_source_edit(file_id.original_file(sema.db), edit); | 227 | source_change.insert_source_edit(file_id.original_file(sema.db), edit); |
415 | source_change.extend(usages.iter().map(|(&file_id, references)| { | 228 | source_change.extend(usages.iter().map(|(&file_id, references)| { |
416 | (file_id, source_edit_from_references(&references, def, new_name)) | 229 | (file_id, source_edit_from_references(references, def, new_name)) |
417 | })); | 230 | })); |
418 | Ok(source_change) | 231 | Ok(source_change) |
419 | } | 232 | } |
@@ -426,7 +239,7 @@ fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Opt | |||
426 | None | 239 | None |
427 | } | 240 | } |
428 | 241 | ||
429 | let impl_def = self_param.syntax().ancestors().find_map(|it| ast::Impl::cast(it))?; | 242 | let impl_def = self_param.syntax().ancestors().find_map(ast::Impl::cast)?; |
430 | let type_name = target_type_name(&impl_def)?; | 243 | let type_name = target_type_name(&impl_def)?; |
431 | 244 | ||
432 | let mut replacement_text = String::from(new_name); | 245 | let mut replacement_text = String::from(new_name); |
@@ -441,150 +254,6 @@ fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Opt | |||
441 | Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) | 254 | Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) |
442 | } | 255 | } |
443 | 256 | ||
444 | fn source_edit_from_references( | ||
445 | references: &[FileReference], | ||
446 | def: Definition, | ||
447 | new_name: &str, | ||
448 | ) -> TextEdit { | ||
449 | let mut edit = TextEdit::builder(); | ||
450 | for reference in references { | ||
451 | let (range, replacement) = match &reference.name { | ||
452 | // if the ranges differ then the node is inside a macro call, we can't really attempt | ||
453 | // to make special rewrites like shorthand syntax and such, so just rename the node in | ||
454 | // the macro input | ||
455 | ast::NameLike::NameRef(name_ref) | ||
456 | if name_ref.syntax().text_range() == reference.range => | ||
457 | { | ||
458 | source_edit_from_name_ref(name_ref, new_name, def) | ||
459 | } | ||
460 | ast::NameLike::Name(name) if name.syntax().text_range() == reference.range => { | ||
461 | source_edit_from_name(name, new_name) | ||
462 | } | ||
463 | _ => None, | ||
464 | } | ||
465 | .unwrap_or_else(|| (reference.range, new_name.to_string())); | ||
466 | edit.replace(range, replacement); | ||
467 | } | ||
468 | edit.finish() | ||
469 | } | ||
470 | |||
471 | fn source_edit_from_name(name: &ast::Name, new_name: &str) -> Option<(TextRange, String)> { | ||
472 | if let Some(_) = ast::RecordPatField::for_field_name(name) { | ||
473 | if let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { | ||
474 | return Some(( | ||
475 | TextRange::empty(ident_pat.syntax().text_range().start()), | ||
476 | [new_name, ": "].concat(), | ||
477 | )); | ||
478 | } | ||
479 | } | ||
480 | None | ||
481 | } | ||
482 | |||
483 | fn source_edit_from_name_ref( | ||
484 | name_ref: &ast::NameRef, | ||
485 | new_name: &str, | ||
486 | def: Definition, | ||
487 | ) -> Option<(TextRange, String)> { | ||
488 | if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) { | ||
489 | let rcf_name_ref = record_field.name_ref(); | ||
490 | let rcf_expr = record_field.expr(); | ||
491 | match (rcf_name_ref, rcf_expr.and_then(|it| it.name_ref())) { | ||
492 | // field: init-expr, check if we can use a field init shorthand | ||
493 | (Some(field_name), Some(init)) => { | ||
494 | if field_name == *name_ref { | ||
495 | if init.text() == new_name { | ||
496 | cov_mark::hit!(test_rename_field_put_init_shorthand); | ||
497 | // same names, we can use a shorthand here instead. | ||
498 | // we do not want to erase attributes hence this range start | ||
499 | let s = field_name.syntax().text_range().start(); | ||
500 | let e = record_field.syntax().text_range().end(); | ||
501 | return Some((TextRange::new(s, e), new_name.to_owned())); | ||
502 | } | ||
503 | } else if init == *name_ref { | ||
504 | if field_name.text() == new_name { | ||
505 | cov_mark::hit!(test_rename_local_put_init_shorthand); | ||
506 | // same names, we can use a shorthand here instead. | ||
507 | // we do not want to erase attributes hence this range start | ||
508 | let s = field_name.syntax().text_range().start(); | ||
509 | let e = record_field.syntax().text_range().end(); | ||
510 | return Some((TextRange::new(s, e), new_name.to_owned())); | ||
511 | } | ||
512 | } | ||
513 | None | ||
514 | } | ||
515 | // init shorthand | ||
516 | // FIXME: instead of splitting the shorthand, recursively trigger a rename of the | ||
517 | // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 | ||
518 | (None, Some(_)) if matches!(def, Definition::Field(_)) => { | ||
519 | cov_mark::hit!(test_rename_field_in_field_shorthand); | ||
520 | let s = name_ref.syntax().text_range().start(); | ||
521 | Some((TextRange::empty(s), format!("{}: ", new_name))) | ||
522 | } | ||
523 | (None, Some(_)) if matches!(def, Definition::Local(_)) => { | ||
524 | cov_mark::hit!(test_rename_local_in_field_shorthand); | ||
525 | let s = name_ref.syntax().text_range().end(); | ||
526 | Some((TextRange::empty(s), format!(": {}", new_name))) | ||
527 | } | ||
528 | _ => None, | ||
529 | } | ||
530 | } else if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) { | ||
531 | let rcf_name_ref = record_field.name_ref(); | ||
532 | let rcf_pat = record_field.pat(); | ||
533 | match (rcf_name_ref, rcf_pat) { | ||
534 | // field: rename | ||
535 | (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => { | ||
536 | // field name is being renamed | ||
537 | if pat.name().map_or(false, |it| it.text() == new_name) { | ||
538 | cov_mark::hit!(test_rename_field_put_init_shorthand_pat); | ||
539 | // same names, we can use a shorthand here instead/ | ||
540 | // we do not want to erase attributes hence this range start | ||
541 | let s = field_name.syntax().text_range().start(); | ||
542 | let e = record_field.syntax().text_range().end(); | ||
543 | Some((TextRange::new(s, e), pat.to_string())) | ||
544 | } else { | ||
545 | None | ||
546 | } | ||
547 | } | ||
548 | _ => None, | ||
549 | } | ||
550 | } else { | ||
551 | None | ||
552 | } | ||
553 | } | ||
554 | |||
555 | fn source_edit_from_def( | ||
556 | sema: &Semantics<RootDatabase>, | ||
557 | def: Definition, | ||
558 | new_name: &str, | ||
559 | ) -> RenameResult<(FileId, TextEdit)> { | ||
560 | let nav = | ||
561 | def.try_to_nav(sema.db).ok_or_else(|| format_err!("No references found at position"))?; | ||
562 | |||
563 | let mut replacement_text = String::new(); | ||
564 | let mut repl_range = | ||
565 | nav.focus_range.ok_or_else(|| format_err!("No identifier available to rename"))?; | ||
566 | if let Definition::Local(local) = def { | ||
567 | if let Either::Left(pat) = local.source(sema.db).value { | ||
568 | if matches!( | ||
569 | pat.syntax().parent().and_then(ast::RecordPatField::cast), | ||
570 | Some(pat_field) if pat_field.name_ref().is_none() | ||
571 | ) { | ||
572 | replacement_text.push_str(": "); | ||
573 | replacement_text.push_str(new_name); | ||
574 | repl_range = TextRange::new( | ||
575 | pat.syntax().text_range().end(), | ||
576 | pat.syntax().text_range().end(), | ||
577 | ); | ||
578 | } | ||
579 | } | ||
580 | } | ||
581 | if replacement_text.is_empty() { | ||
582 | replacement_text.push_str(new_name); | ||
583 | } | ||
584 | let edit = TextEdit::replace(repl_range, replacement_text); | ||
585 | Ok((nav.file_id, edit)) | ||
586 | } | ||
587 | |||
588 | #[cfg(test)] | 257 | #[cfg(test)] |
589 | mod tests { | 258 | mod tests { |
590 | use expect_test::{expect, Expect}; | 259 | use expect_test::{expect, Expect}; |
@@ -659,7 +328,7 @@ mod tests { | |||
659 | fn test_prepare_rename_namelikes() { | 328 | fn test_prepare_rename_namelikes() { |
660 | check_prepare(r"fn name$0<'lifetime>() {}", expect![[r#"3..7: name"#]]); | 329 | check_prepare(r"fn name$0<'lifetime>() {}", expect![[r#"3..7: name"#]]); |
661 | check_prepare(r"fn name<'lifetime$0>() {}", expect![[r#"8..17: 'lifetime"#]]); | 330 | check_prepare(r"fn name<'lifetime$0>() {}", expect![[r#"8..17: 'lifetime"#]]); |
662 | check_prepare(r"fn name<'lifetime>() { name$0(); }", expect![[r#"23..27: name"#]]); | 331 | check_prepare(r"fn name<'lifetime>() { name$0(); }", expect![[r#"3..7: name"#]]); |
663 | } | 332 | } |
664 | 333 | ||
665 | #[test] | 334 | #[test] |
@@ -691,7 +360,7 @@ fn baz() { | |||
691 | x.0$0 = 5; | 360 | x.0$0 = 5; |
692 | } | 361 | } |
693 | "#, | 362 | "#, |
694 | expect![[r#"No identifier available to rename"#]], | 363 | expect![[r#"No references found at position"#]], |
695 | ); | 364 | ); |
696 | } | 365 | } |
697 | 366 | ||
@@ -703,7 +372,7 @@ fn foo() { | |||
703 | let x: i32$0 = 0; | 372 | let x: i32$0 = 0; |
704 | } | 373 | } |
705 | "#, | 374 | "#, |
706 | expect![[r#"Cannot rename builtin type"#]], | 375 | expect![[r#"No references found at position"#]], |
707 | ); | 376 | ); |
708 | } | 377 | } |
709 | 378 | ||
@@ -719,7 +388,7 @@ impl Foo { | |||
719 | } | 388 | } |
720 | } | 389 | } |
721 | "#, | 390 | "#, |
722 | expect![[r#"Cannot rename `Self`"#]], | 391 | expect![[r#"No references found at position"#]], |
723 | ); | 392 | ); |
724 | } | 393 | } |
725 | 394 | ||
@@ -801,7 +470,6 @@ impl Foo { | |||
801 | 470 | ||
802 | #[test] | 471 | #[test] |
803 | fn test_rename_for_local() { | 472 | fn test_rename_for_local() { |
804 | cov_mark::check!(rename_local); | ||
805 | check( | 473 | check( |
806 | "k", | 474 | "k", |
807 | r#" | 475 | r#" |
@@ -2101,4 +1769,22 @@ fn f() { <()>::BAR$0; }"#, | |||
2101 | res, | 1769 | res, |
2102 | ); | 1770 | ); |
2103 | } | 1771 | } |
1772 | |||
1773 | #[test] | ||
1774 | fn macros_are_broken_lol() { | ||
1775 | cov_mark::check!(macros_are_broken_lol); | ||
1776 | check( | ||
1777 | "lol", | ||
1778 | r#" | ||
1779 | macro_rules! m { () => { fn f() {} } } | ||
1780 | m!(); | ||
1781 | fn main() { f$0() } | ||
1782 | "#, | ||
1783 | r#" | ||
1784 | macro_rules! m { () => { fn f() {} } } | ||
1785 | lol | ||
1786 | fn main() { lol() } | ||
1787 | "#, | ||
1788 | ) | ||
1789 | } | ||
2104 | } | 1790 | } |
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 b03f1c71f..e186b82b7 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs | |||
@@ -323,7 +323,7 @@ fn traverse( | |||
323 | 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) { |
324 | if token.is_raw() { | 324 | if token.is_raw() { |
325 | let expanded = element_to_highlight.as_token().unwrap().clone(); | 325 | let expanded = element_to_highlight.as_token().unwrap().clone(); |
326 | if inject::ra_fixture(hl, &sema, token, expanded).is_some() { | 326 | if inject::ra_fixture(hl, sema, token, expanded).is_some() { |
327 | continue; | 327 | continue; |
328 | } | 328 | } |
329 | } | 329 | } |
@@ -334,7 +334,7 @@ fn traverse( | |||
334 | } | 334 | } |
335 | 335 | ||
336 | if let Some((mut highlight, binding_hash)) = highlight::element( | 336 | if let Some((mut highlight, binding_hash)) = highlight::element( |
337 | &sema, | 337 | sema, |
338 | krate, | 338 | krate, |
339 | &mut bindings_shadow_count, | 339 | &mut bindings_shadow_count, |
340 | 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..6834fe11a 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs | |||
@@ -48,7 +48,13 @@ pub(super) fn element( | |||
48 | match name_kind { | 48 | match name_kind { |
49 | Some(NameClass::ExternCrate(_)) => SymbolKind::Module.into(), | 49 | Some(NameClass::ExternCrate(_)) => SymbolKind::Module.into(), |
50 | Some(NameClass::Definition(def)) => { | 50 | Some(NameClass::Definition(def)) => { |
51 | highlight_def(db, krate, def) | HlMod::Definition | 51 | let mut h = highlight_def(db, krate, def) | HlMod::Definition; |
52 | if let Definition::ModuleDef(hir::ModuleDef::Trait(trait_)) = &def { | ||
53 | if trait_.is_unsafe(db) { | ||
54 | h |= HlMod::Unsafe; | ||
55 | } | ||
56 | } | ||
57 | h | ||
52 | } | 58 | } |
53 | Some(NameClass::ConstReference(def)) => highlight_def(db, krate, def), | 59 | Some(NameClass::ConstReference(def)) => highlight_def(db, krate, def), |
54 | Some(NameClass::PatFieldShorthand { field_ref, .. }) => { | 60 | Some(NameClass::PatFieldShorthand { field_ref, .. }) => { |
@@ -87,20 +93,34 @@ pub(super) fn element( | |||
87 | 93 | ||
88 | let mut h = highlight_def(db, krate, def); | 94 | let mut h = highlight_def(db, krate, def); |
89 | 95 | ||
90 | if let Definition::Local(local) = &def { | 96 | match def { |
91 | if is_consumed_lvalue(name_ref.syntax().clone().into(), local, db) { | 97 | Definition::Local(local) |
98 | if is_consumed_lvalue( | ||
99 | name_ref.syntax().clone().into(), | ||
100 | &local, | ||
101 | db, | ||
102 | ) => | ||
103 | { | ||
92 | h |= HlMod::Consuming; | 104 | h |= HlMod::Consuming; |
93 | } | 105 | } |
94 | } | 106 | Definition::ModuleDef(hir::ModuleDef::Trait(trait_)) |
95 | 107 | if trait_.is_unsafe(db) => | |
96 | if let Some(parent) = name_ref.syntax().parent() { | 108 | { |
97 | if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) { | 109 | if ast::Impl::for_trait_name_ref(&name_ref).is_some() { |
98 | if let Definition::Field(field) = def { | 110 | h |= HlMod::Unsafe; |
99 | if let hir::VariantDef::Union(_) = field.parent_def(db) { | 111 | } |
100 | h |= HlMod::Unsafe; | 112 | } |
113 | Definition::Field(field) => { | ||
114 | if let Some(parent) = name_ref.syntax().parent() { | ||
115 | if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) { | ||
116 | if let hir::VariantDef::Union(_) = field.parent_def(db) | ||
117 | { | ||
118 | h |= HlMod::Unsafe; | ||
119 | } | ||
101 | } | 120 | } |
102 | } | 121 | } |
103 | } | 122 | } |
123 | _ => (), | ||
104 | } | 124 | } |
105 | 125 | ||
106 | h | 126 | h |
@@ -131,6 +151,9 @@ pub(super) fn element( | |||
131 | } | 151 | } |
132 | STRING | BYTE_STRING => HlTag::StringLiteral.into(), | 152 | STRING | BYTE_STRING => HlTag::StringLiteral.into(), |
133 | ATTR => HlTag::Attribute.into(), | 153 | ATTR => HlTag::Attribute.into(), |
154 | INT_NUMBER if element.ancestors().nth(1).map_or(false, |it| it.kind() == FIELD_EXPR) => { | ||
155 | SymbolKind::Field.into() | ||
156 | } | ||
134 | INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(), | 157 | INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(), |
135 | BYTE => HlTag::ByteLiteral.into(), | 158 | BYTE => HlTag::ByteLiteral.into(), |
136 | CHAR => HlTag::CharLiteral.into(), | 159 | CHAR => HlTag::CharLiteral.into(), |
@@ -351,15 +374,7 @@ fn highlight_def(db: &RootDatabase, krate: Option<hir::Crate>, def: Definition) | |||
351 | 374 | ||
352 | h | 375 | h |
353 | } | 376 | } |
354 | hir::ModuleDef::Trait(trait_) => { | 377 | hir::ModuleDef::Trait(_) => Highlight::new(HlTag::Symbol(SymbolKind::Trait)), |
355 | let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Trait)); | ||
356 | |||
357 | if trait_.is_unsafe(db) { | ||
358 | h |= HlMod::Unsafe; | ||
359 | } | ||
360 | |||
361 | h | ||
362 | } | ||
363 | hir::ModuleDef::TypeAlias(type_) => { | 378 | hir::ModuleDef::TypeAlias(type_) => { |
364 | let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias)); | 379 | let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias)); |
365 | 380 | ||
@@ -446,12 +461,12 @@ fn highlight_method_call( | |||
446 | krate: Option<hir::Crate>, | 461 | krate: Option<hir::Crate>, |
447 | method_call: &ast::MethodCallExpr, | 462 | method_call: &ast::MethodCallExpr, |
448 | ) -> Option<Highlight> { | 463 | ) -> Option<Highlight> { |
449 | let func = sema.resolve_method_call(&method_call)?; | 464 | let func = sema.resolve_method_call(method_call)?; |
450 | 465 | ||
451 | let mut h = SymbolKind::Function.into(); | 466 | let mut h = SymbolKind::Function.into(); |
452 | h |= HlMod::Associated; | 467 | h |= HlMod::Associated; |
453 | 468 | ||
454 | if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) { | 469 | if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(method_call) { |
455 | h |= HlMod::Unsafe; | 470 | h |= HlMod::Unsafe; |
456 | } | 471 | } |
457 | if func.is_async(sema.db) { | 472 | if func.is_async(sema.db) { |
@@ -523,11 +538,9 @@ fn highlight_name_ref_by_syntax( | |||
523 | }; | 538 | }; |
524 | 539 | ||
525 | match parent.kind() { | 540 | match parent.kind() { |
526 | METHOD_CALL_EXPR => { | 541 | METHOD_CALL_EXPR => ast::MethodCallExpr::cast(parent) |
527 | return ast::MethodCallExpr::cast(parent) | 542 | .and_then(|it| highlight_method_call(sema, krate, &it)) |
528 | .and_then(|it| highlight_method_call(sema, krate, &it)) | 543 | .unwrap_or_else(|| SymbolKind::Function.into()), |
529 | .unwrap_or_else(|| SymbolKind::Function.into()); | ||
530 | } | ||
531 | FIELD_EXPR => { | 544 | FIELD_EXPR => { |
532 | let h = HlTag::Symbol(SymbolKind::Field); | 545 | let h = HlTag::Symbol(SymbolKind::Field); |
533 | let is_union = ast::FieldExpr::cast(parent) | 546 | 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..21376a7ae 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]); |
@@ -67,6 +67,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
67 | .field { color: #94BFF3; } | 67 | .field { color: #94BFF3; } |
68 | .function { color: #93E0E3; } | 68 | .function { color: #93E0E3; } |
69 | .function.unsafe { color: #BC8383; } | 69 | .function.unsafe { color: #BC8383; } |
70 | .trait.unsafe { color: #BC8383; } | ||
70 | .operator.unsafe { color: #BC8383; } | 71 | .operator.unsafe { color: #BC8383; } |
71 | .parameter { color: #94BFF3; } | 72 | .parameter { color: #94BFF3; } |
72 | .text { color: #DCDCCC; } | 73 | .text { color: #DCDCCC; } |
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/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html index a0ea1db34..4e85f7c0b 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html | |||
@@ -15,6 +15,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
15 | .field { color: #94BFF3; } | 15 | .field { color: #94BFF3; } |
16 | .function { color: #93E0E3; } | 16 | .function { color: #93E0E3; } |
17 | .function.unsafe { color: #BC8383; } | 17 | .function.unsafe { color: #BC8383; } |
18 | .trait.unsafe { color: #BC8383; } | ||
18 | .operator.unsafe { color: #BC8383; } | 19 | .operator.unsafe { color: #BC8383; } |
19 | .parameter { color: #94BFF3; } | 20 | .parameter { color: #94BFF3; } |
20 | .text { color: #DCDCCC; } | 21 | .text { color: #DCDCCC; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 921a956e6..79a285107 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html | |||
@@ -15,6 +15,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
15 | .field { color: #94BFF3; } | 15 | .field { color: #94BFF3; } |
16 | .function { color: #93E0E3; } | 16 | .function { color: #93E0E3; } |
17 | .function.unsafe { color: #BC8383; } | 17 | .function.unsafe { color: #BC8383; } |
18 | .trait.unsafe { color: #BC8383; } | ||
18 | .operator.unsafe { color: #BC8383; } | 19 | .operator.unsafe { color: #BC8383; } |
19 | .parameter { color: #94BFF3; } | 20 | .parameter { color: #94BFF3; } |
20 | .text { color: #DCDCCC; } | 21 | .text { color: #DCDCCC; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html index ca9bb1e7d..13f589cc0 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html | |||
@@ -15,6 +15,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
15 | .field { color: #94BFF3; } | 15 | .field { color: #94BFF3; } |
16 | .function { color: #93E0E3; } | 16 | .function { color: #93E0E3; } |
17 | .function.unsafe { color: #BC8383; } | 17 | .function.unsafe { color: #BC8383; } |
18 | .trait.unsafe { color: #BC8383; } | ||
18 | .operator.unsafe { color: #BC8383; } | 19 | .operator.unsafe { color: #BC8383; } |
19 | .parameter { color: #94BFF3; } | 20 | .parameter { color: #94BFF3; } |
20 | .text { color: #DCDCCC; } | 21 | .text { color: #DCDCCC; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html index 6202a03ce..50df376ae 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html | |||
@@ -15,6 +15,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
15 | .field { color: #94BFF3; } | 15 | .field { color: #94BFF3; } |
16 | .function { color: #93E0E3; } | 16 | .function { color: #93E0E3; } |
17 | .function.unsafe { color: #BC8383; } | 17 | .function.unsafe { color: #BC8383; } |
18 | .trait.unsafe { color: #BC8383; } | ||
18 | .operator.unsafe { color: #BC8383; } | 19 | .operator.unsafe { color: #BC8383; } |
19 | .parameter { color: #94BFF3; } | 20 | .parameter { color: #94BFF3; } |
20 | .text { color: #DCDCCC; } | 21 | .text { color: #DCDCCC; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index e860d713e..96cb09642 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html | |||
@@ -15,6 +15,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
15 | .field { color: #94BFF3; } | 15 | .field { color: #94BFF3; } |
16 | .function { color: #93E0E3; } | 16 | .function { color: #93E0E3; } |
17 | .function.unsafe { color: #BC8383; } | 17 | .function.unsafe { color: #BC8383; } |
18 | .trait.unsafe { color: #BC8383; } | ||
18 | .operator.unsafe { color: #BC8383; } | 19 | .operator.unsafe { color: #BC8383; } |
19 | .parameter { color: #94BFF3; } | 20 | .parameter { color: #94BFF3; } |
20 | .text { color: #DCDCCC; } | 21 | .text { color: #DCDCCC; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 68165bdbf..55453468b 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html | |||
@@ -15,6 +15,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
15 | .field { color: #94BFF3; } | 15 | .field { color: #94BFF3; } |
16 | .function { color: #93E0E3; } | 16 | .function { color: #93E0E3; } |
17 | .function.unsafe { color: #BC8383; } | 17 | .function.unsafe { color: #BC8383; } |
18 | .trait.unsafe { color: #BC8383; } | ||
18 | .operator.unsafe { color: #BC8383; } | 19 | .operator.unsafe { color: #BC8383; } |
19 | .parameter { color: #94BFF3; } | 20 | .parameter { color: #94BFF3; } |
20 | .text { color: #DCDCCC; } | 21 | .text { color: #DCDCCC; } |
@@ -61,6 +62,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
61 | <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u16</span><span class="comma">,</span> | 62 | <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u16</span><span class="comma">,</span> |
62 | <span class="brace">}</span> | 63 | <span class="brace">}</span> |
63 | 64 | ||
65 | <span class="keyword unsafe">unsafe</span> <span class="keyword">trait</span> <span class="trait declaration unsafe">UnsafeTrait</span> <span class="brace">{</span><span class="brace">}</span> | ||
66 | <span class="keyword unsafe">unsafe</span> <span class="keyword">impl</span> <span class="trait unsafe">UnsafeTrait</span> <span class="keyword">for</span> <span class="struct">Packed</span> <span class="brace">{</span><span class="brace">}</span> | ||
67 | |||
68 | <span class="keyword">fn</span> <span class="function declaration">require_unsafe_trait</span><span class="angle"><</span><span class="type_param declaration">T</span><span class="colon">:</span> <span class="trait">UnsafeTrait</span><span class="angle">></span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="type_param">T</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> | ||
69 | |||
64 | <span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="brace">{</span> | 70 | <span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="brace">{</span> |
65 | <span class="keyword">fn</span> <span class="function associated declaration trait">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span><span class="semicolon">;</span> | 71 | <span class="keyword">fn</span> <span class="function associated declaration trait">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span><span class="semicolon">;</span> |
66 | <span class="brace">}</span> | 72 | <span class="brace">}</span> |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index a7b5c3b89..9232cf905 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html | |||
@@ -15,6 +15,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
15 | .field { color: #94BFF3; } | 15 | .field { color: #94BFF3; } |
16 | .function { color: #93E0E3; } | 16 | .function { color: #93E0E3; } |
17 | .function.unsafe { color: #BC8383; } | 17 | .function.unsafe { color: #BC8383; } |
18 | .trait.unsafe { color: #BC8383; } | ||
18 | .operator.unsafe { color: #BC8383; } | 19 | .operator.unsafe { color: #BC8383; } |
19 | .parameter { color: #94BFF3; } | 20 | .parameter { color: #94BFF3; } |
20 | .text { color: #DCDCCC; } | 21 | .text { color: #DCDCCC; } |
@@ -215,8 +216,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
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> | 216 | <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> |
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> | 217 | <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> |
217 | 218 | ||
218 | <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> | 219 | <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> |
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="semicolon">;</span> | 220 | <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> |
220 | 221 | ||
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> | 222 | <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> |
222 | 223 | ||
diff --git a/crates/ide/src/syntax_highlighting/test_data/injection.html b/crates/ide/src/syntax_highlighting/test_data/injection.html index 9ab46d05c..082837328 100644 --- a/crates/ide/src/syntax_highlighting/test_data/injection.html +++ b/crates/ide/src/syntax_highlighting/test_data/injection.html | |||
@@ -15,6 +15,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
15 | .field { color: #94BFF3; } | 15 | .field { color: #94BFF3; } |
16 | .function { color: #93E0E3; } | 16 | .function { color: #93E0E3; } |
17 | .function.unsafe { color: #BC8383; } | 17 | .function.unsafe { color: #BC8383; } |
18 | .trait.unsafe { color: #BC8383; } | ||
18 | .operator.unsafe { color: #BC8383; } | 19 | .operator.unsafe { color: #BC8383; } |
19 | .parameter { color: #94BFF3; } | 20 | .parameter { color: #94BFF3; } |
20 | .text { color: #DCDCCC; } | 21 | .text { color: #DCDCCC; } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html index 666b0b228..763917714 100644 --- a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html | |||
@@ -15,6 +15,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
15 | .field { color: #94BFF3; } | 15 | .field { color: #94BFF3; } |
16 | .function { color: #93E0E3; } | 16 | .function { color: #93E0E3; } |
17 | .function.unsafe { color: #BC8383; } | 17 | .function.unsafe { color: #BC8383; } |
18 | .trait.unsafe { color: #BC8383; } | ||
18 | .operator.unsafe { color: #BC8383; } | 19 | .operator.unsafe { color: #BC8383; } |
19 | .parameter { color: #94BFF3; } | 20 | .parameter { color: #94BFF3; } |
20 | .text { color: #DCDCCC; } | 21 | .text { color: #DCDCCC; } |
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 6ad2a362a..4f0b1ce85 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -189,8 +189,8 @@ fn main() { | |||
189 | let a = |x| x; | 189 | let a = |x| x; |
190 | let bar = Foo::baz; | 190 | let bar = Foo::baz; |
191 | 191 | ||
192 | let baz = -42; | 192 | let baz = (-42,); |
193 | let baz = -baz; | 193 | let baz = -baz.0; |
194 | 194 | ||
195 | let _ = !true; | 195 | let _ = !true; |
196 | 196 | ||
@@ -527,6 +527,11 @@ struct Packed { | |||
527 | a: u16, | 527 | a: u16, |
528 | } | 528 | } |
529 | 529 | ||
530 | unsafe trait UnsafeTrait {} | ||
531 | unsafe impl UnsafeTrait for Packed {} | ||
532 | |||
533 | fn require_unsafe_trait<T: UnsafeTrait>(_: T) {} | ||
534 | |||
530 | trait DoTheAutoref { | 535 | trait DoTheAutoref { |
531 | fn calls_autoref(&self); | 536 | fn calls_autoref(&self); |
532 | } | 537 | } |
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/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/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/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..f834bf16a 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)?; |
@@ -361,8 +361,6 @@ pub struct Foo { pub bar: () } | |||
361 | } | 361 | } |
362 | 362 | ||
363 | #[test] | 363 | #[test] |
364 | #[ignore] | ||
365 | // FIXME reenable this test when `Semantics::resolve_record_field` works with union fields | ||
366 | fn fix_visibility_of_union_field() { | 364 | fn fix_visibility_of_union_field() { |
367 | check_assist( | 365 | check_assist( |
368 | fix_visibility, | 366 | fix_visibility, |
@@ -583,25 +581,25 @@ pub struct Foo { pub(crate) bar: () } | |||
583 | } | 581 | } |
584 | 582 | ||
585 | #[test] | 583 | #[test] |
586 | #[ignore] | ||
587 | // FIXME handle reexports properly | ||
588 | fn fix_visibility_of_reexport() { | 584 | fn fix_visibility_of_reexport() { |
585 | // FIXME: broken test, this should fix visibility of the re-export | ||
586 | // rather than the struct. | ||
589 | check_assist( | 587 | check_assist( |
590 | fix_visibility, | 588 | fix_visibility, |
591 | r" | 589 | r#" |
592 | mod foo { | 590 | mod foo { |
593 | use bar::Baz; | 591 | use bar::Baz; |
594 | mod bar { pub(super) struct Baz; } | 592 | mod bar { pub(super) struct Baz; } |
595 | } | 593 | } |
596 | foo::Baz$0 | 594 | foo::Baz$0 |
597 | ", | 595 | "#, |
598 | r" | 596 | r#" |
599 | mod foo { | 597 | mod foo { |
600 | $0pub(crate) use bar::Baz; | 598 | use bar::Baz; |
601 | mod bar { pub(super) struct Baz; } | 599 | mod bar { $0pub(crate) struct Baz; } |
602 | } | 600 | } |
603 | foo::Baz | 601 | foo::Baz |
604 | ", | 602 | "#, |
605 | ) | 603 | ) |
606 | } | 604 | } |
607 | } | 605 | } |
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..6a658d4cf 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. |
@@ -811,9 +811,8 @@ fn bar(baz: Baz::Bof) ${0:-> ()} { | |||
811 | } | 811 | } |
812 | 812 | ||
813 | #[test] | 813 | #[test] |
814 | #[ignore] | ||
815 | // FIXME fix printing the generics of a `Ty` to make this test pass | ||
816 | fn add_function_with_generic_arg() { | 814 | fn add_function_with_generic_arg() { |
815 | // FIXME: This is wrong, generated `bar` should include generic parameter. | ||
817 | check_assist( | 816 | check_assist( |
818 | generate_function, | 817 | generate_function, |
819 | r" | 818 | r" |
@@ -826,7 +825,7 @@ fn foo<T>(t: T) { | |||
826 | bar(t) | 825 | bar(t) |
827 | } | 826 | } |
828 | 827 | ||
829 | fn bar<T>(t: T) ${0:-> ()} { | 828 | fn bar(t: T) ${0:-> ()} { |
830 | todo!() | 829 | todo!() |
831 | } | 830 | } |
832 | ", | 831 | ", |
@@ -834,9 +833,8 @@ fn bar<T>(t: T) ${0:-> ()} { | |||
834 | } | 833 | } |
835 | 834 | ||
836 | #[test] | 835 | #[test] |
837 | #[ignore] | ||
838 | // FIXME Fix function type printing to make this test pass | ||
839 | fn add_function_with_fn_arg() { | 836 | fn add_function_with_fn_arg() { |
837 | // FIXME: The argument in `bar` is wrong. | ||
840 | check_assist( | 838 | check_assist( |
841 | generate_function, | 839 | generate_function, |
842 | r" | 840 | r" |
@@ -857,7 +855,7 @@ fn foo() { | |||
857 | bar(Baz::new); | 855 | bar(Baz::new); |
858 | } | 856 | } |
859 | 857 | ||
860 | fn bar(arg: fn() -> Baz) ${0:-> ()} { | 858 | fn bar(new: fn) ${0:-> ()} { |
861 | todo!() | 859 | todo!() |
862 | } | 860 | } |
863 | ", | 861 | ", |
@@ -865,9 +863,8 @@ fn bar(arg: fn() -> Baz) ${0:-> ()} { | |||
865 | } | 863 | } |
866 | 864 | ||
867 | #[test] | 865 | #[test] |
868 | #[ignore] | ||
869 | // FIXME Fix closure type printing to make this test pass | ||
870 | fn add_function_with_closure_arg() { | 866 | fn add_function_with_closure_arg() { |
867 | // FIXME: The argument in `bar` is wrong. | ||
871 | check_assist( | 868 | check_assist( |
872 | generate_function, | 869 | generate_function, |
873 | r" | 870 | r" |
@@ -882,7 +879,7 @@ fn foo() { | |||
882 | bar(closure) | 879 | bar(closure) |
883 | } | 880 | } |
884 | 881 | ||
885 | fn bar(closure: impl Fn(i64) -> i64) ${0:-> ()} { | 882 | fn bar(closure: ()) ${0:-> ()} { |
886 | todo!() | 883 | todo!() |
887 | } | 884 | } |
888 | ", | 885 | ", |
@@ -986,13 +983,10 @@ fn foo() { | |||
986 | } | 983 | } |
987 | 984 | ||
988 | #[test] | 985 | #[test] |
989 | #[ignore] | ||
990 | // Ignored until local imports are supported. | ||
991 | // See https://github.com/rust-analyzer/rust-analyzer/issues/1165 | ||
992 | fn qualified_path_uses_correct_scope() { | 986 | fn qualified_path_uses_correct_scope() { |
993 | check_assist( | 987 | check_assist( |
994 | generate_function, | 988 | generate_function, |
995 | " | 989 | r#" |
996 | mod foo { | 990 | mod foo { |
997 | pub struct Foo; | 991 | pub struct Foo; |
998 | } | 992 | } |
@@ -1001,8 +995,8 @@ fn bar() { | |||
1001 | let foo = Foo; | 995 | let foo = Foo; |
1002 | baz$0(foo) | 996 | baz$0(foo) |
1003 | } | 997 | } |
1004 | ", | 998 | "#, |
1005 | " | 999 | r#" |
1006 | mod foo { | 1000 | mod foo { |
1007 | pub struct Foo; | 1001 | pub struct Foo; |
1008 | } | 1002 | } |
@@ -1015,7 +1009,7 @@ fn bar() { | |||
1015 | fn baz(foo: foo::Foo) ${0:-> ()} { | 1009 | fn baz(foo: foo::Foo) ${0:-> ()} { |
1016 | todo!() | 1010 | todo!() |
1017 | } | 1011 | } |
1018 | ", | 1012 | "#, |
1019 | ) | 1013 | ) |
1020 | } | 1014 | } |
1021 | 1015 | ||
@@ -1141,40 +1135,29 @@ fn bar() {} | |||
1141 | // The assist is only active if the cursor is on an unresolved path, | 1135 | // The assist is only active if the cursor is on an unresolved path, |
1142 | // but the assist should only be offered if the path is a function call. | 1136 | // but the assist should only be offered if the path is a function call. |
1143 | generate_function, | 1137 | generate_function, |
1144 | r" | 1138 | r#" |
1145 | fn foo() { | 1139 | fn foo() { |
1146 | bar(b$0az); | 1140 | bar(b$0az); |
1147 | } | 1141 | } |
1148 | 1142 | ||
1149 | fn bar(baz: ()) {} | 1143 | fn bar(baz: ()) {} |
1150 | ", | 1144 | "#, |
1151 | ) | 1145 | ) |
1152 | } | 1146 | } |
1153 | 1147 | ||
1154 | #[test] | 1148 | #[test] |
1155 | #[ignore] | ||
1156 | fn create_method_with_no_args() { | 1149 | fn create_method_with_no_args() { |
1157 | check_assist( | 1150 | // FIXME: This is wrong, this should just work. |
1151 | check_assist_not_applicable( | ||
1158 | generate_function, | 1152 | generate_function, |
1159 | r" | 1153 | r#" |
1160 | struct Foo; | 1154 | struct Foo; |
1161 | impl Foo { | 1155 | impl Foo { |
1162 | fn foo(&self) { | 1156 | fn foo(&self) { |
1163 | self.bar()$0; | 1157 | self.bar()$0; |
1164 | } | 1158 | } |
1165 | } | 1159 | } |
1166 | ", | 1160 | "#, |
1167 | r" | ||
1168 | struct Foo; | ||
1169 | impl Foo { | ||
1170 | fn foo(&self) { | ||
1171 | self.bar(); | ||
1172 | } | ||
1173 | fn bar(&self) { | ||
1174 | todo!(); | ||
1175 | } | ||
1176 | } | ||
1177 | ", | ||
1178 | ) | 1161 | ) |
1179 | } | 1162 | } |
1180 | } | 1163 | } |
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/qualify_path.rs b/crates/ide_assists/src/handlers/qualify_path.rs index f91770a76..1d7be183a 100644 --- a/crates/ide_assists/src/handlers/qualify_path.rs +++ b/crates/ide_assists/src/handlers/qualify_path.rs | |||
@@ -216,28 +216,28 @@ mod tests { | |||
216 | cov_mark::check!(qualify_path_unqualified_name); | 216 | cov_mark::check!(qualify_path_unqualified_name); |
217 | check_assist( | 217 | check_assist( |
218 | qualify_path, | 218 | qualify_path, |
219 | r" | 219 | r#" |
220 | mod std { | 220 | mod std { |
221 | pub mod fmt { | 221 | pub mod fmt { |
222 | pub struct Formatter; | 222 | pub struct Formatter; |
223 | } | 223 | } |
224 | } | 224 | } |
225 | 225 | ||
226 | use std::fmt; | 226 | use std::fmt; |
227 | 227 | ||
228 | $0Formatter | 228 | $0Formatter |
229 | ", | 229 | "#, |
230 | r" | 230 | r#" |
231 | mod std { | 231 | mod std { |
232 | pub mod fmt { | 232 | pub mod fmt { |
233 | pub struct Formatter; | 233 | pub struct Formatter; |
234 | } | 234 | } |
235 | } | 235 | } |
236 | 236 | ||
237 | use std::fmt; | 237 | use std::fmt; |
238 | 238 | ||
239 | fmt::Formatter | 239 | fmt::Formatter |
240 | ", | 240 | "#, |
241 | ); | 241 | ); |
242 | } | 242 | } |
243 | 243 | ||
@@ -245,20 +245,20 @@ mod tests { | |||
245 | fn applicable_when_found_an_import() { | 245 | fn applicable_when_found_an_import() { |
246 | check_assist( | 246 | check_assist( |
247 | qualify_path, | 247 | qualify_path, |
248 | r" | 248 | r#" |
249 | $0PubStruct | 249 | $0PubStruct |
250 | 250 | ||
251 | pub mod PubMod { | 251 | pub mod PubMod { |
252 | pub struct PubStruct; | 252 | pub struct PubStruct; |
253 | } | 253 | } |
254 | ", | 254 | "#, |
255 | r" | 255 | r#" |
256 | PubMod::PubStruct | 256 | PubMod::PubStruct |
257 | 257 | ||
258 | pub mod PubMod { | 258 | pub mod PubMod { |
259 | pub struct PubStruct; | 259 | pub struct PubStruct; |
260 | } | 260 | } |
261 | ", | 261 | "#, |
262 | ); | 262 | ); |
263 | } | 263 | } |
264 | 264 | ||
@@ -266,26 +266,26 @@ mod tests { | |||
266 | fn applicable_in_macros() { | 266 | fn applicable_in_macros() { |
267 | check_assist( | 267 | check_assist( |
268 | qualify_path, | 268 | qualify_path, |
269 | r" | 269 | r#" |
270 | macro_rules! foo { | 270 | macro_rules! foo { |
271 | ($i:ident) => { fn foo(a: $i) {} } | 271 | ($i:ident) => { fn foo(a: $i) {} } |
272 | } | 272 | } |
273 | foo!(Pub$0Struct); | 273 | foo!(Pub$0Struct); |
274 | 274 | ||
275 | pub mod PubMod { | 275 | pub mod PubMod { |
276 | pub struct PubStruct; | 276 | pub struct PubStruct; |
277 | } | 277 | } |
278 | ", | 278 | "#, |
279 | r" | 279 | r#" |
280 | macro_rules! foo { | 280 | macro_rules! foo { |
281 | ($i:ident) => { fn foo(a: $i) {} } | 281 | ($i:ident) => { fn foo(a: $i) {} } |
282 | } | 282 | } |
283 | foo!(PubMod::PubStruct); | 283 | foo!(PubMod::PubStruct); |
284 | 284 | ||
285 | pub mod PubMod { | 285 | pub mod PubMod { |
286 | pub struct PubStruct; | 286 | pub struct PubStruct; |
287 | } | 287 | } |
288 | ", | 288 | "#, |
289 | ); | 289 | ); |
290 | } | 290 | } |
291 | 291 | ||
@@ -293,32 +293,32 @@ mod tests { | |||
293 | fn applicable_when_found_multiple_imports() { | 293 | fn applicable_when_found_multiple_imports() { |
294 | check_assist( | 294 | check_assist( |
295 | qualify_path, | 295 | qualify_path, |
296 | r" | 296 | r#" |
297 | PubSt$0ruct | 297 | PubSt$0ruct |
298 | 298 | ||
299 | pub mod PubMod1 { | 299 | pub mod PubMod1 { |
300 | pub struct PubStruct; | 300 | pub struct PubStruct; |
301 | } | 301 | } |
302 | pub mod PubMod2 { | 302 | pub mod PubMod2 { |
303 | pub struct PubStruct; | 303 | pub struct PubStruct; |
304 | } | 304 | } |
305 | pub mod PubMod3 { | 305 | pub mod PubMod3 { |
306 | pub struct PubStruct; | 306 | pub struct PubStruct; |
307 | } | 307 | } |
308 | ", | 308 | "#, |
309 | r" | 309 | r#" |
310 | PubMod3::PubStruct | 310 | PubMod3::PubStruct |
311 | 311 | ||
312 | pub mod PubMod1 { | 312 | pub mod PubMod1 { |
313 | pub struct PubStruct; | 313 | pub struct PubStruct; |
314 | } | 314 | } |
315 | pub mod PubMod2 { | 315 | pub mod PubMod2 { |
316 | pub struct PubStruct; | 316 | pub struct PubStruct; |
317 | } | 317 | } |
318 | pub mod PubMod3 { | 318 | pub mod PubMod3 { |
319 | pub struct PubStruct; | 319 | pub struct PubStruct; |
320 | } | 320 | } |
321 | ", | 321 | "#, |
322 | ); | 322 | ); |
323 | } | 323 | } |
324 | 324 | ||
@@ -326,15 +326,15 @@ mod tests { | |||
326 | fn not_applicable_for_already_imported_types() { | 326 | fn not_applicable_for_already_imported_types() { |
327 | check_assist_not_applicable( | 327 | check_assist_not_applicable( |
328 | qualify_path, | 328 | qualify_path, |
329 | r" | 329 | r#" |
330 | use PubMod::PubStruct; | 330 | use PubMod::PubStruct; |
331 | 331 | ||
332 | PubStruct$0 | 332 | PubStruct$0 |
333 | 333 | ||
334 | pub mod PubMod { | 334 | pub mod PubMod { |
335 | pub struct PubStruct; | 335 | pub struct PubStruct; |
336 | } | 336 | } |
337 | ", | 337 | "#, |
338 | ); | 338 | ); |
339 | } | 339 | } |
340 | 340 | ||
@@ -342,35 +342,32 @@ mod tests { | |||
342 | fn not_applicable_for_types_with_private_paths() { | 342 | fn not_applicable_for_types_with_private_paths() { |
343 | check_assist_not_applicable( | 343 | check_assist_not_applicable( |
344 | qualify_path, | 344 | qualify_path, |
345 | r" | 345 | r#" |
346 | PrivateStruct$0 | 346 | PrivateStruct$0 |
347 | 347 | ||
348 | pub mod PubMod { | 348 | pub mod PubMod { |
349 | struct PrivateStruct; | 349 | struct PrivateStruct; |
350 | } | 350 | } |
351 | ", | 351 | "#, |
352 | ); | 352 | ); |
353 | } | 353 | } |
354 | 354 | ||
355 | #[test] | 355 | #[test] |
356 | fn not_applicable_when_no_imports_found() { | 356 | fn not_applicable_when_no_imports_found() { |
357 | check_assist_not_applicable( | 357 | check_assist_not_applicable(qualify_path, r#"PubStruct$0"#); |
358 | qualify_path, | ||
359 | " | ||
360 | PubStruct$0", | ||
361 | ); | ||
362 | } | 358 | } |
363 | 359 | ||
364 | #[test] | 360 | #[test] |
365 | fn not_applicable_in_import_statements() { | 361 | fn not_applicable_in_import_statements() { |
366 | check_assist_not_applicable( | 362 | check_assist_not_applicable( |
367 | qualify_path, | 363 | qualify_path, |
368 | r" | 364 | r#" |
369 | use PubStruct$0; | 365 | use PubStruct$0; |
370 | 366 | ||
371 | pub mod PubMod { | 367 | pub mod PubMod { |
372 | pub struct PubStruct; | 368 | pub struct PubStruct; |
373 | }", | 369 | } |
370 | "#, | ||
374 | ); | 371 | ); |
375 | } | 372 | } |
376 | 373 | ||
@@ -378,20 +375,20 @@ mod tests { | |||
378 | fn qualify_function() { | 375 | fn qualify_function() { |
379 | check_assist( | 376 | check_assist( |
380 | qualify_path, | 377 | qualify_path, |
381 | r" | 378 | r#" |
382 | test_function$0 | 379 | test_function$0 |
383 | 380 | ||
384 | pub mod PubMod { | 381 | pub mod PubMod { |
385 | pub fn test_function() {}; | 382 | pub fn test_function() {}; |
386 | } | 383 | } |
387 | ", | 384 | "#, |
388 | r" | 385 | r#" |
389 | PubMod::test_function | 386 | PubMod::test_function |
390 | 387 | ||
391 | pub mod PubMod { | 388 | pub mod PubMod { |
392 | pub fn test_function() {}; | 389 | pub fn test_function() {}; |
393 | } | 390 | } |
394 | ", | 391 | "#, |
395 | ); | 392 | ); |
396 | } | 393 | } |
397 | 394 | ||
@@ -399,7 +396,7 @@ mod tests { | |||
399 | fn qualify_macro() { | 396 | fn qualify_macro() { |
400 | check_assist( | 397 | check_assist( |
401 | qualify_path, | 398 | qualify_path, |
402 | r" | 399 | r#" |
403 | //- /lib.rs crate:crate_with_macro | 400 | //- /lib.rs crate:crate_with_macro |
404 | #[macro_export] | 401 | #[macro_export] |
405 | macro_rules! foo { | 402 | macro_rules! foo { |
@@ -410,12 +407,12 @@ macro_rules! foo { | |||
410 | fn main() { | 407 | fn main() { |
411 | foo$0 | 408 | foo$0 |
412 | } | 409 | } |
413 | ", | 410 | "#, |
414 | r" | 411 | r#" |
415 | fn main() { | 412 | fn main() { |
416 | crate_with_macro::foo | 413 | crate_with_macro::foo |
417 | } | 414 | } |
418 | ", | 415 | "#, |
419 | ); | 416 | ); |
420 | } | 417 | } |
421 | 418 | ||
@@ -423,13 +420,13 @@ fn main() { | |||
423 | fn qualify_path_target() { | 420 | fn qualify_path_target() { |
424 | check_assist_target( | 421 | check_assist_target( |
425 | qualify_path, | 422 | qualify_path, |
426 | r" | 423 | r#" |
427 | struct AssistInfo { | 424 | struct AssistInfo { |
428 | group_label: Option<$0GroupLabel>, | 425 | group_label: Option<$0GroupLabel>, |
429 | } | 426 | } |
430 | 427 | ||
431 | mod m { pub struct GroupLabel; } | 428 | mod m { pub struct GroupLabel; } |
432 | ", | 429 | "#, |
433 | "GroupLabel", | 430 | "GroupLabel", |
434 | ) | 431 | ) |
435 | } | 432 | } |
@@ -438,20 +435,20 @@ fn main() { | |||
438 | fn not_applicable_when_path_start_is_imported() { | 435 | fn not_applicable_when_path_start_is_imported() { |
439 | check_assist_not_applicable( | 436 | check_assist_not_applicable( |
440 | qualify_path, | 437 | qualify_path, |
441 | r" | 438 | r#" |
442 | pub mod mod1 { | 439 | pub mod mod1 { |
443 | pub mod mod2 { | 440 | pub mod mod2 { |
444 | pub mod mod3 { | 441 | pub mod mod3 { |
445 | pub struct TestStruct; | 442 | pub struct TestStruct; |
446 | } | 443 | } |
447 | } | 444 | } |
448 | } | 445 | } |
449 | 446 | ||
450 | use mod1::mod2; | 447 | use mod1::mod2; |
451 | fn main() { | 448 | fn main() { |
452 | mod2::mod3::TestStruct$0 | 449 | mod2::mod3::TestStruct$0 |
453 | } | 450 | } |
454 | ", | 451 | "#, |
455 | ); | 452 | ); |
456 | } | 453 | } |
457 | 454 | ||
@@ -459,16 +456,16 @@ fn main() { | |||
459 | fn not_applicable_for_imported_function() { | 456 | fn not_applicable_for_imported_function() { |
460 | check_assist_not_applicable( | 457 | check_assist_not_applicable( |
461 | qualify_path, | 458 | qualify_path, |
462 | r" | 459 | r#" |
463 | pub mod test_mod { | 460 | pub mod test_mod { |
464 | pub fn test_function() {} | 461 | pub fn test_function() {} |
465 | } | 462 | } |
466 | 463 | ||
467 | use test_mod::test_function; | 464 | use test_mod::test_function; |
468 | fn main() { | 465 | fn main() { |
469 | test_function$0 | 466 | test_function$0 |
470 | } | 467 | } |
471 | ", | 468 | "#, |
472 | ); | 469 | ); |
473 | } | 470 | } |
474 | 471 | ||
@@ -476,30 +473,30 @@ fn main() { | |||
476 | fn associated_struct_function() { | 473 | fn associated_struct_function() { |
477 | check_assist( | 474 | check_assist( |
478 | qualify_path, | 475 | qualify_path, |
479 | r" | 476 | r#" |
480 | mod test_mod { | 477 | mod test_mod { |
481 | pub struct TestStruct {} | 478 | pub struct TestStruct {} |
482 | impl TestStruct { | 479 | impl TestStruct { |
483 | pub fn test_function() {} | 480 | pub fn test_function() {} |
484 | } | 481 | } |
485 | } | 482 | } |
486 | 483 | ||
487 | fn main() { | 484 | fn main() { |
488 | TestStruct::test_function$0 | 485 | TestStruct::test_function$0 |
489 | } | 486 | } |
490 | ", | 487 | "#, |
491 | r" | 488 | r#" |
492 | mod test_mod { | 489 | mod test_mod { |
493 | pub struct TestStruct {} | 490 | pub struct TestStruct {} |
494 | impl TestStruct { | 491 | impl TestStruct { |
495 | pub fn test_function() {} | 492 | pub fn test_function() {} |
496 | } | 493 | } |
497 | } | 494 | } |
498 | 495 | ||
499 | fn main() { | 496 | fn main() { |
500 | test_mod::TestStruct::test_function | 497 | test_mod::TestStruct::test_function |
501 | } | 498 | } |
502 | ", | 499 | "#, |
503 | ); | 500 | ); |
504 | } | 501 | } |
505 | 502 | ||
@@ -508,62 +505,50 @@ fn main() { | |||
508 | cov_mark::check!(qualify_path_qualifier_start); | 505 | cov_mark::check!(qualify_path_qualifier_start); |
509 | check_assist( | 506 | check_assist( |
510 | qualify_path, | 507 | qualify_path, |
511 | r" | 508 | r#" |
512 | mod test_mod { | 509 | mod test_mod { |
513 | pub struct TestStruct {} | 510 | pub struct TestStruct {} |
514 | impl TestStruct { | 511 | impl TestStruct { |
515 | const TEST_CONST: u8 = 42; | 512 | const TEST_CONST: u8 = 42; |
516 | } | 513 | } |
517 | } | 514 | } |
518 | 515 | ||
519 | fn main() { | 516 | fn main() { |
520 | TestStruct::TEST_CONST$0 | 517 | TestStruct::TEST_CONST$0 |
521 | } | 518 | } |
522 | ", | 519 | "#, |
523 | r" | 520 | r#" |
524 | mod test_mod { | 521 | mod test_mod { |
525 | pub struct TestStruct {} | 522 | pub struct TestStruct {} |
526 | impl TestStruct { | 523 | impl TestStruct { |
527 | const TEST_CONST: u8 = 42; | 524 | const TEST_CONST: u8 = 42; |
528 | } | 525 | } |
529 | } | 526 | } |
530 | 527 | ||
531 | fn main() { | 528 | fn main() { |
532 | test_mod::TestStruct::TEST_CONST | 529 | test_mod::TestStruct::TEST_CONST |
533 | } | 530 | } |
534 | ", | 531 | "#, |
535 | ); | 532 | ); |
536 | } | 533 | } |
537 | 534 | ||
538 | #[test] | 535 | #[test] |
539 | #[ignore = "FIXME: non-trait assoc items completion is unsupported yet, see FIXME in the import_assets.rs for more details"] | ||
540 | fn associated_struct_const_unqualified() { | 536 | fn associated_struct_const_unqualified() { |
541 | check_assist( | 537 | // FIXME: non-trait assoc items completion is unsupported yet, see FIXME in the import_assets.rs for more details |
538 | check_assist_not_applicable( | ||
542 | qualify_path, | 539 | qualify_path, |
543 | r" | 540 | r#" |
544 | mod test_mod { | 541 | mod test_mod { |
545 | pub struct TestStruct {} | 542 | pub struct TestStruct {} |
546 | impl TestStruct { | 543 | impl TestStruct { |
547 | const TEST_CONST: u8 = 42; | 544 | const TEST_CONST: u8 = 42; |
548 | } | 545 | } |
549 | } | 546 | } |
550 | |||
551 | fn main() { | ||
552 | TEST_CONST$0 | ||
553 | } | ||
554 | ", | ||
555 | r" | ||
556 | mod test_mod { | ||
557 | pub struct TestStruct {} | ||
558 | impl TestStruct { | ||
559 | const TEST_CONST: u8 = 42; | ||
560 | } | ||
561 | } | ||
562 | 547 | ||
563 | fn main() { | 548 | fn main() { |
564 | test_mod::TestStruct::TEST_CONST | 549 | TEST_CONST$0 |
565 | } | 550 | } |
566 | ", | 551 | "#, |
567 | ); | 552 | ); |
568 | } | 553 | } |
569 | 554 | ||
@@ -571,36 +556,36 @@ fn main() { | |||
571 | fn associated_trait_function() { | 556 | fn associated_trait_function() { |
572 | check_assist( | 557 | check_assist( |
573 | qualify_path, | 558 | qualify_path, |
574 | r" | 559 | r#" |
575 | mod test_mod { | 560 | mod test_mod { |
576 | pub trait TestTrait { | 561 | pub trait TestTrait { |
577 | fn test_function(); | 562 | fn test_function(); |
578 | } | 563 | } |
579 | pub struct TestStruct {} | 564 | pub struct TestStruct {} |
580 | impl TestTrait for TestStruct { | 565 | impl TestTrait for TestStruct { |
581 | fn test_function() {} | 566 | fn test_function() {} |
582 | } | 567 | } |
583 | } | 568 | } |
584 | 569 | ||
585 | fn main() { | 570 | fn main() { |
586 | test_mod::TestStruct::test_function$0 | 571 | test_mod::TestStruct::test_function$0 |
587 | } | 572 | } |
588 | ", | 573 | "#, |
589 | r" | 574 | r#" |
590 | mod test_mod { | 575 | mod test_mod { |
591 | pub trait TestTrait { | 576 | pub trait TestTrait { |
592 | fn test_function(); | 577 | fn test_function(); |
593 | } | 578 | } |
594 | pub struct TestStruct {} | 579 | pub struct TestStruct {} |
595 | impl TestTrait for TestStruct { | 580 | impl TestTrait for TestStruct { |
596 | fn test_function() {} | 581 | fn test_function() {} |
597 | } | 582 | } |
598 | } | 583 | } |
599 | 584 | ||
600 | fn main() { | 585 | fn main() { |
601 | <test_mod::TestStruct as test_mod::TestTrait>::test_function | 586 | <test_mod::TestStruct as test_mod::TestTrait>::test_function |
602 | } | 587 | } |
603 | ", | 588 | "#, |
604 | ); | 589 | ); |
605 | } | 590 | } |
606 | 591 | ||
@@ -608,31 +593,31 @@ fn main() { | |||
608 | fn not_applicable_for_imported_trait_for_function() { | 593 | fn not_applicable_for_imported_trait_for_function() { |
609 | check_assist_not_applicable( | 594 | check_assist_not_applicable( |
610 | qualify_path, | 595 | qualify_path, |
611 | r" | 596 | r#" |
612 | mod test_mod { | 597 | mod test_mod { |
613 | pub trait TestTrait { | 598 | pub trait TestTrait { |
614 | fn test_function(); | 599 | fn test_function(); |
615 | } | 600 | } |
616 | pub trait TestTrait2 { | 601 | pub trait TestTrait2 { |
617 | fn test_function(); | 602 | fn test_function(); |
618 | } | 603 | } |
619 | pub enum TestEnum { | 604 | pub enum TestEnum { |
620 | One, | 605 | One, |
621 | Two, | 606 | Two, |
622 | } | 607 | } |
623 | impl TestTrait2 for TestEnum { | 608 | impl TestTrait2 for TestEnum { |
624 | fn test_function() {} | 609 | fn test_function() {} |
625 | } | 610 | } |
626 | impl TestTrait for TestEnum { | 611 | impl TestTrait for TestEnum { |
627 | fn test_function() {} | 612 | fn test_function() {} |
628 | } | 613 | } |
629 | } | 614 | } |
630 | 615 | ||
631 | use test_mod::TestTrait2; | 616 | use test_mod::TestTrait2; |
632 | fn main() { | 617 | fn main() { |
633 | test_mod::TestEnum::test_function$0; | 618 | test_mod::TestEnum::test_function$0; |
634 | } | 619 | } |
635 | ", | 620 | "#, |
636 | ) | 621 | ) |
637 | } | 622 | } |
638 | 623 | ||
@@ -641,36 +626,36 @@ fn main() { | |||
641 | cov_mark::check!(qualify_path_trait_assoc_item); | 626 | cov_mark::check!(qualify_path_trait_assoc_item); |
642 | check_assist( | 627 | check_assist( |
643 | qualify_path, | 628 | qualify_path, |
644 | r" | 629 | r#" |
645 | mod test_mod { | 630 | mod test_mod { |
646 | pub trait TestTrait { | 631 | pub trait TestTrait { |
647 | const TEST_CONST: u8; | 632 | const TEST_CONST: u8; |
648 | } | 633 | } |
649 | pub struct TestStruct {} | 634 | pub struct TestStruct {} |
650 | impl TestTrait for TestStruct { | 635 | impl TestTrait for TestStruct { |
651 | const TEST_CONST: u8 = 42; | 636 | const TEST_CONST: u8 = 42; |
652 | } | 637 | } |
653 | } | 638 | } |
654 | 639 | ||
655 | fn main() { | 640 | fn main() { |
656 | test_mod::TestStruct::TEST_CONST$0 | 641 | test_mod::TestStruct::TEST_CONST$0 |
657 | } | 642 | } |
658 | ", | 643 | "#, |
659 | r" | 644 | r#" |
660 | mod test_mod { | 645 | mod test_mod { |
661 | pub trait TestTrait { | 646 | pub trait TestTrait { |
662 | const TEST_CONST: u8; | 647 | const TEST_CONST: u8; |
663 | } | 648 | } |
664 | pub struct TestStruct {} | 649 | pub struct TestStruct {} |
665 | impl TestTrait for TestStruct { | 650 | impl TestTrait for TestStruct { |
666 | const TEST_CONST: u8 = 42; | 651 | const TEST_CONST: u8 = 42; |
667 | } | 652 | } |
668 | } | 653 | } |
669 | 654 | ||
670 | fn main() { | 655 | fn main() { |
671 | <test_mod::TestStruct as test_mod::TestTrait>::TEST_CONST | 656 | <test_mod::TestStruct as test_mod::TestTrait>::TEST_CONST |
672 | } | 657 | } |
673 | ", | 658 | "#, |
674 | ); | 659 | ); |
675 | } | 660 | } |
676 | 661 | ||
@@ -678,31 +663,31 @@ fn main() { | |||
678 | fn not_applicable_for_imported_trait_for_const() { | 663 | fn not_applicable_for_imported_trait_for_const() { |
679 | check_assist_not_applicable( | 664 | check_assist_not_applicable( |
680 | qualify_path, | 665 | qualify_path, |
681 | r" | 666 | r#" |
682 | mod test_mod { | 667 | mod test_mod { |
683 | pub trait TestTrait { | 668 | pub trait TestTrait { |
684 | const TEST_CONST: u8; | 669 | const TEST_CONST: u8; |
685 | } | 670 | } |
686 | pub trait TestTrait2 { | 671 | pub trait TestTrait2 { |
687 | const TEST_CONST: f64; | 672 | const TEST_CONST: f64; |
688 | } | 673 | } |
689 | pub enum TestEnum { | 674 | pub enum TestEnum { |
690 | One, | 675 | One, |
691 | Two, | 676 | Two, |
692 | } | 677 | } |
693 | impl TestTrait2 for TestEnum { | 678 | impl TestTrait2 for TestEnum { |
694 | const TEST_CONST: f64 = 42.0; | 679 | const TEST_CONST: f64 = 42.0; |
695 | } | 680 | } |
696 | impl TestTrait for TestEnum { | 681 | impl TestTrait for TestEnum { |
697 | const TEST_CONST: u8 = 42; | 682 | const TEST_CONST: u8 = 42; |
698 | } | 683 | } |
699 | } | 684 | } |
700 | 685 | ||
701 | use test_mod::TestTrait2; | 686 | use test_mod::TestTrait2; |
702 | fn main() { | 687 | fn main() { |
703 | test_mod::TestEnum::TEST_CONST$0; | 688 | test_mod::TestEnum::TEST_CONST$0; |
704 | } | 689 | } |
705 | ", | 690 | "#, |
706 | ) | 691 | ) |
707 | } | 692 | } |
708 | 693 | ||
@@ -711,38 +696,38 @@ fn main() { | |||
711 | cov_mark::check!(qualify_path_trait_method); | 696 | cov_mark::check!(qualify_path_trait_method); |
712 | check_assist( | 697 | check_assist( |
713 | qualify_path, | 698 | qualify_path, |
714 | r" | 699 | r#" |
715 | mod test_mod { | 700 | mod test_mod { |
716 | pub trait TestTrait { | 701 | pub trait TestTrait { |
717 | fn test_method(&self); | 702 | fn test_method(&self); |
718 | } | 703 | } |
719 | pub struct TestStruct {} | 704 | pub struct TestStruct {} |
720 | impl TestTrait for TestStruct { | 705 | impl TestTrait for TestStruct { |
721 | fn test_method(&self) {} | 706 | fn test_method(&self) {} |
722 | } | 707 | } |
723 | } | 708 | } |
724 | 709 | ||
725 | fn main() { | 710 | fn main() { |
726 | let test_struct = test_mod::TestStruct {}; | 711 | let test_struct = test_mod::TestStruct {}; |
727 | test_struct.test_meth$0od() | 712 | test_struct.test_meth$0od() |
728 | } | 713 | } |
729 | ", | 714 | "#, |
730 | r" | 715 | r#" |
731 | mod test_mod { | 716 | mod test_mod { |
732 | pub trait TestTrait { | 717 | pub trait TestTrait { |
733 | fn test_method(&self); | 718 | fn test_method(&self); |
734 | } | 719 | } |
735 | pub struct TestStruct {} | 720 | pub struct TestStruct {} |
736 | impl TestTrait for TestStruct { | 721 | impl TestTrait for TestStruct { |
737 | fn test_method(&self) {} | 722 | fn test_method(&self) {} |
738 | } | 723 | } |
739 | } | 724 | } |
740 | 725 | ||
741 | fn main() { | 726 | fn main() { |
742 | let test_struct = test_mod::TestStruct {}; | 727 | let test_struct = test_mod::TestStruct {}; |
743 | test_mod::TestTrait::test_method(&test_struct) | 728 | test_mod::TestTrait::test_method(&test_struct) |
744 | } | 729 | } |
745 | ", | 730 | "#, |
746 | ); | 731 | ); |
747 | } | 732 | } |
748 | 733 | ||
@@ -750,38 +735,38 @@ fn main() { | |||
750 | fn trait_method_multi_params() { | 735 | fn trait_method_multi_params() { |
751 | check_assist( | 736 | check_assist( |
752 | qualify_path, | 737 | qualify_path, |
753 | r" | 738 | r#" |
754 | mod test_mod { | 739 | mod test_mod { |
755 | pub trait TestTrait { | 740 | pub trait TestTrait { |
756 | fn test_method(&self, test: i32); | 741 | fn test_method(&self, test: i32); |
757 | } | 742 | } |
758 | pub struct TestStruct {} | 743 | pub struct TestStruct {} |
759 | impl TestTrait for TestStruct { | 744 | impl TestTrait for TestStruct { |
760 | fn test_method(&self, test: i32) {} | 745 | fn test_method(&self, test: i32) {} |
761 | } | 746 | } |
762 | } | 747 | } |
763 | 748 | ||
764 | fn main() { | 749 | fn main() { |
765 | let test_struct = test_mod::TestStruct {}; | 750 | let test_struct = test_mod::TestStruct {}; |
766 | test_struct.test_meth$0od(42) | 751 | test_struct.test_meth$0od(42) |
767 | } | 752 | } |
768 | ", | 753 | "#, |
769 | r" | 754 | r#" |
770 | mod test_mod { | 755 | mod test_mod { |
771 | pub trait TestTrait { | 756 | pub trait TestTrait { |
772 | fn test_method(&self, test: i32); | 757 | fn test_method(&self, test: i32); |
773 | } | 758 | } |
774 | pub struct TestStruct {} | 759 | pub struct TestStruct {} |
775 | impl TestTrait for TestStruct { | 760 | impl TestTrait for TestStruct { |
776 | fn test_method(&self, test: i32) {} | 761 | fn test_method(&self, test: i32) {} |
777 | } | 762 | } |
778 | } | 763 | } |
779 | 764 | ||
780 | fn main() { | 765 | fn main() { |
781 | let test_struct = test_mod::TestStruct {}; | 766 | let test_struct = test_mod::TestStruct {}; |
782 | test_mod::TestTrait::test_method(&test_struct, 42) | 767 | test_mod::TestTrait::test_method(&test_struct, 42) |
783 | } | 768 | } |
784 | ", | 769 | "#, |
785 | ); | 770 | ); |
786 | } | 771 | } |
787 | 772 | ||
@@ -789,38 +774,38 @@ fn main() { | |||
789 | fn trait_method_consume() { | 774 | fn trait_method_consume() { |
790 | check_assist( | 775 | check_assist( |
791 | qualify_path, | 776 | qualify_path, |
792 | r" | 777 | r#" |
793 | mod test_mod { | 778 | mod test_mod { |
794 | pub trait TestTrait { | 779 | pub trait TestTrait { |
795 | fn test_method(self); | 780 | fn test_method(self); |
796 | } | 781 | } |
797 | pub struct TestStruct {} | 782 | pub struct TestStruct {} |
798 | impl TestTrait for TestStruct { | 783 | impl TestTrait for TestStruct { |
799 | fn test_method(self) {} | 784 | fn test_method(self) {} |
800 | } | 785 | } |
801 | } | 786 | } |
802 | 787 | ||
803 | fn main() { | 788 | fn main() { |
804 | let test_struct = test_mod::TestStruct {}; | 789 | let test_struct = test_mod::TestStruct {}; |
805 | test_struct.test_meth$0od() | 790 | test_struct.test_meth$0od() |
806 | } | 791 | } |
807 | ", | 792 | "#, |
808 | r" | 793 | r#" |
809 | mod test_mod { | 794 | mod test_mod { |
810 | pub trait TestTrait { | 795 | pub trait TestTrait { |
811 | fn test_method(self); | 796 | fn test_method(self); |
812 | } | 797 | } |
813 | pub struct TestStruct {} | 798 | pub struct TestStruct {} |
814 | impl TestTrait for TestStruct { | 799 | impl TestTrait for TestStruct { |
815 | fn test_method(self) {} | 800 | fn test_method(self) {} |
816 | } | 801 | } |
817 | } | 802 | } |
818 | 803 | ||
819 | fn main() { | 804 | fn main() { |
820 | let test_struct = test_mod::TestStruct {}; | 805 | let test_struct = test_mod::TestStruct {}; |
821 | test_mod::TestTrait::test_method(test_struct) | 806 | test_mod::TestTrait::test_method(test_struct) |
822 | } | 807 | } |
823 | ", | 808 | "#, |
824 | ); | 809 | ); |
825 | } | 810 | } |
826 | 811 | ||
@@ -828,29 +813,29 @@ fn main() { | |||
828 | fn trait_method_cross_crate() { | 813 | fn trait_method_cross_crate() { |
829 | check_assist( | 814 | check_assist( |
830 | qualify_path, | 815 | qualify_path, |
831 | r" | 816 | r#" |
832 | //- /main.rs crate:main deps:dep | 817 | //- /main.rs crate:main deps:dep |
833 | fn main() { | 818 | fn main() { |
834 | let test_struct = dep::test_mod::TestStruct {}; | 819 | let test_struct = dep::test_mod::TestStruct {}; |
835 | test_struct.test_meth$0od() | 820 | test_struct.test_meth$0od() |
836 | } | 821 | } |
837 | //- /dep.rs crate:dep | 822 | //- /dep.rs crate:dep |
838 | pub mod test_mod { | 823 | pub mod test_mod { |
839 | pub trait TestTrait { | 824 | pub trait TestTrait { |
840 | fn test_method(&self); | 825 | fn test_method(&self); |
841 | } | 826 | } |
842 | pub struct TestStruct {} | 827 | pub struct TestStruct {} |
843 | impl TestTrait for TestStruct { | 828 | impl TestTrait for TestStruct { |
844 | fn test_method(&self) {} | 829 | fn test_method(&self) {} |
845 | } | 830 | } |
846 | } | 831 | } |
847 | ", | 832 | "#, |
848 | r" | 833 | r#" |
849 | fn main() { | 834 | fn main() { |
850 | let test_struct = dep::test_mod::TestStruct {}; | 835 | let test_struct = dep::test_mod::TestStruct {}; |
851 | dep::test_mod::TestTrait::test_method(&test_struct) | 836 | dep::test_mod::TestTrait::test_method(&test_struct) |
852 | } | 837 | } |
853 | ", | 838 | "#, |
854 | ); | 839 | ); |
855 | } | 840 | } |
856 | 841 | ||
@@ -858,27 +843,27 @@ fn main() { | |||
858 | fn assoc_fn_cross_crate() { | 843 | fn assoc_fn_cross_crate() { |
859 | check_assist( | 844 | check_assist( |
860 | qualify_path, | 845 | qualify_path, |
861 | r" | 846 | r#" |
862 | //- /main.rs crate:main deps:dep | 847 | //- /main.rs crate:main deps:dep |
863 | fn main() { | 848 | fn main() { |
864 | dep::test_mod::TestStruct::test_func$0tion | 849 | dep::test_mod::TestStruct::test_func$0tion |
865 | } | 850 | } |
866 | //- /dep.rs crate:dep | 851 | //- /dep.rs crate:dep |
867 | pub mod test_mod { | 852 | pub mod test_mod { |
868 | pub trait TestTrait { | 853 | pub trait TestTrait { |
869 | fn test_function(); | 854 | fn test_function(); |
870 | } | 855 | } |
871 | pub struct TestStruct {} | 856 | pub struct TestStruct {} |
872 | impl TestTrait for TestStruct { | 857 | impl TestTrait for TestStruct { |
873 | fn test_function() {} | 858 | fn test_function() {} |
874 | } | 859 | } |
875 | } | 860 | } |
876 | ", | 861 | "#, |
877 | r" | 862 | r#" |
878 | fn main() { | 863 | fn main() { |
879 | <dep::test_mod::TestStruct as dep::test_mod::TestTrait>::test_function | 864 | <dep::test_mod::TestStruct as dep::test_mod::TestTrait>::test_function |
880 | } | 865 | } |
881 | ", | 866 | "#, |
882 | ); | 867 | ); |
883 | } | 868 | } |
884 | 869 | ||
@@ -886,27 +871,27 @@ fn main() { | |||
886 | fn assoc_const_cross_crate() { | 871 | fn assoc_const_cross_crate() { |
887 | check_assist( | 872 | check_assist( |
888 | qualify_path, | 873 | qualify_path, |
889 | r" | 874 | r#" |
890 | //- /main.rs crate:main deps:dep | 875 | //- /main.rs crate:main deps:dep |
891 | fn main() { | 876 | fn main() { |
892 | dep::test_mod::TestStruct::CONST$0 | 877 | dep::test_mod::TestStruct::CONST$0 |
893 | } | 878 | } |
894 | //- /dep.rs crate:dep | 879 | //- /dep.rs crate:dep |
895 | pub mod test_mod { | 880 | pub mod test_mod { |
896 | pub trait TestTrait { | 881 | pub trait TestTrait { |
897 | const CONST: bool; | 882 | const CONST: bool; |
898 | } | 883 | } |
899 | pub struct TestStruct {} | 884 | pub struct TestStruct {} |
900 | impl TestTrait for TestStruct { | 885 | impl TestTrait for TestStruct { |
901 | const CONST: bool = true; | 886 | const CONST: bool = true; |
902 | } | 887 | } |
903 | } | 888 | } |
904 | ", | 889 | "#, |
905 | r" | 890 | r#" |
906 | fn main() { | 891 | fn main() { |
907 | <dep::test_mod::TestStruct as dep::test_mod::TestTrait>::CONST | 892 | <dep::test_mod::TestStruct as dep::test_mod::TestTrait>::CONST |
908 | } | 893 | } |
909 | ", | 894 | "#, |
910 | ); | 895 | ); |
911 | } | 896 | } |
912 | 897 | ||
@@ -914,23 +899,23 @@ fn main() { | |||
914 | fn assoc_fn_as_method_cross_crate() { | 899 | fn assoc_fn_as_method_cross_crate() { |
915 | check_assist_not_applicable( | 900 | check_assist_not_applicable( |
916 | qualify_path, | 901 | qualify_path, |
917 | r" | 902 | r#" |
918 | //- /main.rs crate:main deps:dep | 903 | //- /main.rs crate:main deps:dep |
919 | fn main() { | 904 | fn main() { |
920 | let test_struct = dep::test_mod::TestStruct {}; | 905 | let test_struct = dep::test_mod::TestStruct {}; |
921 | test_struct.test_func$0tion() | 906 | test_struct.test_func$0tion() |
922 | } | 907 | } |
923 | //- /dep.rs crate:dep | 908 | //- /dep.rs crate:dep |
924 | pub mod test_mod { | 909 | pub mod test_mod { |
925 | pub trait TestTrait { | 910 | pub trait TestTrait { |
926 | fn test_function(); | 911 | fn test_function(); |
927 | } | 912 | } |
928 | pub struct TestStruct {} | 913 | pub struct TestStruct {} |
929 | impl TestTrait for TestStruct { | 914 | impl TestTrait for TestStruct { |
930 | fn test_function() {} | 915 | fn test_function() {} |
931 | } | 916 | } |
932 | } | 917 | } |
933 | ", | 918 | "#, |
934 | ); | 919 | ); |
935 | } | 920 | } |
936 | 921 | ||
@@ -938,23 +923,23 @@ fn main() { | |||
938 | fn private_trait_cross_crate() { | 923 | fn private_trait_cross_crate() { |
939 | check_assist_not_applicable( | 924 | check_assist_not_applicable( |
940 | qualify_path, | 925 | qualify_path, |
941 | r" | 926 | r#" |
942 | //- /main.rs crate:main deps:dep | 927 | //- /main.rs crate:main deps:dep |
943 | fn main() { | 928 | fn main() { |
944 | let test_struct = dep::test_mod::TestStruct {}; | 929 | let test_struct = dep::test_mod::TestStruct {}; |
945 | test_struct.test_meth$0od() | 930 | test_struct.test_meth$0od() |
946 | } | 931 | } |
947 | //- /dep.rs crate:dep | 932 | //- /dep.rs crate:dep |
948 | pub mod test_mod { | 933 | pub mod test_mod { |
949 | trait TestTrait { | 934 | trait TestTrait { |
950 | fn test_method(&self); | 935 | fn test_method(&self); |
951 | } | 936 | } |
952 | pub struct TestStruct {} | 937 | pub struct TestStruct {} |
953 | impl TestTrait for TestStruct { | 938 | impl TestTrait for TestStruct { |
954 | fn test_method(&self) {} | 939 | fn test_method(&self) {} |
955 | } | 940 | } |
956 | } | 941 | } |
957 | ", | 942 | "#, |
958 | ); | 943 | ); |
959 | } | 944 | } |
960 | 945 | ||
@@ -962,32 +947,32 @@ fn main() { | |||
962 | fn not_applicable_for_imported_trait_for_method() { | 947 | fn not_applicable_for_imported_trait_for_method() { |
963 | check_assist_not_applicable( | 948 | check_assist_not_applicable( |
964 | qualify_path, | 949 | qualify_path, |
965 | r" | 950 | r#" |
966 | mod test_mod { | 951 | mod test_mod { |
967 | pub trait TestTrait { | 952 | pub trait TestTrait { |
968 | fn test_method(&self); | 953 | fn test_method(&self); |
969 | } | 954 | } |
970 | pub trait TestTrait2 { | 955 | pub trait TestTrait2 { |
971 | fn test_method(&self); | 956 | fn test_method(&self); |
972 | } | 957 | } |
973 | pub enum TestEnum { | 958 | pub enum TestEnum { |
974 | One, | 959 | One, |
975 | Two, | 960 | Two, |
976 | } | 961 | } |
977 | impl TestTrait2 for TestEnum { | 962 | impl TestTrait2 for TestEnum { |
978 | fn test_method(&self) {} | 963 | fn test_method(&self) {} |
979 | } | 964 | } |
980 | impl TestTrait for TestEnum { | 965 | impl TestTrait for TestEnum { |
981 | fn test_method(&self) {} | 966 | fn test_method(&self) {} |
982 | } | 967 | } |
983 | } | 968 | } |
984 | 969 | ||
985 | use test_mod::TestTrait2; | 970 | use test_mod::TestTrait2; |
986 | fn main() { | 971 | fn main() { |
987 | let one = test_mod::TestEnum::One; | 972 | let one = test_mod::TestEnum::One; |
988 | one.test$0_method(); | 973 | one.test$0_method(); |
989 | } | 974 | } |
990 | ", | 975 | "#, |
991 | ) | 976 | ) |
992 | } | 977 | } |
993 | 978 | ||
@@ -1114,7 +1099,7 @@ fn main() {} | |||
1114 | fn keep_generic_annotations_leading_colon() { | 1099 | fn keep_generic_annotations_leading_colon() { |
1115 | check_assist( | 1100 | check_assist( |
1116 | qualify_path, | 1101 | qualify_path, |
1117 | r" | 1102 | r#" |
1118 | //- /lib.rs crate:dep | 1103 | //- /lib.rs crate:dep |
1119 | pub mod generic { pub struct Thing<'a, T>(&'a T); } | 1104 | pub mod generic { pub struct Thing<'a, T>(&'a T); } |
1120 | 1105 | ||
@@ -1122,7 +1107,7 @@ pub mod generic { pub struct Thing<'a, T>(&'a T); } | |||
1122 | fn foo() -> Thin$0g::<'static, ()> {} | 1107 | fn foo() -> Thin$0g::<'static, ()> {} |
1123 | 1108 | ||
1124 | fn main() {} | 1109 | fn main() {} |
1125 | ", | 1110 | "#, |
1126 | r" | 1111 | r" |
1127 | fn foo() -> dep::generic::Thing::<'static, ()> {} | 1112 | fn foo() -> dep::generic::Thing::<'static, ()> {} |
1128 | 1113 | ||
@@ -1135,30 +1120,30 @@ fn main() {} | |||
1135 | fn associated_struct_const_generic() { | 1120 | fn associated_struct_const_generic() { |
1136 | check_assist( | 1121 | check_assist( |
1137 | qualify_path, | 1122 | qualify_path, |
1138 | r" | 1123 | r#" |
1139 | mod test_mod { | 1124 | mod test_mod { |
1140 | pub struct TestStruct<T> {} | 1125 | pub struct TestStruct<T> {} |
1141 | impl<T> TestStruct<T> { | 1126 | impl<T> TestStruct<T> { |
1142 | const TEST_CONST: u8 = 42; | 1127 | const TEST_CONST: u8 = 42; |
1143 | } | 1128 | } |
1144 | } | 1129 | } |
1145 | 1130 | ||
1146 | fn main() { | 1131 | fn main() { |
1147 | TestStruct::<()>::TEST_CONST$0 | 1132 | TestStruct::<()>::TEST_CONST$0 |
1148 | } | 1133 | } |
1149 | ", | 1134 | "#, |
1150 | r" | 1135 | r#" |
1151 | mod test_mod { | 1136 | mod test_mod { |
1152 | pub struct TestStruct<T> {} | 1137 | pub struct TestStruct<T> {} |
1153 | impl<T> TestStruct<T> { | 1138 | impl<T> TestStruct<T> { |
1154 | const TEST_CONST: u8 = 42; | 1139 | const TEST_CONST: u8 = 42; |
1155 | } | 1140 | } |
1156 | } | 1141 | } |
1157 | 1142 | ||
1158 | fn main() { | 1143 | fn main() { |
1159 | test_mod::TestStruct::<()>::TEST_CONST | 1144 | test_mod::TestStruct::<()>::TEST_CONST |
1160 | } | 1145 | } |
1161 | ", | 1146 | "#, |
1162 | ); | 1147 | ); |
1163 | } | 1148 | } |
1164 | 1149 | ||
@@ -1166,36 +1151,36 @@ fn main() {} | |||
1166 | fn associated_trait_const_generic() { | 1151 | fn associated_trait_const_generic() { |
1167 | check_assist( | 1152 | check_assist( |
1168 | qualify_path, | 1153 | qualify_path, |
1169 | r" | 1154 | r#" |
1170 | mod test_mod { | 1155 | mod test_mod { |
1171 | pub trait TestTrait { | 1156 | pub trait TestTrait { |
1172 | const TEST_CONST: u8; | 1157 | const TEST_CONST: u8; |
1173 | } | 1158 | } |
1174 | pub struct TestStruct<T> {} | 1159 | pub struct TestStruct<T> {} |
1175 | impl<T> TestTrait for TestStruct<T> { | 1160 | impl<T> TestTrait for TestStruct<T> { |
1176 | const TEST_CONST: u8 = 42; | 1161 | const TEST_CONST: u8 = 42; |
1177 | } | 1162 | } |
1178 | } | 1163 | } |
1179 | 1164 | ||
1180 | fn main() { | 1165 | fn main() { |
1181 | test_mod::TestStruct::<()>::TEST_CONST$0 | 1166 | test_mod::TestStruct::<()>::TEST_CONST$0 |
1182 | } | 1167 | } |
1183 | ", | 1168 | "#, |
1184 | r" | 1169 | r#" |
1185 | mod test_mod { | 1170 | mod test_mod { |
1186 | pub trait TestTrait { | 1171 | pub trait TestTrait { |
1187 | const TEST_CONST: u8; | 1172 | const TEST_CONST: u8; |
1188 | } | 1173 | } |
1189 | pub struct TestStruct<T> {} | 1174 | pub struct TestStruct<T> {} |
1190 | impl<T> TestTrait for TestStruct<T> { | 1175 | impl<T> TestTrait for TestStruct<T> { |
1191 | const TEST_CONST: u8 = 42; | 1176 | const TEST_CONST: u8 = 42; |
1192 | } | 1177 | } |
1193 | } | 1178 | } |
1194 | 1179 | ||
1195 | fn main() { | 1180 | fn main() { |
1196 | <test_mod::TestStruct::<()> as test_mod::TestTrait>::TEST_CONST | 1181 | <test_mod::TestStruct::<()> as test_mod::TestTrait>::TEST_CONST |
1197 | } | 1182 | } |
1198 | ", | 1183 | "#, |
1199 | ); | 1184 | ); |
1200 | } | 1185 | } |
1201 | 1186 | ||
@@ -1203,38 +1188,38 @@ fn main() {} | |||
1203 | fn trait_method_generic() { | 1188 | fn trait_method_generic() { |
1204 | check_assist( | 1189 | check_assist( |
1205 | qualify_path, | 1190 | qualify_path, |
1206 | r" | 1191 | r#" |
1207 | mod test_mod { | 1192 | mod test_mod { |
1208 | pub trait TestTrait { | 1193 | pub trait TestTrait { |
1209 | fn test_method<T>(&self); | 1194 | fn test_method<T>(&self); |
1210 | } | 1195 | } |
1211 | pub struct TestStruct {} | 1196 | pub struct TestStruct {} |
1212 | impl TestTrait for TestStruct { | 1197 | impl TestTrait for TestStruct { |
1213 | fn test_method<T>(&self) {} | 1198 | fn test_method<T>(&self) {} |
1214 | } | 1199 | } |
1215 | } | 1200 | } |
1216 | 1201 | ||
1217 | fn main() { | 1202 | fn main() { |
1218 | let test_struct = test_mod::TestStruct {}; | 1203 | let test_struct = test_mod::TestStruct {}; |
1219 | test_struct.test_meth$0od::<()>() | 1204 | test_struct.test_meth$0od::<()>() |
1220 | } | 1205 | } |
1221 | ", | 1206 | "#, |
1222 | r" | 1207 | r#" |
1223 | mod test_mod { | 1208 | mod test_mod { |
1224 | pub trait TestTrait { | 1209 | pub trait TestTrait { |
1225 | fn test_method<T>(&self); | 1210 | fn test_method<T>(&self); |
1226 | } | 1211 | } |
1227 | pub struct TestStruct {} | 1212 | pub struct TestStruct {} |
1228 | impl TestTrait for TestStruct { | 1213 | impl TestTrait for TestStruct { |
1229 | fn test_method<T>(&self) {} | 1214 | fn test_method<T>(&self) {} |
1230 | } | 1215 | } |
1231 | } | 1216 | } |
1232 | 1217 | ||
1233 | fn main() { | 1218 | fn main() { |
1234 | let test_struct = test_mod::TestStruct {}; | 1219 | let test_struct = test_mod::TestStruct {}; |
1235 | test_mod::TestTrait::test_method::<()>(&test_struct) | 1220 | test_mod::TestTrait::test_method::<()>(&test_struct) |
1236 | } | 1221 | } |
1237 | ", | 1222 | "#, |
1238 | ); | 1223 | ); |
1239 | } | 1224 | } |
1240 | } | 1225 | } |
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/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/replace_impl_trait_with_generic.rs b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs index 540a905cc..a2af2035f 100644 --- a/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs +++ b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs | |||
@@ -105,12 +105,13 @@ fn foo<B: Bar | |||
105 | } | 105 | } |
106 | 106 | ||
107 | #[test] | 107 | #[test] |
108 | #[ignore = "This case is very rare but there is no simple solutions to fix it."] | ||
109 | fn replace_impl_trait_with_exist_generic_letter() { | 108 | fn replace_impl_trait_with_exist_generic_letter() { |
109 | // FIXME: This is wrong, we should pick a different name if the one we | ||
110 | // want is already bound. | ||
110 | check_assist( | 111 | check_assist( |
111 | replace_impl_trait_with_generic, | 112 | replace_impl_trait_with_generic, |
112 | r#"fn foo<B>(bar: $0impl Bar) {}"#, | 113 | r#"fn foo<B>(bar: $0impl Bar) {}"#, |
113 | r#"fn foo<B, C: Bar>(bar: C) {}"#, | 114 | r#"fn foo<B, B: Bar>(bar: B) {}"#, |
114 | ); | 115 | ); |
115 | } | 116 | } |
116 | 117 | ||
diff --git a/crates/ide_assists/src/handlers/unmerge_use.rs b/crates/ide_assists/src/handlers/unmerge_use.rs index 8d271e056..14e862cd0 100644 --- a/crates/ide_assists/src/handlers/unmerge_use.rs +++ b/crates/ide_assists/src/handlers/unmerge_use.rs | |||
@@ -73,7 +73,11 @@ fn resolve_full_path(tree: &ast::UseTree) -> Option<ast::Path> { | |||
73 | for path in paths { | 73 | for path in paths { |
74 | final_path = ast::make::path_concat(path, final_path) | 74 | final_path = ast::make::path_concat(path, final_path) |
75 | } | 75 | } |
76 | Some(final_path) | 76 | if final_path.segment().map_or(false, |it| it.self_token().is_some()) { |
77 | final_path.qualifier() | ||
78 | } else { | ||
79 | Some(final_path) | ||
80 | } | ||
77 | } | 81 | } |
78 | 82 | ||
79 | #[cfg(test)] | 83 | #[cfg(test)] |
@@ -223,4 +227,14 @@ pub use std::fmt::Display; | |||
223 | ", | 227 | ", |
224 | ); | 228 | ); |
225 | } | 229 | } |
230 | |||
231 | #[test] | ||
232 | fn unmerge_use_item_on_self() { | ||
233 | check_assist( | ||
234 | unmerge_use, | ||
235 | r"use std::process::{Command, self$0};", | ||
236 | r"use std::process::{Command}; | ||
237 | use std::process;", | ||
238 | ); | ||
239 | } | ||
226 | } | 240 | } |
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..fa378a622 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs | |||
@@ -17,156 +17,31 @@ mod tests; | |||
17 | pub mod utils; | 17 | pub mod utils; |
18 | pub mod path_transform; | 18 | pub mod path_transform; |
19 | 19 | ||
20 | use std::str::FromStr; | ||
21 | |||
22 | use hir::Semantics; | 20 | use hir::Semantics; |
23 | use ide_db::{base_db::FileRange, label::Label, source_change::SourceChange, RootDatabase}; | 21 | use ide_db::{base_db::FileRange, RootDatabase}; |
24 | use syntax::TextRange; | 22 | use syntax::TextRange; |
25 | 23 | ||
26 | pub(crate) use crate::assist_context::{AssistContext, Assists}; | 24 | pub(crate) use crate::assist_context::{AssistContext, Assists}; |
27 | 25 | ||
28 | pub use assist_config::AssistConfig; | 26 | pub use assist_config::AssistConfig; |
29 | 27 | pub use ide_db::assists::{ | |
30 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 28 | Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel, SingleResolve, |
31 | pub enum AssistKind { | 29 | }; |
32 | // FIXME: does the None variant make sense? Probably not. | 30 | |
33 | None, | 31 | /// Return all the assists applicable at the given position. |
34 | 32 | pub fn assists( | |
35 | QuickFix, | 33 | db: &RootDatabase, |
36 | Generate, | 34 | config: &AssistConfig, |
37 | Refactor, | 35 | resolve: AssistResolveStrategy, |
38 | RefactorExtract, | 36 | range: FileRange, |
39 | RefactorInline, | 37 | ) -> Vec<Assist> { |
40 | RefactorRewrite, | 38 | let sema = Semantics::new(db); |
41 | } | 39 | let ctx = AssistContext::new(sema, config, range); |
42 | 40 | let mut acc = Assists::new(&ctx, resolve); | |
43 | impl AssistKind { | 41 | handlers::all().iter().for_each(|handler| { |
44 | pub fn contains(self, other: AssistKind) -> bool { | 42 | handler(&mut acc, &ctx); |
45 | if self == other { | 43 | }); |
46 | return true; | 44 | acc.finish() |
47 | } | ||
48 | |||
49 | match self { | ||
50 | AssistKind::None | AssistKind::Generate => return true, | ||
51 | AssistKind::Refactor => match other { | ||
52 | AssistKind::RefactorExtract | ||
53 | | AssistKind::RefactorInline | ||
54 | | AssistKind::RefactorRewrite => return true, | ||
55 | _ => return false, | ||
56 | }, | ||
57 | _ => return false, | ||
58 | } | ||
59 | } | ||
60 | |||
61 | pub fn name(&self) -> &str { | ||
62 | match self { | ||
63 | AssistKind::None => "None", | ||
64 | AssistKind::QuickFix => "QuickFix", | ||
65 | AssistKind::Generate => "Generate", | ||
66 | AssistKind::Refactor => "Refactor", | ||
67 | AssistKind::RefactorExtract => "RefactorExtract", | ||
68 | AssistKind::RefactorInline => "RefactorInline", | ||
69 | AssistKind::RefactorRewrite => "RefactorRewrite", | ||
70 | } | ||
71 | } | ||
72 | } | ||
73 | |||
74 | impl FromStr for AssistKind { | ||
75 | type Err = String; | ||
76 | |||
77 | fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
78 | match s { | ||
79 | "None" => Ok(AssistKind::None), | ||
80 | "QuickFix" => Ok(AssistKind::QuickFix), | ||
81 | "Generate" => Ok(AssistKind::Generate), | ||
82 | "Refactor" => Ok(AssistKind::Refactor), | ||
83 | "RefactorExtract" => Ok(AssistKind::RefactorExtract), | ||
84 | "RefactorInline" => Ok(AssistKind::RefactorInline), | ||
85 | "RefactorRewrite" => Ok(AssistKind::RefactorRewrite), | ||
86 | unknown => Err(format!("Unknown AssistKind: '{}'", unknown)), | ||
87 | } | ||
88 | } | ||
89 | } | ||
90 | |||
91 | /// Unique identifier of the assist, should not be shown to the user | ||
92 | /// directly. | ||
93 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
94 | pub struct AssistId(pub &'static str, pub AssistKind); | ||
95 | |||
96 | /// A way to control how many asssist to resolve during the assist resolution. | ||
97 | /// When an assist is resolved, its edits are calculated that might be costly to always do by default. | ||
98 | #[derive(Debug)] | ||
99 | pub enum AssistResolveStrategy { | ||
100 | /// No assists should be resolved. | ||
101 | None, | ||
102 | /// All assists should be resolved. | ||
103 | All, | ||
104 | /// Only a certain assist should be resolved. | ||
105 | Single(SingleResolve), | ||
106 | } | ||
107 | |||
108 | /// Hold the [`AssistId`] data of a certain assist to resolve. | ||
109 | /// The original id object cannot be used due to a `'static` lifetime | ||
110 | /// and the requirement to construct this struct dynamically during the resolve handling. | ||
111 | #[derive(Debug)] | ||
112 | pub struct SingleResolve { | ||
113 | /// The id of the assist. | ||
114 | pub assist_id: String, | ||
115 | // The kind of the assist. | ||
116 | pub assist_kind: AssistKind, | ||
117 | } | ||
118 | |||
119 | impl AssistResolveStrategy { | ||
120 | pub fn should_resolve(&self, id: &AssistId) -> bool { | ||
121 | match self { | ||
122 | AssistResolveStrategy::None => false, | ||
123 | AssistResolveStrategy::All => true, | ||
124 | AssistResolveStrategy::Single(single_resolve) => { | ||
125 | single_resolve.assist_id == id.0 && single_resolve.assist_kind == id.1 | ||
126 | } | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | |||
131 | #[derive(Clone, Debug)] | ||
132 | pub struct GroupLabel(pub String); | ||
133 | |||
134 | #[derive(Debug, Clone)] | ||
135 | pub struct Assist { | ||
136 | pub id: AssistId, | ||
137 | /// Short description of the assist, as shown in the UI. | ||
138 | pub label: Label, | ||
139 | pub group: Option<GroupLabel>, | ||
140 | /// Target ranges are used to sort assists: the smaller the target range, | ||
141 | /// the more specific assist is, and so it should be sorted first. | ||
142 | pub target: TextRange, | ||
143 | /// Computing source change sometimes is much more costly then computing the | ||
144 | /// other fields. Additionally, the actual change is not required to show | ||
145 | /// the lightbulb UI, it only is needed when the user tries to apply an | ||
146 | /// assist. So, we compute it lazily: the API allow requesting assists with | ||
147 | /// or without source change. We could (and in fact, used to) distinguish | ||
148 | /// between resolved and unresolved assists at the type level, but this is | ||
149 | /// cumbersome, especially if you want to embed an assist into another data | ||
150 | /// structure, such as a diagnostic. | ||
151 | pub source_change: Option<SourceChange>, | ||
152 | } | ||
153 | |||
154 | impl Assist { | ||
155 | /// Return all the assists applicable at the given position. | ||
156 | pub fn get( | ||
157 | db: &RootDatabase, | ||
158 | config: &AssistConfig, | ||
159 | resolve: AssistResolveStrategy, | ||
160 | range: FileRange, | ||
161 | ) -> Vec<Assist> { | ||
162 | let sema = Semantics::new(db); | ||
163 | let ctx = AssistContext::new(sema, config, range); | ||
164 | let mut acc = Assists::new(&ctx, resolve); | ||
165 | handlers::all().iter().for_each(|handler| { | ||
166 | handler(&mut acc, &ctx); | ||
167 | }); | ||
168 | acc.finish() | ||
169 | } | ||
170 | } | 45 | } |
171 | 46 | ||
172 | mod handlers { | 47 | mod handlers { |
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs index 2b7c2d581..60cecd94c 100644 --- a/crates/ide_assists/src/tests.rs +++ b/crates/ide_assists/src/tests.rs | |||
@@ -16,8 +16,8 @@ use syntax::TextRange; | |||
16 | use test_utils::{assert_eq_text, extract_offset}; | 16 | use test_utils::{assert_eq_text, extract_offset}; |
17 | 17 | ||
18 | use crate::{ | 18 | use crate::{ |
19 | handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, AssistResolveStrategy, | 19 | assists, handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, |
20 | Assists, SingleResolve, | 20 | AssistResolveStrategy, Assists, SingleResolve, |
21 | }; | 21 | }; |
22 | 22 | ||
23 | pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { | 23 | pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { |
@@ -74,18 +74,18 @@ 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 | ||
81 | let assist = Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::All, frange) | 81 | let assist = assists(&db, &TEST_CONFIG, AssistResolveStrategy::All, frange) |
82 | .into_iter() | 82 | .into_iter() |
83 | .find(|assist| assist.id.0 == assist_id) | 83 | .find(|assist| assist.id.0 == assist_id) |
84 | .unwrap_or_else(|| { | 84 | .unwrap_or_else(|| { |
85 | panic!( | 85 | panic!( |
86 | "\n\nAssist is not applicable: {}\nAvailable assists: {}", | 86 | "\n\nAssist is not applicable: {}\nAvailable assists: {}", |
87 | assist_id, | 87 | assist_id, |
88 | Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange) | 88 | assists(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange) |
89 | .into_iter() | 89 | .into_iter() |
90 | .map(|assist| assist.id.0) | 90 | .map(|assist| assist.id.0) |
91 | .collect::<Vec<_>>() | 91 | .collect::<Vec<_>>() |
@@ -210,7 +210,7 @@ fn assist_order_field_struct() { | |||
210 | let (before_cursor_pos, before) = extract_offset(before); | 210 | let (before_cursor_pos, before) = extract_offset(before); |
211 | let (db, file_id) = with_single_file(&before); | 211 | let (db, file_id) = with_single_file(&before); |
212 | let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; | 212 | let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; |
213 | let assists = Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange); | 213 | let assists = assists(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange); |
214 | let mut assists = assists.iter(); | 214 | let mut assists = assists.iter(); |
215 | 215 | ||
216 | assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); | 216 | assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); |
@@ -235,7 +235,7 @@ pub fn test_some_range(a: int) -> bool { | |||
235 | "#, | 235 | "#, |
236 | ); | 236 | ); |
237 | 237 | ||
238 | let assists = Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange); | 238 | let assists = assists(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange); |
239 | let expected = labels(&assists); | 239 | let expected = labels(&assists); |
240 | 240 | ||
241 | expect![[r#" | 241 | expect![[r#" |
@@ -264,7 +264,7 @@ pub fn test_some_range(a: int) -> bool { | |||
264 | let mut cfg = TEST_CONFIG; | 264 | let mut cfg = TEST_CONFIG; |
265 | cfg.allowed = Some(vec![AssistKind::Refactor]); | 265 | cfg.allowed = Some(vec![AssistKind::Refactor]); |
266 | 266 | ||
267 | let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange); | 267 | let assists = assists(&db, &cfg, AssistResolveStrategy::None, frange); |
268 | let expected = labels(&assists); | 268 | let expected = labels(&assists); |
269 | 269 | ||
270 | expect![[r#" | 270 | expect![[r#" |
@@ -279,7 +279,7 @@ pub fn test_some_range(a: int) -> bool { | |||
279 | { | 279 | { |
280 | let mut cfg = TEST_CONFIG; | 280 | let mut cfg = TEST_CONFIG; |
281 | cfg.allowed = Some(vec![AssistKind::RefactorExtract]); | 281 | cfg.allowed = Some(vec![AssistKind::RefactorExtract]); |
282 | let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange); | 282 | let assists = assists(&db, &cfg, AssistResolveStrategy::None, frange); |
283 | let expected = labels(&assists); | 283 | let expected = labels(&assists); |
284 | 284 | ||
285 | expect![[r#" | 285 | expect![[r#" |
@@ -292,7 +292,7 @@ pub fn test_some_range(a: int) -> bool { | |||
292 | { | 292 | { |
293 | let mut cfg = TEST_CONFIG; | 293 | let mut cfg = TEST_CONFIG; |
294 | cfg.allowed = Some(vec![AssistKind::QuickFix]); | 294 | cfg.allowed = Some(vec![AssistKind::QuickFix]); |
295 | let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange); | 295 | let assists = assists(&db, &cfg, AssistResolveStrategy::None, frange); |
296 | let expected = labels(&assists); | 296 | let expected = labels(&assists); |
297 | 297 | ||
298 | expect![[r#""#]].assert_eq(&expected); | 298 | expect![[r#""#]].assert_eq(&expected); |
@@ -317,7 +317,7 @@ pub fn test_some_range(a: int) -> bool { | |||
317 | cfg.allowed = Some(vec![AssistKind::RefactorExtract]); | 317 | cfg.allowed = Some(vec![AssistKind::RefactorExtract]); |
318 | 318 | ||
319 | { | 319 | { |
320 | let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange); | 320 | let assists = assists(&db, &cfg, AssistResolveStrategy::None, frange); |
321 | assert_eq!(2, assists.len()); | 321 | assert_eq!(2, assists.len()); |
322 | let mut assists = assists.into_iter(); | 322 | let mut assists = assists.into_iter(); |
323 | 323 | ||
@@ -353,7 +353,7 @@ pub fn test_some_range(a: int) -> bool { | |||
353 | } | 353 | } |
354 | 354 | ||
355 | { | 355 | { |
356 | let assists = Assist::get( | 356 | let assists = assists( |
357 | &db, | 357 | &db, |
358 | &cfg, | 358 | &cfg, |
359 | AssistResolveStrategy::Single(SingleResolve { | 359 | AssistResolveStrategy::Single(SingleResolve { |
@@ -397,7 +397,7 @@ pub fn test_some_range(a: int) -> bool { | |||
397 | } | 397 | } |
398 | 398 | ||
399 | { | 399 | { |
400 | let assists = Assist::get( | 400 | let assists = assists( |
401 | &db, | 401 | &db, |
402 | &cfg, | 402 | &cfg, |
403 | AssistResolveStrategy::Single(SingleResolve { | 403 | AssistResolveStrategy::Single(SingleResolve { |
@@ -462,7 +462,7 @@ pub fn test_some_range(a: int) -> bool { | |||
462 | } | 462 | } |
463 | 463 | ||
464 | { | 464 | { |
465 | let assists = Assist::get(&db, &cfg, AssistResolveStrategy::All, frange); | 465 | let assists = assists(&db, &cfg, AssistResolveStrategy::All, frange); |
466 | assert_eq!(2, assists.len()); | 466 | assert_eq!(2, assists.len()); |
467 | let mut assists = assists.into_iter(); | 467 | let mut assists = assists.into_iter(); |
468 | 468 | ||
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 fbd499900..783305005 100644 --- a/crates/ide_completion/src/completions.rs +++ b/crates/ide_completion/src/completions.rs | |||
@@ -29,7 +29,7 @@ use crate::{ | |||
29 | macro_::render_macro, | 29 | macro_::render_macro, |
30 | pattern::{render_struct_pat, render_variant_pat}, | 30 | pattern::{render_struct_pat, render_variant_pat}, |
31 | render_field, render_resolution, render_tuple_field, | 31 | render_field, render_resolution, render_tuple_field, |
32 | type_alias::render_type_alias, | 32 | type_alias::{render_type_alias, render_type_alias_with_eq}, |
33 | RenderContext, | 33 | RenderContext, |
34 | }, | 34 | }, |
35 | CompletionContext, CompletionItem, CompletionItemKind, | 35 | CompletionContext, CompletionItem, CompletionItemKind, |
@@ -109,9 +109,6 @@ impl Completions { | |||
109 | local_name: hir::Name, | 109 | local_name: hir::Name, |
110 | resolution: &hir::ScopeDef, | 110 | resolution: &hir::ScopeDef, |
111 | ) { | 111 | ) { |
112 | if ctx.expects_type() && resolution.is_value_def() { | ||
113 | return; | ||
114 | } | ||
115 | self.add_opt(render_resolution(RenderContext::new(ctx), local_name, resolution)); | 112 | self.add_opt(render_resolution(RenderContext::new(ctx), local_name, resolution)); |
116 | } | 113 | } |
117 | 114 | ||
@@ -134,9 +131,6 @@ impl Completions { | |||
134 | func: hir::Function, | 131 | func: hir::Function, |
135 | local_name: Option<hir::Name>, | 132 | local_name: Option<hir::Name>, |
136 | ) { | 133 | ) { |
137 | if ctx.expects_type() { | ||
138 | return; | ||
139 | } | ||
140 | self.add_opt(render_fn(RenderContext::new(ctx), None, local_name, func)); | 134 | self.add_opt(render_fn(RenderContext::new(ctx), None, local_name, func)); |
141 | } | 135 | } |
142 | 136 | ||
@@ -178,9 +172,6 @@ impl Completions { | |||
178 | } | 172 | } |
179 | 173 | ||
180 | pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) { | 174 | pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) { |
181 | if ctx.expects_type() { | ||
182 | return; | ||
183 | } | ||
184 | self.add_opt(render_const(RenderContext::new(ctx), constant)); | 175 | self.add_opt(render_const(RenderContext::new(ctx), constant)); |
185 | } | 176 | } |
186 | 177 | ||
@@ -188,6 +179,14 @@ impl Completions { | |||
188 | self.add_opt(render_type_alias(RenderContext::new(ctx), type_alias)); | 179 | self.add_opt(render_type_alias(RenderContext::new(ctx), type_alias)); |
189 | } | 180 | } |
190 | 181 | ||
182 | pub(crate) fn add_type_alias_with_eq( | ||
183 | &mut self, | ||
184 | ctx: &CompletionContext, | ||
185 | type_alias: hir::TypeAlias, | ||
186 | ) { | ||
187 | self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias)); | ||
188 | } | ||
189 | |||
191 | pub(crate) fn add_qualified_enum_variant( | 190 | pub(crate) fn add_qualified_enum_variant( |
192 | &mut self, | 191 | &mut self, |
193 | ctx: &CompletionContext, | 192 | ctx: &CompletionContext, |
@@ -204,32 +203,30 @@ impl Completions { | |||
204 | variant: hir::Variant, | 203 | variant: hir::Variant, |
205 | local_name: Option<hir::Name>, | 204 | local_name: Option<hir::Name>, |
206 | ) { | 205 | ) { |
207 | if ctx.expects_type() { | ||
208 | return; | ||
209 | } | ||
210 | let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None); | 206 | let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None); |
211 | self.add(item); | 207 | self.add(item); |
212 | } | 208 | } |
213 | } | 209 | } |
214 | 210 | ||
211 | /// Calls the callback for each variant of the provided enum with the path to the variant. | ||
215 | fn complete_enum_variants( | 212 | fn complete_enum_variants( |
216 | acc: &mut Completions, | 213 | acc: &mut Completions, |
217 | ctx: &CompletionContext, | 214 | ctx: &CompletionContext, |
218 | enum_data: hir::Enum, | 215 | enum_: hir::Enum, |
219 | cb: impl Fn(&mut Completions, &CompletionContext, hir::Variant, hir::ModPath), | 216 | cb: impl Fn(&mut Completions, &CompletionContext, hir::Variant, hir::ModPath), |
220 | ) { | 217 | ) { |
221 | let variants = enum_data.variants(ctx.db); | 218 | let variants = enum_.variants(ctx.db); |
222 | 219 | ||
223 | let module = if let Some(module) = ctx.scope.module() { | 220 | let module = if let Some(module) = ctx.scope.module() { |
224 | // Compute path from the completion site if available. | 221 | // Compute path from the completion site if available. |
225 | module | 222 | module |
226 | } else { | 223 | } else { |
227 | // Otherwise fall back to the enum's definition site. | 224 | // Otherwise fall back to the enum's definition site. |
228 | enum_data.module(ctx.db) | 225 | enum_.module(ctx.db) |
229 | }; | 226 | }; |
230 | 227 | ||
231 | if let Some(impl_) = ctx.impl_def.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) { | 228 | if let Some(impl_) = ctx.impl_def.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) { |
232 | if impl_.self_ty(ctx.db).as_adt() == Some(hir::Adt::Enum(enum_data)) { | 229 | if impl_.self_ty(ctx.db).as_adt() == Some(hir::Adt::Enum(enum_)) { |
233 | for &variant in &variants { | 230 | for &variant in &variants { |
234 | let self_path = hir::ModPath::from_segments( | 231 | let self_path = hir::ModPath::from_segments( |
235 | hir::PathKind::Plain, | 232 | hir::PathKind::Plain, |
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs index 7f76e357e..6df569c2a 100644 --- a/crates/ide_completion/src/completions/attribute.rs +++ b/crates/ide_completion/src/completions/attribute.rs | |||
@@ -219,7 +219,7 @@ static KIND_TO_ATTRIBUTES: Lazy<FxHashMap<SyntaxKind, &[&str]>> = Lazy::new(|| { | |||
219 | }); | 219 | }); |
220 | const EXPR_ATTRIBUTES: &[&str] = attrs!(); | 220 | const EXPR_ATTRIBUTES: &[&str] = attrs!(); |
221 | 221 | ||
222 | /// 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> |
223 | // Keep these sorted for the binary search! | 223 | // Keep these sorted for the binary search! |
224 | const ATTRIBUTES: &[AttrCompletion] = &[ | 224 | const ATTRIBUTES: &[AttrCompletion] = &[ |
225 | 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 d526824fb..7b3133e53 100644 --- a/crates/ide_completion/src/completions/attribute/derive.rs +++ b/crates/ide_completion/src/completions/attribute/derive.rs | |||
@@ -93,57 +93,20 @@ mod tests { | |||
93 | } | 93 | } |
94 | 94 | ||
95 | #[test] | 95 | #[test] |
96 | #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures | ||
97 | fn empty_derive() { | 96 | fn empty_derive() { |
98 | check( | 97 | // FIXME: Add build-in derives to fixture. |
99 | r#"#[derive($0)] struct Test;"#, | 98 | check(r#"#[derive($0)] struct Test;"#, expect![[r#""#]]); |
100 | expect![[r#" | ||
101 | at Clone | ||
102 | at Clone, Copy | ||
103 | at Debug | ||
104 | at Default | ||
105 | at Hash | ||
106 | at PartialEq | ||
107 | at PartialEq, Eq | ||
108 | at PartialEq, PartialOrd | ||
109 | at PartialEq, Eq, PartialOrd, Ord | ||
110 | "#]], | ||
111 | ); | ||
112 | } | 99 | } |
113 | 100 | ||
114 | #[test] | 101 | #[test] |
115 | #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures | ||
116 | fn derive_with_input() { | 102 | fn derive_with_input() { |
117 | check( | 103 | // FIXME: Add build-in derives to fixture. |
118 | r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#, | 104 | check(r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#, expect![[r#""#]]) |
119 | expect![[r#" | ||
120 | at Clone | ||
121 | at Clone, Copy | ||
122 | at Debug | ||
123 | at Default | ||
124 | at Hash | ||
125 | at Eq | ||
126 | at PartialOrd | ||
127 | at Eq, PartialOrd, Ord | ||
128 | "#]], | ||
129 | ) | ||
130 | } | 105 | } |
131 | 106 | ||
132 | #[test] | 107 | #[test] |
133 | #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures | ||
134 | fn derive_with_input2() { | 108 | fn derive_with_input2() { |
135 | check( | 109 | // FIXME: Add build-in derives to fixture. |
136 | r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#, | 110 | check(r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#, expect![[r#""#]]) |
137 | expect![[r#" | ||
138 | at Clone | ||
139 | at Clone, Copy | ||
140 | at Debug | ||
141 | at Default | ||
142 | at Hash | ||
143 | at Eq | ||
144 | at PartialOrd | ||
145 | at Eq, PartialOrd, Ord | ||
146 | "#]], | ||
147 | ) | ||
148 | } | 111 | } |
149 | } | 112 | } |
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs index 8ad57a069..9552875c1 100644 --- a/crates/ide_completion/src/completions/dot.rs +++ b/crates/ide_completion/src/completions/dot.rs | |||
@@ -13,7 +13,7 @@ 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 | }; |
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs index c010cbbca..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,15 +79,15 @@ | |||
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 ide_db::helpers::{ | 93 | use ide_db::helpers::{ |
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs index ba13d3707..0fccbeccf 100644 --- a/crates/ide_completion/src/completions/keyword.rs +++ b/crates/ide_completion/src/completions/keyword.rs | |||
@@ -536,17 +536,11 @@ Some multi-line comment$0 | |||
536 | fn test_completion_await_impls_future() { | 536 | fn test_completion_await_impls_future() { |
537 | check( | 537 | check( |
538 | r#" | 538 | r#" |
539 | //- /main.rs crate:main deps:std | 539 | //- minicore: future |
540 | use std::future::*; | 540 | use core::future::*; |
541 | struct A {} | 541 | struct A {} |
542 | impl Future for A {} | 542 | impl Future for A {} |
543 | fn foo(a: A) { a.$0 } | 543 | fn foo(a: A) { a.$0 } |
544 | |||
545 | //- /std/lib.rs crate:std | ||
546 | pub mod future { | ||
547 | #[lang = "future_trait"] | ||
548 | pub trait Future {} | ||
549 | } | ||
550 | "#, | 544 | "#, |
551 | expect![[r#" | 545 | expect![[r#" |
552 | kw await expr.await | 546 | kw await expr.await |
@@ -555,20 +549,12 @@ pub mod future { | |||
555 | 549 | ||
556 | check( | 550 | check( |
557 | r#" | 551 | r#" |
558 | //- /main.rs crate:main deps:std | 552 | //- minicore: future |
559 | use std::future::*; | 553 | use std::future::*; |
560 | fn foo() { | 554 | fn foo() { |
561 | let a = async {}; | 555 | let a = async {}; |
562 | a.$0 | 556 | a.$0 |
563 | } | 557 | } |
564 | |||
565 | //- /std/lib.rs crate:std | ||
566 | pub mod future { | ||
567 | #[lang = "future_trait"] | ||
568 | pub trait Future { | ||
569 | type Output; | ||
570 | } | ||
571 | } | ||
572 | "#, | 558 | "#, |
573 | expect![[r#" | 559 | expect![[r#" |
574 | kw await expr.await | 560 | kw await expr.await |
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs index 86eb21714..9f98b21be 100644 --- a/crates/ide_completion/src/completions/postfix.rs +++ b/crates/ide_completion/src/completions/postfix.rs | |||
@@ -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 9ebe1dcc0..2dc13c293 100644 --- a/crates/ide_completion/src/completions/postfix/format_like.rs +++ b/crates/ide_completion/src/completions/postfix/format_like.rs | |||
@@ -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 58d4dd9ee..1643eeed4 100644 --- a/crates/ide_completion/src/completions/qualified_path.rs +++ b/crates/ide_completion/src/completions/qualified_path.rs | |||
@@ -15,10 +15,11 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
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 | |||
22 | let context_module = ctx.scope.module(); | 23 | let context_module = ctx.scope.module(); |
23 | 24 | ||
24 | if ctx.expects_item() || ctx.expects_assoc_item() { | 25 | if ctx.expects_item() || ctx.expects_assoc_item() { |
@@ -60,21 +61,31 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
60 | } | 61 | } |
61 | } | 62 | } |
62 | 63 | ||
63 | if let hir::ScopeDef::MacroDef(macro_def) = def { | 64 | let add_resolution = match def { |
64 | if !macro_def.is_fn_like() { | 65 | // Don't suggest attribute macros and derives. |
65 | // Don't suggest attribute macros and derives. | 66 | hir::ScopeDef::MacroDef(mac) => mac.is_fn_like(), |
66 | continue; | 67 | // no values in type places |
68 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Function(_)) | ||
69 | | hir::ScopeDef::ModuleDef(hir::ModuleDef::Variant(_)) | ||
70 | | hir::ScopeDef::ModuleDef(hir::ModuleDef::Static(_)) | ||
71 | | hir::ScopeDef::Local(_) => !ctx.expects_type(), | ||
72 | // unless its a constant in a generic arg list position | ||
73 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Const(_)) => { | ||
74 | !ctx.expects_type() || ctx.expects_generic_arg() | ||
67 | } | 75 | } |
68 | } | 76 | _ => true, |
77 | }; | ||
69 | 78 | ||
70 | acc.add_resolution(ctx, name, &def); | 79 | if add_resolution { |
80 | acc.add_resolution(ctx, name, &def); | ||
81 | } | ||
71 | } | 82 | } |
72 | } | 83 | } |
73 | hir::PathResolution::Def(def @ hir::ModuleDef::Adt(_)) | 84 | hir::PathResolution::Def(def @ hir::ModuleDef::Adt(_)) |
74 | | hir::PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_)) | 85 | | hir::PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_)) |
75 | | hir::PathResolution::Def(def @ hir::ModuleDef::BuiltinType(_)) => { | 86 | | hir::PathResolution::Def(def @ hir::ModuleDef::BuiltinType(_)) => { |
76 | if let hir::ModuleDef::Adt(hir::Adt::Enum(e)) = def { | 87 | if let hir::ModuleDef::Adt(hir::Adt::Enum(e)) = def { |
77 | add_enum_variants(ctx, acc, e); | 88 | add_enum_variants(acc, ctx, e); |
78 | } | 89 | } |
79 | let ty = match def { | 90 | let ty = match def { |
80 | hir::ModuleDef::Adt(adt) => adt.ty(ctx.db), | 91 | hir::ModuleDef::Adt(adt) => adt.ty(ctx.db), |
@@ -82,7 +93,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
82 | let ty = a.ty(ctx.db); | 93 | let ty = a.ty(ctx.db); |
83 | if let Some(hir::Adt::Enum(e)) = ty.as_adt() { | 94 | if let Some(hir::Adt::Enum(e)) = ty.as_adt() { |
84 | cov_mark::hit!(completes_variant_through_alias); | 95 | cov_mark::hit!(completes_variant_through_alias); |
85 | add_enum_variants(ctx, acc, e); | 96 | add_enum_variants(acc, ctx, e); |
86 | } | 97 | } |
87 | ty | 98 | ty |
88 | } | 99 | } |
@@ -107,11 +118,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
107 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | 118 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { |
108 | return None; | 119 | return None; |
109 | } | 120 | } |
110 | match item { | 121 | add_assoc_item(acc, ctx, item); |
111 | hir::AssocItem::Function(func) => acc.add_function(ctx, func, None), | ||
112 | hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), | ||
113 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | ||
114 | } | ||
115 | None::<()> | 122 | None::<()> |
116 | }); | 123 | }); |
117 | 124 | ||
@@ -133,11 +140,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
133 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | 140 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { |
134 | continue; | 141 | continue; |
135 | } | 142 | } |
136 | match item { | 143 | add_assoc_item(acc, ctx, item); |
137 | hir::AssocItem::Function(func) => acc.add_function(ctx, func, None), | ||
138 | hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), | ||
139 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | ||
140 | } | ||
141 | } | 144 | } |
142 | } | 145 | } |
143 | hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => { | 146 | hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => { |
@@ -149,7 +152,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
149 | }; | 152 | }; |
150 | 153 | ||
151 | if let Some(hir::Adt::Enum(e)) = ty.as_adt() { | 154 | if let Some(hir::Adt::Enum(e)) = ty.as_adt() { |
152 | add_enum_variants(ctx, acc, e); | 155 | add_enum_variants(acc, ctx, e); |
153 | } | 156 | } |
154 | 157 | ||
155 | let traits_in_scope = ctx.scope.traits_in_scope(); | 158 | let traits_in_scope = ctx.scope.traits_in_scope(); |
@@ -162,11 +165,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
162 | // We might iterate candidates of a trait multiple times here, so deduplicate | 165 | // We might iterate candidates of a trait multiple times here, so deduplicate |
163 | // them. | 166 | // them. |
164 | if seen.insert(item) { | 167 | if seen.insert(item) { |
165 | match item { | 168 | add_assoc_item(acc, ctx, item); |
166 | hir::AssocItem::Function(func) => acc.add_function(ctx, func, None), | ||
167 | hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), | ||
168 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | ||
169 | } | ||
170 | } | 169 | } |
171 | None::<()> | 170 | None::<()> |
172 | }); | 171 | }); |
@@ -176,10 +175,22 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
176 | } | 175 | } |
177 | } | 176 | } |
178 | 177 | ||
179 | fn add_enum_variants(ctx: &CompletionContext, acc: &mut Completions, e: hir::Enum) { | 178 | fn add_assoc_item(acc: &mut Completions, ctx: &CompletionContext, item: hir::AssocItem) { |
180 | for variant in e.variants(ctx.db) { | 179 | match item { |
181 | acc.add_enum_variant(ctx, variant, None); | 180 | hir::AssocItem::Function(func) if !ctx.expects_type() => acc.add_function(ctx, func, None), |
181 | hir::AssocItem::Const(ct) if !ctx.expects_type() || ctx.expects_generic_arg() => { | ||
182 | acc.add_const(ctx, ct) | ||
183 | } | ||
184 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | ||
185 | _ => (), | ||
186 | } | ||
187 | } | ||
188 | |||
189 | fn add_enum_variants(acc: &mut Completions, ctx: &CompletionContext, e: hir::Enum) { | ||
190 | if ctx.expects_type() { | ||
191 | return; | ||
182 | } | 192 | } |
193 | e.variants(ctx.db).into_iter().for_each(|variant| acc.add_enum_variant(ctx, variant, None)); | ||
183 | } | 194 | } |
184 | 195 | ||
185 | #[cfg(test)] | 196 | #[cfg(test)] |
@@ -927,4 +938,24 @@ fn main() { | |||
927 | "#]], | 938 | "#]], |
928 | ); | 939 | ); |
929 | } | 940 | } |
941 | |||
942 | #[test] | ||
943 | fn completes_types_and_const_in_arg_list() { | ||
944 | check( | ||
945 | r#" | ||
946 | mod foo { | ||
947 | pub const CONST: () = (); | ||
948 | pub type Type = (); | ||
949 | } | ||
950 | |||
951 | struct Foo<T>(t); | ||
952 | |||
953 | fn foo(_: Foo<foo::$0>) {} | ||
954 | "#, | ||
955 | expect![[r#" | ||
956 | ta Type | ||
957 | ct CONST | ||
958 | "#]], | ||
959 | ); | ||
960 | } | ||
930 | } | 961 | } |
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index b1e6b2b77..b5af1c810 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs | |||
@@ -1,8 +1,9 @@ | |||
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_path_disallowed() || !ctx.is_trivial_path() { | 9 | if ctx.is_path_disallowed() || !ctx.is_trivial_path() { |
@@ -35,21 +36,50 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
35 | return; | 36 | return; |
36 | } | 37 | } |
37 | 38 | ||
38 | if let Some(hir::Adt::Enum(e)) = | 39 | if !ctx.expects_type() { |
39 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) | 40 | if let Some(hir::Adt::Enum(e)) = |
40 | { | 41 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) |
41 | super::complete_enum_variants(acc, ctx, e, |acc, ctx, variant, path| { | 42 | { |
42 | acc.add_qualified_enum_variant(ctx, variant, path) | 43 | super::complete_enum_variants(acc, ctx, e, |acc, ctx, variant, path| { |
43 | }); | 44 | acc.add_qualified_enum_variant(ctx, variant, path) |
45 | }); | ||
46 | } | ||
47 | } | ||
48 | |||
49 | if let Some(ImmediateLocation::GenericArgList(arg_list)) = &ctx.completion_location { | ||
50 | if let Some(path_seg) = arg_list.syntax().parent().and_then(ast::PathSegment::cast) { | ||
51 | if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(trait_))) = | ||
52 | ctx.sema.resolve_path(&path_seg.parent_path()) | ||
53 | { | ||
54 | trait_.items(ctx.sema.db).into_iter().for_each(|it| { | ||
55 | if let hir::AssocItem::TypeAlias(alias) = it { | ||
56 | acc.add_type_alias_with_eq(ctx, alias) | ||
57 | } | ||
58 | }); | ||
59 | } | ||
60 | } | ||
44 | } | 61 | } |
45 | 62 | ||
46 | ctx.scope.process_all_names(&mut |name, res| { | 63 | ctx.scope.process_all_names(&mut |name, res| { |
47 | if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res { | 64 | if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_) = |
65 | res | ||
66 | { | ||
48 | cov_mark::hit!(skip_lifetime_completion); | 67 | cov_mark::hit!(skip_lifetime_completion); |
49 | return; | 68 | return; |
50 | } | 69 | } |
51 | let add_resolution = match res { | 70 | let add_resolution = match res { |
71 | // Don't suggest attribute macros and derives. | ||
52 | ScopeDef::MacroDef(mac) => mac.is_fn_like(), | 72 | ScopeDef::MacroDef(mac) => mac.is_fn_like(), |
73 | // no values in type places | ||
74 | ScopeDef::ModuleDef(hir::ModuleDef::Function(_)) | ||
75 | | ScopeDef::ModuleDef(hir::ModuleDef::Variant(_)) | ||
76 | | ScopeDef::ModuleDef(hir::ModuleDef::Static(_)) | ||
77 | | ScopeDef::Local(_) => !ctx.expects_type(), | ||
78 | // unless its a constant in a generic arg list position | ||
79 | ScopeDef::ModuleDef(hir::ModuleDef::Const(_)) | ||
80 | | ScopeDef::GenericParam(hir::GenericParam::ConstParam(_)) => { | ||
81 | !ctx.expects_type() || ctx.expects_generic_arg() | ||
82 | } | ||
53 | _ => true, | 83 | _ => true, |
54 | }; | 84 | }; |
55 | if add_resolution { | 85 | if add_resolution { |
@@ -777,4 +807,30 @@ $0 | |||
777 | "#]], | 807 | "#]], |
778 | ) | 808 | ) |
779 | } | 809 | } |
810 | |||
811 | #[test] | ||
812 | fn completes_types_and_const_in_arg_list() { | ||
813 | check( | ||
814 | r#" | ||
815 | enum Bar { | ||
816 | Baz | ||
817 | } | ||
818 | trait Foo { | ||
819 | type Bar; | ||
820 | } | ||
821 | |||
822 | const CONST: () = (); | ||
823 | |||
824 | fn foo<T: Foo<$0>, const CONST_PARAM: usize>(_: T) {} | ||
825 | "#, | ||
826 | expect![[r#" | ||
827 | ta Bar = type Bar; | ||
828 | tp T | ||
829 | cp CONST_PARAM | ||
830 | tt Foo | ||
831 | en Bar | ||
832 | ct CONST | ||
833 | "#]], | ||
834 | ); | ||
835 | } | ||
780 | } | 836 | } |
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 2c2a4aa6b..a8437d81c 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -276,6 +276,10 @@ impl<'a> CompletionContext<'a> { | |||
276 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) | 276 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) |
277 | } | 277 | } |
278 | 278 | ||
279 | pub(crate) fn expects_generic_arg(&self) -> bool { | ||
280 | matches!(self.completion_location, Some(ImmediateLocation::GenericArgList(_))) | ||
281 | } | ||
282 | |||
279 | pub(crate) fn has_block_expr_parent(&self) -> bool { | 283 | pub(crate) fn has_block_expr_parent(&self) -> bool { |
280 | matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) | 284 | matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) |
281 | } | 285 | } |
@@ -380,7 +384,7 @@ impl<'a> CompletionContext<'a> { | |||
380 | (|| { | 384 | (|| { |
381 | let expr_field = self.token.prev_sibling_or_token()? | 385 | let expr_field = self.token.prev_sibling_or_token()? |
382 | .into_node() | 386 | .into_node() |
383 | .and_then(|node| ast::RecordExprField::cast(node))?; | 387 | .and_then(ast::RecordExprField::cast)?; |
384 | let (_, _, ty) = self.sema.resolve_record_field(&expr_field)?; | 388 | let (_, _, ty) = self.sema.resolve_record_field(&expr_field)?; |
385 | Some(( | 389 | Some(( |
386 | Some(ty), | 390 | Some(ty), |
@@ -467,7 +471,7 @@ impl<'a> CompletionContext<'a> { | |||
467 | self.expected_type = expected_type; | 471 | self.expected_type = expected_type; |
468 | self.expected_name = expected_name; | 472 | self.expected_name = expected_name; |
469 | 473 | ||
470 | let name_like = match find_node_at_offset(&&file_with_fake_ident, offset) { | 474 | let name_like = match find_node_at_offset(&file_with_fake_ident, offset) { |
471 | Some(it) => it, | 475 | Some(it) => it, |
472 | None => return, | 476 | None => return, |
473 | }; | 477 | }; |
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs index ee87bf461..72e67e3c4 100644 --- a/crates/ide_completion/src/patterns.rs +++ b/crates/ide_completion/src/patterns.rs | |||
@@ -47,6 +47,9 @@ pub(crate) enum ImmediateLocation { | |||
47 | receiver_is_ambiguous_float_literal: bool, | 47 | receiver_is_ambiguous_float_literal: bool, |
48 | }, | 48 | }, |
49 | // 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 | ||
50 | /// The record expr of the field name we are completing | 53 | /// The record expr of the field name we are completing |
51 | RecordExpr(ast::RecordExpr), | 54 | RecordExpr(ast::RecordExpr), |
52 | // Original file ast node | 55 | // Original file ast node |
@@ -112,12 +115,12 @@ pub(crate) fn determine_location( | |||
112 | ) -> Option<ImmediateLocation> { | 115 | ) -> Option<ImmediateLocation> { |
113 | let node = match name_like { | 116 | let node = match name_like { |
114 | ast::NameLike::NameRef(name_ref) => { | 117 | ast::NameLike::NameRef(name_ref) => { |
115 | if ast::RecordExprField::for_field_name(&name_ref).is_some() { | 118 | if ast::RecordExprField::for_field_name(name_ref).is_some() { |
116 | return sema | 119 | return sema |
117 | .find_node_at_offset_with_macros(original_file, offset) | 120 | .find_node_at_offset_with_macros(original_file, offset) |
118 | .map(ImmediateLocation::RecordExpr); | 121 | .map(ImmediateLocation::RecordExpr); |
119 | } | 122 | } |
120 | if ast::RecordPatField::for_field_name_ref(&name_ref).is_some() { | 123 | if ast::RecordPatField::for_field_name_ref(name_ref).is_some() { |
121 | return sema | 124 | return sema |
122 | .find_node_at_offset_with_macros(original_file, offset) | 125 | .find_node_at_offset_with_macros(original_file, offset) |
123 | .map(ImmediateLocation::RecordPat); | 126 | .map(ImmediateLocation::RecordPat); |
@@ -125,7 +128,7 @@ pub(crate) fn determine_location( | |||
125 | maximize_name_ref(name_ref) | 128 | maximize_name_ref(name_ref) |
126 | } | 129 | } |
127 | ast::NameLike::Name(name) => { | 130 | ast::NameLike::Name(name) => { |
128 | if ast::RecordPatField::for_field_name(&name).is_some() { | 131 | if ast::RecordPatField::for_field_name(name).is_some() { |
129 | return sema | 132 | return sema |
130 | .find_node_at_offset_with_macros(original_file, offset) | 133 | .find_node_at_offset_with_macros(original_file, offset) |
131 | .map(ImmediateLocation::RecordPat); | 134 | .map(ImmediateLocation::RecordPat); |
@@ -159,7 +162,6 @@ pub(crate) fn determine_location( | |||
159 | } | 162 | } |
160 | } | 163 | } |
161 | }; | 164 | }; |
162 | |||
163 | let res = match_ast! { | 165 | let res = match_ast! { |
164 | match parent { | 166 | match parent { |
165 | ast::IdentPat(_it) => ImmediateLocation::IdentPat, | 167 | ast::IdentPat(_it) => ImmediateLocation::IdentPat, |
@@ -174,6 +176,9 @@ pub(crate) fn determine_location( | |||
174 | Some(TRAIT) => ImmediateLocation::Trait, | 176 | Some(TRAIT) => ImmediateLocation::Trait, |
175 | _ => return None, | 177 | _ => return None, |
176 | }, | 178 | }, |
179 | ast::GenericArgList(_it) => sema | ||
180 | .find_node_at_offset_with_macros(original_file, offset) | ||
181 | .map(ImmediateLocation::GenericArgList)?, | ||
177 | ast::Module(it) => { | 182 | ast::Module(it) => { |
178 | if it.item_list().is_none() { | 183 | if it.item_list().is_none() { |
179 | ImmediateLocation::ModDeclaration(it) | 184 | ImmediateLocation::ModDeclaration(it) |
@@ -254,7 +259,7 @@ fn test_inside_impl_trait_block() { | |||
254 | } | 259 | } |
255 | 260 | ||
256 | pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> { | 261 | pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> { |
257 | element.into_token().and_then(|it| previous_non_trivia_token(it)) | 262 | element.into_token().and_then(previous_non_trivia_token) |
258 | } | 263 | } |
259 | 264 | ||
260 | /// Check if the token previous to the previous one is `for`. | 265 | /// Check if the token previous to the previous one is `for`. |
@@ -262,8 +267,8 @@ pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> { | |||
262 | pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool { | 267 | pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool { |
263 | element | 268 | element |
264 | .into_token() | 269 | .into_token() |
265 | .and_then(|it| previous_non_trivia_token(it)) | 270 | .and_then(previous_non_trivia_token) |
266 | .and_then(|it| previous_non_trivia_token(it)) | 271 | .and_then(previous_non_trivia_token) |
267 | .filter(|it| it.kind() == T![for]) | 272 | .filter(|it| it.kind() == T![for]) |
268 | .is_some() | 273 | .is_some() |
269 | } | 274 | } |
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index d3db55c35..2bd2c44d0 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs | |||
@@ -23,53 +23,6 @@ use crate::{ | |||
23 | render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}, | 23 | render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}, |
24 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, | 24 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, |
25 | }; | 25 | }; |
26 | |||
27 | pub(crate) fn render_field( | ||
28 | ctx: RenderContext<'_>, | ||
29 | receiver: Option<hir::Name>, | ||
30 | field: hir::Field, | ||
31 | ty: &hir::Type, | ||
32 | ) -> CompletionItem { | ||
33 | render_field_(ctx, receiver, field, ty) | ||
34 | } | ||
35 | |||
36 | pub(crate) fn render_tuple_field( | ||
37 | ctx: RenderContext<'_>, | ||
38 | receiver: Option<hir::Name>, | ||
39 | field: usize, | ||
40 | ty: &hir::Type, | ||
41 | ) -> CompletionItem { | ||
42 | render_tuple_field_(ctx, receiver, field, ty) | ||
43 | } | ||
44 | |||
45 | pub(crate) fn render_resolution( | ||
46 | ctx: RenderContext<'_>, | ||
47 | local_name: hir::Name, | ||
48 | resolution: &hir::ScopeDef, | ||
49 | ) -> Option<CompletionItem> { | ||
50 | render_resolution_(ctx, local_name, None, resolution) | ||
51 | } | ||
52 | |||
53 | pub(crate) fn render_resolution_with_import( | ||
54 | ctx: RenderContext<'_>, | ||
55 | import_edit: ImportEdit, | ||
56 | ) -> Option<CompletionItem> { | ||
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 | } | ||
61 | let local_name = match resolution { | ||
62 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db), | ||
63 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?, | ||
64 | hir::ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db), | ||
65 | _ => item_name(ctx.db(), import_edit.import.original_item)?, | ||
66 | }; | ||
67 | render_resolution_(ctx, local_name, Some(import_edit), &resolution).map(|mut item| { | ||
68 | item.completion_kind = CompletionKind::Magic; | ||
69 | item | ||
70 | }) | ||
71 | } | ||
72 | |||
73 | /// Interface for data and methods required for items rendering. | 26 | /// Interface for data and methods required for items rendering. |
74 | #[derive(Debug)] | 27 | #[derive(Debug)] |
75 | pub(crate) struct RenderContext<'a> { | 28 | pub(crate) struct RenderContext<'a> { |
@@ -86,7 +39,7 @@ impl<'a> RenderContext<'a> { | |||
86 | } | 39 | } |
87 | 40 | ||
88 | fn db(&self) -> &'a RootDatabase { | 41 | fn db(&self) -> &'a RootDatabase { |
89 | &self.completion.db | 42 | self.completion.db |
90 | } | 43 | } |
91 | 44 | ||
92 | fn source_range(&self) -> TextRange { | 45 | fn source_range(&self) -> TextRange { |
@@ -122,7 +75,7 @@ impl<'a> RenderContext<'a> { | |||
122 | } | 75 | } |
123 | } | 76 | } |
124 | 77 | ||
125 | fn render_field_( | 78 | pub(crate) fn render_field( |
126 | ctx: RenderContext<'_>, | 79 | ctx: RenderContext<'_>, |
127 | receiver: Option<hir::Name>, | 80 | receiver: Option<hir::Name>, |
128 | field: hir::Field, | 81 | field: hir::Field, |
@@ -135,7 +88,6 @@ fn render_field_( | |||
135 | ctx.source_range(), | 88 | ctx.source_range(), |
136 | receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name)), | 89 | receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name)), |
137 | ); | 90 | ); |
138 | |||
139 | item.set_relevance(CompletionRelevance { | 91 | item.set_relevance(CompletionRelevance { |
140 | type_match: compute_type_match(ctx.completion, ty), | 92 | type_match: compute_type_match(ctx.completion, ty), |
141 | exact_name_match: compute_exact_name_match(ctx.completion, &name), | 93 | exact_name_match: compute_exact_name_match(ctx.completion, &name), |
@@ -146,17 +98,15 @@ fn render_field_( | |||
146 | .set_documentation(field.docs(ctx.db())) | 98 | .set_documentation(field.docs(ctx.db())) |
147 | .set_deprecated(is_deprecated) | 99 | .set_deprecated(is_deprecated) |
148 | .lookup_by(name); | 100 | .lookup_by(name); |
149 | |||
150 | if let Some(_ref_match) = compute_ref_match(ctx.completion, ty) { | 101 | if let Some(_ref_match) = compute_ref_match(ctx.completion, ty) { |
151 | // FIXME | 102 | // FIXME |
152 | // For now we don't properly calculate the edits for ref match | 103 | // For now we don't properly calculate the edits for ref match |
153 | // completions on struct fields, so we've disabled them. See #8058. | 104 | // completions on struct fields, so we've disabled them. See #8058. |
154 | } | 105 | } |
155 | |||
156 | item.build() | 106 | item.build() |
157 | } | 107 | } |
158 | 108 | ||
159 | fn render_tuple_field_( | 109 | pub(crate) fn render_tuple_field( |
160 | ctx: RenderContext<'_>, | 110 | ctx: RenderContext<'_>, |
161 | receiver: Option<hir::Name>, | 111 | receiver: Option<hir::Name>, |
162 | field: usize, | 112 | field: usize, |
@@ -167,14 +117,37 @@ fn render_tuple_field_( | |||
167 | ctx.source_range(), | 117 | ctx.source_range(), |
168 | receiver.map_or_else(|| field.to_string(), |receiver| format!("{}.{}", receiver, field)), | 118 | receiver.map_or_else(|| field.to_string(), |receiver| format!("{}.{}", receiver, field)), |
169 | ); | 119 | ); |
170 | |||
171 | item.kind(SymbolKind::Field) | 120 | item.kind(SymbolKind::Field) |
172 | .detail(ty.display(ctx.db()).to_string()) | 121 | .detail(ty.display(ctx.db()).to_string()) |
173 | .lookup_by(field.to_string()); | 122 | .lookup_by(field.to_string()); |
174 | |||
175 | item.build() | 123 | item.build() |
176 | } | 124 | } |
177 | 125 | ||
126 | pub(crate) fn render_resolution( | ||
127 | ctx: RenderContext<'_>, | ||
128 | local_name: hir::Name, | ||
129 | resolution: &hir::ScopeDef, | ||
130 | ) -> Option<CompletionItem> { | ||
131 | render_resolution_(ctx, local_name, None, resolution) | ||
132 | } | ||
133 | |||
134 | pub(crate) fn render_resolution_with_import( | ||
135 | ctx: RenderContext<'_>, | ||
136 | import_edit: ImportEdit, | ||
137 | ) -> Option<CompletionItem> { | ||
138 | let resolution = hir::ScopeDef::from(import_edit.import.original_item); | ||
139 | let local_name = match resolution { | ||
140 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db), | ||
141 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?, | ||
142 | hir::ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db), | ||
143 | _ => item_name(ctx.db(), import_edit.import.original_item)?, | ||
144 | }; | ||
145 | render_resolution_(ctx, local_name, Some(import_edit), &resolution).map(|mut item| { | ||
146 | item.completion_kind = CompletionKind::Magic; | ||
147 | item | ||
148 | }) | ||
149 | } | ||
150 | |||
178 | fn render_resolution_( | 151 | fn render_resolution_( |
179 | ctx: RenderContext<'_>, | 152 | ctx: RenderContext<'_>, |
180 | local_name: hir::Name, | 153 | local_name: hir::Name, |
@@ -1007,6 +980,7 @@ fn go(world: &WorldSnapshot) { go(w$0) } | |||
1007 | 980 | ||
1008 | #[test] | 981 | #[test] |
1009 | fn too_many_arguments() { | 982 | fn too_many_arguments() { |
983 | cov_mark::check!(too_many_arguments); | ||
1010 | check_relevance( | 984 | check_relevance( |
1011 | r#" | 985 | r#" |
1012 | struct Foo; | 986 | struct Foo; |
@@ -1151,16 +1125,11 @@ fn main() { | |||
1151 | fn suggest_deref() { | 1125 | fn suggest_deref() { |
1152 | check_relevance( | 1126 | check_relevance( |
1153 | r#" | 1127 | r#" |
1154 | #[lang = "deref"] | 1128 | //- minicore: deref |
1155 | trait Deref { | ||
1156 | type Target; | ||
1157 | fn deref(&self) -> &Self::Target; | ||
1158 | } | ||
1159 | |||
1160 | struct S; | 1129 | struct S; |
1161 | struct T(S); | 1130 | struct T(S); |
1162 | 1131 | ||
1163 | impl Deref for T { | 1132 | impl core::ops::Deref for T { |
1164 | type Target = S; | 1133 | type Target = S; |
1165 | 1134 | ||
1166 | fn deref(&self) -> &Self::Target { | 1135 | fn deref(&self) -> &Self::Target { |
@@ -1184,8 +1153,9 @@ fn main() { | |||
1184 | st T [] | 1153 | st T [] |
1185 | st S [] | 1154 | st S [] |
1186 | fn main() [] | 1155 | fn main() [] |
1187 | tt Deref [] | ||
1188 | fn foo(…) [] | 1156 | fn foo(…) [] |
1157 | md core [] | ||
1158 | tt Sized [] | ||
1189 | "#]], | 1159 | "#]], |
1190 | ) | 1160 | ) |
1191 | } | 1161 | } |
@@ -1194,21 +1164,11 @@ fn main() { | |||
1194 | fn suggest_deref_mut() { | 1164 | fn suggest_deref_mut() { |
1195 | check_relevance( | 1165 | check_relevance( |
1196 | r#" | 1166 | r#" |
1197 | #[lang = "deref"] | 1167 | //- minicore: deref_mut |
1198 | trait Deref { | ||
1199 | type Target; | ||
1200 | fn deref(&self) -> &Self::Target; | ||
1201 | } | ||
1202 | |||
1203 | #[lang = "deref_mut"] | ||
1204 | pub trait DerefMut: Deref { | ||
1205 | fn deref_mut(&mut self) -> &mut Self::Target; | ||
1206 | } | ||
1207 | |||
1208 | struct S; | 1168 | struct S; |
1209 | struct T(S); | 1169 | struct T(S); |
1210 | 1170 | ||
1211 | impl Deref for T { | 1171 | impl core::ops::Deref for T { |
1212 | type Target = S; | 1172 | type Target = S; |
1213 | 1173 | ||
1214 | fn deref(&self) -> &Self::Target { | 1174 | fn deref(&self) -> &Self::Target { |
@@ -1216,7 +1176,7 @@ impl Deref for T { | |||
1216 | } | 1176 | } |
1217 | } | 1177 | } |
1218 | 1178 | ||
1219 | impl DerefMut for T { | 1179 | impl core::ops::DerefMut for T { |
1220 | fn deref_mut(&mut self) -> &mut Self::Target { | 1180 | fn deref_mut(&mut self) -> &mut Self::Target { |
1221 | &mut self.0 | 1181 | &mut self.0 |
1222 | } | 1182 | } |
@@ -1235,12 +1195,12 @@ fn main() { | |||
1235 | lc m [local] | 1195 | lc m [local] |
1236 | lc t [local] | 1196 | lc t [local] |
1237 | lc &mut t [type+local] | 1197 | lc &mut t [type+local] |
1238 | tt DerefMut [] | ||
1239 | tt Deref [] | ||
1240 | fn foo(…) [] | ||
1241 | st T [] | 1198 | st T [] |
1242 | st S [] | 1199 | st S [] |
1243 | fn main() [] | 1200 | fn main() [] |
1201 | fn foo(…) [] | ||
1202 | md core [] | ||
1203 | tt Sized [] | ||
1244 | "#]], | 1204 | "#]], |
1245 | ) | 1205 | ) |
1246 | } | 1206 | } |
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs index 429d937c8..3a7238bb8 100644 --- a/crates/ide_completion/src/render/macro_.rs +++ b/crates/ide_completion/src/render/macro_.rs | |||
@@ -180,7 +180,7 @@ fn main() { frobnicate!(); } | |||
180 | /// ``` | 180 | /// ``` |
181 | macro_rules! vec { () => {} } | 181 | macro_rules! vec { () => {} } |
182 | 182 | ||
183 | fn fn main() { v$0 } | 183 | fn main() { v$0 } |
184 | "#, | 184 | "#, |
185 | r#" | 185 | r#" |
186 | /// Creates a [`Vec`] containing the arguments. | 186 | /// Creates a [`Vec`] containing the arguments. |
@@ -193,7 +193,7 @@ fn fn main() { v$0 } | |||
193 | /// ``` | 193 | /// ``` |
194 | macro_rules! vec { () => {} } | 194 | macro_rules! vec { () => {} } |
195 | 195 | ||
196 | fn fn main() { vec![$0] } | 196 | fn main() { vec![$0] } |
197 | "#, | 197 | "#, |
198 | ); | 198 | ); |
199 | 199 | ||
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/assists.rs b/crates/ide_db/src/assists.rs new file mode 100644 index 000000000..7881d8369 --- /dev/null +++ b/crates/ide_db/src/assists.rs | |||
@@ -0,0 +1,136 @@ | |||
1 | //! This module defines the `Assist` data structure. The actual assist live in | ||
2 | //! the `ide_assists` downstream crate. We want to define the data structures in | ||
3 | //! this low-level crate though, because `ide_diagnostics` also need them | ||
4 | //! (fixits for diagnostics and assists are the same thing under the hood). We | ||
5 | //! want to compile `ide_assists` and `ide_diagnostics` in parallel though, so | ||
6 | //! we pull the common definitions upstream, to this crate. | ||
7 | |||
8 | use std::str::FromStr; | ||
9 | |||
10 | use syntax::TextRange; | ||
11 | |||
12 | use crate::{label::Label, source_change::SourceChange}; | ||
13 | |||
14 | #[derive(Debug, Clone)] | ||
15 | pub struct Assist { | ||
16 | pub id: AssistId, | ||
17 | /// Short description of the assist, as shown in the UI. | ||
18 | pub label: Label, | ||
19 | pub group: Option<GroupLabel>, | ||
20 | /// Target ranges are used to sort assists: the smaller the target range, | ||
21 | /// the more specific assist is, and so it should be sorted first. | ||
22 | pub target: TextRange, | ||
23 | /// Computing source change sometimes is much more costly then computing the | ||
24 | /// other fields. Additionally, the actual change is not required to show | ||
25 | /// the lightbulb UI, it only is needed when the user tries to apply an | ||
26 | /// assist. So, we compute it lazily: the API allow requesting assists with | ||
27 | /// or without source change. We could (and in fact, used to) distinguish | ||
28 | /// between resolved and unresolved assists at the type level, but this is | ||
29 | /// cumbersome, especially if you want to embed an assist into another data | ||
30 | /// structure, such as a diagnostic. | ||
31 | pub source_change: Option<SourceChange>, | ||
32 | } | ||
33 | |||
34 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
35 | pub enum AssistKind { | ||
36 | // FIXME: does the None variant make sense? Probably not. | ||
37 | None, | ||
38 | |||
39 | QuickFix, | ||
40 | Generate, | ||
41 | Refactor, | ||
42 | RefactorExtract, | ||
43 | RefactorInline, | ||
44 | RefactorRewrite, | ||
45 | } | ||
46 | |||
47 | impl AssistKind { | ||
48 | pub fn contains(self, other: AssistKind) -> bool { | ||
49 | if self == other { | ||
50 | return true; | ||
51 | } | ||
52 | |||
53 | match self { | ||
54 | AssistKind::None | AssistKind::Generate => true, | ||
55 | AssistKind::Refactor => match other { | ||
56 | AssistKind::RefactorExtract | ||
57 | | AssistKind::RefactorInline | ||
58 | | AssistKind::RefactorRewrite => true, | ||
59 | _ => false, | ||
60 | }, | ||
61 | _ => false, | ||
62 | } | ||
63 | } | ||
64 | |||
65 | pub fn name(&self) -> &str { | ||
66 | match self { | ||
67 | AssistKind::None => "None", | ||
68 | AssistKind::QuickFix => "QuickFix", | ||
69 | AssistKind::Generate => "Generate", | ||
70 | AssistKind::Refactor => "Refactor", | ||
71 | AssistKind::RefactorExtract => "RefactorExtract", | ||
72 | AssistKind::RefactorInline => "RefactorInline", | ||
73 | AssistKind::RefactorRewrite => "RefactorRewrite", | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | |||
78 | impl FromStr for AssistKind { | ||
79 | type Err = String; | ||
80 | |||
81 | fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
82 | match s { | ||
83 | "None" => Ok(AssistKind::None), | ||
84 | "QuickFix" => Ok(AssistKind::QuickFix), | ||
85 | "Generate" => Ok(AssistKind::Generate), | ||
86 | "Refactor" => Ok(AssistKind::Refactor), | ||
87 | "RefactorExtract" => Ok(AssistKind::RefactorExtract), | ||
88 | "RefactorInline" => Ok(AssistKind::RefactorInline), | ||
89 | "RefactorRewrite" => Ok(AssistKind::RefactorRewrite), | ||
90 | unknown => Err(format!("Unknown AssistKind: '{}'", unknown)), | ||
91 | } | ||
92 | } | ||
93 | } | ||
94 | |||
95 | /// Unique identifier of the assist, should not be shown to the user | ||
96 | /// directly. | ||
97 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
98 | pub struct AssistId(pub &'static str, pub AssistKind); | ||
99 | |||
100 | /// A way to control how many asssist to resolve during the assist resolution. | ||
101 | /// When an assist is resolved, its edits are calculated that might be costly to always do by default. | ||
102 | #[derive(Debug)] | ||
103 | pub enum AssistResolveStrategy { | ||
104 | /// No assists should be resolved. | ||
105 | None, | ||
106 | /// All assists should be resolved. | ||
107 | All, | ||
108 | /// Only a certain assist should be resolved. | ||
109 | Single(SingleResolve), | ||
110 | } | ||
111 | |||
112 | /// Hold the [`AssistId`] data of a certain assist to resolve. | ||
113 | /// The original id object cannot be used due to a `'static` lifetime | ||
114 | /// and the requirement to construct this struct dynamically during the resolve handling. | ||
115 | #[derive(Debug)] | ||
116 | pub struct SingleResolve { | ||
117 | /// The id of the assist. | ||
118 | pub assist_id: String, | ||
119 | // The kind of the assist. | ||
120 | pub assist_kind: AssistKind, | ||
121 | } | ||
122 | |||
123 | impl AssistResolveStrategy { | ||
124 | pub fn should_resolve(&self, id: &AssistId) -> bool { | ||
125 | match self { | ||
126 | AssistResolveStrategy::None => false, | ||
127 | AssistResolveStrategy::All => true, | ||
128 | AssistResolveStrategy::Single(single_resolve) => { | ||
129 | single_resolve.assist_id == id.0 && single_resolve.assist_kind == id.1 | ||
130 | } | ||
131 | } | ||
132 | } | ||
133 | } | ||
134 | |||
135 | #[derive(Clone, Debug)] | ||
136 | pub struct GroupLabel(pub String); | ||
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs index 933bcad55..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); |
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/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs index 70b11bf81..5a88ec742 100644 --- a/crates/ide_db/src/helpers/insert_use/tests.rs +++ b/crates/ide_db/src/helpers/insert_use/tests.rs | |||
@@ -511,13 +511,14 @@ use std::io; | |||
511 | } | 511 | } |
512 | 512 | ||
513 | #[test] | 513 | #[test] |
514 | #[ignore] // FIXME: Support this | ||
515 | fn split_out_merge() { | 514 | fn split_out_merge() { |
515 | // FIXME: This is suboptimal, we want to get `use std::fmt::{self, Result}` | ||
516 | // instead. | ||
516 | check_module( | 517 | check_module( |
517 | "std::fmt::Result", | 518 | "std::fmt::Result", |
518 | r"use std::{fmt, io};", | 519 | r"use std::{fmt, io};", |
519 | r"use std::fmt::{self, Result}; | 520 | r"use std::fmt::Result; |
520 | use std::io;", | 521 | use std::{fmt, io};", |
521 | ) | 522 | ) |
522 | } | 523 | } |
523 | 524 | ||
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 105607dca..7bbd08d6f 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs | |||
@@ -3,11 +3,11 @@ | |||
3 | //! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search. | 3 | //! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search. |
4 | 4 | ||
5 | mod apply_change; | 5 | mod apply_change; |
6 | pub mod assists; | ||
6 | pub mod label; | 7 | pub mod label; |
7 | pub mod line_index; | 8 | pub mod line_index; |
8 | pub mod symbol_index; | 9 | pub mod symbol_index; |
9 | pub mod defs; | 10 | pub mod defs; |
10 | pub mod search; | ||
11 | pub mod items_locator; | 11 | pub mod items_locator; |
12 | pub mod source_change; | 12 | pub mod source_change; |
13 | pub mod ty_filter; | 13 | pub mod ty_filter; |
@@ -15,6 +15,9 @@ pub mod traits; | |||
15 | pub mod call_info; | 15 | pub mod call_info; |
16 | pub mod helpers; | 16 | pub mod helpers; |
17 | 17 | ||
18 | pub mod search; | ||
19 | pub mod rename; | ||
20 | |||
18 | use std::{fmt, sync::Arc}; | 21 | use std::{fmt, sync::Arc}; |
19 | 22 | ||
20 | use base_db::{ | 23 | use base_db::{ |
diff --git a/crates/ide_db/src/rename.rs b/crates/ide_db/src/rename.rs new file mode 100644 index 000000000..643e67781 --- /dev/null +++ b/crates/ide_db/src/rename.rs | |||
@@ -0,0 +1,468 @@ | |||
1 | //! Rename infrastructure for rust-analyzer. It is used primarily for the | ||
2 | //! literal "rename" in the ide (look for tests there), but it is also available | ||
3 | //! as a general-purpose service. For example, it is used by the fix for the | ||
4 | //! "incorrect case" diagnostic. | ||
5 | //! | ||
6 | //! It leverages the [`crate::search`] functionality to find what needs to be | ||
7 | //! renamed. The actual renames are tricky -- field shorthands need special | ||
8 | //! attention, and, when renaming modules, you also want to rename files on the | ||
9 | //! file system. | ||
10 | //! | ||
11 | //! Another can of worms are macros: | ||
12 | //! | ||
13 | //! ``` | ||
14 | //! macro_rules! m { () => { fn f() {} } } | ||
15 | //! m!(); | ||
16 | //! fn main() { | ||
17 | //! f() // <- rename me | ||
18 | //! } | ||
19 | //! ``` | ||
20 | //! | ||
21 | //! The correct behavior in such cases is probably to show a dialog to the user. | ||
22 | //! Our current behavior is ¯\_(ツ)_/¯. | ||
23 | use std::fmt; | ||
24 | |||
25 | use base_db::{AnchoredPathBuf, FileId, FileRange}; | ||
26 | use either::Either; | ||
27 | use hir::{AsAssocItem, FieldSource, HasSource, InFile, ModuleSource, Semantics}; | ||
28 | use stdx::never; | ||
29 | use syntax::{ | ||
30 | ast::{self, NameOwner}, | ||
31 | lex_single_syntax_kind, AstNode, SyntaxKind, TextRange, T, | ||
32 | }; | ||
33 | use text_edit::TextEdit; | ||
34 | |||
35 | use crate::{ | ||
36 | defs::Definition, | ||
37 | search::FileReference, | ||
38 | source_change::{FileSystemEdit, SourceChange}, | ||
39 | RootDatabase, | ||
40 | }; | ||
41 | |||
42 | pub type Result<T, E = RenameError> = std::result::Result<T, E>; | ||
43 | |||
44 | #[derive(Debug)] | ||
45 | pub struct RenameError(pub String); | ||
46 | |||
47 | impl fmt::Display for RenameError { | ||
48 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
49 | fmt::Display::fmt(&self.0, f) | ||
50 | } | ||
51 | } | ||
52 | |||
53 | #[macro_export] | ||
54 | macro_rules! _format_err { | ||
55 | ($fmt:expr) => { RenameError(format!($fmt)) }; | ||
56 | ($fmt:expr, $($arg:tt)+) => { RenameError(format!($fmt, $($arg)+)) } | ||
57 | } | ||
58 | pub use _format_err as format_err; | ||
59 | |||
60 | #[macro_export] | ||
61 | macro_rules! _bail { | ||
62 | ($($tokens:tt)*) => { return Err(format_err!($($tokens)*)) } | ||
63 | } | ||
64 | pub use _bail as bail; | ||
65 | |||
66 | impl Definition { | ||
67 | pub fn rename(&self, sema: &Semantics<RootDatabase>, new_name: &str) -> Result<SourceChange> { | ||
68 | match *self { | ||
69 | Definition::ModuleDef(hir::ModuleDef::Module(module)) => { | ||
70 | rename_mod(sema, module, new_name) | ||
71 | } | ||
72 | Definition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => { | ||
73 | bail!("Cannot rename builtin type") | ||
74 | } | ||
75 | Definition::SelfType(_) => bail!("Cannot rename `Self`"), | ||
76 | def => rename_reference(sema, def, new_name), | ||
77 | } | ||
78 | } | ||
79 | |||
80 | /// Textual range of the identifier which will change when renaming this | ||
81 | /// `Definition`. Note that some definitions, like buitin types, can't be | ||
82 | /// renamed. | ||
83 | pub fn range_for_rename(self, sema: &Semantics<RootDatabase>) -> Option<FileRange> { | ||
84 | // FIXME: the `original_file_range` calls here are wrong -- they never fail, | ||
85 | // and _fall back_ to the entirety of the macro call. Such fall back is | ||
86 | // incorrect for renames. The safe behavior would be to return an error for | ||
87 | // such cases. The correct behavior would be to return an auxiliary list of | ||
88 | // "can't rename these occurrences in macros" items, and then show some kind | ||
89 | // of a dialog to the user. See: | ||
90 | cov_mark::hit!(macros_are_broken_lol); | ||
91 | |||
92 | let res = match self { | ||
93 | Definition::Macro(mac) => { | ||
94 | let src = mac.source(sema.db)?; | ||
95 | let name = match &src.value { | ||
96 | Either::Left(it) => it.name()?, | ||
97 | Either::Right(it) => it.name()?, | ||
98 | }; | ||
99 | src.with_value(name.syntax()).original_file_range(sema.db) | ||
100 | } | ||
101 | Definition::Field(field) => { | ||
102 | let src = field.source(sema.db)?; | ||
103 | |||
104 | match &src.value { | ||
105 | FieldSource::Named(record_field) => { | ||
106 | let name = record_field.name()?; | ||
107 | src.with_value(name.syntax()).original_file_range(sema.db) | ||
108 | } | ||
109 | FieldSource::Pos(_) => { | ||
110 | return None; | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | Definition::ModuleDef(module_def) => match module_def { | ||
115 | hir::ModuleDef::Module(module) => { | ||
116 | let src = module.declaration_source(sema.db)?; | ||
117 | let name = src.value.name()?; | ||
118 | src.with_value(name.syntax()).original_file_range(sema.db) | ||
119 | } | ||
120 | hir::ModuleDef::Function(it) => name_range(it, sema)?, | ||
121 | hir::ModuleDef::Adt(adt) => match adt { | ||
122 | hir::Adt::Struct(it) => name_range(it, sema)?, | ||
123 | hir::Adt::Union(it) => name_range(it, sema)?, | ||
124 | hir::Adt::Enum(it) => name_range(it, sema)?, | ||
125 | }, | ||
126 | hir::ModuleDef::Variant(it) => name_range(it, sema)?, | ||
127 | hir::ModuleDef::Const(it) => name_range(it, sema)?, | ||
128 | hir::ModuleDef::Static(it) => name_range(it, sema)?, | ||
129 | hir::ModuleDef::Trait(it) => name_range(it, sema)?, | ||
130 | hir::ModuleDef::TypeAlias(it) => name_range(it, sema)?, | ||
131 | hir::ModuleDef::BuiltinType(_) => return None, | ||
132 | }, | ||
133 | Definition::SelfType(_) => return None, | ||
134 | Definition::Local(local) => { | ||
135 | let src = local.source(sema.db); | ||
136 | let name = match &src.value { | ||
137 | Either::Left(bind_pat) => bind_pat.name()?, | ||
138 | Either::Right(_) => return None, | ||
139 | }; | ||
140 | src.with_value(name.syntax()).original_file_range(sema.db) | ||
141 | } | ||
142 | Definition::GenericParam(generic_param) => match generic_param { | ||
143 | hir::GenericParam::TypeParam(type_param) => { | ||
144 | let src = type_param.source(sema.db)?; | ||
145 | let name = match &src.value { | ||
146 | Either::Left(type_param) => type_param.name()?, | ||
147 | Either::Right(_trait) => return None, | ||
148 | }; | ||
149 | src.with_value(name.syntax()).original_file_range(sema.db) | ||
150 | } | ||
151 | hir::GenericParam::LifetimeParam(lifetime_param) => { | ||
152 | let src = lifetime_param.source(sema.db)?; | ||
153 | let lifetime = src.value.lifetime()?; | ||
154 | src.with_value(lifetime.syntax()).original_file_range(sema.db) | ||
155 | } | ||
156 | hir::GenericParam::ConstParam(it) => name_range(it, sema)?, | ||
157 | }, | ||
158 | Definition::Label(label) => { | ||
159 | let src = label.source(sema.db); | ||
160 | let lifetime = src.value.lifetime()?; | ||
161 | src.with_value(lifetime.syntax()).original_file_range(sema.db) | ||
162 | } | ||
163 | }; | ||
164 | return Some(res); | ||
165 | |||
166 | fn name_range<D>(def: D, sema: &Semantics<RootDatabase>) -> Option<FileRange> | ||
167 | where | ||
168 | D: HasSource, | ||
169 | D::Ast: ast::NameOwner, | ||
170 | { | ||
171 | let src = def.source(sema.db)?; | ||
172 | let name = src.value.name()?; | ||
173 | let res = src.with_value(name.syntax()).original_file_range(sema.db); | ||
174 | Some(res) | ||
175 | } | ||
176 | } | ||
177 | } | ||
178 | |||
179 | fn rename_mod( | ||
180 | sema: &Semantics<RootDatabase>, | ||
181 | module: hir::Module, | ||
182 | new_name: &str, | ||
183 | ) -> Result<SourceChange> { | ||
184 | if IdentifierKind::classify(new_name)? != IdentifierKind::Ident { | ||
185 | bail!("Invalid name `{0}`: cannot rename module to {0}", new_name); | ||
186 | } | ||
187 | |||
188 | let mut source_change = SourceChange::default(); | ||
189 | |||
190 | let InFile { file_id, value: def_source } = module.definition_source(sema.db); | ||
191 | let file_id = file_id.original_file(sema.db); | ||
192 | if let ModuleSource::SourceFile(..) = def_source { | ||
193 | // mod is defined in path/to/dir/mod.rs | ||
194 | let path = if module.is_mod_rs(sema.db) { | ||
195 | format!("../{}/mod.rs", new_name) | ||
196 | } else { | ||
197 | format!("{}.rs", new_name) | ||
198 | }; | ||
199 | let dst = AnchoredPathBuf { anchor: file_id, path }; | ||
200 | let move_file = FileSystemEdit::MoveFile { src: file_id, dst }; | ||
201 | source_change.push_file_system_edit(move_file); | ||
202 | } | ||
203 | |||
204 | if let Some(InFile { file_id, value: decl_source }) = module.declaration_source(sema.db) { | ||
205 | let file_id = file_id.original_file(sema.db); | ||
206 | match decl_source.name() { | ||
207 | Some(name) => source_change.insert_source_edit( | ||
208 | file_id, | ||
209 | TextEdit::replace(name.syntax().text_range(), new_name.to_string()), | ||
210 | ), | ||
211 | _ => never!("Module source node is missing a name"), | ||
212 | } | ||
213 | } | ||
214 | let def = Definition::ModuleDef(hir::ModuleDef::Module(module)); | ||
215 | let usages = def.usages(sema).all(); | ||
216 | let ref_edits = usages.iter().map(|(&file_id, references)| { | ||
217 | (file_id, source_edit_from_references(references, def, new_name)) | ||
218 | }); | ||
219 | source_change.extend(ref_edits); | ||
220 | |||
221 | Ok(source_change) | ||
222 | } | ||
223 | |||
224 | fn rename_reference( | ||
225 | sema: &Semantics<RootDatabase>, | ||
226 | mut def: Definition, | ||
227 | new_name: &str, | ||
228 | ) -> Result<SourceChange> { | ||
229 | let ident_kind = IdentifierKind::classify(new_name)?; | ||
230 | |||
231 | if matches!( | ||
232 | def, // is target a lifetime? | ||
233 | Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_) | ||
234 | ) { | ||
235 | match ident_kind { | ||
236 | IdentifierKind::Ident | IdentifierKind::Underscore => { | ||
237 | cov_mark::hit!(rename_not_a_lifetime_ident_ref); | ||
238 | bail!("Invalid name `{}`: not a lifetime identifier", new_name); | ||
239 | } | ||
240 | IdentifierKind::Lifetime => cov_mark::hit!(rename_lifetime), | ||
241 | } | ||
242 | } else { | ||
243 | match (ident_kind, def) { | ||
244 | (IdentifierKind::Lifetime, _) => { | ||
245 | cov_mark::hit!(rename_not_an_ident_ref); | ||
246 | bail!("Invalid name `{}`: not an identifier", new_name); | ||
247 | } | ||
248 | (IdentifierKind::Ident, _) => cov_mark::hit!(rename_non_local), | ||
249 | (IdentifierKind::Underscore, _) => (), | ||
250 | } | ||
251 | } | ||
252 | |||
253 | def = match def { | ||
254 | // HACK: resolve trait impl items to the item def of the trait definition | ||
255 | // so that we properly resolve all trait item references | ||
256 | Definition::ModuleDef(mod_def) => mod_def | ||
257 | .as_assoc_item(sema.db) | ||
258 | .and_then(|it| it.containing_trait_impl(sema.db)) | ||
259 | .and_then(|it| { | ||
260 | it.items(sema.db).into_iter().find_map(|it| match (it, mod_def) { | ||
261 | (hir::AssocItem::Function(trait_func), hir::ModuleDef::Function(func)) | ||
262 | if trait_func.name(sema.db) == func.name(sema.db) => | ||
263 | { | ||
264 | Some(Definition::ModuleDef(hir::ModuleDef::Function(trait_func))) | ||
265 | } | ||
266 | (hir::AssocItem::Const(trait_konst), hir::ModuleDef::Const(konst)) | ||
267 | if trait_konst.name(sema.db) == konst.name(sema.db) => | ||
268 | { | ||
269 | Some(Definition::ModuleDef(hir::ModuleDef::Const(trait_konst))) | ||
270 | } | ||
271 | ( | ||
272 | hir::AssocItem::TypeAlias(trait_type_alias), | ||
273 | hir::ModuleDef::TypeAlias(type_alias), | ||
274 | ) if trait_type_alias.name(sema.db) == type_alias.name(sema.db) => { | ||
275 | Some(Definition::ModuleDef(hir::ModuleDef::TypeAlias(trait_type_alias))) | ||
276 | } | ||
277 | _ => None, | ||
278 | }) | ||
279 | }) | ||
280 | .unwrap_or(def), | ||
281 | _ => def, | ||
282 | }; | ||
283 | let usages = def.usages(sema).all(); | ||
284 | |||
285 | if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { | ||
286 | cov_mark::hit!(rename_underscore_multiple); | ||
287 | bail!("Cannot rename reference to `_` as it is being referenced multiple times"); | ||
288 | } | ||
289 | let mut source_change = SourceChange::default(); | ||
290 | source_change.extend(usages.iter().map(|(&file_id, references)| { | ||
291 | (file_id, source_edit_from_references(references, def, new_name)) | ||
292 | })); | ||
293 | |||
294 | let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; | ||
295 | source_change.insert_source_edit(file_id, edit); | ||
296 | Ok(source_change) | ||
297 | } | ||
298 | |||
299 | pub fn source_edit_from_references( | ||
300 | references: &[FileReference], | ||
301 | def: Definition, | ||
302 | new_name: &str, | ||
303 | ) -> TextEdit { | ||
304 | let mut edit = TextEdit::builder(); | ||
305 | for reference in references { | ||
306 | let (range, replacement) = match &reference.name { | ||
307 | // if the ranges differ then the node is inside a macro call, we can't really attempt | ||
308 | // to make special rewrites like shorthand syntax and such, so just rename the node in | ||
309 | // the macro input | ||
310 | ast::NameLike::NameRef(name_ref) | ||
311 | if name_ref.syntax().text_range() == reference.range => | ||
312 | { | ||
313 | source_edit_from_name_ref(name_ref, new_name, def) | ||
314 | } | ||
315 | ast::NameLike::Name(name) if name.syntax().text_range() == reference.range => { | ||
316 | source_edit_from_name(name, new_name) | ||
317 | } | ||
318 | _ => None, | ||
319 | } | ||
320 | .unwrap_or_else(|| (reference.range, new_name.to_string())); | ||
321 | edit.replace(range, replacement); | ||
322 | } | ||
323 | edit.finish() | ||
324 | } | ||
325 | |||
326 | fn source_edit_from_name(name: &ast::Name, new_name: &str) -> Option<(TextRange, String)> { | ||
327 | if let Some(_) = ast::RecordPatField::for_field_name(name) { | ||
328 | if let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { | ||
329 | return Some(( | ||
330 | TextRange::empty(ident_pat.syntax().text_range().start()), | ||
331 | [new_name, ": "].concat(), | ||
332 | )); | ||
333 | } | ||
334 | } | ||
335 | None | ||
336 | } | ||
337 | |||
338 | fn source_edit_from_name_ref( | ||
339 | name_ref: &ast::NameRef, | ||
340 | new_name: &str, | ||
341 | def: Definition, | ||
342 | ) -> Option<(TextRange, String)> { | ||
343 | if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) { | ||
344 | let rcf_name_ref = record_field.name_ref(); | ||
345 | let rcf_expr = record_field.expr(); | ||
346 | match (rcf_name_ref, rcf_expr.and_then(|it| it.name_ref())) { | ||
347 | // field: init-expr, check if we can use a field init shorthand | ||
348 | (Some(field_name), Some(init)) => { | ||
349 | if field_name == *name_ref { | ||
350 | if init.text() == new_name { | ||
351 | cov_mark::hit!(test_rename_field_put_init_shorthand); | ||
352 | // same names, we can use a shorthand here instead. | ||
353 | // we do not want to erase attributes hence this range start | ||
354 | let s = field_name.syntax().text_range().start(); | ||
355 | let e = record_field.syntax().text_range().end(); | ||
356 | return Some((TextRange::new(s, e), new_name.to_owned())); | ||
357 | } | ||
358 | } else if init == *name_ref { | ||
359 | if field_name.text() == new_name { | ||
360 | cov_mark::hit!(test_rename_local_put_init_shorthand); | ||
361 | // same names, we can use a shorthand here instead. | ||
362 | // we do not want to erase attributes hence this range start | ||
363 | let s = field_name.syntax().text_range().start(); | ||
364 | let e = record_field.syntax().text_range().end(); | ||
365 | return Some((TextRange::new(s, e), new_name.to_owned())); | ||
366 | } | ||
367 | } | ||
368 | None | ||
369 | } | ||
370 | // init shorthand | ||
371 | // FIXME: instead of splitting the shorthand, recursively trigger a rename of the | ||
372 | // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 | ||
373 | (None, Some(_)) if matches!(def, Definition::Field(_)) => { | ||
374 | cov_mark::hit!(test_rename_field_in_field_shorthand); | ||
375 | let s = name_ref.syntax().text_range().start(); | ||
376 | Some((TextRange::empty(s), format!("{}: ", new_name))) | ||
377 | } | ||
378 | (None, Some(_)) if matches!(def, Definition::Local(_)) => { | ||
379 | cov_mark::hit!(test_rename_local_in_field_shorthand); | ||
380 | let s = name_ref.syntax().text_range().end(); | ||
381 | Some((TextRange::empty(s), format!(": {}", new_name))) | ||
382 | } | ||
383 | _ => None, | ||
384 | } | ||
385 | } else if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) { | ||
386 | let rcf_name_ref = record_field.name_ref(); | ||
387 | let rcf_pat = record_field.pat(); | ||
388 | match (rcf_name_ref, rcf_pat) { | ||
389 | // field: rename | ||
390 | (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => { | ||
391 | // field name is being renamed | ||
392 | if pat.name().map_or(false, |it| it.text() == new_name) { | ||
393 | cov_mark::hit!(test_rename_field_put_init_shorthand_pat); | ||
394 | // same names, we can use a shorthand here instead/ | ||
395 | // we do not want to erase attributes hence this range start | ||
396 | let s = field_name.syntax().text_range().start(); | ||
397 | let e = record_field.syntax().text_range().end(); | ||
398 | Some((TextRange::new(s, e), pat.to_string())) | ||
399 | } else { | ||
400 | None | ||
401 | } | ||
402 | } | ||
403 | _ => None, | ||
404 | } | ||
405 | } else { | ||
406 | None | ||
407 | } | ||
408 | } | ||
409 | |||
410 | fn source_edit_from_def( | ||
411 | sema: &Semantics<RootDatabase>, | ||
412 | def: Definition, | ||
413 | new_name: &str, | ||
414 | ) -> Result<(FileId, TextEdit)> { | ||
415 | let frange = def | ||
416 | .range_for_rename(sema) | ||
417 | .ok_or_else(|| format_err!("No identifier available to rename"))?; | ||
418 | |||
419 | let mut replacement_text = String::new(); | ||
420 | let mut repl_range = frange.range; | ||
421 | if let Definition::Local(local) = def { | ||
422 | if let Either::Left(pat) = local.source(sema.db).value { | ||
423 | if matches!( | ||
424 | pat.syntax().parent().and_then(ast::RecordPatField::cast), | ||
425 | Some(pat_field) if pat_field.name_ref().is_none() | ||
426 | ) { | ||
427 | replacement_text.push_str(": "); | ||
428 | replacement_text.push_str(new_name); | ||
429 | repl_range = TextRange::new( | ||
430 | pat.syntax().text_range().end(), | ||
431 | pat.syntax().text_range().end(), | ||
432 | ); | ||
433 | } | ||
434 | } | ||
435 | } | ||
436 | if replacement_text.is_empty() { | ||
437 | replacement_text.push_str(new_name); | ||
438 | } | ||
439 | let edit = TextEdit::replace(repl_range, replacement_text); | ||
440 | Ok((frange.file_id, edit)) | ||
441 | } | ||
442 | |||
443 | #[derive(Copy, Clone, Debug, PartialEq)] | ||
444 | pub enum IdentifierKind { | ||
445 | Ident, | ||
446 | Lifetime, | ||
447 | Underscore, | ||
448 | } | ||
449 | |||
450 | impl IdentifierKind { | ||
451 | pub fn classify(new_name: &str) -> Result<IdentifierKind> { | ||
452 | match lex_single_syntax_kind(new_name) { | ||
453 | Some(res) => match res { | ||
454 | (SyntaxKind::IDENT, _) => Ok(IdentifierKind::Ident), | ||
455 | (T![_], _) => Ok(IdentifierKind::Underscore), | ||
456 | (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => { | ||
457 | Ok(IdentifierKind::Lifetime) | ||
458 | } | ||
459 | (SyntaxKind::LIFETIME_IDENT, _) => { | ||
460 | bail!("Invalid name `{}`: not a lifetime identifier", new_name) | ||
461 | } | ||
462 | (_, Some(syntax_error)) => bail!("Invalid name `{}`: {}", new_name, syntax_error), | ||
463 | (_, None) => bail!("Invalid name `{}`: not an identifier", new_name), | ||
464 | }, | ||
465 | None => bail!("Invalid name `{}`: not an identifier", new_name), | ||
466 | } | ||
467 | } | ||
468 | } | ||
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index 8152630f5..a840e06a6 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs | |||
@@ -409,7 +409,7 @@ impl<'a> FindUsages<'a> { | |||
409 | if let Some(ast::NameLike::NameRef(name_ref)) = | 409 | if let Some(ast::NameLike::NameRef(name_ref)) = |
410 | sema.find_node_at_offset_with_descend(&tree, offset) | 410 | sema.find_node_at_offset_with_descend(&tree, offset) |
411 | { | 411 | { |
412 | 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) { |
413 | return; | 413 | return; |
414 | } | 414 | } |
415 | } | 415 | } |
@@ -424,7 +424,7 @@ impl<'a> FindUsages<'a> { | |||
424 | name_ref: &ast::NameRef, | 424 | name_ref: &ast::NameRef, |
425 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, | 425 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, |
426 | ) -> bool { | 426 | ) -> bool { |
427 | match NameRefClass::classify(self.sema, &name_ref) { | 427 | match NameRefClass::classify(self.sema, name_ref) { |
428 | Some(NameRefClass::Definition(Definition::SelfType(impl_))) | 428 | Some(NameRefClass::Definition(Definition::SelfType(impl_))) |
429 | if impl_.self_ty(self.sema.db) == *self_ty => | 429 | if impl_.self_ty(self.sema.db) == *self_ty => |
430 | { | 430 | { |
@@ -464,13 +464,13 @@ impl<'a> FindUsages<'a> { | |||
464 | name_ref: &ast::NameRef, | 464 | name_ref: &ast::NameRef, |
465 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, | 465 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, |
466 | ) -> bool { | 466 | ) -> bool { |
467 | match NameRefClass::classify(self.sema, &name_ref) { | 467 | match NameRefClass::classify(self.sema, name_ref) { |
468 | Some(NameRefClass::Definition(def)) if def == self.def => { | 468 | Some(NameRefClass::Definition(def)) if def == self.def => { |
469 | 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()); |
470 | let reference = FileReference { | 470 | let reference = FileReference { |
471 | range, | 471 | range, |
472 | name: ast::NameLike::NameRef(name_ref.clone()), | 472 | name: ast::NameLike::NameRef(name_ref.clone()), |
473 | access: reference_access(&def, &name_ref), | 473 | access: reference_access(&def, name_ref), |
474 | }; | 474 | }; |
475 | sink(file_id, reference) | 475 | sink(file_id, reference) |
476 | } | 476 | } |
@@ -480,7 +480,7 @@ impl<'a> FindUsages<'a> { | |||
480 | let reference = FileReference { | 480 | let reference = FileReference { |
481 | range, | 481 | range, |
482 | name: ast::NameLike::NameRef(name_ref.clone()), | 482 | name: ast::NameLike::NameRef(name_ref.clone()), |
483 | access: reference_access(&def, &name_ref), | 483 | access: reference_access(&def, name_ref), |
484 | }; | 484 | }; |
485 | sink(file_id, reference) | 485 | sink(file_id, reference) |
486 | } else { | 486 | } else { |
@@ -490,11 +490,9 @@ impl<'a> FindUsages<'a> { | |||
490 | Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { | 490 | Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { |
491 | 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()); |
492 | let access = match self.def { | 492 | let access = match self.def { |
493 | Definition::Field(_) if field == self.def => { | 493 | Definition::Field(_) if field == self.def => reference_access(&field, name_ref), |
494 | reference_access(&field, &name_ref) | ||
495 | } | ||
496 | Definition::Local(l) if local == l => { | 494 | Definition::Local(l) if local == l => { |
497 | reference_access(&Definition::Local(local), &name_ref) | 495 | reference_access(&Definition::Local(local), name_ref) |
498 | } | 496 | } |
499 | _ => return false, | 497 | _ => return false, |
500 | }; | 498 | }; |
diff --git a/crates/ide_diagnostics/Cargo.toml b/crates/ide_diagnostics/Cargo.toml new file mode 100644 index 000000000..fa2adf212 --- /dev/null +++ b/crates/ide_diagnostics/Cargo.toml | |||
@@ -0,0 +1,29 @@ | |||
1 | [package] | ||
2 | name = "ide_diagnostics" | ||
3 | version = "0.0.0" | ||
4 | description = "TBD" | ||
5 | license = "MIT OR Apache-2.0" | ||
6 | authors = ["rust-analyzer developers"] | ||
7 | edition = "2018" | ||
8 | |||
9 | [lib] | ||
10 | doctest = false | ||
11 | |||
12 | [dependencies] | ||
13 | cov-mark = "2.0.0-pre.1" | ||
14 | itertools = "0.10.0" | ||
15 | rustc-hash = "1.1.0" | ||
16 | either = "1.5.3" | ||
17 | |||
18 | profile = { path = "../profile", version = "0.0.0" } | ||
19 | stdx = { path = "../stdx", version = "0.0.0" } | ||
20 | syntax = { path = "../syntax", version = "0.0.0" } | ||
21 | text_edit = { path = "../text_edit", version = "0.0.0" } | ||
22 | cfg = { path = "../cfg", version = "0.0.0" } | ||
23 | hir = { path = "../hir", version = "0.0.0" } | ||
24 | ide_db = { path = "../ide_db", version = "0.0.0" } | ||
25 | |||
26 | [dev-dependencies] | ||
27 | expect-test = "1.1" | ||
28 | |||
29 | test_utils = { path = "../test_utils" } | ||
diff --git a/crates/ide_diagnostics/src/handlers/break_outside_of_loop.rs b/crates/ide_diagnostics/src/handlers/break_outside_of_loop.rs new file mode 100644 index 000000000..d12594a4c --- /dev/null +++ b/crates/ide_diagnostics/src/handlers/break_outside_of_loop.rs | |||
@@ -0,0 +1,30 @@ | |||
1 | use crate::{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(crate) 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::tests::check_diagnostics; | ||
20 | |||
21 | #[test] | ||
22 | fn break_outside_of_loop() { | ||
23 | check_diagnostics( | ||
24 | r#" | ||
25 | fn foo() { break; } | ||
26 | //^^^^^ error: break outside of loop | ||
27 | "#, | ||
28 | ); | ||
29 | } | ||
30 | } | ||
diff --git a/crates/ide/src/diagnostics/field_shorthand.rs b/crates/ide_diagnostics/src/handlers/field_shorthand.rs index 01bd2dba6..33152e284 100644 --- a/crates/ide/src/diagnostics/field_shorthand.rs +++ b/crates/ide_diagnostics/src/handlers/field_shorthand.rs | |||
@@ -5,9 +5,9 @@ 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::{fix, Diagnostic, Severity}; |
9 | 9 | ||
10 | pub(super) fn check(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) { | 10 | pub(crate) fn field_shorthand(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) { |
11 | match_ast! { | 11 | match_ast! { |
12 | match node { | 12 | match node { |
13 | ast::RecordExpr(it) => check_expr_field_shorthand(acc, file_id, it), | 13 | ast::RecordExpr(it) => check_expr_field_shorthand(acc, file_id, it), |
@@ -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::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_diagnostics/src/handlers/inactive_code.rs b/crates/ide_diagnostics/src/handlers/inactive_code.rs new file mode 100644 index 000000000..dfd0e3351 --- /dev/null +++ b/crates/ide_diagnostics/src/handlers/inactive_code.rs | |||
@@ -0,0 +1,116 @@ | |||
1 | use cfg::DnfExpr; | ||
2 | use stdx::format_to; | ||
3 | |||
4 | use crate::{Diagnostic, DiagnosticsContext, Severity}; | ||
5 | |||
6 | // Diagnostic: inactive-code | ||
7 | // | ||
8 | // This diagnostic is shown for code with inactive `#[cfg]` attributes. | ||
9 | pub(crate) fn inactive_code( | ||
10 | ctx: &DiagnosticsContext<'_>, | ||
11 | d: &hir::InactiveCode, | ||
12 | ) -> Option<Diagnostic> { | ||
13 | // If there's inactive code somewhere in a macro, don't propagate to the call-site. | ||
14 | if d.node.file_id.expansion_info(ctx.sema.db).is_some() { | ||
15 | return None; | ||
16 | } | ||
17 | |||
18 | let inactive = DnfExpr::new(d.cfg.clone()).why_inactive(&d.opts); | ||
19 | let mut message = "code is inactive due to #[cfg] directives".to_string(); | ||
20 | |||
21 | if let Some(inactive) = inactive { | ||
22 | format_to!(message, ": {}", inactive); | ||
23 | } | ||
24 | |||
25 | let res = Diagnostic::new( | ||
26 | "inactive-code", | ||
27 | message, | ||
28 | ctx.sema.diagnostics_display_range(d.node.clone()).range, | ||
29 | ) | ||
30 | .severity(Severity::WeakWarning) | ||
31 | .with_unused(true); | ||
32 | Some(res) | ||
33 | } | ||
34 | |||
35 | #[cfg(test)] | ||
36 | mod tests { | ||
37 | use crate::{tests::check_diagnostics_with_config, DiagnosticsConfig}; | ||
38 | |||
39 | pub(crate) fn check(ra_fixture: &str) { | ||
40 | let config = DiagnosticsConfig::default(); | ||
41 | check_diagnostics_with_config(config, ra_fixture) | ||
42 | } | ||
43 | |||
44 | #[test] | ||
45 | fn cfg_diagnostics() { | ||
46 | check( | ||
47 | r#" | ||
48 | fn f() { | ||
49 | // The three g̶e̶n̶d̶e̶r̶s̶ statements: | ||
50 | |||
51 | #[cfg(a)] fn f() {} // Item statement | ||
52 | //^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled | ||
53 | #[cfg(a)] {} // Expression statement | ||
54 | //^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled | ||
55 | #[cfg(a)] let x = 0; // let statement | ||
56 | //^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled | ||
57 | |||
58 | abc(#[cfg(a)] 0); | ||
59 | //^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled | ||
60 | let x = Struct { | ||
61 | #[cfg(a)] f: 0, | ||
62 | //^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled | ||
63 | }; | ||
64 | match () { | ||
65 | () => (), | ||
66 | #[cfg(a)] () => (), | ||
67 | //^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled | ||
68 | } | ||
69 | |||
70 | #[cfg(a)] 0 // Trailing expression of block | ||
71 | //^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled | ||
72 | } | ||
73 | "#, | ||
74 | ); | ||
75 | } | ||
76 | |||
77 | #[test] | ||
78 | fn inactive_item() { | ||
79 | // Additional tests in `cfg` crate. This only tests disabled cfgs. | ||
80 | |||
81 | check( | ||
82 | r#" | ||
83 | #[cfg(no)] pub fn f() {} | ||
84 | //^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no is disabled | ||
85 | |||
86 | #[cfg(no)] #[cfg(no2)] mod m; | ||
87 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no and no2 are disabled | ||
88 | |||
89 | #[cfg(all(not(a), b))] enum E {} | ||
90 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: b is disabled | ||
91 | |||
92 | #[cfg(feature = "std")] use std; | ||
93 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: feature = "std" is disabled | ||
94 | "#, | ||
95 | ); | ||
96 | } | ||
97 | |||
98 | /// Tests that `cfg` attributes behind `cfg_attr` is handled properly. | ||
99 | #[test] | ||
100 | fn inactive_via_cfg_attr() { | ||
101 | cov_mark::check!(cfg_attr_active); | ||
102 | check( | ||
103 | r#" | ||
104 | #[cfg_attr(not(never), cfg(no))] fn f() {} | ||
105 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no is disabled | ||
106 | |||
107 | #[cfg_attr(not(never), cfg(not(no)))] fn f() {} | ||
108 | |||
109 | #[cfg_attr(never, cfg(no))] fn g() {} | ||
110 | |||
111 | #[cfg_attr(not(never), inline, cfg(no))] fn h() {} | ||
112 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: no is disabled | ||
113 | "#, | ||
114 | ); | ||
115 | } | ||
116 | } | ||
diff --git a/crates/ide_diagnostics/src/handlers/incorrect_case.rs b/crates/ide_diagnostics/src/handlers/incorrect_case.rs new file mode 100644 index 000000000..68f25f284 --- /dev/null +++ b/crates/ide_diagnostics/src/handlers/incorrect_case.rs | |||
@@ -0,0 +1,459 @@ | |||
1 | use hir::{db::AstDatabase, InFile}; | ||
2 | use ide_db::{assists::Assist, defs::NameClass}; | ||
3 | use syntax::AstNode; | ||
4 | |||
5 | use crate::{ | ||
6 | // references::rename::rename_with_semantics, | ||
7 | unresolved_fix, | ||
8 | Diagnostic, | ||
9 | DiagnosticsContext, | ||
10 | Severity, | ||
11 | }; | ||
12 | |||
13 | // Diagnostic: incorrect-ident-case | ||
14 | // | ||
15 | // 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]. | ||
16 | pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic { | ||
17 | Diagnostic::new( | ||
18 | "incorrect-ident-case", | ||
19 | format!( | ||
20 | "{} `{}` should have {} name, e.g. `{}`", | ||
21 | d.ident_type, d.ident_text, d.expected_case, d.suggested_text | ||
22 | ), | ||
23 | ctx.sema.diagnostics_display_range(InFile::new(d.file, d.ident.clone().into())).range, | ||
24 | ) | ||
25 | .severity(Severity::WeakWarning) | ||
26 | .with_fixes(fixes(ctx, d)) | ||
27 | } | ||
28 | |||
29 | fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option<Vec<Assist>> { | ||
30 | let root = ctx.sema.db.parse_or_expand(d.file)?; | ||
31 | let name_node = d.ident.to_node(&root); | ||
32 | let def = NameClass::classify(&ctx.sema, &name_node)?.defined(ctx.sema.db)?; | ||
33 | |||
34 | let name_node = InFile::new(d.file, name_node.syntax()); | ||
35 | let frange = name_node.original_file_range(ctx.sema.db); | ||
36 | |||
37 | let label = format!("Rename to {}", d.suggested_text); | ||
38 | let mut res = unresolved_fix("change_case", &label, frange.range); | ||
39 | if ctx.resolve.should_resolve(&res.id) { | ||
40 | let source_change = def.rename(&ctx.sema, &d.suggested_text); | ||
41 | res.source_change = Some(source_change.ok().unwrap_or_default()); | ||
42 | } | ||
43 | |||
44 | Some(vec![res]) | ||
45 | } | ||
46 | |||
47 | #[cfg(test)] | ||
48 | mod change_case { | ||
49 | use crate::tests::{check_diagnostics, check_fix}; | ||
50 | |||
51 | #[test] | ||
52 | fn test_rename_incorrect_case() { | ||
53 | check_fix( | ||
54 | r#" | ||
55 | pub struct test_struct$0 { one: i32 } | ||
56 | |||
57 | pub fn some_fn(val: test_struct) -> test_struct { | ||
58 | test_struct { one: val.one + 1 } | ||
59 | } | ||
60 | "#, | ||
61 | r#" | ||
62 | pub struct TestStruct { one: i32 } | ||
63 | |||
64 | pub fn some_fn(val: TestStruct) -> TestStruct { | ||
65 | TestStruct { one: val.one + 1 } | ||
66 | } | ||
67 | "#, | ||
68 | ); | ||
69 | |||
70 | check_fix( | ||
71 | r#" | ||
72 | pub fn some_fn(NonSnakeCase$0: u8) -> u8 { | ||
73 | NonSnakeCase | ||
74 | } | ||
75 | "#, | ||
76 | r#" | ||
77 | pub fn some_fn(non_snake_case: u8) -> u8 { | ||
78 | non_snake_case | ||
79 | } | ||
80 | "#, | ||
81 | ); | ||
82 | |||
83 | check_fix( | ||
84 | r#" | ||
85 | pub fn SomeFn$0(val: u8) -> u8 { | ||
86 | if val != 0 { SomeFn(val - 1) } else { val } | ||
87 | } | ||
88 | "#, | ||
89 | r#" | ||
90 | pub fn some_fn(val: u8) -> u8 { | ||
91 | if val != 0 { some_fn(val - 1) } else { val } | ||
92 | } | ||
93 | "#, | ||
94 | ); | ||
95 | |||
96 | check_fix( | ||
97 | r#" | ||
98 | fn some_fn() { | ||
99 | let whatAWeird_Formatting$0 = 10; | ||
100 | another_func(whatAWeird_Formatting); | ||
101 | } | ||
102 | "#, | ||
103 | r#" | ||
104 | fn some_fn() { | ||
105 | let what_a_weird_formatting = 10; | ||
106 | another_func(what_a_weird_formatting); | ||
107 | } | ||
108 | "#, | ||
109 | ); | ||
110 | } | ||
111 | |||
112 | #[test] | ||
113 | fn test_uppercase_const_no_diagnostics() { | ||
114 | check_diagnostics( | ||
115 | r#" | ||
116 | fn foo() { | ||
117 | const ANOTHER_ITEM: &str = "some_item"; | ||
118 | } | ||
119 | "#, | ||
120 | ); | ||
121 | } | ||
122 | |||
123 | #[test] | ||
124 | fn test_rename_incorrect_case_struct_method() { | ||
125 | check_fix( | ||
126 | r#" | ||
127 | pub struct TestStruct; | ||
128 | |||
129 | impl TestStruct { | ||
130 | pub fn SomeFn$0() -> TestStruct { | ||
131 | TestStruct | ||
132 | } | ||
133 | } | ||
134 | "#, | ||
135 | r#" | ||
136 | pub struct TestStruct; | ||
137 | |||
138 | impl TestStruct { | ||
139 | pub fn some_fn() -> TestStruct { | ||
140 | TestStruct | ||
141 | } | ||
142 | } | ||
143 | "#, | ||
144 | ); | ||
145 | } | ||
146 | |||
147 | #[test] | ||
148 | fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() { | ||
149 | check_diagnostics( | ||
150 | r#" | ||
151 | fn FOO() {} | ||
152 | // ^^^ 💡 weak: Function `FOO` should have snake_case name, e.g. `foo` | ||
153 | "#, | ||
154 | ); | ||
155 | check_fix(r#"fn FOO$0() {}"#, r#"fn foo() {}"#); | ||
156 | } | ||
157 | |||
158 | #[test] | ||
159 | fn incorrect_function_name() { | ||
160 | check_diagnostics( | ||
161 | r#" | ||
162 | fn NonSnakeCaseName() {} | ||
163 | // ^^^^^^^^^^^^^^^^ 💡 weak: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` | ||
164 | "#, | ||
165 | ); | ||
166 | } | ||
167 | |||
168 | #[test] | ||
169 | fn incorrect_function_params() { | ||
170 | check_diagnostics( | ||
171 | r#" | ||
172 | fn foo(SomeParam: u8) {} | ||
173 | // ^^^^^^^^^ 💡 weak: Parameter `SomeParam` should have snake_case name, e.g. `some_param` | ||
174 | |||
175 | fn foo2(ok_param: &str, CAPS_PARAM: u8) {} | ||
176 | // ^^^^^^^^^^ 💡 weak: Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param` | ||
177 | "#, | ||
178 | ); | ||
179 | } | ||
180 | |||
181 | #[test] | ||
182 | fn incorrect_variable_names() { | ||
183 | check_diagnostics( | ||
184 | r#" | ||
185 | fn foo() { | ||
186 | let SOME_VALUE = 10; | ||
187 | // ^^^^^^^^^^ 💡 weak: Variable `SOME_VALUE` should have snake_case name, e.g. `some_value` | ||
188 | let AnotherValue = 20; | ||
189 | // ^^^^^^^^^^^^ 💡 weak: Variable `AnotherValue` should have snake_case name, e.g. `another_value` | ||
190 | } | ||
191 | "#, | ||
192 | ); | ||
193 | } | ||
194 | |||
195 | #[test] | ||
196 | fn incorrect_struct_names() { | ||
197 | check_diagnostics( | ||
198 | r#" | ||
199 | struct non_camel_case_name {} | ||
200 | // ^^^^^^^^^^^^^^^^^^^ 💡 weak: Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName` | ||
201 | |||
202 | struct SCREAMING_CASE {} | ||
203 | // ^^^^^^^^^^^^^^ 💡 weak: Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase` | ||
204 | "#, | ||
205 | ); | ||
206 | } | ||
207 | |||
208 | #[test] | ||
209 | fn no_diagnostic_for_camel_cased_acronyms_in_struct_name() { | ||
210 | check_diagnostics( | ||
211 | r#" | ||
212 | struct AABB {} | ||
213 | "#, | ||
214 | ); | ||
215 | } | ||
216 | |||
217 | #[test] | ||
218 | fn incorrect_struct_field() { | ||
219 | check_diagnostics( | ||
220 | r#" | ||
221 | struct SomeStruct { SomeField: u8 } | ||
222 | // ^^^^^^^^^ 💡 weak: Field `SomeField` should have snake_case name, e.g. `some_field` | ||
223 | "#, | ||
224 | ); | ||
225 | } | ||
226 | |||
227 | #[test] | ||
228 | fn incorrect_enum_names() { | ||
229 | check_diagnostics( | ||
230 | r#" | ||
231 | enum some_enum { Val(u8) } | ||
232 | // ^^^^^^^^^ 💡 weak: Enum `some_enum` should have CamelCase name, e.g. `SomeEnum` | ||
233 | |||
234 | enum SOME_ENUM {} | ||
235 | // ^^^^^^^^^ 💡 weak: Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum` | ||
236 | "#, | ||
237 | ); | ||
238 | } | ||
239 | |||
240 | #[test] | ||
241 | fn no_diagnostic_for_camel_cased_acronyms_in_enum_name() { | ||
242 | check_diagnostics( | ||
243 | r#" | ||
244 | enum AABB {} | ||
245 | "#, | ||
246 | ); | ||
247 | } | ||
248 | |||
249 | #[test] | ||
250 | fn incorrect_enum_variant_name() { | ||
251 | check_diagnostics( | ||
252 | r#" | ||
253 | enum SomeEnum { SOME_VARIANT(u8) } | ||
254 | // ^^^^^^^^^^^^ 💡 weak: Variant `SOME_VARIANT` should have CamelCase name, e.g. `SomeVariant` | ||
255 | "#, | ||
256 | ); | ||
257 | } | ||
258 | |||
259 | #[test] | ||
260 | fn incorrect_const_name() { | ||
261 | check_diagnostics( | ||
262 | r#" | ||
263 | const some_weird_const: u8 = 10; | ||
264 | // ^^^^^^^^^^^^^^^^ 💡 weak: Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` | ||
265 | "#, | ||
266 | ); | ||
267 | } | ||
268 | |||
269 | #[test] | ||
270 | fn incorrect_static_name() { | ||
271 | check_diagnostics( | ||
272 | r#" | ||
273 | static some_weird_const: u8 = 10; | ||
274 | // ^^^^^^^^^^^^^^^^ 💡 weak: Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` | ||
275 | "#, | ||
276 | ); | ||
277 | } | ||
278 | |||
279 | #[test] | ||
280 | fn fn_inside_impl_struct() { | ||
281 | check_diagnostics( | ||
282 | r#" | ||
283 | struct someStruct; | ||
284 | // ^^^^^^^^^^ 💡 weak: Structure `someStruct` should have CamelCase name, e.g. `SomeStruct` | ||
285 | |||
286 | impl someStruct { | ||
287 | fn SomeFunc(&self) { | ||
288 | // ^^^^^^^^ 💡 weak: Function `SomeFunc` should have snake_case name, e.g. `some_func` | ||
289 | let WHY_VAR_IS_CAPS = 10; | ||
290 | // ^^^^^^^^^^^^^^^ 💡 weak: Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps` | ||
291 | } | ||
292 | } | ||
293 | "#, | ||
294 | ); | ||
295 | } | ||
296 | |||
297 | #[test] | ||
298 | fn no_diagnostic_for_enum_varinats() { | ||
299 | check_diagnostics( | ||
300 | r#" | ||
301 | enum Option { Some, None } | ||
302 | |||
303 | fn main() { | ||
304 | match Option::None { | ||
305 | None => (), | ||
306 | Some => (), | ||
307 | } | ||
308 | } | ||
309 | "#, | ||
310 | ); | ||
311 | } | ||
312 | |||
313 | #[test] | ||
314 | fn non_let_bind() { | ||
315 | check_diagnostics( | ||
316 | r#" | ||
317 | enum Option { Some, None } | ||
318 | |||
319 | fn main() { | ||
320 | match Option::None { | ||
321 | SOME_VAR @ None => (), | ||
322 | // ^^^^^^^^ 💡 weak: Variable `SOME_VAR` should have snake_case name, e.g. `some_var` | ||
323 | Some => (), | ||
324 | } | ||
325 | } | ||
326 | "#, | ||
327 | ); | ||
328 | } | ||
329 | |||
330 | #[test] | ||
331 | fn allow_attributes_crate_attr() { | ||
332 | check_diagnostics( | ||
333 | r#" | ||
334 | #![allow(non_snake_case)] | ||
335 | |||
336 | mod F { | ||
337 | fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {} | ||
338 | } | ||
339 | "#, | ||
340 | ); | ||
341 | } | ||
342 | |||
343 | #[test] | ||
344 | fn complex_ignore() { | ||
345 | // FIXME: this should trigger errors for the second case. | ||
346 | check_diagnostics( | ||
347 | r#" | ||
348 | trait T { fn a(); } | ||
349 | struct U {} | ||
350 | impl T for U { | ||
351 | fn a() { | ||
352 | #[allow(non_snake_case)] | ||
353 | trait __BitFlagsOk { | ||
354 | const HiImAlsoBad: u8 = 2; | ||
355 | fn Dirty(&self) -> bool { false } | ||
356 | } | ||
357 | |||
358 | trait __BitFlagsBad { | ||
359 | const HiImAlsoBad: u8 = 2; | ||
360 | fn Dirty(&self) -> bool { false } | ||
361 | } | ||
362 | } | ||
363 | } | ||
364 | "#, | ||
365 | ); | ||
366 | } | ||
367 | |||
368 | #[test] | ||
369 | fn infinite_loop_inner_items() { | ||
370 | check_diagnostics( | ||
371 | r#" | ||
372 | fn qualify() { | ||
373 | mod foo { | ||
374 | use super::*; | ||
375 | } | ||
376 | } | ||
377 | "#, | ||
378 | ) | ||
379 | } | ||
380 | |||
381 | #[test] // Issue #8809. | ||
382 | fn parenthesized_parameter() { | ||
383 | check_diagnostics(r#"fn f((O): _) {}"#) | ||
384 | } | ||
385 | |||
386 | #[test] | ||
387 | fn ignores_extern_items() { | ||
388 | cov_mark::check!(extern_func_incorrect_case_ignored); | ||
389 | cov_mark::check!(extern_static_incorrect_case_ignored); | ||
390 | check_diagnostics( | ||
391 | r#" | ||
392 | extern { | ||
393 | fn NonSnakeCaseName(SOME_VAR: u8) -> u8; | ||
394 | pub static SomeStatic: u8 = 10; | ||
395 | } | ||
396 | "#, | ||
397 | ); | ||
398 | } | ||
399 | |||
400 | #[test] | ||
401 | fn bug_traits_arent_checked() { | ||
402 | // FIXME: Traits and functions in traits aren't currently checked by | ||
403 | // r-a, even though rustc will complain about them. | ||
404 | check_diagnostics( | ||
405 | r#" | ||
406 | trait BAD_TRAIT { | ||
407 | fn BAD_FUNCTION(); | ||
408 | fn BadFunction(); | ||
409 | } | ||
410 | "#, | ||
411 | ); | ||
412 | } | ||
413 | |||
414 | #[test] | ||
415 | fn allow_attributes() { | ||
416 | check_diagnostics( | ||
417 | r#" | ||
418 | #[allow(non_snake_case)] | ||
419 | fn NonSnakeCaseName(SOME_VAR: u8) -> u8{ | ||
420 | // cov_flags generated output from elsewhere in this file | ||
421 | extern "C" { | ||
422 | #[no_mangle] | ||
423 | static lower_case: u8; | ||
424 | } | ||
425 | |||
426 | let OtherVar = SOME_VAR + 1; | ||
427 | OtherVar | ||
428 | } | ||
429 | |||
430 | #[allow(nonstandard_style)] | ||
431 | mod CheckNonstandardStyle { | ||
432 | fn HiImABadFnName() {} | ||
433 | } | ||
434 | |||
435 | #[allow(bad_style)] | ||
436 | mod CheckBadStyle { | ||
437 | fn HiImABadFnName() {} | ||
438 | } | ||
439 | |||
440 | mod F { | ||
441 | #![allow(non_snake_case)] | ||
442 | fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {} | ||
443 | } | ||
444 | |||
445 | #[allow(non_snake_case, non_camel_case_types)] | ||
446 | pub struct some_type { | ||
447 | SOME_FIELD: u8, | ||
448 | SomeField: u16, | ||
449 | } | ||
450 | |||
451 | #[allow(non_upper_case_globals)] | ||
452 | pub const some_const: u8 = 10; | ||
453 | |||
454 | #[allow(non_upper_case_globals)] | ||
455 | pub static SomeStatic: u8 = 10; | ||
456 | "#, | ||
457 | ); | ||
458 | } | ||
459 | } | ||
diff --git a/crates/ide_diagnostics/src/handlers/macro_error.rs b/crates/ide_diagnostics/src/handlers/macro_error.rs new file mode 100644 index 000000000..356f089b2 --- /dev/null +++ b/crates/ide_diagnostics/src/handlers/macro_error.rs | |||
@@ -0,0 +1,173 @@ | |||
1 | use crate::{Diagnostic, DiagnosticsContext}; | ||
2 | |||
3 | // Diagnostic: macro-error | ||
4 | // | ||
5 | // This diagnostic is shown for macro expansion errors. | ||
6 | pub(crate) 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 | 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 | //^^^^^^^^^^^^^^^^^^^^^^^^ error: 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 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `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 | //^^^^^^^^^^^^^^^^^ error: could not convert tokens | ||
112 | include!("does not exist"); | ||
113 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ error: failed to load file `does not exist` | ||
114 | |||
115 | env!(invalid); | ||
116 | //^^^^^^^^^^^^^ error: could not convert tokens | ||
117 | |||
118 | env!("OUT_DIR"); | ||
119 | //^^^^^^^^^^^^^^^ error: `OUT_DIR` not set, enable "run build scripts" to fix | ||
120 | |||
121 | compile_error!("compile_error works"); | ||
122 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: compile_error works | ||
123 | |||
124 | // Lazy: | ||
125 | |||
126 | format_args!(); | ||
127 | //^^^^^^^^^^^^^^ error: 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 | //^^^^^^ error: 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 | } //^^^^^^^^ error: leftover tokens | ||
170 | "#, | ||
171 | ) | ||
172 | } | ||
173 | } | ||
diff --git a/crates/ide_diagnostics/src/handlers/mismatched_arg_count.rs b/crates/ide_diagnostics/src/handlers/mismatched_arg_count.rs new file mode 100644 index 000000000..a9b6d3870 --- /dev/null +++ b/crates/ide_diagnostics/src/handlers/mismatched_arg_count.rs | |||
@@ -0,0 +1,272 @@ | |||
1 | use crate::{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(crate) 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::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 | //^^^^^^^ error: 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 | //^^^^^ error: 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 | } //^^^^^^^^^^^ error: 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 | } //^^^^^^^^^^ error: 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 | } //^^^^^^ error: 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 | } //^^^^^^^^^^^^^^ error: 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 | //^^^^^^^^^^^^^^ error: expected 1 argument, found 2 | ||
166 | Foo::Bar(); | ||
167 | //^^^^^^^^^^ error: 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 | //^^^^^^^^^^^ error: 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 | //^^^ error: expected 1 argument, found 0 | ||
208 | f(()); | ||
209 | f((), ()); | ||
210 | //^^^^^^^^^ error: 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_diagnostics/src/handlers/missing_fields.rs b/crates/ide_diagnostics/src/handlers/missing_fields.rs new file mode 100644 index 000000000..bc56e0342 --- /dev/null +++ b/crates/ide_diagnostics/src/handlers/missing_fields.rs | |||
@@ -0,0 +1,355 @@ | |||
1 | use either::Either; | ||
2 | use hir::{db::AstDatabase, InFile}; | ||
3 | use ide_db::{assists::Assist, source_change::SourceChange}; | ||
4 | use stdx::format_to; | ||
5 | use syntax::{algo, ast::make, AstNode, SyntaxNodePtr}; | ||
6 | use text_edit::TextEdit; | ||
7 | |||
8 | use crate::{fix, Diagnostic, DiagnosticsContext}; | ||
9 | |||
10 | // Diagnostic: missing-fields | ||
11 | // | ||
12 | // This diagnostic is triggered if record lacks some fields that exist in the corresponding structure. | ||
13 | // | ||
14 | // Example: | ||
15 | // | ||
16 | // ```rust | ||
17 | // struct A { a: u8, b: u8 } | ||
18 | // | ||
19 | // let a = A { a: 10 }; | ||
20 | // ``` | ||
21 | pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Diagnostic { | ||
22 | let mut message = String::from("missing structure fields:\n"); | ||
23 | for field in &d.missed_fields { | ||
24 | format_to!(message, "- {}\n", field); | ||
25 | } | ||
26 | |||
27 | let ptr = InFile::new( | ||
28 | d.file, | ||
29 | d.field_list_parent_path | ||
30 | .clone() | ||
31 | .map(SyntaxNodePtr::from) | ||
32 | .unwrap_or_else(|| d.field_list_parent.clone().either(|it| it.into(), |it| it.into())), | ||
33 | ); | ||
34 | |||
35 | Diagnostic::new("missing-fields", message, ctx.sema.diagnostics_display_range(ptr).range) | ||
36 | .with_fixes(fixes(ctx, d)) | ||
37 | } | ||
38 | |||
39 | fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Assist>> { | ||
40 | // Note that although we could add a diagnostics to | ||
41 | // fill the missing tuple field, e.g : | ||
42 | // `struct A(usize);` | ||
43 | // `let a = A { 0: () }` | ||
44 | // but it is uncommon usage and it should not be encouraged. | ||
45 | if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) { | ||
46 | return None; | ||
47 | } | ||
48 | |||
49 | let root = ctx.sema.db.parse_or_expand(d.file)?; | ||
50 | let field_list_parent = match &d.field_list_parent { | ||
51 | Either::Left(record_expr) => record_expr.to_node(&root), | ||
52 | // FIXE: patterns should be fixable as well. | ||
53 | Either::Right(_) => return None, | ||
54 | }; | ||
55 | let old_field_list = field_list_parent.record_expr_field_list()?; | ||
56 | let new_field_list = old_field_list.clone_for_update(); | ||
57 | for f in d.missed_fields.iter() { | ||
58 | let field = | ||
59 | make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit())) | ||
60 | .clone_for_update(); | ||
61 | new_field_list.add_field(field); | ||
62 | } | ||
63 | |||
64 | let edit = { | ||
65 | let mut builder = TextEdit::builder(); | ||
66 | algo::diff(old_field_list.syntax(), new_field_list.syntax()).into_text_edit(&mut builder); | ||
67 | builder.finish() | ||
68 | }; | ||
69 | Some(vec![fix( | ||
70 | "fill_missing_fields", | ||
71 | "Fill struct fields", | ||
72 | SourceChange::from_text_edit(d.file.original_file(ctx.sema.db), edit), | ||
73 | ctx.sema.original_range(field_list_parent.syntax()).range, | ||
74 | )]) | ||
75 | } | ||
76 | |||
77 | #[cfg(test)] | ||
78 | mod tests { | ||
79 | use crate::tests::{check_diagnostics, check_fix}; | ||
80 | |||
81 | #[test] | ||
82 | fn missing_record_pat_field_diagnostic() { | ||
83 | check_diagnostics( | ||
84 | r#" | ||
85 | struct S { foo: i32, bar: () } | ||
86 | fn baz(s: S) { | ||
87 | let S { foo: _ } = s; | ||
88 | //^ error: missing structure fields: | ||
89 | //| - bar | ||
90 | } | ||
91 | "#, | ||
92 | ); | ||
93 | } | ||
94 | |||
95 | #[test] | ||
96 | fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() { | ||
97 | check_diagnostics( | ||
98 | r" | ||
99 | struct S { foo: i32, bar: () } | ||
100 | fn baz(s: S) -> i32 { | ||
101 | match s { | ||
102 | S { foo, .. } => foo, | ||
103 | } | ||
104 | } | ||
105 | ", | ||
106 | ) | ||
107 | } | ||
108 | |||
109 | #[test] | ||
110 | fn missing_record_pat_field_box() { | ||
111 | check_diagnostics( | ||
112 | r" | ||
113 | struct S { s: Box<u32> } | ||
114 | fn x(a: S) { | ||
115 | let S { box s } = a; | ||
116 | } | ||
117 | ", | ||
118 | ) | ||
119 | } | ||
120 | |||
121 | #[test] | ||
122 | fn missing_record_pat_field_ref() { | ||
123 | check_diagnostics( | ||
124 | r" | ||
125 | struct S { s: u32 } | ||
126 | fn x(a: S) { | ||
127 | let S { ref s } = a; | ||
128 | } | ||
129 | ", | ||
130 | ) | ||
131 | } | ||
132 | |||
133 | #[test] | ||
134 | fn range_mapping_out_of_macros() { | ||
135 | // FIXME: this is very wrong, but somewhat tricky to fix. | ||
136 | check_fix( | ||
137 | r#" | ||
138 | fn some() {} | ||
139 | fn items() {} | ||
140 | fn here() {} | ||
141 | |||
142 | macro_rules! id { ($($tt:tt)*) => { $($tt)*}; } | ||
143 | |||
144 | fn main() { | ||
145 | let _x = id![Foo { a: $042 }]; | ||
146 | } | ||
147 | |||
148 | pub struct Foo { pub a: i32, pub b: i32 } | ||
149 | "#, | ||
150 | r#" | ||
151 | fn some(, b: () ) {} | ||
152 | fn items() {} | ||
153 | fn here() {} | ||
154 | |||
155 | macro_rules! id { ($($tt:tt)*) => { $($tt)*}; } | ||
156 | |||
157 | fn main() { | ||
158 | let _x = id![Foo { a: 42 }]; | ||
159 | } | ||
160 | |||
161 | pub struct Foo { pub a: i32, pub b: i32 } | ||
162 | "#, | ||
163 | ); | ||
164 | } | ||
165 | |||
166 | #[test] | ||
167 | fn test_fill_struct_fields_empty() { | ||
168 | check_fix( | ||
169 | r#" | ||
170 | struct TestStruct { one: i32, two: i64 } | ||
171 | |||
172 | fn test_fn() { | ||
173 | let s = TestStruct {$0}; | ||
174 | } | ||
175 | "#, | ||
176 | r#" | ||
177 | struct TestStruct { one: i32, two: i64 } | ||
178 | |||
179 | fn test_fn() { | ||
180 | let s = TestStruct { one: (), two: () }; | ||
181 | } | ||
182 | "#, | ||
183 | ); | ||
184 | } | ||
185 | |||
186 | #[test] | ||
187 | fn test_fill_struct_fields_self() { | ||
188 | check_fix( | ||
189 | r#" | ||
190 | struct TestStruct { one: i32 } | ||
191 | |||
192 | impl TestStruct { | ||
193 | fn test_fn() { let s = Self {$0}; } | ||
194 | } | ||
195 | "#, | ||
196 | r#" | ||
197 | struct TestStruct { one: i32 } | ||
198 | |||
199 | impl TestStruct { | ||
200 | fn test_fn() { let s = Self { one: () }; } | ||
201 | } | ||
202 | "#, | ||
203 | ); | ||
204 | } | ||
205 | |||
206 | #[test] | ||
207 | fn test_fill_struct_fields_enum() { | ||
208 | check_fix( | ||
209 | r#" | ||
210 | enum Expr { | ||
211 | Bin { lhs: Box<Expr>, rhs: Box<Expr> } | ||
212 | } | ||
213 | |||
214 | impl Expr { | ||
215 | fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { | ||
216 | Expr::Bin {$0 } | ||
217 | } | ||
218 | } | ||
219 | "#, | ||
220 | r#" | ||
221 | enum Expr { | ||
222 | Bin { lhs: Box<Expr>, rhs: Box<Expr> } | ||
223 | } | ||
224 | |||
225 | impl Expr { | ||
226 | fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { | ||
227 | Expr::Bin { lhs: (), rhs: () } | ||
228 | } | ||
229 | } | ||
230 | "#, | ||
231 | ); | ||
232 | } | ||
233 | |||
234 | #[test] | ||
235 | fn test_fill_struct_fields_partial() { | ||
236 | check_fix( | ||
237 | r#" | ||
238 | struct TestStruct { one: i32, two: i64 } | ||
239 | |||
240 | fn test_fn() { | ||
241 | let s = TestStruct{ two: 2$0 }; | ||
242 | } | ||
243 | "#, | ||
244 | r" | ||
245 | struct TestStruct { one: i32, two: i64 } | ||
246 | |||
247 | fn test_fn() { | ||
248 | let s = TestStruct{ two: 2, one: () }; | ||
249 | } | ||
250 | ", | ||
251 | ); | ||
252 | } | ||
253 | |||
254 | #[test] | ||
255 | fn test_fill_struct_fields_raw_ident() { | ||
256 | check_fix( | ||
257 | r#" | ||
258 | struct TestStruct { r#type: u8 } | ||
259 | |||
260 | fn test_fn() { | ||
261 | TestStruct { $0 }; | ||
262 | } | ||
263 | "#, | ||
264 | r" | ||
265 | struct TestStruct { r#type: u8 } | ||
266 | |||
267 | fn test_fn() { | ||
268 | TestStruct { r#type: () }; | ||
269 | } | ||
270 | ", | ||
271 | ); | ||
272 | } | ||
273 | |||
274 | #[test] | ||
275 | fn test_fill_struct_fields_no_diagnostic() { | ||
276 | check_diagnostics( | ||
277 | r#" | ||
278 | struct TestStruct { one: i32, two: i64 } | ||
279 | |||
280 | fn test_fn() { | ||
281 | let one = 1; | ||
282 | let s = TestStruct{ one, two: 2 }; | ||
283 | } | ||
284 | "#, | ||
285 | ); | ||
286 | } | ||
287 | |||
288 | #[test] | ||
289 | fn test_fill_struct_fields_no_diagnostic_on_spread() { | ||
290 | check_diagnostics( | ||
291 | r#" | ||
292 | struct TestStruct { one: i32, two: i64 } | ||
293 | |||
294 | fn test_fn() { | ||
295 | let one = 1; | ||
296 | let s = TestStruct{ ..a }; | ||
297 | } | ||
298 | "#, | ||
299 | ); | ||
300 | } | ||
301 | |||
302 | #[test] | ||
303 | fn test_fill_struct_fields_blank_line() { | ||
304 | check_fix( | ||
305 | r#" | ||
306 | struct S { a: (), b: () } | ||
307 | |||
308 | fn f() { | ||
309 | S { | ||
310 | $0 | ||
311 | }; | ||
312 | } | ||
313 | "#, | ||
314 | r#" | ||
315 | struct S { a: (), b: () } | ||
316 | |||
317 | fn f() { | ||
318 | S { | ||
319 | a: (), | ||
320 | b: (), | ||
321 | }; | ||
322 | } | ||
323 | "#, | ||
324 | ); | ||
325 | } | ||
326 | |||
327 | #[test] | ||
328 | fn import_extern_crate_clash_with_inner_item() { | ||
329 | // This is more of a resolver test, but doesn't really work with the hir_def testsuite. | ||
330 | |||
331 | check_diagnostics( | ||
332 | r#" | ||
333 | //- /lib.rs crate:lib deps:jwt | ||
334 | mod permissions; | ||
335 | |||
336 | use permissions::jwt; | ||
337 | |||
338 | fn f() { | ||
339 | fn inner() {} | ||
340 | jwt::Claims {}; // should resolve to the local one with 0 fields, and not get a diagnostic | ||
341 | } | ||
342 | |||
343 | //- /permissions.rs | ||
344 | pub mod jwt { | ||
345 | pub struct Claims {} | ||
346 | } | ||
347 | |||
348 | //- /jwt/lib.rs crate:jwt | ||
349 | pub struct Claims { | ||
350 | field: u8, | ||
351 | } | ||
352 | "#, | ||
353 | ); | ||
354 | } | ||
355 | } | ||
diff --git a/crates/ide_diagnostics/src/handlers/missing_match_arms.rs b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs new file mode 100644 index 000000000..947b0f2e2 --- /dev/null +++ b/crates/ide_diagnostics/src/handlers/missing_match_arms.rs | |||
@@ -0,0 +1,929 @@ | |||
1 | use hir::InFile; | ||
2 | |||
3 | use crate::{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(crate) 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 | mod tests { | ||
21 | use crate::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::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 | //^^ error: missing match arm | ||
35 | match (()) { } | ||
36 | //^^^^ error: 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 | //^^^^^^^^ error: 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 | //^^^^^ error: missing match arm | ||
67 | match false { true => (), } | ||
68 | //^^^^^ error: missing match arm | ||
69 | match (false, true) {} | ||
70 | //^^^^^^^^^^^^^ error: missing match arm | ||
71 | match (false, true) { (true, true) => (), } | ||
72 | //^^^^^^^^^^^^^ error: missing match arm | ||
73 | match (false, true) { | ||
74 | //^^^^^^^^^^^^^ error: missing match arm | ||
75 | (false, true) => (), | ||
76 | (false, false) => (), | ||
77 | (true, false) => (), | ||
78 | } | ||
79 | match (false, true) { (true, _x) => (), } | ||
80 | //^^^^^^^^^^^^^ error: 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 | //^^^^^^^^^^^^^^^^^^^^ error: missing match arm | ||
120 | match (false, ((), false)) { (true, ((), true)) => (), } | ||
121 | //^^^^^^^^^^^^^^^^^^^^ error: missing match arm | ||
122 | match (false, ((), false)) { (true, _) => (), } | ||
123 | //^^^^^^^^^^^^^^^^^^^^ error: 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 | //^^^^^^^^^ error: missing match arm | ||
150 | match Either::B { Either::A => (), } | ||
151 | //^^^^^^^^^ error: missing match arm | ||
152 | |||
153 | match &Either::B { | ||
154 | //^^^^^^^^^^ error: 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 | //^^^^^^^^^ error: missing match arm | ||
178 | match Either::B { | ||
179 | //^^^^^^^^^ error: 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 | //^^^^^^^^^^^^^^^^ error: 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 | //^^^^^^^^^^^^^^^^^^^^^ error: 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 | //^ error: missing match arm | ||
394 | match a { Either::A { foo: true } => () } | ||
395 | //^ error: missing match arm | ||
396 | match a { | ||
397 | Either::A { } => (), | ||
398 | //^^^^^^^^^ error: missing structure fields: | ||
399 | // | - foo | ||
400 | Either::B => (), | ||
401 | } | ||
402 | match a { | ||
403 | //^ error: missing match arm | ||
404 | Either::A { } => (), | ||
405 | } //^^^^^^^^^ error: 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 | //^ error: 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 | //^ error: missing match arm | ||
462 | Either::A { foo: true, .. } => (), | ||
463 | Either::B => (), | ||
464 | } | ||
465 | match a { | ||
466 | //^ error: 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 | //^^^^^^^^^ error: 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 | //^^^^^^^^^ error: 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 | //^^^^^ error: 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 | //^^^^^^^^^^^^^^^^^^^^^ error: 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 | //^^^^^^^^^^^^^^^^^^^^ error: 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 | //^^^^^^^^^^^^^^^^^^^^ error: 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 | //^^^^^^^^^^^^^^^^^^^^ error: 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 | //^ error: missing match arm | ||
618 | match f { Foo { a: true } => () } | ||
619 | //^ error: missing match arm | ||
620 | match &f { Foo { a: true } => () } | ||
621 | //^^ error: 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 | //^ error: missing match arm | ||
643 | match f { Foo(true) => () } | ||
644 | //^ error: 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 | //^ error: 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 | //^ error: missing match arm | ||
674 | match f { | ||
675 | //^ error: 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 | //^^^^^^^^^ error: 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 | //^ error: 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 | //^^^^ error: 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 | //^^^^ error: missing match arm | ||
790 | E::A => {} | ||
791 | E::B => {} | ||
792 | } | ||
793 | match E::A { | ||
794 | //^^^^ error: 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 | //^^^^ error: 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_diagnostics/src/handlers/missing_ok_or_some_in_tail_expr.rs index 715a403b9..63de54570 100644 --- a/crates/ide/src/diagnostics/fixes/wrap_tail_expr.rs +++ b/crates/ide_diagnostics/src/handlers/missing_ok_or_some_in_tail_expr.rs | |||
@@ -1,31 +1,49 @@ | |||
1 | use hir::{db::AstDatabase, diagnostics::MissingOkOrSomeInTailExpr, Semantics}; | 1 | use hir::db::AstDatabase; |
2 | use ide_assists::{Assist, AssistResolveStrategy}; | 2 | use ide_db::{assists::Assist, source_change::SourceChange}; |
3 | use ide_db::{source_change::SourceChange, RootDatabase}; | ||
4 | use syntax::AstNode; | 3 | use syntax::AstNode; |
5 | use text_edit::TextEdit; | 4 | use text_edit::TextEdit; |
6 | 5 | ||
7 | use crate::diagnostics::{fix, DiagnosticWithFixes}; | 6 | use crate::{fix, Diagnostic, DiagnosticsContext}; |
8 | 7 | ||
9 | impl DiagnosticWithFixes for MissingOkOrSomeInTailExpr { | 8 | // Diagnostic: missing-ok-or-some-in-tail-expr |
10 | fn fixes( | 9 | // |
11 | &self, | 10 | // This diagnostic is triggered if a block that should return `Result` returns a value not wrapped in `Ok`, |
12 | sema: &Semantics<RootDatabase>, | 11 | // or if a block that should return `Option` returns a value not wrapped in `Some`. |
13 | _resolve: &AssistResolveStrategy, | 12 | // |
14 | ) -> Option<Vec<Assist>> { | 13 | // Example: |
15 | let root = sema.db.parse_or_expand(self.file)?; | 14 | // |
16 | let tail_expr = self.expr.to_node(&root); | 15 | // ```rust |
17 | let tail_expr_range = tail_expr.syntax().text_range(); | 16 | // fn foo() -> Result<u8, ()> { |
18 | let replacement = format!("{}({})", self.required, tail_expr.syntax()); | 17 | // 10 |
19 | let edit = TextEdit::replace(tail_expr_range, replacement); | 18 | // } |
20 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); | 19 | // ``` |
21 | let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" }; | 20 | pub(crate) fn missing_ok_or_some_in_tail_expr( |
22 | Some(vec![fix("wrap_tail_expr", name, source_change, tail_expr_range)]) | 21 | ctx: &DiagnosticsContext<'_>, |
23 | } | 22 | d: &hir::MissingOkOrSomeInTailExpr, |
23 | ) -> Diagnostic { | ||
24 | Diagnostic::new( | ||
25 | "missing-ok-or-some-in-tail-expr", | ||
26 | format!("wrap return expression in {}", d.required), | ||
27 | ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, | ||
28 | ) | ||
29 | .with_fixes(fixes(ctx, d)) | ||
30 | } | ||
31 | |||
32 | fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingOkOrSomeInTailExpr) -> Option<Vec<Assist>> { | ||
33 | let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?; | ||
34 | let tail_expr = d.expr.value.to_node(&root); | ||
35 | let tail_expr_range = tail_expr.syntax().text_range(); | ||
36 | let replacement = format!("{}({})", d.required, tail_expr.syntax()); | ||
37 | let edit = TextEdit::replace(tail_expr_range, replacement); | ||
38 | let source_change = | ||
39 | SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit); | ||
40 | let name = if d.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" }; | ||
41 | Some(vec![fix("wrap_tail_expr", name, source_change, tail_expr_range)]) | ||
24 | } | 42 | } |
25 | 43 | ||
26 | #[cfg(test)] | 44 | #[cfg(test)] |
27 | mod tests { | 45 | mod tests { |
28 | use crate::diagnostics::tests::{check_fix, check_no_diagnostics}; | 46 | use crate::tests::{check_diagnostics, check_fix}; |
29 | 47 | ||
30 | #[test] | 48 | #[test] |
31 | fn test_wrap_return_type_option() { | 49 | fn test_wrap_return_type_option() { |
@@ -169,7 +187,7 @@ fn div(x: i32, y: i32) -> MyResult<i32> { | |||
169 | 187 | ||
170 | #[test] | 188 | #[test] |
171 | fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { | 189 | fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { |
172 | check_no_diagnostics( | 190 | check_diagnostics( |
173 | r#" | 191 | r#" |
174 | //- /main.rs crate:main deps:core | 192 | //- /main.rs crate:main deps:core |
175 | use core::result::Result::{self, Ok, Err}; | 193 | use core::result::Result::{self, Ok, Err}; |
@@ -189,7 +207,7 @@ pub mod option { | |||
189 | 207 | ||
190 | #[test] | 208 | #[test] |
191 | fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() { | 209 | fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() { |
192 | check_no_diagnostics( | 210 | check_diagnostics( |
193 | r#" | 211 | r#" |
194 | //- /main.rs crate:main deps:core | 212 | //- /main.rs crate:main deps:core |
195 | use core::result::Result::{self, Ok, Err}; | 213 | use core::result::Result::{self, Ok, Err}; |
diff --git a/crates/ide_diagnostics/src/handlers/missing_unsafe.rs b/crates/ide_diagnostics/src/handlers/missing_unsafe.rs new file mode 100644 index 000000000..7acd9228a --- /dev/null +++ b/crates/ide_diagnostics/src/handlers/missing_unsafe.rs | |||
@@ -0,0 +1,101 @@ | |||
1 | use crate::{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(crate) 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::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 | } //^^ error: 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 | //^^^^^^^^^^^ error: this operation is unsafe and requires an unsafe function or block | ||
52 | HasUnsafe.unsafe_fn(); | ||
53 | //^^^^^^^^^^^^^^^^^^^^^ error: 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 | //^^^^^^^^^^ error: 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 | //^^^^^^^^^^^^^^ error: 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_diagnostics/src/handlers/no_such_field.rs index a5f457dce..92e8867f4 100644 --- a/crates/ide/src/diagnostics/fixes/create_field.rs +++ b/crates/ide_diagnostics/src/handlers/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}, |
@@ -6,23 +6,27 @@ use syntax::{ | |||
6 | }; | 6 | }; |
7 | use text_edit::TextEdit; | 7 | use text_edit::TextEdit; |
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{fix, Assist, Diagnostic, DiagnosticsContext}; |
10 | diagnostics::{fix, DiagnosticWithFixes}, | 10 | |
11 | Assist, AssistResolveStrategy, | 11 | // Diagnostic: no-such-field |
12 | }; | 12 | // |
13 | impl DiagnosticWithFixes for NoSuchField { | 13 | // This diagnostic is triggered if created structure does not have field provided in record. |
14 | fn fixes( | 14 | pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic { |
15 | &self, | 15 | Diagnostic::new( |
16 | sema: &Semantics<RootDatabase>, | 16 | "no-such-field", |
17 | _resolve: &AssistResolveStrategy, | 17 | "no such field", |
18 | ) -> Option<Vec<Assist>> { | 18 | ctx.sema.diagnostics_display_range(d.field.clone().map(|it| it.into())).range, |
19 | let root = sema.db.parse_or_expand(self.file)?; | 19 | ) |
20 | missing_record_expr_field_fixes( | 20 | .with_fixes(fixes(ctx, d)) |
21 | &sema, | 21 | } |
22 | self.file.original_file(sema.db), | 22 | |
23 | &self.field.to_node(&root), | 23 | fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assist>> { |
24 | ) | 24 | let root = ctx.sema.db.parse_or_expand(d.field.file_id)?; |
25 | } | 25 | missing_record_expr_field_fixes( |
26 | &ctx.sema, | ||
27 | d.field.file_id.original_file(ctx.sema.db), | ||
28 | &d.field.value.to_node(&root), | ||
29 | ) | ||
26 | } | 30 | } |
27 | 31 | ||
28 | fn missing_record_expr_field_fixes( | 32 | fn missing_record_expr_field_fixes( |
@@ -105,7 +109,130 @@ fn missing_record_expr_field_fixes( | |||
105 | 109 | ||
106 | #[cfg(test)] | 110 | #[cfg(test)] |
107 | mod tests { | 111 | mod tests { |
108 | use crate::diagnostics::tests::check_fix; | 112 | use crate::tests::{check_diagnostics, check_fix}; |
113 | |||
114 | #[test] | ||
115 | fn no_such_field_diagnostics() { | ||
116 | check_diagnostics( | ||
117 | r#" | ||
118 | struct S { foo: i32, bar: () } | ||
119 | impl S { | ||
120 | fn new() -> S { | ||
121 | S { | ||
122 | //^ 💡 error: missing structure fields: | ||
123 | //| - bar | ||
124 | foo: 92, | ||
125 | baz: 62, | ||
126 | //^^^^^^^ 💡 error: no such field | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | "#, | ||
131 | ); | ||
132 | } | ||
133 | #[test] | ||
134 | fn no_such_field_with_feature_flag_diagnostics() { | ||
135 | check_diagnostics( | ||
136 | r#" | ||
137 | //- /lib.rs crate:foo cfg:feature=foo | ||
138 | struct MyStruct { | ||
139 | my_val: usize, | ||
140 | #[cfg(feature = "foo")] | ||
141 | bar: bool, | ||
142 | } | ||
143 | |||
144 | impl MyStruct { | ||
145 | #[cfg(feature = "foo")] | ||
146 | pub(crate) fn new(my_val: usize, bar: bool) -> Self { | ||
147 | Self { my_val, bar } | ||
148 | } | ||
149 | #[cfg(not(feature = "foo"))] | ||
150 | pub(crate) fn new(my_val: usize, _bar: bool) -> Self { | ||
151 | Self { my_val } | ||
152 | } | ||
153 | } | ||
154 | "#, | ||
155 | ); | ||
156 | } | ||
157 | |||
158 | #[test] | ||
159 | fn no_such_field_enum_with_feature_flag_diagnostics() { | ||
160 | check_diagnostics( | ||
161 | r#" | ||
162 | //- /lib.rs crate:foo cfg:feature=foo | ||
163 | enum Foo { | ||
164 | #[cfg(not(feature = "foo"))] | ||
165 | Buz, | ||
166 | #[cfg(feature = "foo")] | ||
167 | Bar, | ||
168 | Baz | ||
169 | } | ||
170 | |||
171 | fn test_fn(f: Foo) { | ||
172 | match f { | ||
173 | Foo::Bar => {}, | ||
174 | Foo::Baz => {}, | ||
175 | } | ||
176 | } | ||
177 | "#, | ||
178 | ); | ||
179 | } | ||
180 | |||
181 | #[test] | ||
182 | fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() { | ||
183 | check_diagnostics( | ||
184 | r#" | ||
185 | //- /lib.rs crate:foo cfg:feature=foo | ||
186 | struct S { | ||
187 | #[cfg(feature = "foo")] | ||
188 | foo: u32, | ||
189 | #[cfg(not(feature = "foo"))] | ||
190 | bar: u32, | ||
191 | } | ||
192 | |||
193 | impl S { | ||
194 | #[cfg(feature = "foo")] | ||
195 | fn new(foo: u32) -> Self { | ||
196 | Self { foo } | ||
197 | } | ||
198 | #[cfg(not(feature = "foo"))] | ||
199 | fn new(bar: u32) -> Self { | ||
200 | Self { bar } | ||
201 | } | ||
202 | fn new2(bar: u32) -> Self { | ||
203 | #[cfg(feature = "foo")] | ||
204 | { Self { foo: bar } } | ||
205 | #[cfg(not(feature = "foo"))] | ||
206 | { Self { bar } } | ||
207 | } | ||
208 | fn new2(val: u32) -> Self { | ||
209 | Self { | ||
210 | #[cfg(feature = "foo")] | ||
211 | foo: val, | ||
212 | #[cfg(not(feature = "foo"))] | ||
213 | bar: val, | ||
214 | } | ||
215 | } | ||
216 | } | ||
217 | "#, | ||
218 | ); | ||
219 | } | ||
220 | |||
221 | #[test] | ||
222 | fn no_such_field_with_type_macro() { | ||
223 | check_diagnostics( | ||
224 | r#" | ||
225 | macro_rules! Type { () => { u32 }; } | ||
226 | struct Foo { bar: Type![] } | ||
227 | |||
228 | impl Foo { | ||
229 | fn new() -> Self { | ||
230 | Foo { bar: 0 } | ||
231 | } | ||
232 | } | ||
233 | "#, | ||
234 | ); | ||
235 | } | ||
109 | 236 | ||
110 | #[test] | 237 | #[test] |
111 | fn test_add_field_from_usage() { | 238 | fn test_add_field_from_usage() { |
diff --git a/crates/ide_diagnostics/src/handlers/remove_this_semicolon.rs b/crates/ide_diagnostics/src/handlers/remove_this_semicolon.rs new file mode 100644 index 000000000..4e639f214 --- /dev/null +++ b/crates/ide_diagnostics/src/handlers/remove_this_semicolon.rs | |||
@@ -0,0 +1,61 @@ | |||
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::{fix, Assist, Diagnostic, DiagnosticsContext}; | ||
7 | |||
8 | // Diagnostic: remove-this-semicolon | ||
9 | // | ||
10 | // This diagnostic is triggered when there's an erroneous `;` at the end of the block. | ||
11 | pub(crate) fn remove_this_semicolon( | ||
12 | ctx: &DiagnosticsContext<'_>, | ||
13 | d: &hir::RemoveThisSemicolon, | ||
14 | ) -> Diagnostic { | ||
15 | Diagnostic::new( | ||
16 | "remove-this-semicolon", | ||
17 | "remove this semicolon", | ||
18 | ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, | ||
19 | ) | ||
20 | .with_fixes(fixes(ctx, d)) | ||
21 | } | ||
22 | |||
23 | fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::RemoveThisSemicolon) -> Option<Vec<Assist>> { | ||
24 | let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?; | ||
25 | |||
26 | let semicolon = d | ||
27 | .expr | ||
28 | .value | ||
29 | .to_node(&root) | ||
30 | .syntax() | ||
31 | .parent() | ||
32 | .and_then(ast::ExprStmt::cast) | ||
33 | .and_then(|expr| expr.semicolon_token())? | ||
34 | .text_range(); | ||
35 | |||
36 | let edit = TextEdit::delete(semicolon); | ||
37 | let source_change = | ||
38 | SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit); | ||
39 | |||
40 | Some(vec![fix("remove_semicolon", "Remove this semicolon", source_change, semicolon)]) | ||
41 | } | ||
42 | |||
43 | #[cfg(test)] | ||
44 | mod tests { | ||
45 | use crate::tests::{check_diagnostics, check_fix}; | ||
46 | |||
47 | #[test] | ||
48 | fn missing_semicolon() { | ||
49 | check_diagnostics( | ||
50 | r#" | ||
51 | fn test() -> i32 { 123; } | ||
52 | //^^^ 💡 error: remove this semicolon | ||
53 | "#, | ||
54 | ); | ||
55 | } | ||
56 | |||
57 | #[test] | ||
58 | fn remove_semicolon() { | ||
59 | check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#); | ||
60 | } | ||
61 | } | ||
diff --git a/crates/ide_diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/crates/ide_diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs new file mode 100644 index 000000000..cd87a10bb --- /dev/null +++ b/crates/ide_diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs | |||
@@ -0,0 +1,179 @@ | |||
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::{fix, Assist, Diagnostic, DiagnosticsContext, Severity}; | ||
10 | |||
11 | // Diagnostic: replace-filter-map-next-with-find-map | ||
12 | // | ||
13 | // This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`. | ||
14 | pub(crate) fn replace_filter_map_next_with_find_map( | ||
15 | ctx: &DiagnosticsContext<'_>, | ||
16 | d: &hir::ReplaceFilterMapNextWithFindMap, | ||
17 | ) -> Diagnostic { | ||
18 | Diagnostic::new( | ||
19 | "replace-filter-map-next-with-find-map", | ||
20 | "replace filter_map(..).next() with find_map(..)", | ||
21 | ctx.sema.diagnostics_display_range(InFile::new(d.file, d.next_expr.clone().into())).range, | ||
22 | ) | ||
23 | .severity(Severity::WeakWarning) | ||
24 | .with_fixes(fixes(ctx, d)) | ||
25 | } | ||
26 | |||
27 | fn fixes( | ||
28 | ctx: &DiagnosticsContext<'_>, | ||
29 | d: &hir::ReplaceFilterMapNextWithFindMap, | ||
30 | ) -> Option<Vec<Assist>> { | ||
31 | let root = ctx.sema.db.parse_or_expand(d.file)?; | ||
32 | let next_expr = d.next_expr.to_node(&root); | ||
33 | let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?; | ||
34 | |||
35 | let filter_map_call = ast::MethodCallExpr::cast(next_call.receiver()?.syntax().clone())?; | ||
36 | let filter_map_name_range = filter_map_call.name_ref()?.ident_token()?.text_range(); | ||
37 | let filter_map_args = filter_map_call.arg_list()?; | ||
38 | |||
39 | let range_to_replace = | ||
40 | TextRange::new(filter_map_name_range.start(), next_expr.syntax().text_range().end()); | ||
41 | let replacement = format!("find_map{}", filter_map_args.syntax().text()); | ||
42 | let trigger_range = next_expr.syntax().text_range(); | ||
43 | |||
44 | let edit = TextEdit::replace(range_to_replace, replacement); | ||
45 | |||
46 | let source_change = SourceChange::from_text_edit(d.file.original_file(ctx.sema.db), edit); | ||
47 | |||
48 | Some(vec![fix( | ||
49 | "replace_with_find_map", | ||
50 | "Replace filter_map(..).next() with find_map()", | ||
51 | source_change, | ||
52 | trigger_range, | ||
53 | )]) | ||
54 | } | ||
55 | |||
56 | #[cfg(test)] | ||
57 | mod tests { | ||
58 | use crate::tests::check_fix; | ||
59 | |||
60 | // Register the required standard library types to make the tests work | ||
61 | #[track_caller] | ||
62 | fn check_diagnostics(ra_fixture: &str) { | ||
63 | let prefix = r#" | ||
64 | //- /main.rs crate:main deps:core | ||
65 | use core::iter::Iterator; | ||
66 | use core::option::Option::{self, Some, None}; | ||
67 | "#; | ||
68 | let suffix = r#" | ||
69 | //- /core/lib.rs crate:core | ||
70 | pub mod option { | ||
71 | pub enum Option<T> { Some(T), None } | ||
72 | } | ||
73 | pub mod iter { | ||
74 | pub trait Iterator { | ||
75 | type Item; | ||
76 | fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap } | ||
77 | fn next(&mut self) -> Option<Self::Item>; | ||
78 | } | ||
79 | pub struct FilterMap {} | ||
80 | impl Iterator for FilterMap { | ||
81 | type Item = i32; | ||
82 | fn next(&mut self) -> i32 { 7 } | ||
83 | } | ||
84 | } | ||
85 | "#; | ||
86 | crate::tests::check_diagnostics(&format!("{}{}{}", prefix, ra_fixture, suffix)) | ||
87 | } | ||
88 | |||
89 | #[test] | ||
90 | fn replace_filter_map_next_with_find_map2() { | ||
91 | check_diagnostics( | ||
92 | r#" | ||
93 | fn foo() { | ||
94 | let m = [1, 2, 3].iter().filter_map(|x| Some(92)).next(); | ||
95 | } //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: replace filter_map(..).next() with find_map(..) | ||
96 | "#, | ||
97 | ); | ||
98 | } | ||
99 | |||
100 | #[test] | ||
101 | fn replace_filter_map_next_with_find_map_no_diagnostic_without_next() { | ||
102 | check_diagnostics( | ||
103 | r#" | ||
104 | fn foo() { | ||
105 | let m = [1, 2, 3] | ||
106 | .iter() | ||
107 | .filter_map(|x| Some(92)) | ||
108 | .len(); | ||
109 | } | ||
110 | "#, | ||
111 | ); | ||
112 | } | ||
113 | |||
114 | #[test] | ||
115 | fn replace_filter_map_next_with_find_map_no_diagnostic_with_intervening_methods() { | ||
116 | check_diagnostics( | ||
117 | r#" | ||
118 | fn foo() { | ||
119 | let m = [1, 2, 3] | ||
120 | .iter() | ||
121 | .filter_map(|x| Some(92)) | ||
122 | .map(|x| x + 2) | ||
123 | .len(); | ||
124 | } | ||
125 | "#, | ||
126 | ); | ||
127 | } | ||
128 | |||
129 | #[test] | ||
130 | fn replace_filter_map_next_with_find_map_no_diagnostic_if_not_in_chain() { | ||
131 | check_diagnostics( | ||
132 | r#" | ||
133 | fn foo() { | ||
134 | let m = [1, 2, 3] | ||
135 | .iter() | ||
136 | .filter_map(|x| Some(92)); | ||
137 | let n = m.next(); | ||
138 | } | ||
139 | "#, | ||
140 | ); | ||
141 | } | ||
142 | |||
143 | #[test] | ||
144 | fn replace_with_wind_map() { | ||
145 | check_fix( | ||
146 | r#" | ||
147 | //- /main.rs crate:main deps:core | ||
148 | use core::iter::Iterator; | ||
149 | use core::option::Option::{self, Some, None}; | ||
150 | fn foo() { | ||
151 | let m = [1, 2, 3].iter().$0filter_map(|x| Some(92)).next(); | ||
152 | } | ||
153 | //- /core/lib.rs crate:core | ||
154 | pub mod option { | ||
155 | pub enum Option<T> { Some(T), None } | ||
156 | } | ||
157 | pub mod iter { | ||
158 | pub trait Iterator { | ||
159 | type Item; | ||
160 | fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap } | ||
161 | fn next(&mut self) -> Option<Self::Item>; | ||
162 | } | ||
163 | pub struct FilterMap {} | ||
164 | impl Iterator for FilterMap { | ||
165 | type Item = i32; | ||
166 | fn next(&mut self) -> i32 { 7 } | ||
167 | } | ||
168 | } | ||
169 | "#, | ||
170 | r#" | ||
171 | use core::iter::Iterator; | ||
172 | use core::option::Option::{self, Some, None}; | ||
173 | fn foo() { | ||
174 | let m = [1, 2, 3].iter().find_map(|x| Some(92)); | ||
175 | } | ||
176 | "#, | ||
177 | ) | ||
178 | } | ||
179 | } | ||
diff --git a/crates/ide_diagnostics/src/handlers/unimplemented_builtin_macro.rs b/crates/ide_diagnostics/src/handlers/unimplemented_builtin_macro.rs new file mode 100644 index 000000000..e879de75c --- /dev/null +++ b/crates/ide_diagnostics/src/handlers/unimplemented_builtin_macro.rs | |||
@@ -0,0 +1,16 @@ | |||
1 | use crate::{Diagnostic, DiagnosticsContext, Severity}; | ||
2 | |||
3 | // Diagnostic: unimplemented-builtin-macro | ||
4 | // | ||
5 | // This diagnostic is shown for builtin macros which are not yet implemented by rust-analyzer | ||
6 | pub(crate) fn unimplemented_builtin_macro( | ||
7 | ctx: &DiagnosticsContext<'_>, | ||
8 | d: &hir::UnimplementedBuiltinMacro, | ||
9 | ) -> Diagnostic { | ||
10 | Diagnostic::new( | ||
11 | "unimplemented-builtin-macro", | ||
12 | "unimplemented built-in macro".to_string(), | ||
13 | ctx.sema.diagnostics_display_range(d.node.clone()).range, | ||
14 | ) | ||
15 | .severity(Severity::WeakWarning) | ||
16 | } | ||
diff --git a/crates/ide_diagnostics/src/handlers/unlinked_file.rs b/crates/ide_diagnostics/src/handlers/unlinked_file.rs new file mode 100644 index 000000000..8e601fa48 --- /dev/null +++ b/crates/ide_diagnostics/src/handlers/unlinked_file.rs | |||
@@ -0,0 +1,298 @@ | |||
1 | //! Diagnostic emitted for files that aren't part of any crate. | ||
2 | |||
3 | use hir::db::DefDatabase; | ||
4 | use ide_db::{ | ||
5 | base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt}, | ||
6 | source_change::SourceChange, | ||
7 | RootDatabase, | ||
8 | }; | ||
9 | use syntax::{ | ||
10 | ast::{self, ModuleItemOwner, NameOwner}, | ||
11 | AstNode, TextRange, TextSize, | ||
12 | }; | ||
13 | use text_edit::TextEdit; | ||
14 | |||
15 | use crate::{fix, Assist, Diagnostic, DiagnosticsContext}; | ||
16 | |||
17 | // Diagnostic: unlinked-file | ||
18 | // | ||
19 | // This diagnostic is shown for files that are not included in any crate, or files that are part of | ||
20 | // crates rust-analyzer failed to discover. The file will not have IDE features available. | ||
21 | pub(crate) fn unlinked_file(ctx: &DiagnosticsContext, acc: &mut Vec<Diagnostic>, file_id: FileId) { | ||
22 | // Limit diagnostic to the first few characters in the file. This matches how VS Code | ||
23 | // renders it with the full span, but on other editors, and is less invasive. | ||
24 | let range = ctx.sema.db.parse(file_id).syntax_node().text_range(); | ||
25 | // FIXME: This is wrong if one of the first three characters is not ascii: `//Ы`. | ||
26 | let range = range.intersect(TextRange::up_to(TextSize::of("..."))).unwrap_or(range); | ||
27 | |||
28 | acc.push( | ||
29 | Diagnostic::new("unlinked-file", "file not included in module tree", range) | ||
30 | .with_fixes(fixes(ctx, file_id)), | ||
31 | ); | ||
32 | } | ||
33 | |||
34 | fn fixes(ctx: &DiagnosticsContext, file_id: FileId) -> Option<Vec<Assist>> { | ||
35 | // If there's an existing module that could add `mod` or `pub mod` items to include the unlinked file, | ||
36 | // suggest that as a fix. | ||
37 | |||
38 | let source_root = ctx.sema.db.source_root(ctx.sema.db.file_source_root(file_id)); | ||
39 | let our_path = source_root.path_for_file(&file_id)?; | ||
40 | let module_name = our_path.name_and_extension()?.0; | ||
41 | |||
42 | // Candidates to look for: | ||
43 | // - `mod.rs` in the same folder | ||
44 | // - we also check `main.rs` and `lib.rs` | ||
45 | // - `$dir.rs` in the parent folder, where `$dir` is the directory containing `self.file_id` | ||
46 | let parent = our_path.parent()?; | ||
47 | let mut paths = vec![parent.join("mod.rs")?, parent.join("lib.rs")?, parent.join("main.rs")?]; | ||
48 | |||
49 | // `submod/bla.rs` -> `submod.rs` | ||
50 | if let Some(newmod) = (|| { | ||
51 | let name = parent.name_and_extension()?.0; | ||
52 | parent.parent()?.join(&format!("{}.rs", name)) | ||
53 | })() { | ||
54 | paths.push(newmod); | ||
55 | } | ||
56 | |||
57 | for path in &paths { | ||
58 | if let Some(parent_id) = source_root.file_for_path(path) { | ||
59 | for krate in ctx.sema.db.relevant_crates(*parent_id).iter() { | ||
60 | let crate_def_map = ctx.sema.db.crate_def_map(*krate); | ||
61 | for (_, module) in crate_def_map.modules() { | ||
62 | if module.origin.is_inline() { | ||
63 | // We don't handle inline `mod parent {}`s, they use different paths. | ||
64 | continue; | ||
65 | } | ||
66 | |||
67 | if module.origin.file_id() == Some(*parent_id) { | ||
68 | return make_fixes(ctx.sema.db, *parent_id, module_name, file_id); | ||
69 | } | ||
70 | } | ||
71 | } | ||
72 | } | ||
73 | } | ||
74 | |||
75 | None | ||
76 | } | ||
77 | |||
78 | fn make_fixes( | ||
79 | db: &RootDatabase, | ||
80 | parent_file_id: FileId, | ||
81 | new_mod_name: &str, | ||
82 | added_file_id: FileId, | ||
83 | ) -> Option<Vec<Assist>> { | ||
84 | fn is_outline_mod(item: &ast::Item) -> bool { | ||
85 | matches!(item, ast::Item::Module(m) if m.item_list().is_none()) | ||
86 | } | ||
87 | |||
88 | let mod_decl = format!("mod {};", new_mod_name); | ||
89 | let pub_mod_decl = format!("pub mod {};", new_mod_name); | ||
90 | |||
91 | let ast: ast::SourceFile = db.parse(parent_file_id).tree(); | ||
92 | |||
93 | let mut mod_decl_builder = TextEdit::builder(); | ||
94 | let mut pub_mod_decl_builder = TextEdit::builder(); | ||
95 | |||
96 | // If there's an existing `mod m;` statement matching the new one, don't emit a fix (it's | ||
97 | // probably `#[cfg]`d out). | ||
98 | for item in ast.items() { | ||
99 | if let ast::Item::Module(m) = item { | ||
100 | if let Some(name) = m.name() { | ||
101 | if m.item_list().is_none() && name.to_string() == new_mod_name { | ||
102 | cov_mark::hit!(unlinked_file_skip_fix_when_mod_already_exists); | ||
103 | return None; | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 | // If there are existing `mod m;` items, append after them (after the first group of them, rather). | ||
110 | match ast | ||
111 | .items() | ||
112 | .skip_while(|item| !is_outline_mod(item)) | ||
113 | .take_while(|item| is_outline_mod(item)) | ||
114 | .last() | ||
115 | { | ||
116 | Some(last) => { | ||
117 | cov_mark::hit!(unlinked_file_append_to_existing_mods); | ||
118 | let offset = last.syntax().text_range().end(); | ||
119 | mod_decl_builder.insert(offset, format!("\n{}", mod_decl)); | ||
120 | pub_mod_decl_builder.insert(offset, format!("\n{}", pub_mod_decl)); | ||
121 | } | ||
122 | None => { | ||
123 | // Prepend before the first item in the file. | ||
124 | match ast.items().next() { | ||
125 | Some(item) => { | ||
126 | cov_mark::hit!(unlinked_file_prepend_before_first_item); | ||
127 | let offset = item.syntax().text_range().start(); | ||
128 | mod_decl_builder.insert(offset, format!("{}\n\n", mod_decl)); | ||
129 | pub_mod_decl_builder.insert(offset, format!("{}\n\n", pub_mod_decl)); | ||
130 | } | ||
131 | None => { | ||
132 | // No items in the file, so just append at the end. | ||
133 | cov_mark::hit!(unlinked_file_empty_file); | ||
134 | let offset = ast.syntax().text_range().end(); | ||
135 | mod_decl_builder.insert(offset, format!("{}\n", mod_decl)); | ||
136 | pub_mod_decl_builder.insert(offset, format!("{}\n", pub_mod_decl)); | ||
137 | } | ||
138 | } | ||
139 | } | ||
140 | } | ||
141 | |||
142 | let trigger_range = db.parse(added_file_id).tree().syntax().text_range(); | ||
143 | Some(vec![ | ||
144 | fix( | ||
145 | "add_mod_declaration", | ||
146 | &format!("Insert `{}`", mod_decl), | ||
147 | SourceChange::from_text_edit(parent_file_id, mod_decl_builder.finish()), | ||
148 | trigger_range, | ||
149 | ), | ||
150 | fix( | ||
151 | "add_pub_mod_declaration", | ||
152 | &format!("Insert `{}`", pub_mod_decl), | ||
153 | SourceChange::from_text_edit(parent_file_id, pub_mod_decl_builder.finish()), | ||
154 | trigger_range, | ||
155 | ), | ||
156 | ]) | ||
157 | } | ||
158 | |||
159 | #[cfg(test)] | ||
160 | mod tests { | ||
161 | use crate::tests::{check_diagnostics, check_fix, check_fixes, check_no_fix}; | ||
162 | |||
163 | #[test] | ||
164 | fn unlinked_file_prepend_first_item() { | ||
165 | cov_mark::check!(unlinked_file_prepend_before_first_item); | ||
166 | // Only tests the first one for `pub mod` since the rest are the same | ||
167 | check_fixes( | ||
168 | r#" | ||
169 | //- /main.rs | ||
170 | fn f() {} | ||
171 | //- /foo.rs | ||
172 | $0 | ||
173 | "#, | ||
174 | vec![ | ||
175 | r#" | ||
176 | mod foo; | ||
177 | |||
178 | fn f() {} | ||
179 | "#, | ||
180 | r#" | ||
181 | pub mod foo; | ||
182 | |||
183 | fn f() {} | ||
184 | "#, | ||
185 | ], | ||
186 | ); | ||
187 | } | ||
188 | |||
189 | #[test] | ||
190 | fn unlinked_file_append_mod() { | ||
191 | cov_mark::check!(unlinked_file_append_to_existing_mods); | ||
192 | check_fix( | ||
193 | r#" | ||
194 | //- /main.rs | ||
195 | //! Comment on top | ||
196 | |||
197 | mod preexisting; | ||
198 | |||
199 | mod preexisting2; | ||
200 | |||
201 | struct S; | ||
202 | |||
203 | mod preexisting_bottom;) | ||
204 | //- /foo.rs | ||
205 | $0 | ||
206 | "#, | ||
207 | r#" | ||
208 | //! Comment on top | ||
209 | |||
210 | mod preexisting; | ||
211 | |||
212 | mod preexisting2; | ||
213 | mod foo; | ||
214 | |||
215 | struct S; | ||
216 | |||
217 | mod preexisting_bottom;) | ||
218 | "#, | ||
219 | ); | ||
220 | } | ||
221 | |||
222 | #[test] | ||
223 | fn unlinked_file_insert_in_empty_file() { | ||
224 | cov_mark::check!(unlinked_file_empty_file); | ||
225 | check_fix( | ||
226 | r#" | ||
227 | //- /main.rs | ||
228 | //- /foo.rs | ||
229 | $0 | ||
230 | "#, | ||
231 | r#" | ||
232 | mod foo; | ||
233 | "#, | ||
234 | ); | ||
235 | } | ||
236 | |||
237 | #[test] | ||
238 | fn unlinked_file_old_style_modrs() { | ||
239 | check_fix( | ||
240 | r#" | ||
241 | //- /main.rs | ||
242 | mod submod; | ||
243 | //- /submod/mod.rs | ||
244 | // in mod.rs | ||
245 | //- /submod/foo.rs | ||
246 | $0 | ||
247 | "#, | ||
248 | r#" | ||
249 | // in mod.rs | ||
250 | mod foo; | ||
251 | "#, | ||
252 | ); | ||
253 | } | ||
254 | |||
255 | #[test] | ||
256 | fn unlinked_file_new_style_mod() { | ||
257 | check_fix( | ||
258 | r#" | ||
259 | //- /main.rs | ||
260 | mod submod; | ||
261 | //- /submod.rs | ||
262 | //- /submod/foo.rs | ||
263 | $0 | ||
264 | "#, | ||
265 | r#" | ||
266 | mod foo; | ||
267 | "#, | ||
268 | ); | ||
269 | } | ||
270 | |||
271 | #[test] | ||
272 | fn unlinked_file_with_cfg_off() { | ||
273 | cov_mark::check!(unlinked_file_skip_fix_when_mod_already_exists); | ||
274 | check_no_fix( | ||
275 | r#" | ||
276 | //- /main.rs | ||
277 | #[cfg(never)] | ||
278 | mod foo; | ||
279 | |||
280 | //- /foo.rs | ||
281 | $0 | ||
282 | "#, | ||
283 | ); | ||
284 | } | ||
285 | |||
286 | #[test] | ||
287 | fn unlinked_file_with_cfg_on() { | ||
288 | check_diagnostics( | ||
289 | r#" | ||
290 | //- /main.rs | ||
291 | #[cfg(not(never))] | ||
292 | mod foo; | ||
293 | |||
294 | //- /foo.rs | ||
295 | "#, | ||
296 | ); | ||
297 | } | ||
298 | } | ||
diff --git a/crates/ide_diagnostics/src/handlers/unresolved_extern_crate.rs b/crates/ide_diagnostics/src/handlers/unresolved_extern_crate.rs new file mode 100644 index 000000000..74e4a69c6 --- /dev/null +++ b/crates/ide_diagnostics/src/handlers/unresolved_extern_crate.rs | |||
@@ -0,0 +1,49 @@ | |||
1 | use crate::{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(crate) 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::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 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ error: 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 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ error: 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_diagnostics/src/handlers/unresolved_import.rs b/crates/ide_diagnostics/src/handlers/unresolved_import.rs new file mode 100644 index 000000000..e52a88459 --- /dev/null +++ b/crates/ide_diagnostics/src/handlers/unresolved_import.rs | |||
@@ -0,0 +1,90 @@ | |||
1 | use crate::{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(crate) 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::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 | //^^^^^^^^^^^^^^ error: 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 | //^^^^^^^^^^^ error: unresolved import | ||
47 | |||
48 | use {does_not_exist::*, does_exist}; | ||
49 | //^^^^^^^^^^^^^^^^^ error: unresolved import | ||
50 | |||
51 | use does_not_exist::{ | ||
52 | a, | ||
53 | //^ error: unresolved import | ||
54 | b, | ||
55 | //^ error: unresolved import | ||
56 | c, | ||
57 | //^ error: 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 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ error: 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 | //^^^^^^^^^^^^^^^^^^^ error: unresolved import | ||
81 | } | ||
82 | |||
83 | mod m { | ||
84 | use super::doesnotexist; | ||
85 | //^^^^^^^^^^^^^^^^^^^ error: unresolved import | ||
86 | } | ||
87 | "#, | ||
88 | ); | ||
89 | } | ||
90 | } | ||
diff --git a/crates/ide_diagnostics/src/handlers/unresolved_macro_call.rs b/crates/ide_diagnostics/src/handlers/unresolved_macro_call.rs new file mode 100644 index 000000000..f0f7725db --- /dev/null +++ b/crates/ide_diagnostics/src/handlers/unresolved_macro_call.rs | |||
@@ -0,0 +1,84 @@ | |||
1 | use hir::{db::AstDatabase, InFile}; | ||
2 | use syntax::{AstNode, SyntaxNodePtr}; | ||
3 | |||
4 | use crate::{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(crate) 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::tests::check_diagnostics; | ||
36 | |||
37 | #[test] | ||
38 | fn unresolved_macro_diag() { | ||
39 | check_diagnostics( | ||
40 | r#" | ||
41 | fn f() { | ||
42 | m!(); | ||
43 | } //^ error: 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 | //^^^ error: 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 | //^^ error: 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 | //^^ error: unresolved macro `self::m2!` | ||
81 | "#, | ||
82 | ); | ||
83 | } | ||
84 | } | ||
diff --git a/crates/ide/src/diagnostics/fixes/unresolved_module.rs b/crates/ide_diagnostics/src/handlers/unresolved_module.rs index b3d0283bb..61fc43604 100644 --- a/crates/ide/src/diagnostics/fixes/unresolved_module.rs +++ b/crates/ide_diagnostics/src/handlers/unresolved_module.rs | |||
@@ -1,39 +1,61 @@ | |||
1 | use hir::{db::AstDatabase, diagnostics::UnresolvedModule, Semantics}; | 1 | use hir::db::AstDatabase; |
2 | use ide_assists::{Assist, AssistResolveStrategy}; | 2 | use ide_db::{assists::Assist, base_db::AnchoredPathBuf, source_change::FileSystemEdit}; |
3 | use ide_db::{base_db::AnchoredPathBuf, source_change::FileSystemEdit, RootDatabase}; | ||
4 | use syntax::AstNode; | 3 | use syntax::AstNode; |
5 | 4 | ||
6 | use crate::diagnostics::{fix, DiagnosticWithFixes}; | 5 | use crate::{fix, Diagnostic, DiagnosticsContext}; |
7 | 6 | ||
8 | impl DiagnosticWithFixes for UnresolvedModule { | 7 | // Diagnostic: unresolved-module |
9 | fn fixes( | 8 | // |
10 | &self, | 9 | // This diagnostic is triggered if rust-analyzer is unable to discover referred module. |
11 | sema: &Semantics<RootDatabase>, | 10 | pub(crate) fn unresolved_module( |
12 | _resolve: &AssistResolveStrategy, | 11 | ctx: &DiagnosticsContext<'_>, |
13 | ) -> Option<Vec<Assist>> { | 12 | d: &hir::UnresolvedModule, |
14 | let root = sema.db.parse_or_expand(self.file)?; | 13 | ) -> Diagnostic { |
15 | let unresolved_module = self.decl.to_node(&root); | 14 | Diagnostic::new( |
16 | Some(vec![fix( | 15 | "unresolved-module", |
17 | "create_module", | 16 | "unresolved module", |
18 | "Create module", | 17 | ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range, |
19 | FileSystemEdit::CreateFile { | 18 | ) |
20 | dst: AnchoredPathBuf { | 19 | .with_fixes(fixes(ctx, d)) |
21 | anchor: self.file.original_file(sema.db), | 20 | } |
22 | path: self.candidate.clone(), | 21 | |
23 | }, | 22 | fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule) -> Option<Vec<Assist>> { |
24 | initial_contents: "".to_string(), | 23 | let root = ctx.sema.db.parse_or_expand(d.decl.file_id)?; |
25 | } | 24 | let unresolved_module = d.decl.value.to_node(&root); |
26 | .into(), | 25 | Some(vec![fix( |
27 | unresolved_module.syntax().text_range(), | 26 | "create_module", |
28 | )]) | 27 | "Create module", |
29 | } | 28 | FileSystemEdit::CreateFile { |
29 | dst: AnchoredPathBuf { | ||
30 | anchor: d.decl.file_id.original_file(ctx.sema.db), | ||
31 | path: d.candidate.clone(), | ||
32 | }, | ||
33 | initial_contents: "".to_string(), | ||
34 | } | ||
35 | .into(), | ||
36 | unresolved_module.syntax().text_range(), | ||
37 | )]) | ||
30 | } | 38 | } |
31 | 39 | ||
32 | #[cfg(test)] | 40 | #[cfg(test)] |
33 | mod tests { | 41 | mod tests { |
34 | use expect_test::expect; | 42 | use expect_test::expect; |
35 | 43 | ||
36 | use crate::diagnostics::tests::check_expect; | 44 | use crate::tests::{check_diagnostics, check_expect}; |
45 | |||
46 | #[test] | ||
47 | fn unresolved_module() { | ||
48 | check_diagnostics( | ||
49 | r#" | ||
50 | //- /lib.rs | ||
51 | mod foo; | ||
52 | mod bar; | ||
53 | //^^^^^^^^ 💡 error: unresolved module | ||
54 | mod baz {} | ||
55 | //- /foo.rs | ||
56 | "#, | ||
57 | ); | ||
58 | } | ||
37 | 59 | ||
38 | #[test] | 60 | #[test] |
39 | fn test_unresolved_module_diagnostic() { | 61 | fn test_unresolved_module_diagnostic() { |
@@ -42,9 +64,14 @@ mod tests { | |||
42 | expect![[r#" | 64 | expect![[r#" |
43 | [ | 65 | [ |
44 | Diagnostic { | 66 | Diagnostic { |
67 | code: DiagnosticCode( | ||
68 | "unresolved-module", | ||
69 | ), | ||
45 | message: "unresolved module", | 70 | message: "unresolved module", |
46 | range: 0..8, | 71 | range: 0..8, |
47 | severity: Error, | 72 | severity: Error, |
73 | unused: false, | ||
74 | experimental: false, | ||
48 | fixes: Some( | 75 | fixes: Some( |
49 | [ | 76 | [ |
50 | Assist { | 77 | Assist { |
@@ -75,12 +102,6 @@ mod tests { | |||
75 | }, | 102 | }, |
76 | ], | 103 | ], |
77 | ), | 104 | ), |
78 | unused: false, | ||
79 | code: Some( | ||
80 | DiagnosticCode( | ||
81 | "unresolved-module", | ||
82 | ), | ||
83 | ), | ||
84 | }, | 105 | }, |
85 | ] | 106 | ] |
86 | "#]], | 107 | "#]], |
diff --git a/crates/ide_diagnostics/src/handlers/unresolved_proc_macro.rs b/crates/ide_diagnostics/src/handlers/unresolved_proc_macro.rs new file mode 100644 index 000000000..fde1d1323 --- /dev/null +++ b/crates/ide_diagnostics/src/handlers/unresolved_proc_macro.rs | |||
@@ -0,0 +1,27 @@ | |||
1 | use crate::{Diagnostic, DiagnosticsContext, Severity}; | ||
2 | |||
3 | // Diagnostic: unresolved-proc-macro | ||
4 | // | ||
5 | // This diagnostic is shown when a procedural macro can not be found. This usually means that | ||
6 | // procedural macro support is simply disabled (and hence is only a weak hint instead of an error), | ||
7 | // but can also indicate project setup problems. | ||
8 | // | ||
9 | // If you are seeing a lot of "proc macro not expanded" warnings, you can add this option to the | ||
10 | // `rust-analyzer.diagnostics.disabled` list to prevent them from showing. Alternatively you can | ||
11 | // enable support for procedural macros (see `rust-analyzer.procMacro.enable`). | ||
12 | pub(crate) fn unresolved_proc_macro( | ||
13 | ctx: &DiagnosticsContext<'_>, | ||
14 | d: &hir::UnresolvedProcMacro, | ||
15 | ) -> Diagnostic { | ||
16 | // Use more accurate position if available. | ||
17 | let display_range = d | ||
18 | .precise_location | ||
19 | .unwrap_or_else(|| ctx.sema.diagnostics_display_range(d.node.clone()).range); | ||
20 | // FIXME: it would be nice to tell the user whether proc macros are currently disabled | ||
21 | let message = match &d.macro_name { | ||
22 | Some(name) => format!("proc macro `{}` not expanded", name), | ||
23 | None => "proc macro not expanded".to_string(), | ||
24 | }; | ||
25 | |||
26 | Diagnostic::new("unresolved-proc-macro", message, display_range).severity(Severity::WeakWarning) | ||
27 | } | ||
diff --git a/crates/ide_diagnostics/src/handlers/useless_braces.rs b/crates/ide_diagnostics/src/handlers/useless_braces.rs new file mode 100644 index 000000000..8b9330e04 --- /dev/null +++ b/crates/ide_diagnostics/src/handlers/useless_braces.rs | |||
@@ -0,0 +1,148 @@ | |||
1 | use ide_db::{base_db::FileId, source_change::SourceChange}; | ||
2 | use itertools::Itertools; | ||
3 | use syntax::{ast, AstNode, SyntaxNode, TextRange}; | ||
4 | use text_edit::TextEdit; | ||
5 | |||
6 | use crate::{fix, Diagnostic, Severity}; | ||
7 | |||
8 | // Diagnostic: unnecessary-braces | ||
9 | // | ||
10 | // Diagnostic for unnecessary braces in `use` items. | ||
11 | pub(crate) fn useless_braces( | ||
12 | acc: &mut Vec<Diagnostic>, | ||
13 | file_id: FileId, | ||
14 | node: &SyntaxNode, | ||
15 | ) -> Option<()> { | ||
16 | let use_tree_list = ast::UseTreeList::cast(node.clone())?; | ||
17 | if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() { | ||
18 | // If there is a comment inside the bracketed `use`, | ||
19 | // assume it is a commented out module path and don't show diagnostic. | ||
20 | if use_tree_list.has_inner_comment() { | ||
21 | return Some(()); | ||
22 | } | ||
23 | |||
24 | let use_range = use_tree_list.syntax().text_range(); | ||
25 | let edit = remove_braces(&single_use_tree).unwrap_or_else(|| { | ||
26 | let to_replace = single_use_tree.syntax().text().to_string(); | ||
27 | let mut edit_builder = TextEdit::builder(); | ||
28 | edit_builder.delete(use_range); | ||
29 | edit_builder.insert(use_range.start(), to_replace); | ||
30 | edit_builder.finish() | ||
31 | }); | ||
32 | |||
33 | acc.push( | ||
34 | Diagnostic::new( | ||
35 | "unnecessary-braces", | ||
36 | "Unnecessary braces in use statement".to_string(), | ||
37 | use_range, | ||
38 | ) | ||
39 | .severity(Severity::WeakWarning) | ||
40 | .with_fixes(Some(vec![fix( | ||
41 | "remove_braces", | ||
42 | "Remove unnecessary braces", | ||
43 | SourceChange::from_text_edit(file_id, edit), | ||
44 | use_range, | ||
45 | )])), | ||
46 | ); | ||
47 | } | ||
48 | |||
49 | Some(()) | ||
50 | } | ||
51 | |||
52 | fn remove_braces(single_use_tree: &ast::UseTree) -> Option<TextEdit> { | ||
53 | let use_tree_list_node = single_use_tree.syntax().parent()?; | ||
54 | if single_use_tree.path()?.segment()?.self_token().is_some() { | ||
55 | let start = use_tree_list_node.prev_sibling_or_token()?.text_range().start(); | ||
56 | let end = use_tree_list_node.text_range().end(); | ||
57 | return Some(TextEdit::delete(TextRange::new(start, end))); | ||
58 | } | ||
59 | None | ||
60 | } | ||
61 | |||
62 | #[cfg(test)] | ||
63 | mod tests { | ||
64 | use crate::tests::{check_diagnostics, check_fix}; | ||
65 | |||
66 | #[test] | ||
67 | fn test_check_unnecessary_braces_in_use_statement() { | ||
68 | check_diagnostics( | ||
69 | r#" | ||
70 | use a; | ||
71 | use a::{c, d::e}; | ||
72 | |||
73 | mod a { | ||
74 | mod c {} | ||
75 | mod d { | ||
76 | mod e {} | ||
77 | } | ||
78 | } | ||
79 | "#, | ||
80 | ); | ||
81 | check_diagnostics( | ||
82 | r#" | ||
83 | use a; | ||
84 | use a::{ | ||
85 | c, | ||
86 | // d::e | ||
87 | }; | ||
88 | |||
89 | mod a { | ||
90 | mod c {} | ||
91 | mod d { | ||
92 | mod e {} | ||
93 | } | ||
94 | } | ||
95 | "#, | ||
96 | ); | ||
97 | check_fix( | ||
98 | r#" | ||
99 | mod b {} | ||
100 | use {$0b}; | ||
101 | "#, | ||
102 | r#" | ||
103 | mod b {} | ||
104 | use b; | ||
105 | "#, | ||
106 | ); | ||
107 | check_fix( | ||
108 | r#" | ||
109 | mod b {} | ||
110 | use {b$0}; | ||
111 | "#, | ||
112 | r#" | ||
113 | mod b {} | ||
114 | use b; | ||
115 | "#, | ||
116 | ); | ||
117 | check_fix( | ||
118 | r#" | ||
119 | mod a { mod c {} } | ||
120 | use a::{c$0}; | ||
121 | "#, | ||
122 | r#" | ||
123 | mod a { mod c {} } | ||
124 | use a::c; | ||
125 | "#, | ||
126 | ); | ||
127 | check_fix( | ||
128 | r#" | ||
129 | mod a {} | ||
130 | use a::{self$0}; | ||
131 | "#, | ||
132 | r#" | ||
133 | mod a {} | ||
134 | use a; | ||
135 | "#, | ||
136 | ); | ||
137 | check_fix( | ||
138 | r#" | ||
139 | mod a { mod c {} mod d { mod e {} } } | ||
140 | use a::{c, d::{e$0}}; | ||
141 | "#, | ||
142 | r#" | ||
143 | mod a { mod c {} mod d { mod e {} } } | ||
144 | use a::{c, d::e}; | ||
145 | "#, | ||
146 | ); | ||
147 | } | ||
148 | } | ||
diff --git a/crates/ide_diagnostics/src/lib.rs b/crates/ide_diagnostics/src/lib.rs new file mode 100644 index 000000000..6ad1b4373 --- /dev/null +++ b/crates/ide_diagnostics/src/lib.rs | |||
@@ -0,0 +1,374 @@ | |||
1 | //! Diagnostics rendering and fixits. | ||
2 | //! | ||
3 | //! Most of the diagnostics originate from the dark depth of the compiler, and | ||
4 | //! are originally expressed in term of IR. When we emit the diagnostic, we are | ||
5 | //! usually not in the position to decide how to best "render" it in terms of | ||
6 | //! user-authored source code. We are especially not in the position to offer | ||
7 | //! fixits, as the compiler completely lacks the infrastructure to edit the | ||
8 | //! source code. | ||
9 | //! | ||
10 | //! Instead, we "bubble up" raw, structured diagnostics until the `hir` crate, | ||
11 | //! where we "cook" them so that each diagnostic is formulated in terms of `hir` | ||
12 | //! types. Well, at least that's the aspiration, the "cooking" is somewhat | ||
13 | //! ad-hoc at the moment. Anyways, we get a bunch of ide-friendly diagnostic | ||
14 | //! structs from hir, and we want to render them to unified serializable | ||
15 | //! representation (span, level, message) here. If we can, we also provide | ||
16 | //! fixits. By the way, that's why we want to keep diagnostics structured | ||
17 | //! internally -- so that we have all the info to make fixes. | ||
18 | //! | ||
19 | //! We have one "handler" module per diagnostic code. Such a module contains | ||
20 | //! rendering, optional fixes and tests. It's OK if some low-level compiler | ||
21 | //! functionality ends up being tested via a diagnostic. | ||
22 | //! | ||
23 | //! There are also a couple of ad-hoc diagnostics implemented directly here, we | ||
24 | //! don't yet have a great pattern for how to do them properly. | ||
25 | |||
26 | mod handlers { | ||
27 | pub(crate) mod break_outside_of_loop; | ||
28 | pub(crate) mod inactive_code; | ||
29 | pub(crate) mod incorrect_case; | ||
30 | pub(crate) mod macro_error; | ||
31 | pub(crate) mod mismatched_arg_count; | ||
32 | pub(crate) mod missing_fields; | ||
33 | pub(crate) mod missing_match_arms; | ||
34 | pub(crate) mod missing_ok_or_some_in_tail_expr; | ||
35 | pub(crate) mod missing_unsafe; | ||
36 | pub(crate) mod no_such_field; | ||
37 | pub(crate) mod remove_this_semicolon; | ||
38 | pub(crate) mod replace_filter_map_next_with_find_map; | ||
39 | pub(crate) mod unimplemented_builtin_macro; | ||
40 | pub(crate) mod unresolved_extern_crate; | ||
41 | pub(crate) mod unresolved_import; | ||
42 | pub(crate) mod unresolved_macro_call; | ||
43 | pub(crate) mod unresolved_module; | ||
44 | pub(crate) mod unresolved_proc_macro; | ||
45 | |||
46 | // The handlers bellow are unusual, the implement the diagnostics as well. | ||
47 | pub(crate) mod field_shorthand; | ||
48 | pub(crate) mod useless_braces; | ||
49 | pub(crate) mod unlinked_file; | ||
50 | } | ||
51 | |||
52 | use hir::{diagnostics::AnyDiagnostic, Semantics}; | ||
53 | use ide_db::{ | ||
54 | assists::{Assist, AssistId, AssistKind, AssistResolveStrategy}, | ||
55 | base_db::{FileId, SourceDatabase}, | ||
56 | label::Label, | ||
57 | source_change::SourceChange, | ||
58 | RootDatabase, | ||
59 | }; | ||
60 | use rustc_hash::FxHashSet; | ||
61 | use syntax::{ast::AstNode, TextRange}; | ||
62 | |||
63 | #[derive(Copy, Clone, Debug, PartialEq)] | ||
64 | pub struct DiagnosticCode(pub &'static str); | ||
65 | |||
66 | impl DiagnosticCode { | ||
67 | pub fn as_str(&self) -> &str { | ||
68 | self.0 | ||
69 | } | ||
70 | } | ||
71 | |||
72 | #[derive(Debug)] | ||
73 | pub struct Diagnostic { | ||
74 | pub code: DiagnosticCode, | ||
75 | pub message: String, | ||
76 | pub range: TextRange, | ||
77 | pub severity: Severity, | ||
78 | pub unused: bool, | ||
79 | pub experimental: bool, | ||
80 | pub fixes: Option<Vec<Assist>>, | ||
81 | } | ||
82 | |||
83 | impl Diagnostic { | ||
84 | fn new(code: &'static str, message: impl Into<String>, range: TextRange) -> Diagnostic { | ||
85 | let message = message.into(); | ||
86 | Diagnostic { | ||
87 | code: DiagnosticCode(code), | ||
88 | message, | ||
89 | range, | ||
90 | severity: Severity::Error, | ||
91 | unused: false, | ||
92 | experimental: false, | ||
93 | fixes: None, | ||
94 | } | ||
95 | } | ||
96 | |||
97 | fn experimental(mut self) -> Diagnostic { | ||
98 | self.experimental = true; | ||
99 | self | ||
100 | } | ||
101 | |||
102 | fn severity(mut self, severity: Severity) -> Diagnostic { | ||
103 | self.severity = severity; | ||
104 | self | ||
105 | } | ||
106 | |||
107 | fn with_fixes(mut self, fixes: Option<Vec<Assist>>) -> Diagnostic { | ||
108 | self.fixes = fixes; | ||
109 | self | ||
110 | } | ||
111 | |||
112 | fn with_unused(mut self, unused: bool) -> Diagnostic { | ||
113 | self.unused = unused; | ||
114 | self | ||
115 | } | ||
116 | } | ||
117 | |||
118 | #[derive(Debug, Copy, Clone)] | ||
119 | pub enum Severity { | ||
120 | Error, | ||
121 | // We don't actually emit this one yet, but we should at some point. | ||
122 | // Warning, | ||
123 | WeakWarning, | ||
124 | } | ||
125 | |||
126 | #[derive(Default, Debug, Clone)] | ||
127 | pub struct DiagnosticsConfig { | ||
128 | pub disable_experimental: bool, | ||
129 | pub disabled: FxHashSet<String>, | ||
130 | } | ||
131 | |||
132 | struct DiagnosticsContext<'a> { | ||
133 | config: &'a DiagnosticsConfig, | ||
134 | sema: Semantics<'a, RootDatabase>, | ||
135 | resolve: &'a AssistResolveStrategy, | ||
136 | } | ||
137 | |||
138 | pub fn diagnostics( | ||
139 | db: &RootDatabase, | ||
140 | config: &DiagnosticsConfig, | ||
141 | resolve: &AssistResolveStrategy, | ||
142 | file_id: FileId, | ||
143 | ) -> Vec<Diagnostic> { | ||
144 | let _p = profile::span("diagnostics"); | ||
145 | let sema = Semantics::new(db); | ||
146 | let parse = db.parse(file_id); | ||
147 | let mut res = Vec::new(); | ||
148 | |||
149 | // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily. | ||
150 | res.extend( | ||
151 | parse.errors().iter().take(128).map(|err| { | ||
152 | Diagnostic::new("syntax-error", format!("Syntax Error: {}", err), err.range()) | ||
153 | }), | ||
154 | ); | ||
155 | |||
156 | for node in parse.tree().syntax().descendants() { | ||
157 | handlers::useless_braces::useless_braces(&mut res, file_id, &node); | ||
158 | handlers::field_shorthand::field_shorthand(&mut res, file_id, &node); | ||
159 | } | ||
160 | |||
161 | let module = sema.to_module_def(file_id); | ||
162 | |||
163 | let ctx = DiagnosticsContext { config, sema, resolve }; | ||
164 | if module.is_none() { | ||
165 | handlers::unlinked_file::unlinked_file(&ctx, &mut res, file_id); | ||
166 | } | ||
167 | |||
168 | let mut diags = Vec::new(); | ||
169 | if let Some(m) = module { | ||
170 | m.diagnostics(db, &mut diags) | ||
171 | } | ||
172 | |||
173 | for diag in diags { | ||
174 | #[rustfmt::skip] | ||
175 | let d = match diag { | ||
176 | AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d), | ||
177 | AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d), | ||
178 | AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d), | ||
179 | AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d), | ||
180 | AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d), | ||
181 | AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d), | ||
182 | AnyDiagnostic::MissingOkOrSomeInTailExpr(d) => handlers::missing_ok_or_some_in_tail_expr::missing_ok_or_some_in_tail_expr(&ctx, &d), | ||
183 | AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d), | ||
184 | AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d), | ||
185 | AnyDiagnostic::RemoveThisSemicolon(d) => handlers::remove_this_semicolon::remove_this_semicolon(&ctx, &d), | ||
186 | AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d), | ||
187 | AnyDiagnostic::UnimplementedBuiltinMacro(d) => handlers::unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d), | ||
188 | AnyDiagnostic::UnresolvedExternCrate(d) => handlers::unresolved_extern_crate::unresolved_extern_crate(&ctx, &d), | ||
189 | AnyDiagnostic::UnresolvedImport(d) => handlers::unresolved_import::unresolved_import(&ctx, &d), | ||
190 | AnyDiagnostic::UnresolvedMacroCall(d) => handlers::unresolved_macro_call::unresolved_macro_call(&ctx, &d), | ||
191 | AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d), | ||
192 | AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d), | ||
193 | |||
194 | AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) { | ||
195 | Some(it) => it, | ||
196 | None => continue, | ||
197 | } | ||
198 | }; | ||
199 | res.push(d) | ||
200 | } | ||
201 | |||
202 | res.retain(|d| { | ||
203 | !ctx.config.disabled.contains(d.code.as_str()) | ||
204 | && !(ctx.config.disable_experimental && d.experimental) | ||
205 | }); | ||
206 | |||
207 | res | ||
208 | } | ||
209 | |||
210 | fn fix(id: &'static str, label: &str, source_change: SourceChange, target: TextRange) -> Assist { | ||
211 | let mut res = unresolved_fix(id, label, target); | ||
212 | res.source_change = Some(source_change); | ||
213 | res | ||
214 | } | ||
215 | |||
216 | fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist { | ||
217 | assert!(!id.contains(' ')); | ||
218 | Assist { | ||
219 | id: AssistId(id, AssistKind::QuickFix), | ||
220 | label: Label::new(label), | ||
221 | group: None, | ||
222 | target, | ||
223 | source_change: None, | ||
224 | } | ||
225 | } | ||
226 | |||
227 | #[cfg(test)] | ||
228 | mod tests { | ||
229 | use expect_test::Expect; | ||
230 | use ide_db::{ | ||
231 | assists::AssistResolveStrategy, | ||
232 | base_db::{fixture::WithFixture, SourceDatabaseExt}, | ||
233 | RootDatabase, | ||
234 | }; | ||
235 | use stdx::trim_indent; | ||
236 | use test_utils::{assert_eq_text, extract_annotations}; | ||
237 | |||
238 | use crate::{DiagnosticsConfig, Severity}; | ||
239 | |||
240 | /// Takes a multi-file input fixture with annotated cursor positions, | ||
241 | /// and checks that: | ||
242 | /// * a diagnostic is produced | ||
243 | /// * the first diagnostic fix trigger range touches the input cursor position | ||
244 | /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied | ||
245 | #[track_caller] | ||
246 | pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { | ||
247 | check_nth_fix(0, ra_fixture_before, ra_fixture_after); | ||
248 | } | ||
249 | /// Takes a multi-file input fixture with annotated cursor positions, | ||
250 | /// and checks that: | ||
251 | /// * a diagnostic is produced | ||
252 | /// * every diagnostic fixes trigger range touches the input cursor position | ||
253 | /// * that the contents of the file containing the cursor match `after` after each diagnostic fix is applied | ||
254 | pub(crate) fn check_fixes(ra_fixture_before: &str, ra_fixtures_after: Vec<&str>) { | ||
255 | for (i, ra_fixture_after) in ra_fixtures_after.iter().enumerate() { | ||
256 | check_nth_fix(i, ra_fixture_before, ra_fixture_after) | ||
257 | } | ||
258 | } | ||
259 | |||
260 | #[track_caller] | ||
261 | fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) { | ||
262 | let after = trim_indent(ra_fixture_after); | ||
263 | |||
264 | let (db, file_position) = RootDatabase::with_position(ra_fixture_before); | ||
265 | let diagnostic = super::diagnostics( | ||
266 | &db, | ||
267 | &DiagnosticsConfig::default(), | ||
268 | &AssistResolveStrategy::All, | ||
269 | file_position.file_id, | ||
270 | ) | ||
271 | .pop() | ||
272 | .expect("no diagnostics"); | ||
273 | let fix = &diagnostic.fixes.expect("diagnostic misses fixes")[nth]; | ||
274 | let actual = { | ||
275 | let source_change = fix.source_change.as_ref().unwrap(); | ||
276 | let file_id = *source_change.source_file_edits.keys().next().unwrap(); | ||
277 | let mut actual = db.file_text(file_id).to_string(); | ||
278 | |||
279 | for edit in source_change.source_file_edits.values() { | ||
280 | edit.apply(&mut actual); | ||
281 | } | ||
282 | actual | ||
283 | }; | ||
284 | |||
285 | assert_eq_text!(&after, &actual); | ||
286 | assert!( | ||
287 | fix.target.contains_inclusive(file_position.offset), | ||
288 | "diagnostic fix range {:?} does not touch cursor position {:?}", | ||
289 | fix.target, | ||
290 | file_position.offset | ||
291 | ); | ||
292 | } | ||
293 | |||
294 | /// Checks that there's a diagnostic *without* fix at `$0`. | ||
295 | pub(crate) fn check_no_fix(ra_fixture: &str) { | ||
296 | let (db, file_position) = RootDatabase::with_position(ra_fixture); | ||
297 | let diagnostic = super::diagnostics( | ||
298 | &db, | ||
299 | &DiagnosticsConfig::default(), | ||
300 | &AssistResolveStrategy::All, | ||
301 | file_position.file_id, | ||
302 | ) | ||
303 | .pop() | ||
304 | .unwrap(); | ||
305 | assert!(diagnostic.fixes.is_none(), "got a fix when none was expected: {:?}", diagnostic); | ||
306 | } | ||
307 | |||
308 | pub(crate) fn check_expect(ra_fixture: &str, expect: Expect) { | ||
309 | let (db, file_id) = RootDatabase::with_single_file(ra_fixture); | ||
310 | let diagnostics = super::diagnostics( | ||
311 | &db, | ||
312 | &DiagnosticsConfig::default(), | ||
313 | &AssistResolveStrategy::All, | ||
314 | file_id, | ||
315 | ); | ||
316 | expect.assert_debug_eq(&diagnostics) | ||
317 | } | ||
318 | |||
319 | #[track_caller] | ||
320 | pub(crate) fn check_diagnostics(ra_fixture: &str) { | ||
321 | let mut config = DiagnosticsConfig::default(); | ||
322 | config.disabled.insert("inactive-code".to_string()); | ||
323 | check_diagnostics_with_config(config, ra_fixture) | ||
324 | } | ||
325 | |||
326 | #[track_caller] | ||
327 | pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixture: &str) { | ||
328 | let (db, files) = RootDatabase::with_many_files(ra_fixture); | ||
329 | for file_id in files { | ||
330 | let diagnostics = | ||
331 | super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id); | ||
332 | |||
333 | let expected = extract_annotations(&*db.file_text(file_id)); | ||
334 | let mut actual = diagnostics | ||
335 | .into_iter() | ||
336 | .map(|d| { | ||
337 | let mut annotation = String::new(); | ||
338 | if let Some(fixes) = &d.fixes { | ||
339 | assert!(!fixes.is_empty()); | ||
340 | annotation.push_str("💡 ") | ||
341 | } | ||
342 | annotation.push_str(match d.severity { | ||
343 | Severity::Error => "error", | ||
344 | Severity::WeakWarning => "weak", | ||
345 | }); | ||
346 | annotation.push_str(": "); | ||
347 | annotation.push_str(&d.message); | ||
348 | (d.range, annotation) | ||
349 | }) | ||
350 | .collect::<Vec<_>>(); | ||
351 | actual.sort_by_key(|(range, _)| range.start()); | ||
352 | assert_eq!(expected, actual); | ||
353 | } | ||
354 | } | ||
355 | |||
356 | #[test] | ||
357 | fn test_disabled_diagnostics() { | ||
358 | let mut config = DiagnosticsConfig::default(); | ||
359 | config.disabled.insert("unresolved-module".into()); | ||
360 | |||
361 | let (db, file_id) = RootDatabase::with_single_file(r#"mod foo;"#); | ||
362 | |||
363 | let diagnostics = super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id); | ||
364 | assert!(diagnostics.is_empty()); | ||
365 | |||
366 | let diagnostics = super::diagnostics( | ||
367 | &db, | ||
368 | &DiagnosticsConfig::default(), | ||
369 | &AssistResolveStrategy::All, | ||
370 | file_id, | ||
371 | ); | ||
372 | assert!(!diagnostics.is_empty()); | ||
373 | } | ||
374 | } | ||
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/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs index c982eb58f..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 | ||
@@ -221,7 +221,7 @@ impl BindingsBuilder { | |||
221 | 221 | ||
222 | fn build_inner(&self, bindings: &mut Bindings, link_nodes: &[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 | } |
@@ -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,7 +487,7 @@ 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 | } |
@@ -495,7 +495,7 @@ fn match_loop_inner<'t>( | |||
495 | Some(err) => { | 495 | Some(err) => { |
496 | res.add_err(err); | 496 | res.add_err(err); |
497 | if let Some(fragment) = match_res.value { | 497 | if let Some(fragment) = match_res.value { |
498 | bindings_builder.push_fragment(&mut item.bindings, &name, fragment); | 498 | bindings_builder.push_fragment(&mut item.bindings, name, fragment); |
499 | } | 499 | } |
500 | item.is_error = true; | 500 | item.is_error = true; |
501 | error_items.push(item); | 501 | error_items.push(item); |
@@ -504,7 +504,7 @@ fn match_loop_inner<'t>( | |||
504 | } | 504 | } |
505 | } | 505 | } |
506 | OpDelimited::Op(Op::Leaf(leaf)) => { | 506 | OpDelimited::Op(Op::Leaf(leaf)) => { |
507 | if let Err(err) = match_leaf(&leaf, &mut src.clone()) { | 507 | if let Err(err) = match_leaf(leaf, &mut src.clone()) { |
508 | res.add_err(err); | 508 | res.add_err(err); |
509 | item.is_error = true; | 509 | item.is_error = true; |
510 | } else { | 510 | } else { |
@@ -640,10 +640,10 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match { | |||
640 | let (iter, match_res) = item.meta_result.take().unwrap(); | 640 | let (iter, match_res) = item.meta_result.take().unwrap(); |
641 | match match_res.value { | 641 | match match_res.value { |
642 | Some(fragment) => { | 642 | Some(fragment) => { |
643 | bindings_builder.push_fragment(&mut item.bindings, &name, fragment); | 643 | bindings_builder.push_fragment(&mut item.bindings, name, fragment); |
644 | } | 644 | } |
645 | None if match_res.err.is_none() => { | 645 | None if match_res.err.is_none() => { |
646 | bindings_builder.push_optional(&mut item.bindings, &name); | 646 | bindings_builder.push_optional(&mut item.bindings, name); |
647 | } | 647 | } |
648 | _ => {} | 648 | _ => {} |
649 | } | 649 | } |
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 380a50744..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,7 +290,7 @@ 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 |
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs index 04c0d3e75..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,7 +206,7 @@ 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 | }; |
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 978c75747..cdc22425d 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs | |||
@@ -633,7 +633,7 @@ impl<'a> TreeSink for TtTreeSink<'a> { | |||
633 | } | 633 | } |
634 | } | 634 | } |
635 | }; | 635 | }; |
636 | self.buf += &text; | 636 | self.buf += text; |
637 | self.text_pos += TextSize::of(text); | 637 | self.text_pos += TextSize::of(text); |
638 | } | 638 | } |
639 | 639 | ||
diff --git a/crates/mbe/src/tests/expand.rs b/crates/mbe/src/tests/expand.rs index 75c88687c..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 | } |
diff --git a/crates/mbe/src/tt_iter.rs b/crates/mbe/src/tt_iter.rs index bd54f2442..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 | ||
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/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 14eed4289..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(); |
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/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/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 6ef58c9c1..fbcb9e3c2 100644 --- a/crates/profile/src/memory_usage.rs +++ b/crates/profile/src/memory_usage.rs | |||
@@ -32,9 +32,7 @@ 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; | ||
37 | MemoryUsage { allocated: Bytes(alloc) } | ||
38 | } else if #[cfg(windows)] { | 36 | } else if #[cfg(windows)] { |
39 | // There doesn't seem to be an API for determining heap usage, so we try to | 37 | // There doesn't seem to be an API for determining heap usage, so we try to |
40 | // approximate that by using the Commit Charge value. | 38 | // approximate that by using the Commit Charge value. |
@@ -58,6 +56,37 @@ impl MemoryUsage { | |||
58 | } | 56 | } |
59 | } | 57 | } |
60 | 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 | |||
61 | #[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] | 90 | #[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] |
62 | pub struct Bytes(isize); | 91 | pub struct Bytes(isize); |
63 | 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/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/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 19cb1c046..b5f5519b4 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs | |||
@@ -126,7 +126,7 @@ fn load_crate_graph( | |||
126 | } | 126 | } |
127 | } | 127 | } |
128 | } | 128 | } |
129 | let source_roots = source_root_config.partition(&vfs); | 129 | let source_roots = source_root_config.partition(vfs); |
130 | analysis_change.set_roots(source_roots); | 130 | analysis_change.set_roots(source_roots); |
131 | 131 | ||
132 | analysis_change.set_crate_graph(crate_graph); | 132 | analysis_change.set_crate_graph(crate_graph); |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 91a9b55a8..c4757a1cb 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -1064,8 +1064,8 @@ mod tests { | |||
1064 | let package_json_path = project_root().join("editors/code/package.json"); | 1064 | let package_json_path = project_root().join("editors/code/package.json"); |
1065 | let mut package_json = fs::read_to_string(&package_json_path).unwrap(); | 1065 | let mut package_json = fs::read_to_string(&package_json_path).unwrap(); |
1066 | 1066 | ||
1067 | let start_marker = " \"$generated-start\": false,\n"; | 1067 | let start_marker = " \"$generated-start\": {},\n"; |
1068 | let end_marker = " \"$generated-end\": false\n"; | 1068 | let end_marker = " \"$generated-end\": {}\n"; |
1069 | 1069 | ||
1070 | let start = package_json.find(start_marker).unwrap() + start_marker.len(); | 1070 | let start = package_json.find(start_marker).unwrap() + start_marker.len(); |
1071 | let end = package_json.find(end_marker).unwrap(); | 1071 | let end = package_json.find(end_marker).unwrap(); |
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 582a89667..583900cfe 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -194,7 +194,7 @@ impl GlobalState { | |||
194 | change.change_file(file.file_id, text); | 194 | change.change_file(file.file_id, text); |
195 | } | 195 | } |
196 | if has_fs_changes { | 196 | if has_fs_changes { |
197 | let roots = self.source_root_config.partition(&vfs); | 197 | let roots = self.source_root_config.partition(vfs); |
198 | change.set_roots(roots); | 198 | change.set_roots(roots); |
199 | } | 199 | } |
200 | change | 200 | change |
@@ -291,7 +291,7 @@ impl GlobalStateSnapshot { | |||
291 | } | 291 | } |
292 | 292 | ||
293 | pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> { | 293 | pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> { |
294 | let path = from_proto::vfs_path(&url).ok()?; | 294 | let path = from_proto::vfs_path(url).ok()?; |
295 | Some(self.mem_docs.get(&path)?.version) | 295 | Some(self.mem_docs.get(&path)?.version) |
296 | } | 296 | } |
297 | 297 | ||
@@ -300,7 +300,7 @@ impl GlobalStateSnapshot { | |||
300 | base.pop(); | 300 | base.pop(); |
301 | let path = base.join(&path.path).unwrap(); | 301 | let path = base.join(&path.path).unwrap(); |
302 | let path = path.as_path().unwrap(); | 302 | let path = path.as_path().unwrap(); |
303 | url_from_abs_path(&path) | 303 | url_from_abs_path(path) |
304 | } | 304 | } |
305 | 305 | ||
306 | pub(crate) fn cargo_target_for_crate_root( | 306 | pub(crate) fn cargo_target_for_crate_root( |
@@ -312,7 +312,7 @@ impl GlobalStateSnapshot { | |||
312 | let path = path.as_path()?; | 312 | let path = path.as_path()?; |
313 | self.workspaces.iter().find_map(|ws| match ws { | 313 | self.workspaces.iter().find_map(|ws| match ws { |
314 | ProjectWorkspace::Cargo { cargo, .. } => { | 314 | ProjectWorkspace::Cargo { cargo, .. } => { |
315 | cargo.target_by_root(&path).map(|it| (cargo, it)) | 315 | cargo.target_by_root(path).map(|it| (cargo, it)) |
316 | } | 316 | } |
317 | ProjectWorkspace::Json { .. } => None, | 317 | ProjectWorkspace::Json { .. } => None, |
318 | ProjectWorkspace::DetachedFiles { .. } => None, | 318 | ProjectWorkspace::DetachedFiles { .. } => None, |
@@ -323,7 +323,7 @@ impl GlobalStateSnapshot { | |||
323 | 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 { |
324 | let path = vfs.file_path(id); | 324 | let path = vfs.file_path(id); |
325 | let path = path.as_path().unwrap(); | 325 | let path = path.as_path().unwrap(); |
326 | url_from_abs_path(&path) | 326 | url_from_abs_path(path) |
327 | } | 327 | } |
328 | 328 | ||
329 | 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 40dd0da3e..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 | } |
@@ -1540,7 +1539,7 @@ fn runnable_action_links( | |||
1540 | snap: &GlobalStateSnapshot, | 1539 | snap: &GlobalStateSnapshot, |
1541 | runnable: Runnable, | 1540 | runnable: Runnable, |
1542 | ) -> Option<lsp_ext::CommandLinkGroup> { | 1541 | ) -> Option<lsp_ext::CommandLinkGroup> { |
1543 | 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()?; |
1544 | let hover_config = snap.config.hover(); | 1543 | let hover_config = snap.config.hover(); |
1545 | 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()) { |
1546 | return None; | 1545 | return None; |
@@ -1624,7 +1623,7 @@ fn run_rustfmt( | |||
1624 | text_document: TextDocumentIdentifier, | 1623 | text_document: TextDocumentIdentifier, |
1625 | range: Option<lsp_types::Range>, | 1624 | range: Option<lsp_types::Range>, |
1626 | ) -> Result<Option<Vec<lsp_types::TextEdit>>> { | 1625 | ) -> Result<Option<Vec<lsp_types::TextEdit>>> { |
1627 | let file_id = from_proto::file_id(&snap, &text_document.uri)?; | 1626 | let file_id = from_proto::file_id(snap, &text_document.uri)?; |
1628 | let file = snap.analysis.file_text(file_id)?; | 1627 | let file = snap.analysis.file_text(file_id)?; |
1629 | let crate_ids = snap.analysis.crate_for(file_id)?; | 1628 | let crate_ids = snap.analysis.crate_for(file_id)?; |
1630 | 1629 | ||
@@ -1671,7 +1670,7 @@ fn run_rustfmt( | |||
1671 | .into()); | 1670 | .into()); |
1672 | } | 1671 | } |
1673 | 1672 | ||
1674 | let frange = from_proto::file_range(&snap, text_document, range)?; | 1673 | let frange = from_proto::file_range(snap, text_document, range)?; |
1675 | 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; |
1676 | 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; |
1677 | 1676 | ||
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/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..260a504e7 100644 --- a/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/crates/rust-analyzer/tests/slow-tests/support.rs | |||
@@ -75,7 +75,9 @@ impl<'a> Project<'a> { | |||
75 | profile::init_from(crate::PROFILE); | 75 | profile::init_from(crate::PROFILE); |
76 | }); | 76 | }); |
77 | 77 | ||
78 | for entry in Fixture::parse(self.fixture) { | 78 | let (mini_core, fixtures) = Fixture::parse(self.fixture); |
79 | assert!(mini_core.is_none()); | ||
80 | for entry in fixtures { | ||
79 | let path = tmp_dir.path().join(&entry.path['/'.len_utf8()..]); | 81 | let path = tmp_dir.path().join(&entry.path['/'.len_utf8()..]); |
80 | fs::create_dir_all(path.parent().unwrap()).unwrap(); | 82 | fs::create_dir_all(path.parent().unwrap()).unwrap(); |
81 | fs::write(path.as_path(), entry.text.as_bytes()).unwrap(); | 83 | fs::write(path.as_path(), entry.text.as_bytes()).unwrap(); |
@@ -323,7 +325,7 @@ fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Valu | |||
323 | 325 | ||
324 | if !l.is_empty() { | 326 | if !l.is_empty() { |
325 | assert!(!r.is_empty()); | 327 | assert!(!r.is_empty()); |
326 | Some((&l[0], &r[0])) | 328 | Some((l[0], r[0])) |
327 | } else { | 329 | } else { |
328 | assert_eq!(r.len(), 0); | 330 | assert_eq!(r.len(), 0); |
329 | None | 331 | 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/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 3d27d2c1a..2bd9ad867 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs | |||
@@ -325,6 +325,15 @@ impl ast::Impl { | |||
325 | let second = types.next(); | 325 | let second = types.next(); |
326 | (first, second) | 326 | (first, second) |
327 | } | 327 | } |
328 | |||
329 | pub fn for_trait_name_ref(name_ref: &ast::NameRef) -> Option<ast::Impl> { | ||
330 | let this = name_ref.syntax().ancestors().find_map(ast::Impl::cast)?; | ||
331 | if this.trait_()?.syntax().text_range().start() == name_ref.syntax().text_range().start() { | ||
332 | Some(this) | ||
333 | } else { | ||
334 | None | ||
335 | } | ||
336 | } | ||
328 | } | 337 | } |
329 | 338 | ||
330 | #[derive(Debug, Clone, PartialEq, Eq)] | 339 | #[derive(Debug, Clone, PartialEq, Eq)] |
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 304f47b3d..186cc9e74 100644 --- a/crates/syntax/src/parsing/reparsing.rs +++ b/crates/syntax/src/parsing/reparsing.rs | |||
@@ -26,11 +26,11 @@ 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 |
@@ -52,7 +52,7 @@ fn reparse_token( | |||
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 |
diff --git a/crates/syntax/src/tests.rs b/crates/syntax/src/tests.rs index 9f2426171..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 | } |
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 ff5877a7b..31f76589d 100644 --- a/crates/syntax/test_data/parser/ok/0011_outer_attribute.rast +++ b/crates/syntax/test_data/parser/ok/0011_outer_attribute.rast | |||
@@ -21,7 +21,7 @@ [email protected] | |||
21 | [email protected] | 21 | [email protected] |
22 | [email protected] | 22 | [email protected] |
23 | [email protected] | 23 | [email protected] |
24 | [email protected] "ignore" | 24 | [email protected] "Ignore" |
25 | [email protected] "]" | 25 | [email protected] "]" |
26 | [email protected] "\n" | 26 | [email protected] "\n" |
27 | [email protected] "fn" | 27 | [email protected] "fn" |
diff --git a/crates/syntax/test_data/parser/ok/0011_outer_attribute.rs b/crates/syntax/test_data/parser/ok/0011_outer_attribute.rs index 3d2e01d5c..6f04cb171 100644 --- a/crates/syntax/test_data/parser/ok/0011_outer_attribute.rs +++ b/crates/syntax/test_data/parser/ok/0011_outer_attribute.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | #[cfg(test)] | 1 | #[cfg(test)] |
2 | #[ignore] | 2 | #[Ignore] |
3 | fn foo() {} | 3 | fn foo() {} |
4 | 4 | ||
5 | #[path = "a.rs"] | 5 | #[path = "a.rs"] |
diff --git a/crates/test_utils/src/fixture.rs b/crates/test_utils/src/fixture.rs index d0bddf7d8..005a5c092 100644 --- a/crates/test_utils/src/fixture.rs +++ b/crates/test_utils/src/fixture.rs | |||
@@ -77,6 +77,11 @@ pub struct Fixture { | |||
77 | pub introduce_new_source_root: bool, | 77 | pub introduce_new_source_root: bool, |
78 | } | 78 | } |
79 | 79 | ||
80 | pub struct MiniCore { | ||
81 | activated_flags: Vec<String>, | ||
82 | valid_flags: Vec<String>, | ||
83 | } | ||
84 | |||
80 | impl Fixture { | 85 | impl Fixture { |
81 | /// Parses text which looks like this: | 86 | /// Parses text which looks like this: |
82 | /// | 87 | /// |
@@ -86,12 +91,28 @@ impl Fixture { | |||
86 | /// line 2 | 91 | /// line 2 |
87 | /// //- other meta | 92 | /// //- other meta |
88 | /// ``` | 93 | /// ``` |
89 | pub fn parse(ra_fixture: &str) -> Vec<Fixture> { | 94 | /// |
95 | /// Fixture can also start with a minicore declaration: | ||
96 | /// | ||
97 | /// ``` | ||
98 | /// //- minicore: sized | ||
99 | /// ``` | ||
100 | /// | ||
101 | /// That will include a subset of `libcore` into the fixture, see | ||
102 | /// `minicore.rs` for what's available. | ||
103 | pub fn parse(ra_fixture: &str) -> (Option<MiniCore>, Vec<Fixture>) { | ||
90 | let fixture = trim_indent(ra_fixture); | 104 | let fixture = trim_indent(ra_fixture); |
91 | 105 | let mut fixture = fixture.as_str(); | |
106 | let mut mini_core = None; | ||
92 | let mut res: Vec<Fixture> = Vec::new(); | 107 | let mut res: Vec<Fixture> = Vec::new(); |
93 | 108 | ||
94 | let default = if ra_fixture.contains("//-") { None } else { Some("//- /main.rs") }; | 109 | if fixture.starts_with("//- minicore:") { |
110 | let first_line = fixture.split_inclusive('\n').next().unwrap(); | ||
111 | mini_core = Some(MiniCore::parse(first_line)); | ||
112 | fixture = &fixture[first_line.len()..]; | ||
113 | } | ||
114 | |||
115 | let default = if fixture.contains("//-") { None } else { Some("//- /main.rs") }; | ||
95 | 116 | ||
96 | for (ix, line) in default.into_iter().chain(fixture.split_inclusive('\n')).enumerate() { | 117 | for (ix, line) in default.into_iter().chain(fixture.split_inclusive('\n')).enumerate() { |
97 | if line.contains("//-") { | 118 | if line.contains("//-") { |
@@ -108,12 +129,22 @@ impl Fixture { | |||
108 | if line.starts_with("//-") { | 129 | if line.starts_with("//-") { |
109 | let meta = Fixture::parse_meta_line(line); | 130 | let meta = Fixture::parse_meta_line(line); |
110 | res.push(meta) | 131 | res.push(meta) |
111 | } else if let Some(entry) = res.last_mut() { | 132 | } else { |
112 | entry.text.push_str(line); | 133 | if line.starts_with("// ") |
134 | && line.contains(":") | ||
135 | && !line.contains("::") | ||
136 | && line.chars().all(|it| !it.is_uppercase()) | ||
137 | { | ||
138 | panic!("looks like invalid metadata line: {:?}", line) | ||
139 | } | ||
140 | |||
141 | if let Some(entry) = res.last_mut() { | ||
142 | entry.text.push_str(line); | ||
143 | } | ||
113 | } | 144 | } |
114 | } | 145 | } |
115 | 146 | ||
116 | res | 147 | (mini_core, res) |
117 | } | 148 | } |
118 | 149 | ||
119 | //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo | 150 | //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo |
@@ -133,7 +164,9 @@ impl Fixture { | |||
133 | let mut env = FxHashMap::default(); | 164 | let mut env = FxHashMap::default(); |
134 | let mut introduce_new_source_root = false; | 165 | let mut introduce_new_source_root = false; |
135 | for component in components[1..].iter() { | 166 | for component in components[1..].iter() { |
136 | let (key, value) = component.split_once(':').unwrap(); | 167 | let (key, value) = component |
168 | .split_once(':') | ||
169 | .unwrap_or_else(|| panic!("invalid meta line: {:?}", meta)); | ||
137 | match key { | 170 | match key { |
138 | "crate" => krate = Some(value.to_string()), | 171 | "crate" => krate = Some(value.to_string()), |
139 | "deps" => deps = value.split(',').map(|it| it.to_string()).collect(), | 172 | "deps" => deps = value.split(',').map(|it| it.to_string()).collect(), |
@@ -172,6 +205,139 @@ impl Fixture { | |||
172 | } | 205 | } |
173 | } | 206 | } |
174 | 207 | ||
208 | impl MiniCore { | ||
209 | fn has_flag(&self, flag: &str) -> bool { | ||
210 | self.activated_flags.iter().any(|it| it == flag) | ||
211 | } | ||
212 | |||
213 | #[track_caller] | ||
214 | fn assert_valid_flag(&self, flag: &str) { | ||
215 | if !self.valid_flags.iter().any(|it| it == flag) { | ||
216 | panic!("invalid flag: {:?}, valid flags: {:?}", flag, self.valid_flags); | ||
217 | } | ||
218 | } | ||
219 | |||
220 | fn parse(line: &str) -> MiniCore { | ||
221 | let mut res = MiniCore { activated_flags: Vec::new(), valid_flags: Vec::new() }; | ||
222 | |||
223 | let line = line.strip_prefix("//- minicore:").unwrap().trim(); | ||
224 | for entry in line.split(", ") { | ||
225 | if res.has_flag(entry) { | ||
226 | panic!("duplicate minicore flag: {:?}", entry) | ||
227 | } | ||
228 | res.activated_flags.push(entry.to_string()) | ||
229 | } | ||
230 | |||
231 | res | ||
232 | } | ||
233 | |||
234 | /// Strips parts of minicore.rs which are flagged by inactive flags. | ||
235 | /// | ||
236 | /// This is probably over-engineered to support flags dependencies. | ||
237 | pub fn source_code(mut self) -> String { | ||
238 | let mut buf = String::new(); | ||
239 | let raw_mini_core = include_str!("./minicore.rs"); | ||
240 | let mut lines = raw_mini_core.split_inclusive('\n'); | ||
241 | |||
242 | let mut parsing_flags = false; | ||
243 | let mut implications = Vec::new(); | ||
244 | |||
245 | // Parse `//!` preamble and extract flags and dependencies. | ||
246 | for line in lines.by_ref() { | ||
247 | let line = match line.strip_prefix("//!") { | ||
248 | Some(it) => it, | ||
249 | None => { | ||
250 | assert!(line.trim().is_empty()); | ||
251 | break; | ||
252 | } | ||
253 | }; | ||
254 | |||
255 | if parsing_flags { | ||
256 | let (flag, deps) = line.split_once(':').unwrap(); | ||
257 | let flag = flag.trim(); | ||
258 | self.valid_flags.push(flag.to_string()); | ||
259 | for dep in deps.split(", ") { | ||
260 | let dep = dep.trim(); | ||
261 | if !dep.is_empty() { | ||
262 | self.assert_valid_flag(dep); | ||
263 | implications.push((flag, dep)); | ||
264 | } | ||
265 | } | ||
266 | } | ||
267 | |||
268 | if line.contains("Available flags:") { | ||
269 | parsing_flags = true; | ||
270 | } | ||
271 | } | ||
272 | |||
273 | for flag in &self.activated_flags { | ||
274 | self.assert_valid_flag(flag); | ||
275 | } | ||
276 | |||
277 | // Fixed point loop to compute transitive closure of flags. | ||
278 | loop { | ||
279 | let mut changed = false; | ||
280 | for &(u, v) in implications.iter() { | ||
281 | if self.has_flag(u) && !self.has_flag(v) { | ||
282 | self.activated_flags.push(v.to_string()); | ||
283 | changed = true; | ||
284 | } | ||
285 | } | ||
286 | if !changed { | ||
287 | break; | ||
288 | } | ||
289 | } | ||
290 | |||
291 | let mut active_regions = Vec::new(); | ||
292 | let mut seen_regions = Vec::new(); | ||
293 | for line in lines { | ||
294 | let trimmed = line.trim(); | ||
295 | if let Some(region) = trimmed.strip_prefix("// region:") { | ||
296 | active_regions.push(region); | ||
297 | continue; | ||
298 | } | ||
299 | if let Some(region) = trimmed.strip_prefix("// endregion:") { | ||
300 | let prev = active_regions.pop().unwrap(); | ||
301 | assert_eq!(prev, region); | ||
302 | continue; | ||
303 | } | ||
304 | |||
305 | let mut line_region = false; | ||
306 | if let Some(idx) = trimmed.find("// :") { | ||
307 | line_region = true; | ||
308 | active_regions.push(&trimmed[idx + "// :".len()..]); | ||
309 | } | ||
310 | |||
311 | let mut keep = true; | ||
312 | for ®ion in &active_regions { | ||
313 | assert!( | ||
314 | !region.starts_with(' '), | ||
315 | "region marker starts with a space: {:?}", | ||
316 | region | ||
317 | ); | ||
318 | self.assert_valid_flag(region); | ||
319 | seen_regions.push(region); | ||
320 | keep &= self.has_flag(region); | ||
321 | } | ||
322 | |||
323 | if keep { | ||
324 | buf.push_str(line) | ||
325 | } | ||
326 | if line_region { | ||
327 | active_regions.pop().unwrap(); | ||
328 | } | ||
329 | } | ||
330 | |||
331 | for flag in &self.valid_flags { | ||
332 | if !seen_regions.iter().any(|it| it == flag) { | ||
333 | panic!("unused minicore flag: {:?}", flag); | ||
334 | } | ||
335 | } | ||
336 | format!("{}", buf); | ||
337 | buf | ||
338 | } | ||
339 | } | ||
340 | |||
175 | #[test] | 341 | #[test] |
176 | #[should_panic] | 342 | #[should_panic] |
177 | fn parse_fixture_checks_further_indented_metadata() { | 343 | fn parse_fixture_checks_further_indented_metadata() { |
@@ -189,12 +355,14 @@ fn parse_fixture_checks_further_indented_metadata() { | |||
189 | 355 | ||
190 | #[test] | 356 | #[test] |
191 | fn parse_fixture_gets_full_meta() { | 357 | fn parse_fixture_gets_full_meta() { |
192 | let parsed = Fixture::parse( | 358 | let (mini_core, parsed) = Fixture::parse( |
193 | r" | 359 | r#" |
194 | //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo | 360 | //- minicore: coerce_unsized |
195 | mod m; | 361 | //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo |
196 | ", | 362 | mod m; |
363 | "#, | ||
197 | ); | 364 | ); |
365 | assert_eq!(mini_core.unwrap().activated_flags, vec!["coerce_unsized".to_string()]); | ||
198 | assert_eq!(1, parsed.len()); | 366 | assert_eq!(1, parsed.len()); |
199 | 367 | ||
200 | let meta = &parsed[0]; | 368 | let meta = &parsed[0]; |
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index ac5a9509d..d55bae62a 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs | |||
@@ -23,7 +23,10 @@ use text_size::{TextRange, TextSize}; | |||
23 | pub use dissimilar::diff as __diff; | 23 | pub use dissimilar::diff as __diff; |
24 | pub use rustc_hash::FxHashMap; | 24 | pub use rustc_hash::FxHashMap; |
25 | 25 | ||
26 | pub use crate::{assert_linear::AssertLinear, fixture::Fixture}; | 26 | pub use crate::{ |
27 | assert_linear::AssertLinear, | ||
28 | fixture::{Fixture, MiniCore}, | ||
29 | }; | ||
27 | 30 | ||
28 | pub const CURSOR_MARKER: &str = "$0"; | 31 | pub const CURSOR_MARKER: &str = "$0"; |
29 | pub const ESCAPED_CURSOR_MARKER: &str = "\\$0"; | 32 | pub const ESCAPED_CURSOR_MARKER: &str = "\\$0"; |
@@ -190,10 +193,21 @@ pub fn add_cursor(text: &str, offset: TextSize) -> String { | |||
190 | res | 193 | res |
191 | } | 194 | } |
192 | 195 | ||
193 | /// Extracts `//^ some text` annotations | 196 | /// Extracts `//^^^ some text` annotations. |
197 | /// | ||
198 | /// A run of `^^^` can be arbitrary long and points to the corresponding range | ||
199 | /// in the line above. | ||
200 | /// | ||
201 | /// The `// ^file text` syntax can be used to attach `text` to the entirety of | ||
202 | /// the file. | ||
203 | /// | ||
204 | /// Multiline string values are supported: | ||
205 | /// | ||
206 | /// // ^^^ first line | ||
207 | /// // | second line | ||
194 | pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> { | 208 | pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> { |
195 | let mut res = Vec::new(); | 209 | let mut res = Vec::new(); |
196 | let mut prev_line_start: Option<TextSize> = None; | 210 | let mut prev_line_start: Option<TextSize> = Some(0.into()); |
197 | let mut line_start: TextSize = 0.into(); | 211 | let mut line_start: TextSize = 0.into(); |
198 | let mut prev_line_annotations: Vec<(TextSize, usize)> = Vec::new(); | 212 | let mut prev_line_annotations: Vec<(TextSize, usize)> = Vec::new(); |
199 | for line in text.split_inclusive('\n') { | 213 | for line in text.split_inclusive('\n') { |
@@ -202,10 +216,15 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> { | |||
202 | let annotation_offset = TextSize::of(&line[..idx + "//".len()]); | 216 | let annotation_offset = TextSize::of(&line[..idx + "//".len()]); |
203 | for annotation in extract_line_annotations(&line[idx + "//".len()..]) { | 217 | for annotation in extract_line_annotations(&line[idx + "//".len()..]) { |
204 | match annotation { | 218 | match annotation { |
205 | LineAnnotation::Annotation { mut range, content } => { | 219 | LineAnnotation::Annotation { mut range, content, file } => { |
206 | range += annotation_offset; | 220 | range += annotation_offset; |
207 | this_line_annotations.push((range.end(), res.len())); | 221 | this_line_annotations.push((range.end(), res.len())); |
208 | res.push((range + prev_line_start.unwrap(), content)) | 222 | let range = if file { |
223 | TextRange::up_to(TextSize::of(text)) | ||
224 | } else { | ||
225 | range + prev_line_start.unwrap() | ||
226 | }; | ||
227 | res.push((range, content)) | ||
209 | } | 228 | } |
210 | LineAnnotation::Continuation { mut offset, content } => { | 229 | LineAnnotation::Continuation { mut offset, content } => { |
211 | offset += annotation_offset; | 230 | offset += annotation_offset; |
@@ -226,11 +245,12 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> { | |||
226 | 245 | ||
227 | prev_line_annotations = this_line_annotations; | 246 | prev_line_annotations = this_line_annotations; |
228 | } | 247 | } |
248 | |||
229 | res | 249 | res |
230 | } | 250 | } |
231 | 251 | ||
232 | enum LineAnnotation { | 252 | enum LineAnnotation { |
233 | Annotation { range: TextRange, content: String }, | 253 | Annotation { range: TextRange, content: String, file: bool }, |
234 | Continuation { offset: TextSize, content: String }, | 254 | Continuation { offset: TextSize, content: String }, |
235 | } | 255 | } |
236 | 256 | ||
@@ -251,12 +271,20 @@ fn extract_line_annotations(mut line: &str) -> Vec<LineAnnotation> { | |||
251 | } | 271 | } |
252 | let range = TextRange::at(offset, len.try_into().unwrap()); | 272 | let range = TextRange::at(offset, len.try_into().unwrap()); |
253 | let next = line[len..].find(marker).map_or(line.len(), |it| it + len); | 273 | let next = line[len..].find(marker).map_or(line.len(), |it| it + len); |
254 | let content = line[len..][..next - len].trim().to_string(); | 274 | let mut content = &line[len..][..next - len]; |
275 | |||
276 | let mut file = false; | ||
277 | if !continuation && content.starts_with("file") { | ||
278 | file = true; | ||
279 | content = &content["file".len()..] | ||
280 | } | ||
281 | |||
282 | let content = content.trim().to_string(); | ||
255 | 283 | ||
256 | let annotation = if continuation { | 284 | let annotation = if continuation { |
257 | LineAnnotation::Continuation { offset: range.end(), content } | 285 | LineAnnotation::Continuation { offset: range.end(), content } |
258 | } else { | 286 | } else { |
259 | LineAnnotation::Annotation { range, content } | 287 | LineAnnotation::Annotation { range, content, file } |
260 | }; | 288 | }; |
261 | res.push(annotation); | 289 | res.push(annotation); |
262 | 290 | ||
@@ -277,16 +305,20 @@ fn main() { | |||
277 | zoo + 1 | 305 | zoo + 1 |
278 | } //^^^ type: | 306 | } //^^^ type: |
279 | // | i32 | 307 | // | i32 |
308 | |||
309 | // ^file | ||
280 | "#, | 310 | "#, |
281 | ); | 311 | ); |
282 | let res = extract_annotations(&text) | 312 | let res = extract_annotations(&text) |
283 | .into_iter() | 313 | .into_iter() |
284 | .map(|(range, ann)| (&text[range], ann)) | 314 | .map(|(range, ann)| (&text[range], ann)) |
285 | .collect::<Vec<_>>(); | 315 | .collect::<Vec<_>>(); |
316 | |||
286 | assert_eq!( | 317 | assert_eq!( |
287 | res, | 318 | res[..3], |
288 | vec![("x", "def".into()), ("y", "def".into()), ("zoo", "type:\ni32\n".into()),] | 319 | [("x", "def".into()), ("y", "def".into()), ("zoo", "type:\ni32\n".into())] |
289 | ); | 320 | ); |
321 | assert_eq!(res[3].0.len(), 115); | ||
290 | } | 322 | } |
291 | 323 | ||
292 | /// Returns `false` if slow tests should not run, otherwise returns `true` and | 324 | /// Returns `false` if slow tests should not run, otherwise returns `true` and |
diff --git a/crates/test_utils/src/minicore.rs b/crates/test_utils/src/minicore.rs new file mode 100644 index 000000000..e04ca58d2 --- /dev/null +++ b/crates/test_utils/src/minicore.rs | |||
@@ -0,0 +1,234 @@ | |||
1 | //! This is a fixture we use for tests that need lang items. | ||
2 | //! | ||
3 | //! We want to include the minimal subset of core for each test, so this file | ||
4 | //! supports "conditional compilation". Tests use the following syntax to include minicore: | ||
5 | //! | ||
6 | //! //- minicore: flag1, flag2 | ||
7 | //! | ||
8 | //! We then strip all the code marked with other flags. | ||
9 | //! | ||
10 | //! Available flags: | ||
11 | //! sized: | ||
12 | //! unsize: sized | ||
13 | //! coerce_unsized: unsize | ||
14 | //! slice: | ||
15 | //! range: | ||
16 | //! deref: sized | ||
17 | //! deref_mut: deref | ||
18 | //! fn: | ||
19 | //! pin: | ||
20 | //! future: pin | ||
21 | //! option: | ||
22 | //! result: | ||
23 | |||
24 | pub mod marker { | ||
25 | // region:sized | ||
26 | #[lang = "sized"] | ||
27 | #[fundamental] | ||
28 | #[rustc_specialization_trait] | ||
29 | pub trait Sized {} | ||
30 | // endregion:sized | ||
31 | |||
32 | // region:unsize | ||
33 | #[lang = "unsize"] | ||
34 | pub trait Unsize<T: ?Sized> {} | ||
35 | // endregion:unsize | ||
36 | } | ||
37 | |||
38 | pub mod ops { | ||
39 | // region:coerce_unsized | ||
40 | mod unsize { | ||
41 | use crate::marker::Unsize; | ||
42 | |||
43 | #[lang = "coerce_unsized"] | ||
44 | pub trait CoerceUnsized<T: ?Sized> {} | ||
45 | |||
46 | impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {} | ||
47 | impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b mut T {} | ||
48 | impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for &'a mut T {} | ||
49 | impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for &'a mut T {} | ||
50 | |||
51 | impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} | ||
52 | impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for &'a T {} | ||
53 | |||
54 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} | ||
55 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *mut T {} | ||
56 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {} | ||
57 | } | ||
58 | pub use self::unsize::CoerceUnsized; | ||
59 | // endregion:coerce_unsized | ||
60 | |||
61 | // region:deref | ||
62 | mod deref { | ||
63 | #[lang = "deref"] | ||
64 | pub trait Deref { | ||
65 | #[lang = "deref_target"] | ||
66 | type Target: ?Sized; | ||
67 | fn deref(&self) -> &Self::Target; | ||
68 | } | ||
69 | // region:deref_mut | ||
70 | #[lang = "deref_mut"] | ||
71 | pub trait DerefMut: Deref { | ||
72 | fn deref_mut(&mut self) -> &mut Self::Target; | ||
73 | } | ||
74 | // endregion:deref_mut | ||
75 | } | ||
76 | pub use self::deref::Deref; | ||
77 | pub use self::deref::DerefMut; //:deref_mut | ||
78 | // endregion:deref | ||
79 | |||
80 | // region:range | ||
81 | mod range { | ||
82 | #[lang = "RangeFull"] | ||
83 | pub struct RangeFull; | ||
84 | |||
85 | #[lang = "Range"] | ||
86 | pub struct Range<Idx> { | ||
87 | pub start: Idx, | ||
88 | pub end: Idx, | ||
89 | } | ||
90 | |||
91 | #[lang = "RangeFrom"] | ||
92 | pub struct RangeFrom<Idx> { | ||
93 | pub start: Idx, | ||
94 | } | ||
95 | |||
96 | #[lang = "RangeTo"] | ||
97 | pub struct RangeTo<Idx> { | ||
98 | pub end: Idx, | ||
99 | } | ||
100 | |||
101 | #[lang = "RangeInclusive"] | ||
102 | pub struct RangeInclusive<Idx> { | ||
103 | pub(crate) start: Idx, | ||
104 | pub(crate) end: Idx, | ||
105 | pub(crate) exhausted: bool, | ||
106 | } | ||
107 | |||
108 | #[lang = "RangeToInclusive"] | ||
109 | pub struct RangeToInclusive<Idx> { | ||
110 | pub end: Idx, | ||
111 | } | ||
112 | } | ||
113 | pub use self::range::{Range, RangeFrom, RangeFull, RangeTo}; | ||
114 | pub use self::range::{RangeInclusive, RangeToInclusive}; | ||
115 | // endregion:range | ||
116 | |||
117 | // region:fn | ||
118 | mod function { | ||
119 | #[lang = "fn"] | ||
120 | #[fundamental] | ||
121 | pub trait Fn<Args>: FnMut<Args> {} | ||
122 | |||
123 | #[lang = "fn_mut"] | ||
124 | #[fundamental] | ||
125 | pub trait FnMut<Args>: FnOnce<Args> {} | ||
126 | |||
127 | #[lang = "fn_once"] | ||
128 | #[fundamental] | ||
129 | pub trait FnOnce<Args> { | ||
130 | #[lang = "fn_once_output"] | ||
131 | type Output; | ||
132 | } | ||
133 | } | ||
134 | pub use self::function::{Fn, FnMut, FnOnce}; | ||
135 | // endregion:fn | ||
136 | } | ||
137 | |||
138 | // region:slice | ||
139 | pub mod slice { | ||
140 | #[lang = "slice"] | ||
141 | impl<T> [T] { | ||
142 | pub fn len(&self) -> usize { | ||
143 | loop {} | ||
144 | } | ||
145 | } | ||
146 | } | ||
147 | // endregion:slice | ||
148 | |||
149 | // region:option | ||
150 | pub mod option { | ||
151 | pub enum Option<T> { | ||
152 | #[lang = "None"] | ||
153 | None, | ||
154 | #[lang = "Some"] | ||
155 | Some(T), | ||
156 | } | ||
157 | } | ||
158 | // endregion:option | ||
159 | |||
160 | // region:result | ||
161 | pub mod result { | ||
162 | pub enum Result<T, E> { | ||
163 | #[lang = "Ok"] | ||
164 | Ok(T), | ||
165 | #[lang = "Err"] | ||
166 | Err(E), | ||
167 | } | ||
168 | } | ||
169 | // endregion:result | ||
170 | |||
171 | // region:pin | ||
172 | pub mod pin { | ||
173 | #[lang = "pin"] | ||
174 | #[fundamental] | ||
175 | pub struct Pin<P> { | ||
176 | pointer: P, | ||
177 | } | ||
178 | } | ||
179 | // endregion:pin | ||
180 | |||
181 | // region:future | ||
182 | pub mod future { | ||
183 | use crate::{ | ||
184 | pin::Pin, | ||
185 | task::{Context, Poll}, | ||
186 | }; | ||
187 | |||
188 | #[lang = "future_trait"] | ||
189 | pub trait Future { | ||
190 | type Output; | ||
191 | #[lang = "poll"] | ||
192 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>; | ||
193 | } | ||
194 | } | ||
195 | pub mod task { | ||
196 | pub enum Poll<T> { | ||
197 | #[lang = "Ready"] | ||
198 | Ready(T), | ||
199 | #[lang = "Pending"] | ||
200 | Pending, | ||
201 | } | ||
202 | |||
203 | pub struct Context<'a> { | ||
204 | waker: &'a (), | ||
205 | } | ||
206 | } | ||
207 | // endregion:future | ||
208 | |||
209 | pub mod prelude { | ||
210 | pub mod v1 { | ||
211 | pub use crate::{ | ||
212 | marker::Sized, // :sized | ||
213 | ops::{Fn, FnMut, FnOnce}, // :fn | ||
214 | option::Option::{self, None, Some}, // :option | ||
215 | result::Result::{self, Err, Ok}, // :result | ||
216 | }; | ||
217 | } | ||
218 | |||
219 | pub mod rust_2015 { | ||
220 | pub use super::v1::*; | ||
221 | } | ||
222 | |||
223 | pub mod rust_2018 { | ||
224 | pub use super::v1::*; | ||
225 | } | ||
226 | |||
227 | pub mod rust_2021 { | ||
228 | pub use super::v1::*; | ||
229 | } | ||
230 | } | ||
231 | |||
232 | #[prelude_import] | ||
233 | #[allow(unused)] | ||
234 | use prelude::v1::*; | ||
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 |