diff options
Diffstat (limited to 'crates')
186 files changed, 9452 insertions, 5947 deletions
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs index 8d4641355..0132565e4 100644 --- a/crates/base_db/src/fixture.rs +++ b/crates/base_db/src/fixture.rs | |||
@@ -1,62 +1,4 @@ | |||
1 | //! Fixtures are strings containing rust source code with optional metadata. | 1 | //! A set of high-level utility fixture methods to use in tests. |
2 | //! A fixture without metadata is parsed into a single source file. | ||
3 | //! Use this to test functionality local to one file. | ||
4 | //! | ||
5 | //! Simple Example: | ||
6 | //! ``` | ||
7 | //! r#" | ||
8 | //! fn main() { | ||
9 | //! println!("Hello World") | ||
10 | //! } | ||
11 | //! "# | ||
12 | //! ``` | ||
13 | //! | ||
14 | //! Metadata can be added to a fixture after a `//-` comment. | ||
15 | //! The basic form is specifying filenames, | ||
16 | //! which is also how to define multiple files in a single test fixture | ||
17 | //! | ||
18 | //! Example using two files in the same crate: | ||
19 | //! ``` | ||
20 | //! " | ||
21 | //! //- /main.rs | ||
22 | //! mod foo; | ||
23 | //! fn main() { | ||
24 | //! foo::bar(); | ||
25 | //! } | ||
26 | //! | ||
27 | //! //- /foo.rs | ||
28 | //! pub fn bar() {} | ||
29 | //! " | ||
30 | //! ``` | ||
31 | //! | ||
32 | //! Example using two crates with one file each, with one crate depending on the other: | ||
33 | //! ``` | ||
34 | //! r#" | ||
35 | //! //- /main.rs crate:a deps:b | ||
36 | //! fn main() { | ||
37 | //! b::foo(); | ||
38 | //! } | ||
39 | //! //- /lib.rs crate:b | ||
40 | //! pub fn b() { | ||
41 | //! println!("Hello World") | ||
42 | //! } | ||
43 | //! "# | ||
44 | //! ``` | ||
45 | //! | ||
46 | //! Metadata allows specifying all settings and variables | ||
47 | //! that are available in a real rust project: | ||
48 | //! - crate names via `crate:cratename` | ||
49 | //! - dependencies via `deps:dep1,dep2` | ||
50 | //! - configuration settings via `cfg:dbg=false,opt_level=2` | ||
51 | //! - environment variables via `env:PATH=/bin,RUST_LOG=debug` | ||
52 | //! | ||
53 | //! Example using all available metadata: | ||
54 | //! ``` | ||
55 | //! " | ||
56 | //! //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo | ||
57 | //! fn insert_source_code_here() {} | ||
58 | //! " | ||
59 | //! ``` | ||
60 | use std::{mem, str::FromStr, sync::Arc}; | 2 | use std::{mem, str::FromStr, sync::Arc}; |
61 | 3 | ||
62 | use cfg::CfgOptions; | 4 | use cfg::CfgOptions; |
@@ -93,7 +35,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { | |||
93 | fn with_position(ra_fixture: &str) -> (Self, FilePosition) { | 35 | fn with_position(ra_fixture: &str) -> (Self, FilePosition) { |
94 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); | 36 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); |
95 | let offset = match range_or_offset { | 37 | let offset = match range_or_offset { |
96 | RangeOrOffset::Range(_) => panic!(), | 38 | RangeOrOffset::Range(_) => panic!("Expected a cursor position, got a range instead"), |
97 | RangeOrOffset::Offset(it) => it, | 39 | RangeOrOffset::Offset(it) => it, |
98 | }; | 40 | }; |
99 | (db, FilePosition { file_id, offset }) | 41 | (db, FilePosition { file_id, offset }) |
@@ -103,7 +45,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { | |||
103 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); | 45 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); |
104 | let range = match range_or_offset { | 46 | let range = match range_or_offset { |
105 | RangeOrOffset::Range(it) => it, | 47 | RangeOrOffset::Range(it) => it, |
106 | RangeOrOffset::Offset(_) => panic!(), | 48 | RangeOrOffset::Offset(_) => panic!("Expected a cursor range, got a position instead"), |
107 | }; | 49 | }; |
108 | (db, FileRange { file_id, range }) | 50 | (db, FileRange { file_id, range }) |
109 | } | 51 | } |
@@ -112,7 +54,9 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { | |||
112 | let fixture = ChangeFixture::parse(ra_fixture); | 54 | let fixture = ChangeFixture::parse(ra_fixture); |
113 | let mut db = Self::default(); | 55 | let mut db = Self::default(); |
114 | fixture.change.apply(&mut db); | 56 | fixture.change.apply(&mut db); |
115 | let (file_id, range_or_offset) = fixture.file_position.unwrap(); | 57 | let (file_id, range_or_offset) = fixture |
58 | .file_position | ||
59 | .expect("Could not find file position in fixture. Did you forget to add an `$0`?"); | ||
116 | (db, file_id, range_or_offset) | 60 | (db, file_id, range_or_offset) |
117 | } | 61 | } |
118 | 62 | ||
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs index 07628935f..0ef77ef5d 100644 --- a/crates/base_db/src/input.rs +++ b/crates/base_db/src/input.rs | |||
@@ -239,6 +239,7 @@ impl CrateGraph { | |||
239 | name: CrateName, | 239 | name: CrateName, |
240 | to: CrateId, | 240 | to: CrateId, |
241 | ) -> Result<(), CyclicDependenciesError> { | 241 | ) -> Result<(), CyclicDependenciesError> { |
242 | let _p = profile::span("add_dep"); | ||
242 | if self.dfs_find(from, to, &mut FxHashSet::default()) { | 243 | if self.dfs_find(from, to, &mut FxHashSet::default()) { |
243 | return Err(CyclicDependenciesError { | 244 | return Err(CyclicDependenciesError { |
244 | from: (from, self[from].display_name.clone()), | 245 | from: (from, self[from].display_name.clone()), |
diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml index 2a1a21b28..18b9ce7df 100644 --- a/crates/flycheck/Cargo.toml +++ b/crates/flycheck/Cargo.toml | |||
@@ -13,6 +13,7 @@ doctest = false | |||
13 | crossbeam-channel = "0.5.0" | 13 | crossbeam-channel = "0.5.0" |
14 | log = "0.4.8" | 14 | log = "0.4.8" |
15 | cargo_metadata = "0.13" | 15 | cargo_metadata = "0.13" |
16 | serde = { version = "1.0.106", features = ["derive"] } | ||
16 | serde_json = "1.0.48" | 17 | serde_json = "1.0.48" |
17 | jod-thread = "0.1.1" | 18 | jod-thread = "0.1.1" |
18 | 19 | ||
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index e2a59497a..1682d8bde 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs | |||
@@ -4,13 +4,14 @@ | |||
4 | 4 | ||
5 | use std::{ | 5 | use std::{ |
6 | fmt, | 6 | fmt, |
7 | io::{self, BufReader}, | 7 | io::{self, BufRead, BufReader}, |
8 | path::PathBuf, | 8 | path::PathBuf, |
9 | process::{self, Command, Stdio}, | 9 | process::{self, Command, Stdio}, |
10 | time::Duration, | 10 | time::Duration, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; | 13 | use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; |
14 | use serde::Deserialize; | ||
14 | use stdx::JodChild; | 15 | use stdx::JodChild; |
15 | 16 | ||
16 | pub use cargo_metadata::diagnostic::{ | 17 | pub use cargo_metadata::diagnostic::{ |
@@ -128,7 +129,7 @@ struct FlycheckActor { | |||
128 | 129 | ||
129 | enum Event { | 130 | enum Event { |
130 | Restart(Restart), | 131 | Restart(Restart), |
131 | CheckEvent(Option<cargo_metadata::Message>), | 132 | CheckEvent(Option<CargoMessage>), |
132 | } | 133 | } |
133 | 134 | ||
134 | impl FlycheckActor { | 135 | impl FlycheckActor { |
@@ -180,21 +181,16 @@ impl FlycheckActor { | |||
180 | self.progress(Progress::DidFinish(res)); | 181 | self.progress(Progress::DidFinish(res)); |
181 | } | 182 | } |
182 | Event::CheckEvent(Some(message)) => match message { | 183 | Event::CheckEvent(Some(message)) => match message { |
183 | cargo_metadata::Message::CompilerArtifact(msg) => { | 184 | CargoMessage::CompilerArtifact(msg) => { |
184 | self.progress(Progress::DidCheckCrate(msg.target.name)); | 185 | self.progress(Progress::DidCheckCrate(msg.target.name)); |
185 | } | 186 | } |
186 | 187 | ||
187 | cargo_metadata::Message::CompilerMessage(msg) => { | 188 | CargoMessage::Diagnostic(msg) => { |
188 | self.send(Message::AddDiagnostic { | 189 | self.send(Message::AddDiagnostic { |
189 | workspace_root: self.workspace_root.clone(), | 190 | workspace_root: self.workspace_root.clone(), |
190 | diagnostic: msg.message, | 191 | diagnostic: msg, |
191 | }); | 192 | }); |
192 | } | 193 | } |
193 | |||
194 | cargo_metadata::Message::BuildScriptExecuted(_) | ||
195 | | cargo_metadata::Message::BuildFinished(_) | ||
196 | | cargo_metadata::Message::TextLine(_) | ||
197 | | _ => {} | ||
198 | }, | 194 | }, |
199 | } | 195 | } |
200 | } | 196 | } |
@@ -261,7 +257,7 @@ struct CargoHandle { | |||
261 | child: JodChild, | 257 | child: JodChild, |
262 | #[allow(unused)] | 258 | #[allow(unused)] |
263 | thread: jod_thread::JoinHandle<io::Result<bool>>, | 259 | thread: jod_thread::JoinHandle<io::Result<bool>>, |
264 | receiver: Receiver<cargo_metadata::Message>, | 260 | receiver: Receiver<CargoMessage>, |
265 | } | 261 | } |
266 | 262 | ||
267 | impl CargoHandle { | 263 | impl CargoHandle { |
@@ -294,14 +290,11 @@ impl CargoHandle { | |||
294 | 290 | ||
295 | struct CargoActor { | 291 | struct CargoActor { |
296 | child_stdout: process::ChildStdout, | 292 | child_stdout: process::ChildStdout, |
297 | sender: Sender<cargo_metadata::Message>, | 293 | sender: Sender<CargoMessage>, |
298 | } | 294 | } |
299 | 295 | ||
300 | impl CargoActor { | 296 | impl CargoActor { |
301 | fn new( | 297 | fn new(child_stdout: process::ChildStdout, sender: Sender<CargoMessage>) -> CargoActor { |
302 | child_stdout: process::ChildStdout, | ||
303 | sender: Sender<cargo_metadata::Message>, | ||
304 | ) -> CargoActor { | ||
305 | CargoActor { child_stdout, sender } | 298 | CargoActor { child_stdout, sender } |
306 | } | 299 | } |
307 | fn run(self) -> io::Result<bool> { | 300 | fn run(self) -> io::Result<bool> { |
@@ -315,7 +308,7 @@ impl CargoActor { | |||
315 | // erroneus output. | 308 | // erroneus output. |
316 | let stdout = BufReader::new(self.child_stdout); | 309 | let stdout = BufReader::new(self.child_stdout); |
317 | let mut read_at_least_one_message = false; | 310 | let mut read_at_least_one_message = false; |
318 | for message in cargo_metadata::Message::parse_stream(stdout) { | 311 | for message in stdout.lines() { |
319 | let message = match message { | 312 | let message = match message { |
320 | Ok(message) => message, | 313 | Ok(message) => message, |
321 | Err(err) => { | 314 | Err(err) => { |
@@ -326,13 +319,44 @@ impl CargoActor { | |||
326 | 319 | ||
327 | read_at_least_one_message = true; | 320 | read_at_least_one_message = true; |
328 | 321 | ||
329 | // Skip certain kinds of messages to only spend time on what's useful | 322 | // Try to deserialize a message from Cargo or Rustc. |
330 | match &message { | 323 | let mut deserializer = serde_json::Deserializer::from_str(&message); |
331 | cargo_metadata::Message::CompilerArtifact(artifact) if artifact.fresh => (), | 324 | deserializer.disable_recursion_limit(); |
332 | cargo_metadata::Message::BuildScriptExecuted(_) => (), | 325 | if let Ok(message) = JsonMessage::deserialize(&mut deserializer) { |
333 | _ => self.sender.send(message).unwrap(), | 326 | match message { |
327 | // Skip certain kinds of messages to only spend time on what's useful | ||
328 | JsonMessage::Cargo(message) => match message { | ||
329 | cargo_metadata::Message::CompilerArtifact(artifact) if !artifact.fresh => { | ||
330 | self.sender.send(CargoMessage::CompilerArtifact(artifact)).unwrap() | ||
331 | } | ||
332 | cargo_metadata::Message::CompilerMessage(msg) => { | ||
333 | self.sender.send(CargoMessage::Diagnostic(msg.message)).unwrap() | ||
334 | } | ||
335 | |||
336 | cargo_metadata::Message::CompilerArtifact(_) | ||
337 | | cargo_metadata::Message::BuildScriptExecuted(_) | ||
338 | | cargo_metadata::Message::BuildFinished(_) | ||
339 | | cargo_metadata::Message::TextLine(_) | ||
340 | | _ => (), | ||
341 | }, | ||
342 | JsonMessage::Rustc(message) => { | ||
343 | self.sender.send(CargoMessage::Diagnostic(message)).unwrap() | ||
344 | } | ||
345 | } | ||
334 | } | 346 | } |
335 | } | 347 | } |
336 | Ok(read_at_least_one_message) | 348 | Ok(read_at_least_one_message) |
337 | } | 349 | } |
338 | } | 350 | } |
351 | |||
352 | enum CargoMessage { | ||
353 | CompilerArtifact(cargo_metadata::Artifact), | ||
354 | Diagnostic(Diagnostic), | ||
355 | } | ||
356 | |||
357 | #[derive(Deserialize)] | ||
358 | #[serde(untagged)] | ||
359 | enum JsonMessage { | ||
360 | Cargo(cargo_metadata::Message), | ||
361 | Rustc(Diagnostic), | ||
362 | } | ||
diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml index 2ef5bcbc9..9e329656f 100644 --- a/crates/hir/Cargo.toml +++ b/crates/hir/Cargo.toml | |||
@@ -13,7 +13,7 @@ doctest = false | |||
13 | log = "0.4.8" | 13 | log = "0.4.8" |
14 | rustc-hash = "1.1.0" | 14 | rustc-hash = "1.1.0" |
15 | either = "1.5.3" | 15 | either = "1.5.3" |
16 | arrayvec = "0.6" | 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 | 19 | ||
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 993772aac..01a4d205f 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs | |||
@@ -9,6 +9,7 @@ use hir_ty::display::{ | |||
9 | write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError, | 9 | write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError, |
10 | HirFormatter, | 10 | HirFormatter, |
11 | }; | 11 | }; |
12 | use hir_ty::Interner; | ||
12 | use syntax::ast::{self, NameOwner}; | 13 | use syntax::ast::{self, NameOwner}; |
13 | 14 | ||
14 | use crate::{ | 15 | use crate::{ |
@@ -235,7 +236,8 @@ impl HirDisplay for TypeParam { | |||
235 | write!(f, "{}", self.name(f.db))?; | 236 | write!(f, "{}", self.name(f.db))?; |
236 | let bounds = f.db.generic_predicates_for_param(self.id); | 237 | let bounds = f.db.generic_predicates_for_param(self.id); |
237 | let substs = TyBuilder::type_params_subst(f.db, self.id.parent); | 238 | let substs = TyBuilder::type_params_subst(f.db, self.id.parent); |
238 | let predicates = bounds.iter().cloned().map(|b| b.subst(&substs)).collect::<Vec<_>>(); | 239 | let predicates = |
240 | bounds.iter().cloned().map(|b| b.substitute(&Interner, &substs)).collect::<Vec<_>>(); | ||
239 | if !(predicates.is_empty() || f.omit_verbose_types()) { | 241 | if !(predicates.is_empty() || f.omit_verbose_types()) { |
240 | write_bounds_like_dyn_trait_with_prefix(":", &predicates, f)?; | 242 | write_bounds_like_dyn_trait_with_prefix(":", &predicates, f)?; |
241 | } | 243 | } |
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 19901ed33..0acfa582a 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -53,12 +53,14 @@ use hir_def::{ | |||
53 | use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind}; | 53 | use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind}; |
54 | use hir_ty::{ | 54 | use hir_ty::{ |
55 | autoderef, could_unify, | 55 | autoderef, could_unify, |
56 | method_resolution::{self, TyFingerprint}, | 56 | method_resolution::{self, def_crates, TyFingerprint}, |
57 | primitive::UintTy, | 57 | primitive::UintTy, |
58 | traits::{FnTrait, Solution, SolutionVariables}, | 58 | subst_prefix, |
59 | traits::FnTrait, | ||
59 | AliasEq, AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, | 60 | AliasEq, AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, |
60 | DebruijnIndex, InEnvironment, Interner, QuantifiedWhereClause, Scalar, Substitution, | 61 | DebruijnIndex, InEnvironment, Interner, QuantifiedWhereClause, Scalar, Solution, Substitution, |
61 | TraitEnvironment, Ty, TyBuilder, TyDefId, TyKind, TyVariableKind, WhereClause, | 62 | TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, TyVariableKind, |
63 | WhereClause, | ||
62 | }; | 64 | }; |
63 | use itertools::Itertools; | 65 | use itertools::Itertools; |
64 | use rustc_hash::FxHashSet; | 66 | use rustc_hash::FxHashSet; |
@@ -515,7 +517,7 @@ impl Field { | |||
515 | VariantDef::Variant(it) => it.parent.id.into(), | 517 | VariantDef::Variant(it) => it.parent.id.into(), |
516 | }; | 518 | }; |
517 | let substs = TyBuilder::type_params_subst(db, generic_def_id); | 519 | let substs = TyBuilder::type_params_subst(db, generic_def_id); |
518 | let ty = db.field_types(var_id)[self.id].clone().subst(&substs); | 520 | let ty = db.field_types(var_id)[self.id].clone().substitute(&Interner, &substs); |
519 | Type::new(db, self.parent.module(db).id.krate(), var_id, ty) | 521 | Type::new(db, self.parent.module(db).id.krate(), var_id, ty) |
520 | } | 522 | } |
521 | 523 | ||
@@ -701,7 +703,7 @@ impl_from!(Struct, Union, Enum for Adt); | |||
701 | impl Adt { | 703 | impl Adt { |
702 | pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { | 704 | pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { |
703 | let subst = db.generic_defaults(self.into()); | 705 | let subst = db.generic_defaults(self.into()); |
704 | subst.iter().any(|ty| ty.value.is_unknown()) | 706 | subst.iter().any(|ty| ty.skip_binders().is_unknown()) |
705 | } | 707 | } |
706 | 708 | ||
707 | /// Turns this ADT into a type. Any type parameters of the ADT will be | 709 | /// Turns this ADT into a type. Any type parameters of the ADT will be |
@@ -1088,7 +1090,7 @@ pub struct TypeAlias { | |||
1088 | impl TypeAlias { | 1090 | impl TypeAlias { |
1089 | pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { | 1091 | pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { |
1090 | let subst = db.generic_defaults(self.id.into()); | 1092 | let subst = db.generic_defaults(self.id.into()); |
1091 | subst.iter().any(|ty| ty.value.is_unknown()) | 1093 | subst.iter().any(|ty| ty.skip_binders().is_unknown()) |
1092 | } | 1094 | } |
1093 | 1095 | ||
1094 | pub fn module(self, db: &dyn HirDatabase) -> Module { | 1096 | pub fn module(self, db: &dyn HirDatabase) -> Module { |
@@ -1502,7 +1504,7 @@ impl TypeParam { | |||
1502 | let krate = self.id.parent.module(db.upcast()).krate(); | 1504 | let krate = self.id.parent.module(db.upcast()).krate(); |
1503 | let ty = params.get(local_idx)?.clone(); | 1505 | let ty = params.get(local_idx)?.clone(); |
1504 | let subst = TyBuilder::type_params_subst(db, self.id.parent); | 1506 | let subst = TyBuilder::type_params_subst(db, self.id.parent); |
1505 | let ty = ty.subst(&subst.prefix(local_idx)); | 1507 | let ty = ty.substitute(&Interner, &subst_prefix(&subst, local_idx)); |
1506 | Some(Type::new_with_resolver_inner(db, krate, &resolver, ty)) | 1508 | Some(Type::new_with_resolver_inner(db, krate, &resolver, ty)) |
1507 | } | 1509 | } |
1508 | } | 1510 | } |
@@ -1567,7 +1569,7 @@ impl Impl { | |||
1567 | } | 1569 | } |
1568 | 1570 | ||
1569 | pub fn all_for_type(db: &dyn HirDatabase, Type { krate, ty, .. }: Type) -> Vec<Impl> { | 1571 | pub fn all_for_type(db: &dyn HirDatabase, Type { krate, ty, .. }: Type) -> Vec<Impl> { |
1570 | let def_crates = match ty.def_crates(db, krate) { | 1572 | let def_crates = match def_crates(db, &ty, krate) { |
1571 | Some(def_crates) => def_crates, | 1573 | Some(def_crates) => def_crates, |
1572 | None => return Vec::new(), | 1574 | None => return Vec::new(), |
1573 | }; | 1575 | }; |
@@ -1578,11 +1580,24 @@ impl Impl { | |||
1578 | ty.equals_ctor(rref.as_ref().map_or(&self_ty.ty, |it| &it.ty)) | 1580 | ty.equals_ctor(rref.as_ref().map_or(&self_ty.ty, |it| &it.ty)) |
1579 | }; | 1581 | }; |
1580 | 1582 | ||
1583 | let fp = TyFingerprint::for_inherent_impl(&ty); | ||
1584 | let fp = if let Some(fp) = fp { | ||
1585 | fp | ||
1586 | } else { | ||
1587 | return Vec::new(); | ||
1588 | }; | ||
1589 | |||
1581 | let mut all = Vec::new(); | 1590 | let mut all = Vec::new(); |
1582 | def_crates.iter().for_each(|&id| { | 1591 | def_crates.iter().for_each(|&id| { |
1583 | all.extend(db.inherent_impls_in_crate(id).all_impls().map(Self::from).filter(filter)) | 1592 | all.extend( |
1593 | db.inherent_impls_in_crate(id) | ||
1594 | .for_self_ty(&ty) | ||
1595 | .into_iter() | ||
1596 | .cloned() | ||
1597 | .map(Self::from) | ||
1598 | .filter(filter), | ||
1599 | ) | ||
1584 | }); | 1600 | }); |
1585 | let fp = TyFingerprint::for_impl(&ty); | ||
1586 | for id in def_crates | 1601 | for id in def_crates |
1587 | .iter() | 1602 | .iter() |
1588 | .flat_map(|&id| Crate { id }.transitive_reverse_dependencies(db)) | 1603 | .flat_map(|&id| Crate { id }.transitive_reverse_dependencies(db)) |
@@ -1590,13 +1605,12 @@ impl Impl { | |||
1590 | .chain(def_crates.iter().copied()) | 1605 | .chain(def_crates.iter().copied()) |
1591 | .unique() | 1606 | .unique() |
1592 | { | 1607 | { |
1593 | match fp { | 1608 | all.extend( |
1594 | Some(fp) => all.extend( | 1609 | db.trait_impls_in_crate(id) |
1595 | db.trait_impls_in_crate(id).for_self_ty(fp).map(Self::from).filter(filter), | 1610 | .for_self_ty_without_blanket_impls(fp) |
1596 | ), | 1611 | .map(Self::from) |
1597 | None => all | 1612 | .filter(filter), |
1598 | .extend(db.trait_impls_in_crate(id).all_impls().map(Self::from).filter(filter)), | 1613 | ); |
1599 | } | ||
1600 | } | 1614 | } |
1601 | all | 1615 | all |
1602 | } | 1616 | } |
@@ -1789,7 +1803,7 @@ impl Type { | |||
1789 | .build(); | 1803 | .build(); |
1790 | 1804 | ||
1791 | let goal = Canonical { | 1805 | let goal = Canonical { |
1792 | value: hir_ty::InEnvironment::new(self.env.env.clone(), trait_ref.cast(&Interner)), | 1806 | value: hir_ty::InEnvironment::new(&self.env.env, trait_ref.cast(&Interner)), |
1793 | binders: CanonicalVarKinds::empty(&Interner), | 1807 | binders: CanonicalVarKinds::empty(&Interner), |
1794 | }; | 1808 | }; |
1795 | 1809 | ||
@@ -1806,9 +1820,9 @@ impl Type { | |||
1806 | .push(self.ty.clone()) | 1820 | .push(self.ty.clone()) |
1807 | .fill(args.iter().map(|t| t.ty.clone())) | 1821 | .fill(args.iter().map(|t| t.ty.clone())) |
1808 | .build(); | 1822 | .build(); |
1809 | let goal = Canonical::new( | 1823 | let goal = hir_ty::make_canonical( |
1810 | InEnvironment::new( | 1824 | InEnvironment::new( |
1811 | self.env.env.clone(), | 1825 | &self.env.env, |
1812 | AliasEq { | 1826 | AliasEq { |
1813 | alias: AliasTy::Projection(projection), | 1827 | alias: AliasTy::Projection(projection), |
1814 | ty: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)) | 1828 | ty: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)) |
@@ -1820,9 +1834,10 @@ impl Type { | |||
1820 | ); | 1834 | ); |
1821 | 1835 | ||
1822 | match db.trait_solve(self.krate, goal)? { | 1836 | match db.trait_solve(self.krate, goal)? { |
1823 | Solution::Unique(SolutionVariables(subst)) => subst | 1837 | Solution::Unique(s) => s |
1824 | .value | 1838 | .value |
1825 | .interned(&Interner) | 1839 | .subst |
1840 | .as_slice(&Interner) | ||
1826 | .first() | 1841 | .first() |
1827 | .map(|ty| self.derived(ty.assert_ty_ref(&Interner).clone())), | 1842 | .map(|ty| self.derived(ty.assert_ty_ref(&Interner).clone())), |
1828 | Solution::Ambig(_) => None, | 1843 | Solution::Ambig(_) => None, |
@@ -1875,7 +1890,7 @@ impl Type { | |||
1875 | 1890 | ||
1876 | fn go(ty: &Ty) -> bool { | 1891 | fn go(ty: &Ty) -> bool { |
1877 | match ty.kind(&Interner) { | 1892 | match ty.kind(&Interner) { |
1878 | TyKind::Unknown => true, | 1893 | TyKind::Error => true, |
1879 | 1894 | ||
1880 | TyKind::Adt(_, substs) | 1895 | TyKind::Adt(_, substs) |
1881 | | TyKind::AssociatedType(_, substs) | 1896 | | TyKind::AssociatedType(_, substs) |
@@ -1886,9 +1901,10 @@ impl Type { | |||
1886 | substs.iter(&Interner).filter_map(|a| a.ty(&Interner)).any(go) | 1901 | substs.iter(&Interner).filter_map(|a| a.ty(&Interner)).any(go) |
1887 | } | 1902 | } |
1888 | 1903 | ||
1889 | TyKind::Array(ty) | TyKind::Slice(ty) | TyKind::Raw(_, ty) | TyKind::Ref(_, ty) => { | 1904 | TyKind::Array(ty, _) |
1890 | go(ty) | 1905 | | TyKind::Slice(ty) |
1891 | } | 1906 | | TyKind::Raw(_, ty) |
1907 | | TyKind::Ref(_, _, ty) => go(ty), | ||
1892 | 1908 | ||
1893 | TyKind::Scalar(_) | 1909 | TyKind::Scalar(_) |
1894 | | TyKind::Str | 1910 | | TyKind::Str |
@@ -1899,7 +1915,9 @@ impl Type { | |||
1899 | | TyKind::Dyn(_) | 1915 | | TyKind::Dyn(_) |
1900 | | TyKind::Function(_) | 1916 | | TyKind::Function(_) |
1901 | | TyKind::Alias(_) | 1917 | | TyKind::Alias(_) |
1902 | | TyKind::ForeignType(_) => false, | 1918 | | TyKind::Foreign(_) |
1919 | | TyKind::Generator(..) | ||
1920 | | TyKind::GeneratorWitness(..) => false, | ||
1903 | } | 1921 | } |
1904 | } | 1922 | } |
1905 | } | 1923 | } |
@@ -1915,7 +1933,7 @@ impl Type { | |||
1915 | .iter() | 1933 | .iter() |
1916 | .map(|(local_id, ty)| { | 1934 | .map(|(local_id, ty)| { |
1917 | let def = Field { parent: variant_id.into(), id: local_id }; | 1935 | let def = Field { parent: variant_id.into(), id: local_id }; |
1918 | let ty = ty.clone().subst(substs); | 1936 | let ty = ty.clone().substitute(&Interner, substs); |
1919 | (def, self.derived(ty)) | 1937 | (def, self.derived(ty)) |
1920 | }) | 1938 | }) |
1921 | .collect() | 1939 | .collect() |
@@ -1952,7 +1970,7 @@ impl Type { | |||
1952 | krate: Crate, | 1970 | krate: Crate, |
1953 | mut callback: impl FnMut(AssocItem) -> Option<T>, | 1971 | mut callback: impl FnMut(AssocItem) -> Option<T>, |
1954 | ) -> Option<T> { | 1972 | ) -> Option<T> { |
1955 | for krate in self.ty.def_crates(db, krate.id)? { | 1973 | for krate in def_crates(db, &self.ty, krate.id)? { |
1956 | let impls = db.inherent_impls_in_crate(krate); | 1974 | let impls = db.inherent_impls_in_crate(krate); |
1957 | 1975 | ||
1958 | for impl_def in impls.for_self_ty(&self.ty) { | 1976 | for impl_def in impls.for_self_ty(&self.ty) { |
@@ -1969,9 +1987,9 @@ impl Type { | |||
1969 | pub fn type_parameters(&self) -> impl Iterator<Item = Type> + '_ { | 1987 | pub fn type_parameters(&self) -> impl Iterator<Item = Type> + '_ { |
1970 | self.ty | 1988 | self.ty |
1971 | .strip_references() | 1989 | .strip_references() |
1972 | .substs() | 1990 | .as_adt() |
1973 | .into_iter() | 1991 | .into_iter() |
1974 | .flat_map(|substs| substs.iter(&Interner)) | 1992 | .flat_map(|(_, substs)| substs.iter(&Interner)) |
1975 | .filter_map(|arg| arg.ty(&Interner).cloned()) | 1993 | .filter_map(|arg| arg.ty(&Interner).cloned()) |
1976 | .map(move |ty| self.derived(ty)) | 1994 | .map(move |ty| self.derived(ty)) |
1977 | } | 1995 | } |
@@ -2048,6 +2066,18 @@ impl Type { | |||
2048 | self.ty.dyn_trait().map(Into::into) | 2066 | self.ty.dyn_trait().map(Into::into) |
2049 | } | 2067 | } |
2050 | 2068 | ||
2069 | /// If a type can be represented as `dyn Trait`, returns all traits accessible via this type, | ||
2070 | /// or an empty iterator otherwise. | ||
2071 | pub fn applicable_inherent_traits<'a>( | ||
2072 | &'a self, | ||
2073 | db: &'a dyn HirDatabase, | ||
2074 | ) -> impl Iterator<Item = Trait> + 'a { | ||
2075 | self.autoderef(db) | ||
2076 | .filter_map(|derefed_type| derefed_type.ty.dyn_trait()) | ||
2077 | .flat_map(move |dyn_trait_id| hir_ty::all_super_traits(db.upcast(), dyn_trait_id)) | ||
2078 | .map(Trait::from) | ||
2079 | } | ||
2080 | |||
2051 | pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> { | 2081 | pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> { |
2052 | self.ty.impl_trait_bounds(db).map(|it| { | 2082 | self.ty.impl_trait_bounds(db).map(|it| { |
2053 | it.into_iter() | 2083 | it.into_iter() |
@@ -2112,18 +2142,22 @@ impl Type { | |||
2112 | fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) { | 2142 | fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) { |
2113 | let ty = type_.ty.strip_references(); | 2143 | let ty = type_.ty.strip_references(); |
2114 | match ty.kind(&Interner) { | 2144 | match ty.kind(&Interner) { |
2115 | TyKind::Adt(..) => { | 2145 | TyKind::Adt(_, substs) => { |
2116 | cb(type_.derived(ty.clone())); | 2146 | cb(type_.derived(ty.clone())); |
2147 | walk_substs(db, type_, &substs, cb); | ||
2117 | } | 2148 | } |
2118 | TyKind::AssociatedType(..) => { | 2149 | TyKind::AssociatedType(_, substs) => { |
2119 | if let Some(_) = ty.associated_type_parent_trait(db) { | 2150 | if let Some(_) = ty.associated_type_parent_trait(db) { |
2120 | cb(type_.derived(ty.clone())); | 2151 | cb(type_.derived(ty.clone())); |
2121 | } | 2152 | } |
2153 | walk_substs(db, type_, &substs, cb); | ||
2122 | } | 2154 | } |
2123 | TyKind::OpaqueType(..) => { | 2155 | TyKind::OpaqueType(_, subst) => { |
2124 | if let Some(bounds) = ty.impl_trait_bounds(db) { | 2156 | if let Some(bounds) = ty.impl_trait_bounds(db) { |
2125 | walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); | 2157 | walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); |
2126 | } | 2158 | } |
2159 | |||
2160 | walk_substs(db, type_, subst, cb); | ||
2127 | } | 2161 | } |
2128 | TyKind::Alias(AliasTy::Opaque(opaque_ty)) => { | 2162 | TyKind::Alias(AliasTy::Opaque(opaque_ty)) => { |
2129 | if let Some(bounds) = ty.impl_trait_bounds(db) { | 2163 | if let Some(bounds) = ty.impl_trait_bounds(db) { |
@@ -2146,15 +2180,24 @@ impl Type { | |||
2146 | ); | 2180 | ); |
2147 | } | 2181 | } |
2148 | 2182 | ||
2149 | TyKind::Ref(_, ty) | TyKind::Raw(_, ty) | TyKind::Array(ty) | TyKind::Slice(ty) => { | 2183 | TyKind::Ref(_, _, ty) |
2184 | | TyKind::Raw(_, ty) | ||
2185 | | TyKind::Array(ty, _) | ||
2186 | | TyKind::Slice(ty) => { | ||
2150 | walk_type(db, &type_.derived(ty.clone()), cb); | 2187 | walk_type(db, &type_.derived(ty.clone()), cb); |
2151 | } | 2188 | } |
2152 | 2189 | ||
2190 | TyKind::FnDef(_, substs) | ||
2191 | | TyKind::Tuple(_, substs) | ||
2192 | | TyKind::Closure(.., substs) => { | ||
2193 | walk_substs(db, type_, &substs, cb); | ||
2194 | } | ||
2195 | TyKind::Function(hir_ty::FnPointer { substitution, .. }) => { | ||
2196 | walk_substs(db, type_, &substitution.0, cb); | ||
2197 | } | ||
2198 | |||
2153 | _ => {} | 2199 | _ => {} |
2154 | } | 2200 | } |
2155 | if let Some(substs) = ty.substs() { | ||
2156 | walk_substs(db, type_, &substs, cb); | ||
2157 | } | ||
2158 | } | 2201 | } |
2159 | 2202 | ||
2160 | walk_type(db, self, &mut cb); | 2203 | walk_type(db, self, &mut cb); |
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 3bf722d2a..62500602a 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs | |||
@@ -6,10 +6,11 @@ use std::{cell::RefCell, fmt, iter::successors}; | |||
6 | 6 | ||
7 | use base_db::{FileId, FileRange}; | 7 | use base_db::{FileId, FileRange}; |
8 | use hir_def::{ | 8 | use hir_def::{ |
9 | body, | ||
9 | resolver::{self, HasResolver, Resolver, TypeNs}, | 10 | resolver::{self, HasResolver, Resolver, TypeNs}, |
10 | AsMacroCall, FunctionId, TraitId, VariantId, | 11 | AsMacroCall, FunctionId, TraitId, VariantId, |
11 | }; | 12 | }; |
12 | use hir_expand::{hygiene::Hygiene, name::AsName, ExpansionInfo}; | 13 | use hir_expand::{name::AsName, ExpansionInfo}; |
13 | use hir_ty::associated_type_shorthand_candidates; | 14 | use hir_ty::associated_type_shorthand_candidates; |
14 | use itertools::Itertools; | 15 | use itertools::Itertools; |
15 | use rustc_hash::{FxHashMap, FxHashSet}; | 16 | use rustc_hash::{FxHashMap, FxHashSet}; |
@@ -494,9 +495,9 @@ impl<'db> SemanticsImpl<'db> { | |||
494 | fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> { | 495 | fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> { |
495 | // FIXME: this erases Substs | 496 | // FIXME: this erases Substs |
496 | let func = self.resolve_method_call(call)?; | 497 | let func = self.resolve_method_call(call)?; |
497 | let ty = self.db.value_ty(func.into()); | 498 | let (ty, _) = self.db.value_ty(func.into()).into_value_and_skipped_binders(); |
498 | let resolver = self.analyze(call.syntax()).resolver; | 499 | let resolver = self.analyze(call.syntax()).resolver; |
499 | let ty = Type::new_with_resolver(self.db, &resolver, ty.value)?; | 500 | let ty = Type::new_with_resolver(self.db, &resolver, ty)?; |
500 | let mut res = ty.as_callable(self.db)?; | 501 | let mut res = ty.as_callable(self.db)?; |
501 | res.is_bound_method = true; | 502 | res.is_bound_method = true; |
502 | Some(res) | 503 | Some(res) |
@@ -853,8 +854,8 @@ impl<'a> SemanticsScope<'a> { | |||
853 | /// Resolve a path as-if it was written at the given scope. This is | 854 | /// Resolve a path as-if it was written at the given scope. This is |
854 | /// necessary a heuristic, as it doesn't take hygiene into account. | 855 | /// necessary a heuristic, as it doesn't take hygiene into account. |
855 | pub fn speculative_resolve(&self, path: &ast::Path) -> Option<PathResolution> { | 856 | pub fn speculative_resolve(&self, path: &ast::Path) -> Option<PathResolution> { |
856 | let hygiene = Hygiene::new(self.db.upcast(), self.file_id); | 857 | let ctx = body::LowerCtx::new(self.db.upcast(), self.file_id); |
857 | let path = Path::from_src(path.clone(), &hygiene)?; | 858 | let path = Path::from_src(path.clone(), &ctx)?; |
858 | resolve_hir_path(self.db, &self.resolver, &path) | 859 | resolve_hir_path(self.db, &self.resolver, &path) |
859 | } | 860 | } |
860 | } | 861 | } |
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 8e9ea0a03..0895bd6f1 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs | |||
@@ -9,6 +9,7 @@ use std::{iter::once, sync::Arc}; | |||
9 | 9 | ||
10 | use hir_def::{ | 10 | use hir_def::{ |
11 | body::{ | 11 | body::{ |
12 | self, | ||
12 | scope::{ExprScopes, ScopeId}, | 13 | scope::{ExprScopes, ScopeId}, |
13 | Body, BodySourceMap, | 14 | Body, BodySourceMap, |
14 | }, | 15 | }, |
@@ -20,7 +21,7 @@ use hir_def::{ | |||
20 | use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; | 21 | use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; |
21 | use hir_ty::{ | 22 | use hir_ty::{ |
22 | diagnostics::{record_literal_missing_fields, record_pattern_missing_fields}, | 23 | diagnostics::{record_literal_missing_fields, record_pattern_missing_fields}, |
23 | InferenceResult, Substitution, TyLoweringContext, | 24 | InferenceResult, Interner, Substitution, TyExt, TyLoweringContext, |
24 | }; | 25 | }; |
25 | use syntax::{ | 26 | use syntax::{ |
26 | ast::{self, AstNode}, | 27 | ast::{self, AstNode}, |
@@ -161,14 +162,15 @@ impl SourceAnalyzer { | |||
161 | db: &dyn HirDatabase, | 162 | db: &dyn HirDatabase, |
162 | field: &ast::RecordExprField, | 163 | field: &ast::RecordExprField, |
163 | ) -> Option<(Field, Option<Local>)> { | 164 | ) -> Option<(Field, Option<Local>)> { |
164 | let expr_id = | 165 | let record_expr = ast::RecordExpr::cast(field.syntax().parent().and_then(|p| p.parent())?)?; |
165 | self.body_source_map.as_ref()?.node_field(InFile::new(self.file_id, field))?; | 166 | let expr = ast::Expr::from(record_expr); |
167 | let expr_id = self.body_source_map.as_ref()?.node_expr(InFile::new(self.file_id, &expr))?; | ||
166 | 168 | ||
169 | let local_name = field.field_name()?.as_name(); | ||
167 | let local = if field.name_ref().is_some() { | 170 | let local = if field.name_ref().is_some() { |
168 | None | 171 | None |
169 | } else { | 172 | } else { |
170 | let local_name = field.field_name()?.as_name(); | 173 | let path = ModPath::from_segments(PathKind::Plain, once(local_name.clone())); |
171 | let path = ModPath::from_segments(PathKind::Plain, once(local_name)); | ||
172 | match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) { | 174 | match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) { |
173 | Some(ValueNs::LocalBinding(pat_id)) => { | 175 | Some(ValueNs::LocalBinding(pat_id)) => { |
174 | Some(Local { pat_id, parent: self.resolver.body_owner()? }) | 176 | Some(Local { pat_id, parent: self.resolver.body_owner()? }) |
@@ -176,18 +178,24 @@ impl SourceAnalyzer { | |||
176 | _ => None, | 178 | _ => None, |
177 | } | 179 | } |
178 | }; | 180 | }; |
179 | let struct_field = self.infer.as_ref()?.record_field_resolution(expr_id)?; | 181 | let variant = self.infer.as_ref()?.variant_resolution_for_expr(expr_id)?; |
180 | Some((struct_field.into(), local)) | 182 | let variant_data = variant.variant_data(db.upcast()); |
183 | let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? }; | ||
184 | Some((field.into(), local)) | ||
181 | } | 185 | } |
182 | 186 | ||
183 | pub(crate) fn resolve_record_pat_field( | 187 | pub(crate) fn resolve_record_pat_field( |
184 | &self, | 188 | &self, |
185 | _db: &dyn HirDatabase, | 189 | db: &dyn HirDatabase, |
186 | field: &ast::RecordPatField, | 190 | field: &ast::RecordPatField, |
187 | ) -> Option<Field> { | 191 | ) -> Option<Field> { |
188 | let pat_id = self.pat_id(&field.pat()?)?; | 192 | let field_name = field.field_name()?.as_name(); |
189 | let struct_field = self.infer.as_ref()?.record_pat_field_resolution(pat_id)?; | 193 | let record_pat = ast::RecordPat::cast(field.syntax().parent().and_then(|p| p.parent())?)?; |
190 | Some(struct_field.into()) | 194 | let pat_id = self.pat_id(&record_pat.into())?; |
195 | let variant = self.infer.as_ref()?.variant_resolution_for_pat(pat_id)?; | ||
196 | let variant_data = variant.variant_data(db.upcast()); | ||
197 | let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? }; | ||
198 | Some(field.into()) | ||
191 | } | 199 | } |
192 | 200 | ||
193 | pub(crate) fn resolve_macro_call( | 201 | pub(crate) fn resolve_macro_call( |
@@ -195,8 +203,8 @@ impl SourceAnalyzer { | |||
195 | db: &dyn HirDatabase, | 203 | db: &dyn HirDatabase, |
196 | macro_call: InFile<&ast::MacroCall>, | 204 | macro_call: InFile<&ast::MacroCall>, |
197 | ) -> Option<MacroDef> { | 205 | ) -> Option<MacroDef> { |
198 | let hygiene = Hygiene::new(db.upcast(), macro_call.file_id); | 206 | let ctx = body::LowerCtx::new(db.upcast(), macro_call.file_id); |
199 | let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &hygiene))?; | 207 | let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?; |
200 | self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(|it| it.into()) | 208 | self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(|it| it.into()) |
201 | } | 209 | } |
202 | 210 | ||
@@ -274,7 +282,9 @@ impl SourceAnalyzer { | |||
274 | } | 282 | } |
275 | 283 | ||
276 | // This must be a normal source file rather than macro file. | 284 | // This must be a normal source file rather than macro file. |
277 | let hir_path = Path::from_src(path.clone(), &Hygiene::new(db.upcast(), self.file_id))?; | 285 | let hygiene = Hygiene::new(db.upcast(), self.file_id); |
286 | let ctx = body::LowerCtx::with_hygiene(&hygiene); | ||
287 | let hir_path = Path::from_src(path.clone(), &ctx)?; | ||
278 | 288 | ||
279 | // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we | 289 | // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we |
280 | // trying to resolve foo::bar. | 290 | // trying to resolve foo::bar. |
@@ -299,7 +309,7 @@ impl SourceAnalyzer { | |||
299 | let infer = self.infer.as_ref()?; | 309 | let infer = self.infer.as_ref()?; |
300 | 310 | ||
301 | let expr_id = self.expr_id(db, &literal.clone().into())?; | 311 | let expr_id = self.expr_id(db, &literal.clone().into())?; |
302 | let substs = infer.type_of_expr[expr_id].substs()?; | 312 | let substs = infer.type_of_expr[expr_id].as_adt()?.1; |
303 | 313 | ||
304 | let (variant, missing_fields, _exhaustive) = | 314 | let (variant, missing_fields, _exhaustive) = |
305 | record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?; | 315 | record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?; |
@@ -317,7 +327,7 @@ impl SourceAnalyzer { | |||
317 | let infer = self.infer.as_ref()?; | 327 | let infer = self.infer.as_ref()?; |
318 | 328 | ||
319 | let pat_id = self.pat_id(&pattern.clone().into())?; | 329 | let pat_id = self.pat_id(&pattern.clone().into())?; |
320 | let substs = infer.type_of_pat[pat_id].substs()?; | 330 | let substs = infer.type_of_pat[pat_id].as_adt()?.1; |
321 | 331 | ||
322 | let (variant, missing_fields, _exhaustive) = | 332 | let (variant, missing_fields, _exhaustive) = |
323 | record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?; | 333 | record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?; |
@@ -339,7 +349,7 @@ impl SourceAnalyzer { | |||
339 | .into_iter() | 349 | .into_iter() |
340 | .map(|local_id| { | 350 | .map(|local_id| { |
341 | let field = FieldId { parent: variant, local_id }; | 351 | let field = FieldId { parent: variant, local_id }; |
342 | let ty = field_types[local_id].clone().subst(substs); | 352 | let ty = field_types[local_id].clone().substitute(&Interner, substs); |
343 | (field.into(), Type::new_with_resolver_inner(db, krate, &self.resolver, ty)) | 353 | (field.into(), Type::new_with_resolver_inner(db, krate, &self.resolver, ty)) |
344 | }) | 354 | }) |
345 | .collect() | 355 | .collect() |
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index 442c5fb5b..d9294d93a 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -1,18 +1,22 @@ | |||
1 | //! A higher level attributes based on TokenTree, with also some shortcuts. | 1 | //! A higher level attributes based on TokenTree, with also some shortcuts. |
2 | 2 | ||
3 | use std::{ops, sync::Arc}; | 3 | use std::{ |
4 | convert::{TryFrom, TryInto}, | ||
5 | ops, | ||
6 | sync::Arc, | ||
7 | }; | ||
4 | 8 | ||
5 | use base_db::CrateId; | 9 | use base_db::CrateId; |
6 | use cfg::{CfgExpr, CfgOptions}; | 10 | use cfg::{CfgExpr, CfgOptions}; |
7 | use either::Either; | 11 | use either::Either; |
8 | use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile}; | 12 | use hir_expand::{hygiene::Hygiene, name::AsName, AstId, AttrId, InFile}; |
9 | use itertools::Itertools; | 13 | use itertools::Itertools; |
10 | use la_arena::ArenaMap; | 14 | use la_arena::ArenaMap; |
11 | use mbe::ast_to_token_tree; | 15 | use mbe::ast_to_token_tree; |
12 | use smallvec::{smallvec, SmallVec}; | 16 | use smallvec::{smallvec, SmallVec}; |
13 | use syntax::{ | 17 | use syntax::{ |
14 | ast::{self, AstNode, AttrsOwner}, | 18 | ast::{self, AstNode, AttrsOwner}, |
15 | match_ast, AstToken, SmolStr, SyntaxNode, | 19 | match_ast, AstPtr, AstToken, SmolStr, SyntaxNode, TextRange, TextSize, |
16 | }; | 20 | }; |
17 | use tt::Subtree; | 21 | use tt::Subtree; |
18 | 22 | ||
@@ -94,13 +98,16 @@ impl RawAttrs { | |||
94 | pub(crate) fn new(owner: &dyn ast::AttrsOwner, hygiene: &Hygiene) -> Self { | 98 | pub(crate) fn new(owner: &dyn ast::AttrsOwner, hygiene: &Hygiene) -> Self { |
95 | let entries = collect_attrs(owner) | 99 | let entries = collect_attrs(owner) |
96 | .enumerate() | 100 | .enumerate() |
97 | .flat_map(|(i, attr)| match attr { | 101 | .flat_map(|(i, attr)| { |
98 | Either::Left(attr) => Attr::from_src(attr, hygiene, i as u32), | 102 | let index = AttrId(i as u32); |
99 | Either::Right(comment) => comment.doc_comment().map(|doc| Attr { | 103 | match attr { |
100 | index: i as u32, | 104 | Either::Left(attr) => Attr::from_src(attr, hygiene, index), |
101 | input: Some(AttrInput::Literal(SmolStr::new(doc))), | 105 | Either::Right(comment) => comment.doc_comment().map(|doc| Attr { |
102 | path: Interned::new(ModPath::from(hir_expand::name!(doc))), | 106 | id: index, |
103 | }), | 107 | input: Some(AttrInput::Literal(SmolStr::new(doc))), |
108 | path: Interned::new(ModPath::from(hir_expand::name!(doc))), | ||
109 | }), | ||
110 | } | ||
104 | }) | 111 | }) |
105 | .collect::<Arc<_>>(); | 112 | .collect::<Arc<_>>(); |
106 | 113 | ||
@@ -157,7 +164,7 @@ impl RawAttrs { | |||
157 | let cfg = parts.next().unwrap(); | 164 | let cfg = parts.next().unwrap(); |
158 | let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() }; | 165 | let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() }; |
159 | let cfg = CfgExpr::parse(&cfg); | 166 | let cfg = CfgExpr::parse(&cfg); |
160 | let index = attr.index; | 167 | let index = attr.id; |
161 | let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| { | 168 | let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| { |
162 | let tree = Subtree { delimiter: None, token_trees: attr.to_vec() }; | 169 | let tree = Subtree { delimiter: None, token_trees: attr.to_vec() }; |
163 | let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; | 170 | let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; |
@@ -211,12 +218,11 @@ impl Attrs { | |||
211 | let mut res = ArenaMap::default(); | 218 | let mut res = ArenaMap::default(); |
212 | 219 | ||
213 | for (id, fld) in src.value.iter() { | 220 | for (id, fld) in src.value.iter() { |
214 | let attrs = match fld { | 221 | let owner: &dyn AttrsOwner = match fld { |
215 | Either::Left(_tuple) => Attrs::default(), | 222 | Either::Left(tuple) => tuple, |
216 | Either::Right(record) => { | 223 | Either::Right(record) => record, |
217 | RawAttrs::from_attrs_owner(db, src.with_value(record)).filter(db, krate) | ||
218 | } | ||
219 | }; | 224 | }; |
225 | let attrs = RawAttrs::from_attrs_owner(db, src.with_value(owner)).filter(db, krate); | ||
220 | 226 | ||
221 | res.insert(id, attrs); | 227 | res.insert(id, attrs); |
222 | } | 228 | } |
@@ -400,10 +406,14 @@ impl AttrsWithOwner { | |||
400 | return AttrSourceMap { attrs }; | 406 | return AttrSourceMap { attrs }; |
401 | } | 407 | } |
402 | AttrDefId::FieldId(id) => { | 408 | AttrDefId::FieldId(id) => { |
403 | id.parent.child_source(db).map(|source| match &source[id.local_id] { | 409 | let map = db.fields_attrs_source_map(id.parent); |
404 | Either::Left(field) => ast::AttrsOwnerNode::new(field.clone()), | 410 | let file_id = id.parent.file_id(db); |
405 | Either::Right(field) => ast::AttrsOwnerNode::new(field.clone()), | 411 | let root = db.parse_or_expand(file_id).unwrap(); |
406 | }) | 412 | let owner = match &map[id.local_id] { |
413 | Either::Left(it) => ast::AttrsOwnerNode::new(it.to_node(&root)), | ||
414 | Either::Right(it) => ast::AttrsOwnerNode::new(it.to_node(&root)), | ||
415 | }; | ||
416 | InFile::new(file_id, owner) | ||
407 | } | 417 | } |
408 | AttrDefId::AdtId(adt) => match adt { | 418 | AttrDefId::AdtId(adt) => match adt { |
409 | AdtId::StructId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), | 419 | AdtId::StructId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), |
@@ -411,10 +421,12 @@ impl AttrsWithOwner { | |||
411 | AdtId::EnumId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), | 421 | AdtId::EnumId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), |
412 | }, | 422 | }, |
413 | AttrDefId::FunctionId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), | 423 | AttrDefId::FunctionId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), |
414 | AttrDefId::EnumVariantId(id) => id | 424 | AttrDefId::EnumVariantId(id) => { |
415 | .parent | 425 | let map = db.variants_attrs_source_map(id.parent); |
416 | .child_source(db) | 426 | let file_id = id.parent.lookup(db).id.file_id(); |
417 | .map(|source| ast::AttrsOwnerNode::new(source[id.local_id].clone())), | 427 | let root = db.parse_or_expand(file_id).unwrap(); |
428 | InFile::new(file_id, ast::AttrsOwnerNode::new(map[id.local_id].to_node(&root))) | ||
429 | } | ||
418 | AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), | 430 | AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), |
419 | AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), | 431 | AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), |
420 | AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), | 432 | AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), |
@@ -452,6 +464,55 @@ impl AttrsWithOwner { | |||
452 | .collect(), | 464 | .collect(), |
453 | } | 465 | } |
454 | } | 466 | } |
467 | |||
468 | pub fn docs_with_rangemap( | ||
469 | &self, | ||
470 | db: &dyn DefDatabase, | ||
471 | ) -> Option<(Documentation, DocsRangeMap)> { | ||
472 | // FIXME: code duplication in `docs` above | ||
473 | let docs = self.by_key("doc").attrs().flat_map(|attr| match attr.input.as_ref()? { | ||
474 | AttrInput::Literal(s) => Some((s, attr.id)), | ||
475 | AttrInput::TokenTree(_) => None, | ||
476 | }); | ||
477 | let indent = docs | ||
478 | .clone() | ||
479 | .flat_map(|(s, _)| s.lines()) | ||
480 | .filter(|line| !line.chars().all(|c| c.is_whitespace())) | ||
481 | .map(|line| line.chars().take_while(|c| c.is_whitespace()).count()) | ||
482 | .min() | ||
483 | .unwrap_or(0); | ||
484 | let mut buf = String::new(); | ||
485 | let mut mapping = Vec::new(); | ||
486 | for (doc, idx) in docs { | ||
487 | // str::lines doesn't yield anything for the empty string | ||
488 | if !doc.is_empty() { | ||
489 | for line in doc.split('\n') { | ||
490 | let line = line.trim_end(); | ||
491 | let line_len = line.len(); | ||
492 | let (offset, line) = match line.char_indices().nth(indent) { | ||
493 | Some((offset, _)) => (offset, &line[offset..]), | ||
494 | None => (0, line), | ||
495 | }; | ||
496 | let buf_offset = buf.len(); | ||
497 | buf.push_str(line); | ||
498 | mapping.push(( | ||
499 | TextRange::new(buf_offset.try_into().ok()?, buf.len().try_into().ok()?), | ||
500 | idx, | ||
501 | TextRange::new(offset.try_into().ok()?, line_len.try_into().ok()?), | ||
502 | )); | ||
503 | buf.push('\n'); | ||
504 | } | ||
505 | } else { | ||
506 | buf.push('\n'); | ||
507 | } | ||
508 | } | ||
509 | buf.pop(); | ||
510 | if buf.is_empty() { | ||
511 | None | ||
512 | } else { | ||
513 | Some((Documentation(buf), DocsRangeMap { mapping, source: self.source_map(db).attrs })) | ||
514 | } | ||
515 | } | ||
455 | } | 516 | } |
456 | 517 | ||
457 | fn inner_attributes( | 518 | fn inner_attributes( |
@@ -484,7 +545,7 @@ fn inner_attributes( | |||
484 | _ => return None, | 545 | _ => return None, |
485 | } | 546 | } |
486 | }; | 547 | }; |
487 | let attrs = attrs.filter(|attr| attr.excl_token().is_some()); | 548 | let attrs = attrs.filter(|attr| attr.kind().is_inner()); |
488 | let docs = docs.filter(|doc| doc.is_inner()); | 549 | let docs = docs.filter(|doc| doc.is_inner()); |
489 | Some((attrs, docs)) | 550 | Some((attrs, docs)) |
490 | } | 551 | } |
@@ -502,15 +563,53 @@ impl AttrSourceMap { | |||
502 | /// the attribute represented by `Attr`. | 563 | /// the attribute represented by `Attr`. |
503 | pub fn source_of(&self, attr: &Attr) -> InFile<&Either<ast::Attr, ast::Comment>> { | 564 | pub fn source_of(&self, attr: &Attr) -> InFile<&Either<ast::Attr, ast::Comment>> { |
504 | self.attrs | 565 | self.attrs |
505 | .get(attr.index as usize) | 566 | .get(attr.id.0 as usize) |
506 | .unwrap_or_else(|| panic!("cannot find `Attr` at index {}", attr.index)) | 567 | .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", attr.id)) |
507 | .as_ref() | 568 | .as_ref() |
508 | } | 569 | } |
509 | } | 570 | } |
510 | 571 | ||
572 | /// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree. | ||
573 | pub struct DocsRangeMap { | ||
574 | source: Vec<InFile<Either<ast::Attr, ast::Comment>>>, | ||
575 | // (docstring-line-range, attr_index, attr-string-range) | ||
576 | // a mapping from the text range of a line of the [`Documentation`] to the attribute index and | ||
577 | // the original (untrimmed) syntax doc line | ||
578 | mapping: Vec<(TextRange, AttrId, TextRange)>, | ||
579 | } | ||
580 | |||
581 | impl DocsRangeMap { | ||
582 | pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> { | ||
583 | let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?; | ||
584 | let (line_docs_range, idx, original_line_src_range) = self.mapping[found].clone(); | ||
585 | if !line_docs_range.contains_range(range) { | ||
586 | return None; | ||
587 | } | ||
588 | |||
589 | let relative_range = range - line_docs_range.start(); | ||
590 | |||
591 | let &InFile { file_id, value: ref source } = &self.source[idx.0 as usize]; | ||
592 | match source { | ||
593 | Either::Left(_) => None, // FIXME, figure out a nice way to handle doc attributes here | ||
594 | // as well as for whats done in syntax highlight doc injection | ||
595 | Either::Right(comment) => { | ||
596 | let text_range = comment.syntax().text_range(); | ||
597 | let range = TextRange::at( | ||
598 | text_range.start() | ||
599 | + TextSize::try_from(comment.prefix().len()).ok()? | ||
600 | + original_line_src_range.start() | ||
601 | + relative_range.start(), | ||
602 | text_range.len().min(range.len()), | ||
603 | ); | ||
604 | Some(InFile { file_id, value: range }) | ||
605 | } | ||
606 | } | ||
607 | } | ||
608 | } | ||
609 | |||
511 | #[derive(Debug, Clone, PartialEq, Eq)] | 610 | #[derive(Debug, Clone, PartialEq, Eq)] |
512 | pub struct Attr { | 611 | pub struct Attr { |
513 | index: u32, | 612 | pub(crate) id: AttrId, |
514 | pub(crate) path: Interned<ModPath>, | 613 | pub(crate) path: Interned<ModPath>, |
515 | pub(crate) input: Option<AttrInput>, | 614 | pub(crate) input: Option<AttrInput>, |
516 | } | 615 | } |
@@ -524,7 +623,7 @@ pub enum AttrInput { | |||
524 | } | 623 | } |
525 | 624 | ||
526 | impl Attr { | 625 | impl Attr { |
527 | fn from_src(ast: ast::Attr, hygiene: &Hygiene, index: u32) -> Option<Attr> { | 626 | fn from_src(ast: ast::Attr, hygiene: &Hygiene, id: AttrId) -> Option<Attr> { |
528 | let path = Interned::new(ModPath::from_src(ast.path()?, hygiene)?); | 627 | let path = Interned::new(ModPath::from_src(ast.path()?, hygiene)?); |
529 | let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() { | 628 | let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() { |
530 | let value = match lit.kind() { | 629 | let value = match lit.kind() { |
@@ -537,7 +636,7 @@ impl Attr { | |||
537 | } else { | 636 | } else { |
538 | None | 637 | None |
539 | }; | 638 | }; |
540 | Some(Attr { index, path, input }) | 639 | Some(Attr { id, path, input }) |
541 | } | 640 | } |
542 | 641 | ||
543 | /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths | 642 | /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths |
@@ -641,7 +740,7 @@ fn collect_attrs( | |||
641 | let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) | 740 | let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) |
642 | .map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs))); | 741 | .map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs))); |
643 | 742 | ||
644 | let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none()); | 743 | let outer_attrs = owner.attrs().filter(|attr| attr.kind().is_outer()); |
645 | let attrs = outer_attrs | 744 | let attrs = outer_attrs |
646 | .chain(inner_attrs.into_iter().flatten()) | 745 | .chain(inner_attrs.into_iter().flatten()) |
647 | .map(|attr| (attr.syntax().text_range().start(), Either::Left(attr))); | 746 | .map(|attr| (attr.syntax().text_range().start(), Either::Left(attr))); |
@@ -652,7 +751,38 @@ fn collect_attrs( | |||
652 | .chain(inner_docs.into_iter().flatten()) | 751 | .chain(inner_docs.into_iter().flatten()) |
653 | .map(|docs_text| (docs_text.syntax().text_range().start(), Either::Right(docs_text))); | 752 | .map(|docs_text| (docs_text.syntax().text_range().start(), Either::Right(docs_text))); |
654 | // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved | 753 | // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved |
655 | let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect(); | 754 | docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).map(|(_, attr)| attr) |
755 | } | ||
756 | |||
757 | pub(crate) fn variants_attrs_source_map( | ||
758 | db: &dyn DefDatabase, | ||
759 | def: EnumId, | ||
760 | ) -> Arc<ArenaMap<LocalEnumVariantId, AstPtr<ast::Variant>>> { | ||
761 | let mut res = ArenaMap::default(); | ||
762 | let child_source = def.child_source(db); | ||
763 | |||
764 | for (idx, variant) in child_source.value.iter() { | ||
765 | res.insert(idx, AstPtr::new(variant)); | ||
766 | } | ||
767 | |||
768 | Arc::new(res) | ||
769 | } | ||
770 | |||
771 | pub(crate) fn fields_attrs_source_map( | ||
772 | db: &dyn DefDatabase, | ||
773 | def: VariantId, | ||
774 | ) -> Arc<ArenaMap<LocalFieldId, Either<AstPtr<ast::TupleField>, AstPtr<ast::RecordField>>>> { | ||
775 | let mut res = ArenaMap::default(); | ||
776 | let child_source = def.child_source(db); | ||
777 | |||
778 | for (idx, variant) in child_source.value.iter() { | ||
779 | res.insert( | ||
780 | idx, | ||
781 | variant | ||
782 | .as_ref() | ||
783 | .either(|l| Either::Left(AstPtr::new(l)), |r| Either::Right(AstPtr::new(r))), | ||
784 | ); | ||
785 | } | ||
656 | 786 | ||
657 | attrs.into_iter().map(|(_, attr)| attr) | 787 | Arc::new(res) |
658 | } | 788 | } |
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs index 96b959967..131f424cc 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs | |||
@@ -21,7 +21,7 @@ use profile::Count; | |||
21 | use rustc_hash::FxHashMap; | 21 | use rustc_hash::FxHashMap; |
22 | use syntax::{ast, AstNode, AstPtr}; | 22 | use syntax::{ast, AstNode, AstPtr}; |
23 | 23 | ||
24 | pub(crate) use lower::LowerCtx; | 24 | pub use lower::LowerCtx; |
25 | 25 | ||
26 | use crate::{ | 26 | use crate::{ |
27 | attr::{Attrs, RawAttrs}, | 27 | attr::{Attrs, RawAttrs}, |
@@ -37,13 +37,15 @@ use crate::{ | |||
37 | 37 | ||
38 | /// A subset of Expander that only deals with cfg attributes. We only need it to | 38 | /// A subset of Expander that only deals with cfg attributes. We only need it to |
39 | /// avoid cyclic queries in crate def map during enum processing. | 39 | /// avoid cyclic queries in crate def map during enum processing. |
40 | #[derive(Debug)] | ||
40 | pub(crate) struct CfgExpander { | 41 | pub(crate) struct CfgExpander { |
41 | cfg_options: CfgOptions, | 42 | cfg_options: CfgOptions, |
42 | hygiene: Hygiene, | 43 | hygiene: Hygiene, |
43 | krate: CrateId, | 44 | krate: CrateId, |
44 | } | 45 | } |
45 | 46 | ||
46 | pub(crate) struct Expander { | 47 | #[derive(Debug)] |
48 | pub struct Expander { | ||
47 | cfg_expander: CfgExpander, | 49 | cfg_expander: CfgExpander, |
48 | def_map: Arc<DefMap>, | 50 | def_map: Arc<DefMap>, |
49 | current_file_id: HirFileId, | 51 | current_file_id: HirFileId, |
@@ -80,11 +82,7 @@ impl CfgExpander { | |||
80 | } | 82 | } |
81 | 83 | ||
82 | impl Expander { | 84 | impl Expander { |
83 | pub(crate) fn new( | 85 | pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander { |
84 | db: &dyn DefDatabase, | ||
85 | current_file_id: HirFileId, | ||
86 | module: ModuleId, | ||
87 | ) -> Expander { | ||
88 | let cfg_expander = CfgExpander::new(db, current_file_id, module.krate); | 86 | let cfg_expander = CfgExpander::new(db, current_file_id, module.krate); |
89 | let def_map = module.def_map(db); | 87 | let def_map = module.def_map(db); |
90 | let ast_id_map = db.ast_id_map(current_file_id); | 88 | let ast_id_map = db.ast_id_map(current_file_id); |
@@ -98,7 +96,7 @@ impl Expander { | |||
98 | } | 96 | } |
99 | } | 97 | } |
100 | 98 | ||
101 | pub(crate) fn enter_expand<T: ast::AstNode>( | 99 | pub fn enter_expand<T: ast::AstNode>( |
102 | &mut self, | 100 | &mut self, |
103 | db: &dyn DefDatabase, | 101 | db: &dyn DefDatabase, |
104 | macro_call: ast::MacroCall, | 102 | macro_call: ast::MacroCall, |
@@ -170,7 +168,7 @@ impl Expander { | |||
170 | Ok(ExpandResult { value: Some((mark, node)), err }) | 168 | Ok(ExpandResult { value: Some((mark, node)), err }) |
171 | } | 169 | } |
172 | 170 | ||
173 | pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { | 171 | pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { |
174 | self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id); | 172 | self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id); |
175 | self.current_file_id = mark.file_id; | 173 | self.current_file_id = mark.file_id; |
176 | self.ast_id_map = mem::take(&mut mark.ast_id_map); | 174 | self.ast_id_map = mem::take(&mut mark.ast_id_map); |
@@ -190,8 +188,13 @@ impl Expander { | |||
190 | &self.cfg_expander.cfg_options | 188 | &self.cfg_expander.cfg_options |
191 | } | 189 | } |
192 | 190 | ||
191 | pub fn current_file_id(&self) -> HirFileId { | ||
192 | self.current_file_id | ||
193 | } | ||
194 | |||
193 | fn parse_path(&mut self, path: ast::Path) -> Option<Path> { | 195 | fn parse_path(&mut self, path: ast::Path) -> Option<Path> { |
194 | Path::from_src(path, &self.cfg_expander.hygiene) | 196 | let ctx = LowerCtx::with_hygiene(&self.cfg_expander.hygiene); |
197 | Path::from_src(path, &ctx) | ||
195 | } | 198 | } |
196 | 199 | ||
197 | fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId> { | 200 | fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId> { |
@@ -204,7 +207,8 @@ impl Expander { | |||
204 | } | 207 | } |
205 | } | 208 | } |
206 | 209 | ||
207 | pub(crate) struct Mark { | 210 | #[derive(Debug)] |
211 | pub struct Mark { | ||
208 | file_id: HirFileId, | 212 | file_id: HirFileId, |
209 | ast_id_map: Arc<AstIdMap>, | 213 | ast_id_map: Arc<AstIdMap>, |
210 | bomb: DropBomb, | 214 | bomb: DropBomb, |
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 63e89a1f4..c11da30d2 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -1,10 +1,11 @@ | |||
1 | //! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` | 1 | //! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` |
2 | //! representation. | 2 | //! representation. |
3 | 3 | ||
4 | use std::mem; | 4 | use std::{mem, sync::Arc}; |
5 | 5 | ||
6 | use either::Either; | 6 | use either::Either; |
7 | use hir_expand::{ | 7 | use hir_expand::{ |
8 | ast_id_map::{AstIdMap, FileAstId}, | ||
8 | hygiene::Hygiene, | 9 | hygiene::Hygiene, |
9 | name::{name, AsName, Name}, | 10 | name::{name, AsName, Name}, |
10 | ExpandError, HirFileId, | 11 | ExpandError, HirFileId, |
@@ -30,6 +31,7 @@ use crate::{ | |||
30 | LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, | 31 | LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, |
31 | Statement, | 32 | Statement, |
32 | }, | 33 | }, |
34 | intern::Interned, | ||
33 | item_scope::BuiltinShadowMode, | 35 | item_scope::BuiltinShadowMode, |
34 | path::{GenericArgs, Path}, | 36 | path::{GenericArgs, Path}, |
35 | type_ref::{Mutability, Rawness, TypeRef}, | 37 | type_ref::{Mutability, Rawness, TypeRef}, |
@@ -38,20 +40,39 @@ use crate::{ | |||
38 | 40 | ||
39 | use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; | 41 | use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; |
40 | 42 | ||
41 | pub(crate) struct LowerCtx { | 43 | pub struct LowerCtx { |
42 | hygiene: Hygiene, | 44 | hygiene: Hygiene, |
45 | file_id: Option<HirFileId>, | ||
46 | source_ast_id_map: Option<Arc<AstIdMap>>, | ||
43 | } | 47 | } |
44 | 48 | ||
45 | impl LowerCtx { | 49 | impl LowerCtx { |
46 | pub(crate) fn new(db: &dyn DefDatabase, file_id: HirFileId) -> Self { | 50 | pub fn new(db: &dyn DefDatabase, file_id: HirFileId) -> Self { |
47 | LowerCtx { hygiene: Hygiene::new(db.upcast(), file_id) } | 51 | LowerCtx { |
52 | hygiene: Hygiene::new(db.upcast(), file_id), | ||
53 | file_id: Some(file_id), | ||
54 | source_ast_id_map: Some(db.ast_id_map(file_id)), | ||
55 | } | ||
48 | } | 56 | } |
49 | pub(crate) fn with_hygiene(hygiene: &Hygiene) -> Self { | 57 | |
50 | LowerCtx { hygiene: hygiene.clone() } | 58 | pub fn with_hygiene(hygiene: &Hygiene) -> Self { |
59 | LowerCtx { hygiene: hygiene.clone(), file_id: None, source_ast_id_map: None } | ||
60 | } | ||
61 | |||
62 | pub(crate) fn hygiene(&self) -> &Hygiene { | ||
63 | &self.hygiene | ||
64 | } | ||
65 | |||
66 | pub(crate) fn file_id(&self) -> HirFileId { | ||
67 | self.file_id.unwrap() | ||
51 | } | 68 | } |
52 | 69 | ||
53 | pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> { | 70 | pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> { |
54 | Path::from_src(ast, &self.hygiene) | 71 | Path::from_src(ast, self) |
72 | } | ||
73 | |||
74 | pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> Option<FileAstId<N>> { | ||
75 | self.source_ast_id_map.as_ref().map(|ast_id_map| ast_id_map.ast_id(item)) | ||
55 | } | 76 | } |
56 | } | 77 | } |
57 | 78 | ||
@@ -322,8 +343,10 @@ impl ExprCollector<'_> { | |||
322 | Vec::new() | 343 | Vec::new() |
323 | }; | 344 | }; |
324 | let method_name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); | 345 | let method_name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); |
325 | let generic_args = | 346 | let generic_args = e |
326 | e.generic_arg_list().and_then(|it| GenericArgs::from_ast(&self.ctx(), it)); | 347 | .generic_arg_list() |
348 | .and_then(|it| GenericArgs::from_ast(&self.ctx(), it)) | ||
349 | .map(Box::new); | ||
327 | self.alloc_expr( | 350 | self.alloc_expr( |
328 | Expr::MethodCall { receiver, method_name, args, generic_args }, | 351 | Expr::MethodCall { receiver, method_name, args, generic_args }, |
329 | syntax_ptr, | 352 | syntax_ptr, |
@@ -385,7 +408,7 @@ impl ExprCollector<'_> { | |||
385 | self.alloc_expr(Expr::Yield { expr }, syntax_ptr) | 408 | self.alloc_expr(Expr::Yield { expr }, syntax_ptr) |
386 | } | 409 | } |
387 | ast::Expr::RecordExpr(e) => { | 410 | ast::Expr::RecordExpr(e) => { |
388 | let path = e.path().and_then(|path| self.expander.parse_path(path)); | 411 | let path = e.path().and_then(|path| self.expander.parse_path(path)).map(Box::new); |
389 | let record_lit = if let Some(nfl) = e.record_expr_field_list() { | 412 | let record_lit = if let Some(nfl) = e.record_expr_field_list() { |
390 | let fields = nfl | 413 | let fields = nfl |
391 | .fields() | 414 | .fields() |
@@ -430,7 +453,7 @@ impl ExprCollector<'_> { | |||
430 | } | 453 | } |
431 | ast::Expr::CastExpr(e) => { | 454 | ast::Expr::CastExpr(e) => { |
432 | let expr = self.collect_expr_opt(e.expr()); | 455 | let expr = self.collect_expr_opt(e.expr()); |
433 | let type_ref = TypeRef::from_ast_opt(&self.ctx(), e.ty()); | 456 | let type_ref = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty())); |
434 | self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr) | 457 | self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr) |
435 | } | 458 | } |
436 | ast::Expr::RefExpr(e) => { | 459 | ast::Expr::RefExpr(e) => { |
@@ -464,13 +487,16 @@ impl ExprCollector<'_> { | |||
464 | if let Some(pl) = e.param_list() { | 487 | if let Some(pl) = e.param_list() { |
465 | for param in pl.params() { | 488 | for param in pl.params() { |
466 | let pat = self.collect_pat_opt(param.pat()); | 489 | let pat = self.collect_pat_opt(param.pat()); |
467 | let type_ref = param.ty().map(|it| TypeRef::from_ast(&self.ctx(), it)); | 490 | let type_ref = |
491 | param.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it))); | ||
468 | args.push(pat); | 492 | args.push(pat); |
469 | arg_types.push(type_ref); | 493 | arg_types.push(type_ref); |
470 | } | 494 | } |
471 | } | 495 | } |
472 | let ret_type = | 496 | let ret_type = e |
473 | e.ret_type().and_then(|r| r.ty()).map(|it| TypeRef::from_ast(&self.ctx(), it)); | 497 | .ret_type() |
498 | .and_then(|r| r.ty()) | ||
499 | .map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it))); | ||
474 | let body = self.collect_expr_opt(e.body()); | 500 | let body = self.collect_expr_opt(e.body()); |
475 | self.alloc_expr(Expr::Lambda { args, arg_types, ret_type, body }, syntax_ptr) | 501 | self.alloc_expr(Expr::Lambda { args, arg_types, ret_type, body }, syntax_ptr) |
476 | } | 502 | } |
@@ -525,8 +551,9 @@ impl ExprCollector<'_> { | |||
525 | } | 551 | } |
526 | } | 552 | } |
527 | ast::Expr::MacroCall(e) => { | 553 | ast::Expr::MacroCall(e) => { |
554 | let macro_ptr = AstPtr::new(&e); | ||
528 | let mut ids = vec![]; | 555 | let mut ids = vec![]; |
529 | self.collect_macro_call(e, syntax_ptr.clone(), true, |this, expansion| { | 556 | self.collect_macro_call(e, macro_ptr, true, |this, expansion| { |
530 | ids.push(match expansion { | 557 | ids.push(match expansion { |
531 | Some(it) => this.collect_expr(it), | 558 | Some(it) => this.collect_expr(it), |
532 | None => this.alloc_expr(Expr::Missing, syntax_ptr.clone()), | 559 | None => this.alloc_expr(Expr::Missing, syntax_ptr.clone()), |
@@ -549,7 +576,7 @@ impl ExprCollector<'_> { | |||
549 | fn collect_macro_call<F: FnMut(&mut Self, Option<T>), T: ast::AstNode>( | 576 | fn collect_macro_call<F: FnMut(&mut Self, Option<T>), T: ast::AstNode>( |
550 | &mut self, | 577 | &mut self, |
551 | e: ast::MacroCall, | 578 | e: ast::MacroCall, |
552 | syntax_ptr: AstPtr<ast::Expr>, | 579 | syntax_ptr: AstPtr<ast::MacroCall>, |
553 | is_error_recoverable: bool, | 580 | is_error_recoverable: bool, |
554 | mut collector: F, | 581 | mut collector: F, |
555 | ) { | 582 | ) { |
@@ -561,9 +588,13 @@ impl ExprCollector<'_> { | |||
561 | 588 | ||
562 | let res = match res { | 589 | let res = match res { |
563 | Ok(res) => res, | 590 | Ok(res) => res, |
564 | Err(UnresolvedMacro) => { | 591 | Err(UnresolvedMacro { path }) => { |
565 | self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall( | 592 | self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall( |
566 | UnresolvedMacroCall { file: outer_file, node: syntax_ptr.cast().unwrap() }, | 593 | UnresolvedMacroCall { |
594 | file: outer_file, | ||
595 | node: syntax_ptr.cast().unwrap(), | ||
596 | path, | ||
597 | }, | ||
567 | )); | 598 | )); |
568 | collector(self, None); | 599 | collector(self, None); |
569 | return; | 600 | return; |
@@ -625,7 +656,8 @@ impl ExprCollector<'_> { | |||
625 | return; | 656 | return; |
626 | } | 657 | } |
627 | let pat = self.collect_pat_opt(stmt.pat()); | 658 | let pat = self.collect_pat_opt(stmt.pat()); |
628 | let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it)); | 659 | let type_ref = |
660 | stmt.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it))); | ||
629 | let initializer = stmt.initializer().map(|e| self.collect_expr(e)); | 661 | let initializer = stmt.initializer().map(|e| self.collect_expr(e)); |
630 | self.statements_in_scope.push(Statement::Let { pat, type_ref, initializer }); | 662 | self.statements_in_scope.push(Statement::Let { pat, type_ref, initializer }); |
631 | } | 663 | } |
@@ -636,10 +668,14 @@ impl ExprCollector<'_> { | |||
636 | 668 | ||
637 | // Note that macro could be expended to multiple statements | 669 | // Note that macro could be expended to multiple statements |
638 | if let Some(ast::Expr::MacroCall(m)) = stmt.expr() { | 670 | if let Some(ast::Expr::MacroCall(m)) = stmt.expr() { |
671 | let macro_ptr = AstPtr::new(&m); | ||
639 | let syntax_ptr = AstPtr::new(&stmt.expr().unwrap()); | 672 | let syntax_ptr = AstPtr::new(&stmt.expr().unwrap()); |
640 | 673 | ||
641 | self.collect_macro_call(m, syntax_ptr.clone(), false, |this, expansion| { | 674 | self.collect_macro_call( |
642 | match expansion { | 675 | m, |
676 | macro_ptr, | ||
677 | false, | ||
678 | |this, expansion| match expansion { | ||
643 | Some(expansion) => { | 679 | Some(expansion) => { |
644 | let statements: ast::MacroStmts = expansion; | 680 | let statements: ast::MacroStmts = expansion; |
645 | 681 | ||
@@ -653,8 +689,8 @@ impl ExprCollector<'_> { | |||
653 | let expr = this.alloc_expr(Expr::Missing, syntax_ptr.clone()); | 689 | let expr = this.alloc_expr(Expr::Missing, syntax_ptr.clone()); |
654 | this.statements_in_scope.push(Statement::Expr(expr)); | 690 | this.statements_in_scope.push(Statement::Expr(expr)); |
655 | } | 691 | } |
656 | } | 692 | }, |
657 | }); | 693 | ); |
658 | } else { | 694 | } else { |
659 | let expr = self.collect_expr_opt(stmt.expr()); | 695 | let expr = self.collect_expr_opt(stmt.expr()); |
660 | self.statements_in_scope.push(Statement::Expr(expr)); | 696 | self.statements_in_scope.push(Statement::Expr(expr)); |
@@ -755,7 +791,7 @@ impl ExprCollector<'_> { | |||
755 | } | 791 | } |
756 | } | 792 | } |
757 | ast::Pat::TupleStructPat(p) => { | 793 | ast::Pat::TupleStructPat(p) => { |
758 | let path = p.path().and_then(|path| self.expander.parse_path(path)); | 794 | let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new); |
759 | let (args, ellipsis) = self.collect_tuple_pat(p.fields()); | 795 | let (args, ellipsis) = self.collect_tuple_pat(p.fields()); |
760 | Pat::TupleStruct { path, args, ellipsis } | 796 | Pat::TupleStruct { path, args, ellipsis } |
761 | } | 797 | } |
@@ -765,7 +801,7 @@ impl ExprCollector<'_> { | |||
765 | Pat::Ref { pat, mutability } | 801 | Pat::Ref { pat, mutability } |
766 | } | 802 | } |
767 | ast::Pat::PathPat(p) => { | 803 | ast::Pat::PathPat(p) => { |
768 | let path = p.path().and_then(|path| self.expander.parse_path(path)); | 804 | let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new); |
769 | path.map(Pat::Path).unwrap_or(Pat::Missing) | 805 | path.map(Pat::Path).unwrap_or(Pat::Missing) |
770 | } | 806 | } |
771 | ast::Pat::OrPat(p) => { | 807 | ast::Pat::OrPat(p) => { |
@@ -779,7 +815,7 @@ impl ExprCollector<'_> { | |||
779 | } | 815 | } |
780 | ast::Pat::WildcardPat(_) => Pat::Wild, | 816 | ast::Pat::WildcardPat(_) => Pat::Wild, |
781 | ast::Pat::RecordPat(p) => { | 817 | ast::Pat::RecordPat(p) => { |
782 | let path = p.path().and_then(|path| self.expander.parse_path(path)); | 818 | let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new); |
783 | let args: Vec<_> = p | 819 | let args: Vec<_> = p |
784 | .record_pat_field_list() | 820 | .record_pat_field_list() |
785 | .expect("every struct should have a field list") | 821 | .expect("every struct should have a field list") |
@@ -841,8 +877,23 @@ impl ExprCollector<'_> { | |||
841 | Pat::Missing | 877 | Pat::Missing |
842 | } | 878 | } |
843 | } | 879 | } |
880 | ast::Pat::MacroPat(mac) => match mac.macro_call() { | ||
881 | Some(call) => { | ||
882 | let macro_ptr = AstPtr::new(&call); | ||
883 | let mut pat = None; | ||
884 | self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| { | ||
885 | pat = Some(this.collect_pat_opt(expanded_pat)); | ||
886 | }); | ||
887 | |||
888 | match pat { | ||
889 | Some(pat) => return pat, | ||
890 | None => Pat::Missing, | ||
891 | } | ||
892 | } | ||
893 | None => Pat::Missing, | ||
894 | }, | ||
844 | // FIXME: implement | 895 | // FIXME: implement |
845 | ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing, | 896 | ast::Pat::RangePat(_) => Pat::Missing, |
846 | }; | 897 | }; |
847 | let ptr = AstPtr::new(&pat); | 898 | let ptr = AstPtr::new(&pat); |
848 | self.alloc_pat(pattern, Either::Left(ptr)) | 899 | self.alloc_pat(pattern, Either::Left(ptr)) |
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs index faa133297..3e8f16306 100644 --- a/crates/hir_def/src/body/tests.rs +++ b/crates/hir_def/src/body/tests.rs | |||
@@ -40,6 +40,14 @@ fn block_def_map_at(ra_fixture: &str) -> String { | |||
40 | module.def_map(&db).dump(&db) | 40 | module.def_map(&db).dump(&db) |
41 | } | 41 | } |
42 | 42 | ||
43 | fn check_block_scopes_at(ra_fixture: &str, expect: Expect) { | ||
44 | let (db, position) = crate::test_db::TestDB::with_position(ra_fixture); | ||
45 | |||
46 | let module = db.module_at_position(position); | ||
47 | let actual = module.def_map(&db).dump_block_scopes(&db); | ||
48 | expect.assert_eq(&actual); | ||
49 | } | ||
50 | |||
43 | fn check_at(ra_fixture: &str, expect: Expect) { | 51 | fn check_at(ra_fixture: &str, expect: Expect) { |
44 | let actual = block_def_map_at(ra_fixture); | 52 | let actual = block_def_map_at(ra_fixture); |
45 | expect.assert_eq(&actual); | 53 | expect.assert_eq(&actual); |
@@ -143,7 +151,7 @@ fn f() { | |||
143 | //^^^^^^^^^^^^^ could not convert tokens | 151 | //^^^^^^^^^^^^^ could not convert tokens |
144 | 152 | ||
145 | env!("OUT_DIR"); | 153 | env!("OUT_DIR"); |
146 | //^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "load out dirs from check" to fix | 154 | //^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix |
147 | 155 | ||
148 | compile_error!("compile_error works"); | 156 | compile_error!("compile_error works"); |
149 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ compile_error works | 157 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ compile_error works |
@@ -180,7 +188,7 @@ fn unresolved_macro_diag() { | |||
180 | r#" | 188 | r#" |
181 | fn f() { | 189 | fn f() { |
182 | m!(); | 190 | m!(); |
183 | //^^^^ unresolved macro call | 191 | //^^^^ unresolved macro `m!` |
184 | } | 192 | } |
185 | "#, | 193 | "#, |
186 | ); | 194 | ); |
diff --git a/crates/hir_def/src/body/tests/block.rs b/crates/hir_def/src/body/tests/block.rs index 3b6ba4cde..bc3d0f138 100644 --- a/crates/hir_def/src/body/tests/block.rs +++ b/crates/hir_def/src/body/tests/block.rs | |||
@@ -134,6 +134,30 @@ struct Struct {} | |||
134 | } | 134 | } |
135 | 135 | ||
136 | #[test] | 136 | #[test] |
137 | fn nested_module_scoping() { | ||
138 | check_block_scopes_at( | ||
139 | r#" | ||
140 | fn f() { | ||
141 | mod module { | ||
142 | struct Struct {} | ||
143 | fn f() { | ||
144 | use self::Struct; | ||
145 | $0 | ||
146 | } | ||
147 | } | ||
148 | } | ||
149 | "#, | ||
150 | expect![[r#" | ||
151 | BlockId(1) in ModuleId { krate: CrateId(0), block: Some(BlockId(0)), local_id: Idx::<ModuleData>(0) } | ||
152 | BlockId(0) in ModuleId { krate: CrateId(0), block: None, local_id: Idx::<ModuleData>(0) } | ||
153 | crate scope | ||
154 | "#]], | ||
155 | ); | ||
156 | // FIXME: The module nesting here is wrong! | ||
157 | // The first block map should be located in module #1 (`mod module`), not #0 (BlockId(0) root module) | ||
158 | } | ||
159 | |||
160 | #[test] | ||
137 | fn legacy_macro_items() { | 161 | fn legacy_macro_items() { |
138 | // Checks that legacy-scoped `macro_rules!` from parent namespaces are resolved and expanded | 162 | // Checks that legacy-scoped `macro_rules!` from parent namespaces are resolved and expanded |
139 | // correctly. | 163 | // correctly. |
diff --git a/crates/hir_def/src/child_by_source.rs b/crates/hir_def/src/child_by_source.rs index f40a7f80d..f2e809ca9 100644 --- a/crates/hir_def/src/child_by_source.rs +++ b/crates/hir_def/src/child_by_source.rs | |||
@@ -80,6 +80,10 @@ impl ChildBySource for ModuleId { | |||
80 | impl ChildBySource for ItemScope { | 80 | impl ChildBySource for ItemScope { |
81 | fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) { | 81 | fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) { |
82 | self.declarations().for_each(|item| add_module_def(db, res, item)); | 82 | self.declarations().for_each(|item| add_module_def(db, res, item)); |
83 | self.unnamed_consts().for_each(|konst| { | ||
84 | let src = konst.lookup(db).source(db); | ||
85 | res[keys::CONST].insert(src, konst); | ||
86 | }); | ||
83 | self.impls().for_each(|imp| add_impl(db, res, imp)); | 87 | self.impls().for_each(|imp| add_impl(db, res, imp)); |
84 | 88 | ||
85 | fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) { | 89 | fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) { |
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs index 068b2ee38..7eadc8e0d 100644 --- a/crates/hir_def/src/db.rs +++ b/crates/hir_def/src/db.rs | |||
@@ -2,9 +2,10 @@ | |||
2 | use std::sync::Arc; | 2 | use std::sync::Arc; |
3 | 3 | ||
4 | use base_db::{salsa, CrateId, SourceDatabase, Upcast}; | 4 | use base_db::{salsa, CrateId, SourceDatabase, Upcast}; |
5 | use either::Either; | ||
5 | use hir_expand::{db::AstDatabase, HirFileId}; | 6 | use hir_expand::{db::AstDatabase, HirFileId}; |
6 | use la_arena::ArenaMap; | 7 | use la_arena::ArenaMap; |
7 | use syntax::SmolStr; | 8 | use syntax::{ast, AstPtr, SmolStr}; |
8 | 9 | ||
9 | use crate::{ | 10 | use crate::{ |
10 | adt::{EnumData, StructData}, | 11 | adt::{EnumData, StructData}, |
@@ -13,6 +14,7 @@ use crate::{ | |||
13 | data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, | 14 | data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, |
14 | generics::GenericParams, | 15 | generics::GenericParams, |
15 | import_map::ImportMap, | 16 | import_map::ImportMap, |
17 | intern::Interned, | ||
16 | item_tree::ItemTree, | 18 | item_tree::ItemTree, |
17 | lang_item::{LangItemTarget, LangItems}, | 19 | lang_item::{LangItemTarget, LangItems}, |
18 | nameres::DefMap, | 20 | nameres::DefMap, |
@@ -113,7 +115,7 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | |||
113 | fn expr_scopes(&self, def: DefWithBodyId) -> Arc<ExprScopes>; | 115 | fn expr_scopes(&self, def: DefWithBodyId) -> Arc<ExprScopes>; |
114 | 116 | ||
115 | #[salsa::invoke(GenericParams::generic_params_query)] | 117 | #[salsa::invoke(GenericParams::generic_params_query)] |
116 | fn generic_params(&self, def: GenericDefId) -> Arc<GenericParams>; | 118 | fn generic_params(&self, def: GenericDefId) -> Interned<GenericParams>; |
117 | 119 | ||
118 | #[salsa::invoke(Attrs::variants_attrs_query)] | 120 | #[salsa::invoke(Attrs::variants_attrs_query)] |
119 | fn variants_attrs(&self, def: EnumId) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>>; | 121 | fn variants_attrs(&self, def: EnumId) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>>; |
@@ -121,6 +123,18 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | |||
121 | #[salsa::invoke(Attrs::fields_attrs_query)] | 123 | #[salsa::invoke(Attrs::fields_attrs_query)] |
122 | fn fields_attrs(&self, def: VariantId) -> Arc<ArenaMap<LocalFieldId, Attrs>>; | 124 | fn fields_attrs(&self, def: VariantId) -> Arc<ArenaMap<LocalFieldId, Attrs>>; |
123 | 125 | ||
126 | #[salsa::invoke(crate::attr::variants_attrs_source_map)] | ||
127 | fn variants_attrs_source_map( | ||
128 | &self, | ||
129 | def: EnumId, | ||
130 | ) -> Arc<ArenaMap<LocalEnumVariantId, AstPtr<ast::Variant>>>; | ||
131 | |||
132 | #[salsa::invoke(crate::attr::fields_attrs_source_map)] | ||
133 | fn fields_attrs_source_map( | ||
134 | &self, | ||
135 | def: VariantId, | ||
136 | ) -> Arc<ArenaMap<LocalFieldId, Either<AstPtr<ast::TupleField>, AstPtr<ast::RecordField>>>>; | ||
137 | |||
124 | #[salsa::invoke(AttrsWithOwner::attrs_query)] | 138 | #[salsa::invoke(AttrsWithOwner::attrs_query)] |
125 | fn attrs(&self, def: AttrDefId) -> AttrsWithOwner; | 139 | fn attrs(&self, def: AttrDefId) -> AttrsWithOwner; |
126 | 140 | ||
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs index 97abf8653..a71ae2668 100644 --- a/crates/hir_def/src/diagnostics.rs +++ b/crates/hir_def/src/diagnostics.rs | |||
@@ -8,7 +8,7 @@ use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink}; | |||
8 | use hir_expand::{HirFileId, InFile}; | 8 | use hir_expand::{HirFileId, InFile}; |
9 | use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; | 9 | use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; |
10 | 10 | ||
11 | use crate::{db::DefDatabase, DefWithBodyId}; | 11 | use crate::{db::DefDatabase, path::ModPath, DefWithBodyId}; |
12 | 12 | ||
13 | pub fn validate_body(db: &dyn DefDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) { | 13 | pub fn validate_body(db: &dyn DefDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) { |
14 | let source_map = db.body_with_source_map(owner).1; | 14 | let source_map = db.body_with_source_map(owner).1; |
@@ -103,6 +103,7 @@ impl Diagnostic for UnresolvedImport { | |||
103 | pub struct UnresolvedMacroCall { | 103 | pub struct UnresolvedMacroCall { |
104 | pub file: HirFileId, | 104 | pub file: HirFileId, |
105 | pub node: AstPtr<ast::MacroCall>, | 105 | pub node: AstPtr<ast::MacroCall>, |
106 | pub path: ModPath, | ||
106 | } | 107 | } |
107 | 108 | ||
108 | impl Diagnostic for UnresolvedMacroCall { | 109 | impl Diagnostic for UnresolvedMacroCall { |
@@ -110,7 +111,7 @@ impl Diagnostic for UnresolvedMacroCall { | |||
110 | DiagnosticCode("unresolved-macro-call") | 111 | DiagnosticCode("unresolved-macro-call") |
111 | } | 112 | } |
112 | fn message(&self) -> String { | 113 | fn message(&self) -> String { |
113 | "unresolved macro call".to_string() | 114 | format!("unresolved macro `{}!`", self.path) |
114 | } | 115 | } |
115 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | 116 | fn display_source(&self) -> InFile<SyntaxNodePtr> { |
116 | InFile::new(self.file, self.node.clone().into()) | 117 | InFile::new(self.file, self.node.clone().into()) |
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs index 6c7376fad..b4ad984bd 100644 --- a/crates/hir_def/src/expr.rs +++ b/crates/hir_def/src/expr.rs | |||
@@ -18,6 +18,7 @@ use syntax::ast::RangeOp; | |||
18 | 18 | ||
19 | use crate::{ | 19 | use crate::{ |
20 | builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint}, | 20 | builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint}, |
21 | intern::Interned, | ||
21 | path::{GenericArgs, Path}, | 22 | path::{GenericArgs, Path}, |
22 | type_ref::{Mutability, Rawness, TypeRef}, | 23 | type_ref::{Mutability, Rawness, TypeRef}, |
23 | BlockId, | 24 | BlockId, |
@@ -86,7 +87,7 @@ pub enum Expr { | |||
86 | receiver: ExprId, | 87 | receiver: ExprId, |
87 | method_name: Name, | 88 | method_name: Name, |
88 | args: Vec<ExprId>, | 89 | args: Vec<ExprId>, |
89 | generic_args: Option<GenericArgs>, | 90 | generic_args: Option<Box<GenericArgs>>, |
90 | }, | 91 | }, |
91 | Match { | 92 | Match { |
92 | expr: ExprId, | 93 | expr: ExprId, |
@@ -106,7 +107,7 @@ pub enum Expr { | |||
106 | expr: Option<ExprId>, | 107 | expr: Option<ExprId>, |
107 | }, | 108 | }, |
108 | RecordLit { | 109 | RecordLit { |
109 | path: Option<Path>, | 110 | path: Option<Box<Path>>, |
110 | fields: Vec<RecordLitField>, | 111 | fields: Vec<RecordLitField>, |
111 | spread: Option<ExprId>, | 112 | spread: Option<ExprId>, |
112 | }, | 113 | }, |
@@ -131,7 +132,7 @@ pub enum Expr { | |||
131 | }, | 132 | }, |
132 | Cast { | 133 | Cast { |
133 | expr: ExprId, | 134 | expr: ExprId, |
134 | type_ref: TypeRef, | 135 | type_ref: Interned<TypeRef>, |
135 | }, | 136 | }, |
136 | Ref { | 137 | Ref { |
137 | expr: ExprId, | 138 | expr: ExprId, |
@@ -161,8 +162,8 @@ pub enum Expr { | |||
161 | }, | 162 | }, |
162 | Lambda { | 163 | Lambda { |
163 | args: Vec<PatId>, | 164 | args: Vec<PatId>, |
164 | arg_types: Vec<Option<TypeRef>>, | 165 | arg_types: Vec<Option<Interned<TypeRef>>>, |
165 | ret_type: Option<TypeRef>, | 166 | ret_type: Option<Interned<TypeRef>>, |
166 | body: ExprId, | 167 | body: ExprId, |
167 | }, | 168 | }, |
168 | Tuple { | 169 | Tuple { |
@@ -240,7 +241,7 @@ pub struct RecordLitField { | |||
240 | 241 | ||
241 | #[derive(Debug, Clone, Eq, PartialEq)] | 242 | #[derive(Debug, Clone, Eq, PartialEq)] |
242 | pub enum Statement { | 243 | pub enum Statement { |
243 | Let { pat: PatId, type_ref: Option<TypeRef>, initializer: Option<ExprId> }, | 244 | Let { pat: PatId, type_ref: Option<Interned<TypeRef>>, initializer: Option<ExprId> }, |
244 | Expr(ExprId), | 245 | Expr(ExprId), |
245 | } | 246 | } |
246 | 247 | ||
@@ -412,13 +413,13 @@ pub enum Pat { | |||
412 | Wild, | 413 | Wild, |
413 | Tuple { args: Vec<PatId>, ellipsis: Option<usize> }, | 414 | Tuple { args: Vec<PatId>, ellipsis: Option<usize> }, |
414 | Or(Vec<PatId>), | 415 | Or(Vec<PatId>), |
415 | Record { path: Option<Path>, args: Vec<RecordFieldPat>, ellipsis: bool }, | 416 | Record { path: Option<Box<Path>>, args: Vec<RecordFieldPat>, ellipsis: bool }, |
416 | Range { start: ExprId, end: ExprId }, | 417 | Range { start: ExprId, end: ExprId }, |
417 | Slice { prefix: Vec<PatId>, slice: Option<PatId>, suffix: Vec<PatId> }, | 418 | Slice { prefix: Vec<PatId>, slice: Option<PatId>, suffix: Vec<PatId> }, |
418 | Path(Path), | 419 | Path(Box<Path>), |
419 | Lit(ExprId), | 420 | Lit(ExprId), |
420 | Bind { mode: BindingAnnotation, name: Name, subpat: Option<PatId> }, | 421 | Bind { mode: BindingAnnotation, name: Name, subpat: Option<PatId> }, |
421 | TupleStruct { path: Option<Path>, args: Vec<PatId>, ellipsis: Option<usize> }, | 422 | TupleStruct { path: Option<Box<Path>>, args: Vec<PatId>, ellipsis: Option<usize> }, |
422 | Ref { pat: PatId, mutability: Mutability }, | 423 | Ref { pat: PatId, mutability: Mutability }, |
423 | Box { inner: PatId }, | 424 | Box { inner: PatId }, |
424 | ConstBlock(ExprId), | 425 | ConstBlock(ExprId), |
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs index 109d3552f..dc3f2908f 100644 --- a/crates/hir_def/src/find_path.rs +++ b/crates/hir_def/src/find_path.rs | |||
@@ -119,8 +119,7 @@ fn find_path_inner( | |||
119 | 119 | ||
120 | // - if the item is the crate root, return `crate` | 120 | // - if the item is the crate root, return `crate` |
121 | let root = def_map.crate_root(db); | 121 | let root = def_map.crate_root(db); |
122 | if item == ItemInNs::Types(ModuleDefId::ModuleId(root)) && def_map.block_id().is_none() { | 122 | if item == ItemInNs::Types(ModuleDefId::ModuleId(root)) { |
123 | // FIXME: the `block_id()` check should be unnecessary, but affects the result | ||
124 | return Some(ModPath::from_segments(PathKind::Crate, Vec::new())); | 123 | return Some(ModPath::from_segments(PathKind::Crate, Vec::new())); |
125 | } | 124 | } |
126 | 125 | ||
@@ -131,7 +130,7 @@ fn find_path_inner( | |||
131 | } | 130 | } |
132 | 131 | ||
133 | // - if the item is the crate root of a dependency crate, return the name from the extern prelude | 132 | // - if the item is the crate root of a dependency crate, return the name from the extern prelude |
134 | for (name, def_id) in def_map.extern_prelude() { | 133 | for (name, def_id) in root.def_map(db).extern_prelude() { |
135 | if item == ItemInNs::Types(*def_id) { | 134 | if item == ItemInNs::Types(*def_id) { |
136 | let name = scope_name.unwrap_or_else(|| name.clone()); | 135 | let name = scope_name.unwrap_or_else(|| name.clone()); |
137 | return Some(ModPath::from_segments(PathKind::Plain, vec![name])); | 136 | return Some(ModPath::from_segments(PathKind::Plain, vec![name])); |
@@ -298,6 +297,7 @@ fn find_local_import_locations( | |||
298 | let data = &def_map[from.local_id]; | 297 | let data = &def_map[from.local_id]; |
299 | let mut worklist = | 298 | let mut worklist = |
300 | data.children.values().map(|child| def_map.module_id(*child)).collect::<Vec<_>>(); | 299 | data.children.values().map(|child| def_map.module_id(*child)).collect::<Vec<_>>(); |
300 | // FIXME: do we need to traverse out of block expressions here? | ||
301 | for ancestor in iter::successors(from.containing_module(db), |m| m.containing_module(db)) { | 301 | for ancestor in iter::successors(from.containing_module(db), |m| m.containing_module(db)) { |
302 | worklist.push(ancestor); | 302 | worklist.push(ancestor); |
303 | } | 303 | } |
@@ -425,106 +425,142 @@ mod tests { | |||
425 | 425 | ||
426 | #[test] | 426 | #[test] |
427 | fn same_module() { | 427 | fn same_module() { |
428 | let code = r#" | 428 | check_found_path( |
429 | //- /main.rs | 429 | r#" |
430 | struct S; | 430 | struct S; |
431 | $0 | 431 | $0 |
432 | "#; | 432 | "#, |
433 | check_found_path(code, "S", "S", "crate::S", "self::S"); | 433 | "S", |
434 | "S", | ||
435 | "crate::S", | ||
436 | "self::S", | ||
437 | ); | ||
434 | } | 438 | } |
435 | 439 | ||
436 | #[test] | 440 | #[test] |
437 | fn enum_variant() { | 441 | fn enum_variant() { |
438 | let code = r#" | 442 | check_found_path( |
439 | //- /main.rs | 443 | r#" |
440 | enum E { A } | 444 | enum E { A } |
441 | $0 | 445 | $0 |
442 | "#; | 446 | "#, |
443 | check_found_path(code, "E::A", "E::A", "E::A", "E::A"); | 447 | "E::A", |
448 | "E::A", | ||
449 | "E::A", | ||
450 | "E::A", | ||
451 | ); | ||
444 | } | 452 | } |
445 | 453 | ||
446 | #[test] | 454 | #[test] |
447 | fn sub_module() { | 455 | fn sub_module() { |
448 | let code = r#" | 456 | check_found_path( |
449 | //- /main.rs | 457 | r#" |
450 | mod foo { | 458 | mod foo { |
451 | pub struct S; | 459 | pub struct S; |
452 | } | 460 | } |
453 | $0 | 461 | $0 |
454 | "#; | 462 | "#, |
455 | check_found_path(code, "foo::S", "foo::S", "crate::foo::S", "self::foo::S"); | 463 | "foo::S", |
464 | "foo::S", | ||
465 | "crate::foo::S", | ||
466 | "self::foo::S", | ||
467 | ); | ||
456 | } | 468 | } |
457 | 469 | ||
458 | #[test] | 470 | #[test] |
459 | fn super_module() { | 471 | fn super_module() { |
460 | let code = r#" | 472 | check_found_path( |
461 | //- /main.rs | 473 | r#" |
462 | mod foo; | 474 | //- /main.rs |
463 | //- /foo.rs | 475 | mod foo; |
464 | mod bar; | 476 | //- /foo.rs |
465 | struct S; | 477 | mod bar; |
466 | //- /foo/bar.rs | 478 | struct S; |
467 | $0 | 479 | //- /foo/bar.rs |
468 | "#; | 480 | $0 |
469 | check_found_path(code, "super::S", "super::S", "crate::foo::S", "super::S"); | 481 | "#, |
482 | "super::S", | ||
483 | "super::S", | ||
484 | "crate::foo::S", | ||
485 | "super::S", | ||
486 | ); | ||
470 | } | 487 | } |
471 | 488 | ||
472 | #[test] | 489 | #[test] |
473 | fn self_module() { | 490 | fn self_module() { |
474 | let code = r#" | 491 | check_found_path( |
475 | //- /main.rs | 492 | r#" |
476 | mod foo; | 493 | //- /main.rs |
477 | //- /foo.rs | 494 | mod foo; |
478 | $0 | 495 | //- /foo.rs |
479 | "#; | 496 | $0 |
480 | check_found_path(code, "self", "self", "crate::foo", "self"); | 497 | "#, |
498 | "self", | ||
499 | "self", | ||
500 | "crate::foo", | ||
501 | "self", | ||
502 | ); | ||
481 | } | 503 | } |
482 | 504 | ||
483 | #[test] | 505 | #[test] |
484 | fn crate_root() { | 506 | fn crate_root() { |
485 | let code = r#" | 507 | check_found_path( |
486 | //- /main.rs | 508 | r#" |
487 | mod foo; | 509 | //- /main.rs |
488 | //- /foo.rs | 510 | mod foo; |
489 | $0 | 511 | //- /foo.rs |
490 | "#; | 512 | $0 |
491 | check_found_path(code, "crate", "crate", "crate", "crate"); | 513 | "#, |
514 | "crate", | ||
515 | "crate", | ||
516 | "crate", | ||
517 | "crate", | ||
518 | ); | ||
492 | } | 519 | } |
493 | 520 | ||
494 | #[test] | 521 | #[test] |
495 | fn same_crate() { | 522 | fn same_crate() { |
496 | let code = r#" | 523 | check_found_path( |
497 | //- /main.rs | 524 | r#" |
498 | mod foo; | 525 | //- /main.rs |
499 | struct S; | 526 | mod foo; |
500 | //- /foo.rs | 527 | struct S; |
501 | $0 | 528 | //- /foo.rs |
502 | "#; | 529 | $0 |
503 | check_found_path(code, "crate::S", "crate::S", "crate::S", "crate::S"); | 530 | "#, |
531 | "crate::S", | ||
532 | "crate::S", | ||
533 | "crate::S", | ||
534 | "crate::S", | ||
535 | ); | ||
504 | } | 536 | } |
505 | 537 | ||
506 | #[test] | 538 | #[test] |
507 | fn different_crate() { | 539 | fn different_crate() { |
508 | let code = r#" | 540 | check_found_path( |
509 | //- /main.rs crate:main deps:std | 541 | r#" |
510 | $0 | 542 | //- /main.rs crate:main deps:std |
511 | //- /std.rs crate:std | 543 | $0 |
512 | pub struct S; | 544 | //- /std.rs crate:std |
513 | "#; | 545 | pub struct S; |
514 | check_found_path(code, "std::S", "std::S", "std::S", "std::S"); | 546 | "#, |
547 | "std::S", | ||
548 | "std::S", | ||
549 | "std::S", | ||
550 | "std::S", | ||
551 | ); | ||
515 | } | 552 | } |
516 | 553 | ||
517 | #[test] | 554 | #[test] |
518 | fn different_crate_renamed() { | 555 | fn different_crate_renamed() { |
519 | let code = r#" | ||
520 | //- /main.rs crate:main deps:std | ||
521 | extern crate std as std_renamed; | ||
522 | $0 | ||
523 | //- /std.rs crate:std | ||
524 | pub struct S; | ||
525 | "#; | ||
526 | check_found_path( | 556 | check_found_path( |
527 | code, | 557 | r#" |
558 | //- /main.rs crate:main deps:std | ||
559 | extern crate std as std_renamed; | ||
560 | $0 | ||
561 | //- /std.rs crate:std | ||
562 | pub struct S; | ||
563 | "#, | ||
528 | "std_renamed::S", | 564 | "std_renamed::S", |
529 | "std_renamed::S", | 565 | "std_renamed::S", |
530 | "std_renamed::S", | 566 | "std_renamed::S", |
@@ -537,41 +573,38 @@ mod tests { | |||
537 | cov_mark::check!(partially_imported); | 573 | cov_mark::check!(partially_imported); |
538 | // Tests that short paths are used even for external items, when parts of the path are | 574 | // Tests that short paths are used even for external items, when parts of the path are |
539 | // already in scope. | 575 | // already in scope. |
540 | let code = r#" | 576 | check_found_path( |
541 | //- /main.rs crate:main deps:syntax | 577 | r#" |
578 | //- /main.rs crate:main deps:syntax | ||
542 | 579 | ||
543 | use syntax::ast; | 580 | use syntax::ast; |
544 | $0 | 581 | $0 |
545 | 582 | ||
546 | //- /lib.rs crate:syntax | 583 | //- /lib.rs crate:syntax |
547 | pub mod ast { | 584 | pub mod ast { |
548 | pub enum ModuleItem { | 585 | pub enum ModuleItem { |
549 | A, B, C, | 586 | A, B, C, |
550 | } | 587 | } |
551 | } | 588 | } |
552 | "#; | 589 | "#, |
553 | check_found_path( | ||
554 | code, | ||
555 | "ast::ModuleItem", | 590 | "ast::ModuleItem", |
556 | "syntax::ast::ModuleItem", | 591 | "syntax::ast::ModuleItem", |
557 | "syntax::ast::ModuleItem", | 592 | "syntax::ast::ModuleItem", |
558 | "syntax::ast::ModuleItem", | 593 | "syntax::ast::ModuleItem", |
559 | ); | 594 | ); |
560 | 595 | ||
561 | let code = r#" | ||
562 | //- /main.rs crate:main deps:syntax | ||
563 | |||
564 | $0 | ||
565 | |||
566 | //- /lib.rs crate:syntax | ||
567 | pub mod ast { | ||
568 | pub enum ModuleItem { | ||
569 | A, B, C, | ||
570 | } | ||
571 | } | ||
572 | "#; | ||
573 | check_found_path( | 596 | check_found_path( |
574 | code, | 597 | r#" |
598 | //- /main.rs crate:main deps:syntax | ||
599 | $0 | ||
600 | |||
601 | //- /lib.rs crate:syntax | ||
602 | pub mod ast { | ||
603 | pub enum ModuleItem { | ||
604 | A, B, C, | ||
605 | } | ||
606 | } | ||
607 | "#, | ||
575 | "syntax::ast::ModuleItem", | 608 | "syntax::ast::ModuleItem", |
576 | "syntax::ast::ModuleItem", | 609 | "syntax::ast::ModuleItem", |
577 | "syntax::ast::ModuleItem", | 610 | "syntax::ast::ModuleItem", |
@@ -581,68 +614,86 @@ mod tests { | |||
581 | 614 | ||
582 | #[test] | 615 | #[test] |
583 | fn same_crate_reexport() { | 616 | fn same_crate_reexport() { |
584 | let code = r#" | 617 | check_found_path( |
585 | //- /main.rs | 618 | r#" |
586 | mod bar { | 619 | mod bar { |
587 | mod foo { pub(super) struct S; } | 620 | mod foo { pub(super) struct S; } |
588 | pub(crate) use foo::*; | 621 | pub(crate) use foo::*; |
589 | } | 622 | } |
590 | $0 | 623 | $0 |
591 | "#; | 624 | "#, |
592 | check_found_path(code, "bar::S", "bar::S", "crate::bar::S", "self::bar::S"); | 625 | "bar::S", |
626 | "bar::S", | ||
627 | "crate::bar::S", | ||
628 | "self::bar::S", | ||
629 | ); | ||
593 | } | 630 | } |
594 | 631 | ||
595 | #[test] | 632 | #[test] |
596 | fn same_crate_reexport_rename() { | 633 | fn same_crate_reexport_rename() { |
597 | let code = r#" | 634 | check_found_path( |
598 | //- /main.rs | 635 | r#" |
599 | mod bar { | 636 | mod bar { |
600 | mod foo { pub(super) struct S; } | 637 | mod foo { pub(super) struct S; } |
601 | pub(crate) use foo::S as U; | 638 | pub(crate) use foo::S as U; |
602 | } | 639 | } |
603 | $0 | 640 | $0 |
604 | "#; | 641 | "#, |
605 | check_found_path(code, "bar::U", "bar::U", "crate::bar::U", "self::bar::U"); | 642 | "bar::U", |
643 | "bar::U", | ||
644 | "crate::bar::U", | ||
645 | "self::bar::U", | ||
646 | ); | ||
606 | } | 647 | } |
607 | 648 | ||
608 | #[test] | 649 | #[test] |
609 | fn different_crate_reexport() { | 650 | fn different_crate_reexport() { |
610 | let code = r#" | 651 | check_found_path( |
611 | //- /main.rs crate:main deps:std | 652 | r#" |
612 | $0 | 653 | //- /main.rs crate:main deps:std |
613 | //- /std.rs crate:std deps:core | 654 | $0 |
614 | pub use core::S; | 655 | //- /std.rs crate:std deps:core |
615 | //- /core.rs crate:core | 656 | pub use core::S; |
616 | pub struct S; | 657 | //- /core.rs crate:core |
617 | "#; | 658 | pub struct S; |
618 | check_found_path(code, "std::S", "std::S", "std::S", "std::S"); | 659 | "#, |
660 | "std::S", | ||
661 | "std::S", | ||
662 | "std::S", | ||
663 | "std::S", | ||
664 | ); | ||
619 | } | 665 | } |
620 | 666 | ||
621 | #[test] | 667 | #[test] |
622 | fn prelude() { | 668 | fn prelude() { |
623 | let code = r#" | 669 | check_found_path( |
624 | //- /main.rs crate:main deps:std | 670 | r#" |
625 | $0 | 671 | //- /main.rs crate:main deps:std |
626 | //- /std.rs crate:std | 672 | $0 |
627 | pub mod prelude { pub struct S; } | 673 | //- /std.rs crate:std |
628 | #[prelude_import] | 674 | pub mod prelude { pub struct S; } |
629 | pub use prelude::*; | 675 | #[prelude_import] |
630 | "#; | 676 | pub use prelude::*; |
631 | check_found_path(code, "S", "S", "S", "S"); | 677 | "#, |
678 | "S", | ||
679 | "S", | ||
680 | "S", | ||
681 | "S", | ||
682 | ); | ||
632 | } | 683 | } |
633 | 684 | ||
634 | #[test] | 685 | #[test] |
635 | fn enum_variant_from_prelude() { | 686 | fn enum_variant_from_prelude() { |
636 | let code = r#" | 687 | let code = r#" |
637 | //- /main.rs crate:main deps:std | 688 | //- /main.rs crate:main deps:std |
638 | $0 | 689 | $0 |
639 | //- /std.rs crate:std | 690 | //- /std.rs crate:std |
640 | pub mod prelude { | 691 | pub mod prelude { |
641 | pub enum Option<T> { Some(T), None } | 692 | pub enum Option<T> { Some(T), None } |
642 | pub use Option::*; | 693 | pub use Option::*; |
643 | } | 694 | } |
644 | #[prelude_import] | 695 | #[prelude_import] |
645 | pub use prelude::*; | 696 | pub use prelude::*; |
646 | "#; | 697 | "#; |
647 | check_found_path(code, "None", "None", "None", "None"); | 698 | check_found_path(code, "None", "None", "None", "None"); |
648 | check_found_path(code, "Some", "Some", "Some", "Some"); | 699 | check_found_path(code, "Some", "Some", "Some", "Some"); |
@@ -650,71 +701,85 @@ mod tests { | |||
650 | 701 | ||
651 | #[test] | 702 | #[test] |
652 | fn shortest_path() { | 703 | fn shortest_path() { |
653 | let code = r#" | 704 | check_found_path( |
654 | //- /main.rs | 705 | r#" |
655 | pub mod foo; | 706 | //- /main.rs |
656 | pub mod baz; | 707 | pub mod foo; |
657 | struct S; | 708 | pub mod baz; |
658 | $0 | 709 | struct S; |
659 | //- /foo.rs | 710 | $0 |
660 | pub mod bar { pub struct S; } | 711 | //- /foo.rs |
661 | //- /baz.rs | 712 | pub mod bar { pub struct S; } |
662 | pub use crate::foo::bar::S; | 713 | //- /baz.rs |
663 | "#; | 714 | pub use crate::foo::bar::S; |
664 | check_found_path(code, "baz::S", "baz::S", "crate::baz::S", "self::baz::S"); | 715 | "#, |
716 | "baz::S", | ||
717 | "baz::S", | ||
718 | "crate::baz::S", | ||
719 | "self::baz::S", | ||
720 | ); | ||
665 | } | 721 | } |
666 | 722 | ||
667 | #[test] | 723 | #[test] |
668 | fn discount_private_imports() { | 724 | fn discount_private_imports() { |
669 | let code = r#" | 725 | check_found_path( |
670 | //- /main.rs | 726 | r#" |
671 | mod foo; | 727 | //- /main.rs |
672 | pub mod bar { pub struct S; } | 728 | mod foo; |
673 | use bar::S; | 729 | pub mod bar { pub struct S; } |
674 | //- /foo.rs | 730 | use bar::S; |
675 | $0 | 731 | //- /foo.rs |
676 | "#; | 732 | $0 |
677 | // crate::S would be shorter, but using private imports seems wrong | 733 | "#, |
678 | check_found_path(code, "crate::bar::S", "crate::bar::S", "crate::bar::S", "crate::bar::S"); | 734 | // crate::S would be shorter, but using private imports seems wrong |
735 | "crate::bar::S", | ||
736 | "crate::bar::S", | ||
737 | "crate::bar::S", | ||
738 | "crate::bar::S", | ||
739 | ); | ||
679 | } | 740 | } |
680 | 741 | ||
681 | #[test] | 742 | #[test] |
682 | fn import_cycle() { | 743 | fn import_cycle() { |
683 | let code = r#" | 744 | check_found_path( |
684 | //- /main.rs | 745 | r#" |
685 | pub mod foo; | 746 | //- /main.rs |
686 | pub mod bar; | 747 | pub mod foo; |
687 | pub mod baz; | 748 | pub mod bar; |
688 | //- /bar.rs | 749 | pub mod baz; |
689 | $0 | 750 | //- /bar.rs |
690 | //- /foo.rs | 751 | $0 |
691 | pub use super::baz; | 752 | //- /foo.rs |
692 | pub struct S; | 753 | pub use super::baz; |
693 | //- /baz.rs | 754 | pub struct S; |
694 | pub use super::foo; | 755 | //- /baz.rs |
695 | "#; | 756 | pub use super::foo; |
696 | check_found_path(code, "crate::foo::S", "crate::foo::S", "crate::foo::S", "crate::foo::S"); | 757 | "#, |
758 | "crate::foo::S", | ||
759 | "crate::foo::S", | ||
760 | "crate::foo::S", | ||
761 | "crate::foo::S", | ||
762 | ); | ||
697 | } | 763 | } |
698 | 764 | ||
699 | #[test] | 765 | #[test] |
700 | fn prefer_std_paths_over_alloc() { | 766 | fn prefer_std_paths_over_alloc() { |
701 | cov_mark::check!(prefer_std_paths); | 767 | cov_mark::check!(prefer_std_paths); |
702 | let code = r#" | 768 | check_found_path( |
703 | //- /main.rs crate:main deps:alloc,std | 769 | r#" |
704 | $0 | 770 | //- /main.rs crate:main deps:alloc,std |
771 | $0 | ||
705 | 772 | ||
706 | //- /std.rs crate:std deps:alloc | 773 | //- /std.rs crate:std deps:alloc |
707 | pub mod sync { | 774 | pub mod sync { |
708 | pub use alloc::sync::Arc; | 775 | pub use alloc::sync::Arc; |
709 | } | 776 | } |
710 | 777 | ||
711 | //- /zzz.rs crate:alloc | 778 | //- /zzz.rs crate:alloc |
712 | pub mod sync { | 779 | pub mod sync { |
713 | pub struct Arc; | 780 | pub struct Arc; |
714 | } | 781 | } |
715 | "#; | 782 | "#, |
716 | check_found_path( | ||
717 | code, | ||
718 | "std::sync::Arc", | 783 | "std::sync::Arc", |
719 | "std::sync::Arc", | 784 | "std::sync::Arc", |
720 | "std::sync::Arc", | 785 | "std::sync::Arc", |
@@ -725,26 +790,25 @@ mod tests { | |||
725 | #[test] | 790 | #[test] |
726 | fn prefer_core_paths_over_std() { | 791 | fn prefer_core_paths_over_std() { |
727 | cov_mark::check!(prefer_no_std_paths); | 792 | cov_mark::check!(prefer_no_std_paths); |
728 | let code = r#" | 793 | check_found_path( |
729 | //- /main.rs crate:main deps:core,std | 794 | r#" |
730 | #![no_std] | 795 | //- /main.rs crate:main deps:core,std |
796 | #![no_std] | ||
731 | 797 | ||
732 | $0 | 798 | $0 |
733 | 799 | ||
734 | //- /std.rs crate:std deps:core | 800 | //- /std.rs crate:std deps:core |
735 | 801 | ||
736 | pub mod fmt { | 802 | pub mod fmt { |
737 | pub use core::fmt::Error; | 803 | pub use core::fmt::Error; |
738 | } | 804 | } |
739 | 805 | ||
740 | //- /zzz.rs crate:core | 806 | //- /zzz.rs crate:core |
741 | 807 | ||
742 | pub mod fmt { | 808 | pub mod fmt { |
743 | pub struct Error; | 809 | pub struct Error; |
744 | } | 810 | } |
745 | "#; | 811 | "#, |
746 | check_found_path( | ||
747 | code, | ||
748 | "core::fmt::Error", | 812 | "core::fmt::Error", |
749 | "core::fmt::Error", | 813 | "core::fmt::Error", |
750 | "core::fmt::Error", | 814 | "core::fmt::Error", |
@@ -754,26 +818,25 @@ mod tests { | |||
754 | 818 | ||
755 | #[test] | 819 | #[test] |
756 | fn prefer_alloc_paths_over_std() { | 820 | fn prefer_alloc_paths_over_std() { |
757 | let code = r#" | 821 | check_found_path( |
758 | //- /main.rs crate:main deps:alloc,std | 822 | r#" |
759 | #![no_std] | 823 | //- /main.rs crate:main deps:alloc,std |
824 | #![no_std] | ||
760 | 825 | ||
761 | $0 | 826 | $0 |
762 | 827 | ||
763 | //- /std.rs crate:std deps:alloc | 828 | //- /std.rs crate:std deps:alloc |
764 | 829 | ||
765 | pub mod sync { | 830 | pub mod sync { |
766 | pub use alloc::sync::Arc; | 831 | pub use alloc::sync::Arc; |
767 | } | 832 | } |
768 | 833 | ||
769 | //- /zzz.rs crate:alloc | 834 | //- /zzz.rs crate:alloc |
770 | 835 | ||
771 | pub mod sync { | 836 | pub mod sync { |
772 | pub struct Arc; | 837 | pub struct Arc; |
773 | } | 838 | } |
774 | "#; | 839 | "#, |
775 | check_found_path( | ||
776 | code, | ||
777 | "alloc::sync::Arc", | 840 | "alloc::sync::Arc", |
778 | "alloc::sync::Arc", | 841 | "alloc::sync::Arc", |
779 | "alloc::sync::Arc", | 842 | "alloc::sync::Arc", |
@@ -783,20 +846,19 @@ mod tests { | |||
783 | 846 | ||
784 | #[test] | 847 | #[test] |
785 | fn prefer_shorter_paths_if_not_alloc() { | 848 | fn prefer_shorter_paths_if_not_alloc() { |
786 | let code = r#" | 849 | check_found_path( |
787 | //- /main.rs crate:main deps:megaalloc,std | 850 | r#" |
788 | $0 | 851 | //- /main.rs crate:main deps:megaalloc,std |
852 | $0 | ||
789 | 853 | ||
790 | //- /std.rs crate:std deps:megaalloc | 854 | //- /std.rs crate:std deps:megaalloc |
791 | pub mod sync { | 855 | pub mod sync { |
792 | pub use megaalloc::sync::Arc; | 856 | pub use megaalloc::sync::Arc; |
793 | } | 857 | } |
794 | 858 | ||
795 | //- /zzz.rs crate:megaalloc | 859 | //- /zzz.rs crate:megaalloc |
796 | pub struct Arc; | 860 | pub struct Arc; |
797 | "#; | 861 | "#, |
798 | check_found_path( | ||
799 | code, | ||
800 | "megaalloc::Arc", | 862 | "megaalloc::Arc", |
801 | "megaalloc::Arc", | 863 | "megaalloc::Arc", |
802 | "megaalloc::Arc", | 864 | "megaalloc::Arc", |
@@ -807,12 +869,11 @@ mod tests { | |||
807 | #[test] | 869 | #[test] |
808 | fn builtins_are_in_scope() { | 870 | fn builtins_are_in_scope() { |
809 | let code = r#" | 871 | let code = r#" |
810 | //- /main.rs | 872 | $0 |
811 | $0 | ||
812 | 873 | ||
813 | pub mod primitive { | 874 | pub mod primitive { |
814 | pub use u8; | 875 | pub use u8; |
815 | } | 876 | } |
816 | "#; | 877 | "#; |
817 | check_found_path(code, "u8", "u8", "u8", "u8"); | 878 | check_found_path(code, "u8", "u8", "u8", "u8"); |
818 | check_found_path(code, "u16", "u16", "u16", "u16"); | 879 | check_found_path(code, "u16", "u16", "u16", "u16"); |
@@ -822,10 +883,10 @@ mod tests { | |||
822 | fn inner_items() { | 883 | fn inner_items() { |
823 | check_found_path( | 884 | check_found_path( |
824 | r#" | 885 | r#" |
825 | fn main() { | 886 | fn main() { |
826 | struct Inner {} | 887 | struct Inner {} |
827 | $0 | 888 | $0 |
828 | } | 889 | } |
829 | "#, | 890 | "#, |
830 | "Inner", | 891 | "Inner", |
831 | "Inner", | 892 | "Inner", |
@@ -838,12 +899,12 @@ mod tests { | |||
838 | fn inner_items_from_outer_scope() { | 899 | fn inner_items_from_outer_scope() { |
839 | check_found_path( | 900 | check_found_path( |
840 | r#" | 901 | r#" |
841 | fn main() { | 902 | fn main() { |
842 | struct Struct {} | 903 | struct Struct {} |
843 | { | 904 | { |
844 | $0 | 905 | $0 |
845 | } | 906 | } |
846 | } | 907 | } |
847 | "#, | 908 | "#, |
848 | "Struct", | 909 | "Struct", |
849 | "Struct", | 910 | "Struct", |
@@ -857,14 +918,14 @@ mod tests { | |||
857 | cov_mark::check!(prefixed_in_block_expression); | 918 | cov_mark::check!(prefixed_in_block_expression); |
858 | check_found_path( | 919 | check_found_path( |
859 | r#" | 920 | r#" |
860 | fn main() { | 921 | fn main() { |
861 | mod module { | 922 | mod module { |
862 | struct Struct {} | 923 | struct Struct {} |
863 | } | 924 | } |
864 | { | 925 | { |
865 | $0 | 926 | $0 |
866 | } | 927 | } |
867 | } | 928 | } |
868 | "#, | 929 | "#, |
869 | "module::Struct", | 930 | "module::Struct", |
870 | "module::Struct", | 931 | "module::Struct", |
@@ -877,19 +938,65 @@ mod tests { | |||
877 | fn outer_items_with_inner_items_present() { | 938 | fn outer_items_with_inner_items_present() { |
878 | check_found_path( | 939 | check_found_path( |
879 | r#" | 940 | r#" |
880 | mod module { | 941 | mod module { |
881 | pub struct CompleteMe; | 942 | pub struct CompleteMe; |
882 | } | 943 | } |
883 | 944 | ||
884 | fn main() { | 945 | fn main() { |
885 | fn inner() {} | 946 | fn inner() {} |
886 | $0 | 947 | $0 |
887 | } | 948 | } |
888 | "#, | 949 | "#, |
950 | // FIXME: these could use fewer/better prefixes | ||
889 | "module::CompleteMe", | 951 | "module::CompleteMe", |
890 | "module::CompleteMe", | ||
891 | "crate::module::CompleteMe", | 952 | "crate::module::CompleteMe", |
892 | "self::module::CompleteMe", | 953 | "crate::module::CompleteMe", |
954 | "crate::module::CompleteMe", | ||
955 | ) | ||
956 | } | ||
957 | |||
958 | #[test] | ||
959 | fn from_inside_module() { | ||
960 | // This worked correctly, but the test suite logic was broken. | ||
961 | cov_mark::check!(submodule_in_testdb); | ||
962 | check_found_path( | ||
963 | r#" | ||
964 | mod baz { | ||
965 | pub struct Foo {} | ||
966 | } | ||
967 | |||
968 | mod bar { | ||
969 | fn bar() { | ||
970 | $0 | ||
971 | } | ||
972 | } | ||
973 | "#, | ||
974 | "crate::baz::Foo", | ||
975 | "crate::baz::Foo", | ||
976 | "crate::baz::Foo", | ||
977 | "crate::baz::Foo", | ||
978 | ) | ||
979 | } | ||
980 | |||
981 | #[test] | ||
982 | fn from_inside_module_with_inner_items() { | ||
983 | check_found_path( | ||
984 | r#" | ||
985 | mod baz { | ||
986 | pub struct Foo {} | ||
987 | } | ||
988 | |||
989 | mod bar { | ||
990 | fn bar() { | ||
991 | fn inner() {} | ||
992 | $0 | ||
993 | } | ||
994 | } | ||
995 | "#, | ||
996 | "crate::baz::Foo", | ||
997 | "crate::baz::Foo", | ||
998 | "crate::baz::Foo", | ||
999 | "crate::baz::Foo", | ||
893 | ) | 1000 | ) |
894 | } | 1001 | } |
895 | 1002 | ||
@@ -920,4 +1027,34 @@ pub mod name { | |||
920 | "self::name::AsName", | 1027 | "self::name::AsName", |
921 | ); | 1028 | ); |
922 | } | 1029 | } |
1030 | |||
1031 | #[test] | ||
1032 | fn extern_crate() { | ||
1033 | check_found_path( | ||
1034 | r#" | ||
1035 | //- /main.rs crate:main deps:dep | ||
1036 | $0 | ||
1037 | //- /dep.rs crate:dep | ||
1038 | "#, | ||
1039 | "dep", | ||
1040 | "dep", | ||
1041 | "dep", | ||
1042 | "dep", | ||
1043 | ); | ||
1044 | |||
1045 | check_found_path( | ||
1046 | r#" | ||
1047 | //- /main.rs crate:main deps:dep | ||
1048 | fn f() { | ||
1049 | fn inner() {} | ||
1050 | $0 | ||
1051 | } | ||
1052 | //- /dep.rs crate:dep | ||
1053 | "#, | ||
1054 | "dep", | ||
1055 | "dep", | ||
1056 | "dep", | ||
1057 | "dep", | ||
1058 | ); | ||
1059 | } | ||
923 | } | 1060 | } |
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs index 7c6cbff11..de5acced8 100644 --- a/crates/hir_def/src/generics.rs +++ b/crates/hir_def/src/generics.rs | |||
@@ -2,7 +2,6 @@ | |||
2 | //! structs, impls, traits, etc. This module provides a common HIR for these | 2 | //! structs, impls, traits, etc. This module provides a common HIR for these |
3 | //! generic parameters. See also the `Generics` type and the `generics_of` query | 3 | //! generic parameters. See also the `Generics` type and the `generics_of` query |
4 | //! in rustc. | 4 | //! in rustc. |
5 | use std::sync::Arc; | ||
6 | 5 | ||
7 | use base_db::FileId; | 6 | use base_db::FileId; |
8 | use either::Either; | 7 | use either::Either; |
@@ -18,6 +17,7 @@ use crate::{ | |||
18 | child_by_source::ChildBySource, | 17 | child_by_source::ChildBySource, |
19 | db::DefDatabase, | 18 | db::DefDatabase, |
20 | dyn_map::DynMap, | 19 | dyn_map::DynMap, |
20 | intern::Interned, | ||
21 | keys, | 21 | keys, |
22 | src::{HasChildSource, HasSource}, | 22 | src::{HasChildSource, HasSource}, |
23 | type_ref::{LifetimeRef, TypeBound, TypeRef}, | 23 | type_ref::{LifetimeRef, TypeBound, TypeRef}, |
@@ -26,27 +26,27 @@ use crate::{ | |||
26 | }; | 26 | }; |
27 | 27 | ||
28 | /// Data about a generic type parameter (to a function, struct, impl, ...). | 28 | /// Data about a generic type parameter (to a function, struct, impl, ...). |
29 | #[derive(Clone, PartialEq, Eq, Debug)] | 29 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] |
30 | pub struct TypeParamData { | 30 | pub struct TypeParamData { |
31 | pub name: Option<Name>, | 31 | pub name: Option<Name>, |
32 | pub default: Option<TypeRef>, | 32 | pub default: Option<Interned<TypeRef>>, |
33 | pub provenance: TypeParamProvenance, | 33 | pub provenance: TypeParamProvenance, |
34 | } | 34 | } |
35 | 35 | ||
36 | /// Data about a generic lifetime parameter (to a function, struct, impl, ...). | 36 | /// Data about a generic lifetime parameter (to a function, struct, impl, ...). |
37 | #[derive(Clone, PartialEq, Eq, Debug)] | 37 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] |
38 | pub struct LifetimeParamData { | 38 | pub struct LifetimeParamData { |
39 | pub name: Name, | 39 | pub name: Name, |
40 | } | 40 | } |
41 | 41 | ||
42 | /// Data about a generic const parameter (to a function, struct, impl, ...). | 42 | /// Data about a generic const parameter (to a function, struct, impl, ...). |
43 | #[derive(Clone, PartialEq, Eq, Debug)] | 43 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] |
44 | pub struct ConstParamData { | 44 | pub struct ConstParamData { |
45 | pub name: Name, | 45 | pub name: Name, |
46 | pub ty: TypeRef, | 46 | pub ty: Interned<TypeRef>, |
47 | } | 47 | } |
48 | 48 | ||
49 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | 49 | #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] |
50 | pub enum TypeParamProvenance { | 50 | pub enum TypeParamProvenance { |
51 | TypeParamList, | 51 | TypeParamList, |
52 | TraitSelf, | 52 | TraitSelf, |
@@ -54,7 +54,7 @@ pub enum TypeParamProvenance { | |||
54 | } | 54 | } |
55 | 55 | ||
56 | /// Data about the generic parameters of a function, struct, impl, etc. | 56 | /// Data about the generic parameters of a function, struct, impl, etc. |
57 | #[derive(Clone, PartialEq, Eq, Debug, Default)] | 57 | #[derive(Clone, PartialEq, Eq, Debug, Default, Hash)] |
58 | pub struct GenericParams { | 58 | pub struct GenericParams { |
59 | pub types: Arena<TypeParamData>, | 59 | pub types: Arena<TypeParamData>, |
60 | pub lifetimes: Arena<LifetimeParamData>, | 60 | pub lifetimes: Arena<LifetimeParamData>, |
@@ -66,16 +66,16 @@ pub struct GenericParams { | |||
66 | /// where clauses like `where T: Foo + Bar` are turned into multiple of these. | 66 | /// where clauses like `where T: Foo + Bar` are turned into multiple of these. |
67 | /// It might still result in multiple actual predicates though, because of | 67 | /// It might still result in multiple actual predicates though, because of |
68 | /// associated type bindings like `Iterator<Item = u32>`. | 68 | /// associated type bindings like `Iterator<Item = u32>`. |
69 | #[derive(Clone, PartialEq, Eq, Debug)] | 69 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] |
70 | pub enum WherePredicate { | 70 | pub enum WherePredicate { |
71 | TypeBound { target: WherePredicateTypeTarget, bound: TypeBound }, | 71 | TypeBound { target: WherePredicateTypeTarget, bound: TypeBound }, |
72 | Lifetime { target: LifetimeRef, bound: LifetimeRef }, | 72 | Lifetime { target: LifetimeRef, bound: LifetimeRef }, |
73 | ForLifetime { lifetimes: Box<[Name]>, target: WherePredicateTypeTarget, bound: TypeBound }, | 73 | ForLifetime { lifetimes: Box<[Name]>, target: WherePredicateTypeTarget, bound: TypeBound }, |
74 | } | 74 | } |
75 | 75 | ||
76 | #[derive(Clone, PartialEq, Eq, Debug)] | 76 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] |
77 | pub enum WherePredicateTypeTarget { | 77 | pub enum WherePredicateTypeTarget { |
78 | TypeRef(TypeRef), | 78 | TypeRef(Interned<TypeRef>), |
79 | /// For desugared where predicates that can directly refer to a type param. | 79 | /// For desugared where predicates that can directly refer to a type param. |
80 | TypeParam(LocalTypeParamId), | 80 | TypeParam(LocalTypeParamId), |
81 | } | 81 | } |
@@ -91,7 +91,7 @@ impl GenericParams { | |||
91 | pub(crate) fn generic_params_query( | 91 | pub(crate) fn generic_params_query( |
92 | db: &dyn DefDatabase, | 92 | db: &dyn DefDatabase, |
93 | def: GenericDefId, | 93 | def: GenericDefId, |
94 | ) -> Arc<GenericParams> { | 94 | ) -> Interned<GenericParams> { |
95 | let _p = profile::span("generic_params_query"); | 95 | let _p = profile::span("generic_params_query"); |
96 | 96 | ||
97 | let generics = match def { | 97 | let generics = match def { |
@@ -99,47 +99,49 @@ impl GenericParams { | |||
99 | let id = id.lookup(db).id; | 99 | let id = id.lookup(db).id; |
100 | let tree = id.item_tree(db); | 100 | let tree = id.item_tree(db); |
101 | let item = &tree[id.value]; | 101 | let item = &tree[id.value]; |
102 | tree[item.generic_params].clone() | 102 | item.generic_params.clone() |
103 | } | 103 | } |
104 | GenericDefId::AdtId(AdtId::StructId(id)) => { | 104 | GenericDefId::AdtId(AdtId::StructId(id)) => { |
105 | let id = id.lookup(db).id; | 105 | let id = id.lookup(db).id; |
106 | let tree = id.item_tree(db); | 106 | let tree = id.item_tree(db); |
107 | let item = &tree[id.value]; | 107 | let item = &tree[id.value]; |
108 | tree[item.generic_params].clone() | 108 | item.generic_params.clone() |
109 | } | 109 | } |
110 | GenericDefId::AdtId(AdtId::EnumId(id)) => { | 110 | GenericDefId::AdtId(AdtId::EnumId(id)) => { |
111 | let id = id.lookup(db).id; | 111 | let id = id.lookup(db).id; |
112 | let tree = id.item_tree(db); | 112 | let tree = id.item_tree(db); |
113 | let item = &tree[id.value]; | 113 | let item = &tree[id.value]; |
114 | tree[item.generic_params].clone() | 114 | item.generic_params.clone() |
115 | } | 115 | } |
116 | GenericDefId::AdtId(AdtId::UnionId(id)) => { | 116 | GenericDefId::AdtId(AdtId::UnionId(id)) => { |
117 | let id = id.lookup(db).id; | 117 | let id = id.lookup(db).id; |
118 | let tree = id.item_tree(db); | 118 | let tree = id.item_tree(db); |
119 | let item = &tree[id.value]; | 119 | let item = &tree[id.value]; |
120 | tree[item.generic_params].clone() | 120 | item.generic_params.clone() |
121 | } | 121 | } |
122 | GenericDefId::TraitId(id) => { | 122 | GenericDefId::TraitId(id) => { |
123 | let id = id.lookup(db).id; | 123 | let id = id.lookup(db).id; |
124 | let tree = id.item_tree(db); | 124 | let tree = id.item_tree(db); |
125 | let item = &tree[id.value]; | 125 | let item = &tree[id.value]; |
126 | tree[item.generic_params].clone() | 126 | item.generic_params.clone() |
127 | } | 127 | } |
128 | GenericDefId::TypeAliasId(id) => { | 128 | GenericDefId::TypeAliasId(id) => { |
129 | let id = id.lookup(db).id; | 129 | let id = id.lookup(db).id; |
130 | let tree = id.item_tree(db); | 130 | let tree = id.item_tree(db); |
131 | let item = &tree[id.value]; | 131 | let item = &tree[id.value]; |
132 | tree[item.generic_params].clone() | 132 | item.generic_params.clone() |
133 | } | 133 | } |
134 | GenericDefId::ImplId(id) => { | 134 | GenericDefId::ImplId(id) => { |
135 | let id = id.lookup(db).id; | 135 | let id = id.lookup(db).id; |
136 | let tree = id.item_tree(db); | 136 | let tree = id.item_tree(db); |
137 | let item = &tree[id.value]; | 137 | let item = &tree[id.value]; |
138 | tree[item.generic_params].clone() | 138 | item.generic_params.clone() |
139 | } | ||
140 | GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => { | ||
141 | Interned::new(GenericParams::default()) | ||
139 | } | 142 | } |
140 | GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => GenericParams::default(), | ||
141 | }; | 143 | }; |
142 | Arc::new(generics) | 144 | generics |
143 | } | 145 | } |
144 | 146 | ||
145 | fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) { | 147 | fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) { |
@@ -217,6 +219,7 @@ impl GenericParams { | |||
217 | GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => FileId(!0).into(), | 219 | GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => FileId(!0).into(), |
218 | }; | 220 | }; |
219 | 221 | ||
222 | generics.shrink_to_fit(); | ||
220 | (generics, InFile::new(file_id, sm)) | 223 | (generics, InFile::new(file_id, sm)) |
221 | } | 224 | } |
222 | 225 | ||
@@ -256,7 +259,8 @@ impl GenericParams { | |||
256 | for type_param in params.type_params() { | 259 | for type_param in params.type_params() { |
257 | let name = type_param.name().map_or_else(Name::missing, |it| it.as_name()); | 260 | let name = type_param.name().map_or_else(Name::missing, |it| it.as_name()); |
258 | // FIXME: Use `Path::from_src` | 261 | // FIXME: Use `Path::from_src` |
259 | let default = type_param.default_type().map(|it| TypeRef::from_ast(lower_ctx, it)); | 262 | let default = |
263 | type_param.default_type().map(|it| Interned::new(TypeRef::from_ast(lower_ctx, it))); | ||
260 | let param = TypeParamData { | 264 | let param = TypeParamData { |
261 | name: Some(name.clone()), | 265 | name: Some(name.clone()), |
262 | default, | 266 | default, |
@@ -280,7 +284,7 @@ impl GenericParams { | |||
280 | for const_param in params.const_params() { | 284 | for const_param in params.const_params() { |
281 | let name = const_param.name().map_or_else(Name::missing, |it| it.as_name()); | 285 | let name = const_param.name().map_or_else(Name::missing, |it| it.as_name()); |
282 | let ty = const_param.ty().map_or(TypeRef::Error, |it| TypeRef::from_ast(lower_ctx, it)); | 286 | let ty = const_param.ty().map_or(TypeRef::Error, |it| TypeRef::from_ast(lower_ctx, it)); |
283 | let param = ConstParamData { name, ty }; | 287 | let param = ConstParamData { name, ty: Interned::new(ty) }; |
284 | let param_id = self.consts.alloc(param); | 288 | let param_id = self.consts.alloc(param); |
285 | sm.const_params.insert(param_id, const_param.clone()); | 289 | sm.const_params.insert(param_id, const_param.clone()); |
286 | } | 290 | } |
@@ -334,11 +338,11 @@ impl GenericParams { | |||
334 | (Either::Left(type_ref), bound) => match hrtb_lifetimes { | 338 | (Either::Left(type_ref), bound) => match hrtb_lifetimes { |
335 | Some(hrtb_lifetimes) => WherePredicate::ForLifetime { | 339 | Some(hrtb_lifetimes) => WherePredicate::ForLifetime { |
336 | lifetimes: hrtb_lifetimes.clone(), | 340 | lifetimes: hrtb_lifetimes.clone(), |
337 | target: WherePredicateTypeTarget::TypeRef(type_ref), | 341 | target: WherePredicateTypeTarget::TypeRef(Interned::new(type_ref)), |
338 | bound, | 342 | bound, |
339 | }, | 343 | }, |
340 | None => WherePredicate::TypeBound { | 344 | None => WherePredicate::TypeBound { |
341 | target: WherePredicateTypeTarget::TypeRef(type_ref), | 345 | target: WherePredicateTypeTarget::TypeRef(Interned::new(type_ref)), |
342 | bound, | 346 | bound, |
343 | }, | 347 | }, |
344 | }, | 348 | }, |
@@ -369,6 +373,14 @@ impl GenericParams { | |||
369 | }); | 373 | }); |
370 | } | 374 | } |
371 | 375 | ||
376 | pub(crate) fn shrink_to_fit(&mut self) { | ||
377 | let Self { consts, lifetimes, types, where_predicates } = self; | ||
378 | consts.shrink_to_fit(); | ||
379 | lifetimes.shrink_to_fit(); | ||
380 | types.shrink_to_fit(); | ||
381 | where_predicates.shrink_to_fit(); | ||
382 | } | ||
383 | |||
372 | pub fn find_type_by_name(&self, name: &Name) -> Option<LocalTypeParamId> { | 384 | pub fn find_type_by_name(&self, name: &Name) -> Option<LocalTypeParamId> { |
373 | self.types | 385 | self.types |
374 | .iter() | 386 | .iter() |
diff --git a/crates/hir_def/src/intern.rs b/crates/hir_def/src/intern.rs index d163f633f..abc304ef0 100644 --- a/crates/hir_def/src/intern.rs +++ b/crates/hir_def/src/intern.rs | |||
@@ -5,7 +5,7 @@ | |||
5 | use std::{ | 5 | use std::{ |
6 | collections::HashMap, | 6 | collections::HashMap, |
7 | fmt::{self, Debug}, | 7 | fmt::{self, Debug}, |
8 | hash::{BuildHasherDefault, Hash}, | 8 | hash::{BuildHasherDefault, Hash, Hasher}, |
9 | ops::Deref, | 9 | ops::Deref, |
10 | sync::Arc, | 10 | sync::Arc, |
11 | }; | 11 | }; |
@@ -14,11 +14,12 @@ use dashmap::{lock::RwLockWriteGuard, DashMap, SharedValue}; | |||
14 | use once_cell::sync::OnceCell; | 14 | use once_cell::sync::OnceCell; |
15 | use rustc_hash::FxHasher; | 15 | use rustc_hash::FxHasher; |
16 | 16 | ||
17 | use crate::generics::GenericParams; | ||
18 | |||
17 | type InternMap<T> = DashMap<Arc<T>, (), BuildHasherDefault<FxHasher>>; | 19 | type InternMap<T> = DashMap<Arc<T>, (), BuildHasherDefault<FxHasher>>; |
18 | type Guard<T> = | 20 | type Guard<T> = |
19 | RwLockWriteGuard<'static, HashMap<Arc<T>, SharedValue<()>, BuildHasherDefault<FxHasher>>>; | 21 | RwLockWriteGuard<'static, HashMap<Arc<T>, SharedValue<()>, BuildHasherDefault<FxHasher>>>; |
20 | 22 | ||
21 | #[derive(Hash)] | ||
22 | pub struct Interned<T: Internable + ?Sized> { | 23 | pub struct Interned<T: Internable + ?Sized> { |
23 | arc: Arc<T>, | 24 | arc: Arc<T>, |
24 | } | 25 | } |
@@ -135,6 +136,13 @@ impl PartialEq for Interned<str> { | |||
135 | 136 | ||
136 | impl Eq for Interned<str> {} | 137 | impl Eq for Interned<str> {} |
137 | 138 | ||
139 | impl<T: Internable + ?Sized> Hash for Interned<T> { | ||
140 | fn hash<H: Hasher>(&self, state: &mut H) { | ||
141 | // NOTE: Cast disposes vtable pointer / slice/str length. | ||
142 | state.write_usize(Arc::as_ptr(&self.arc) as *const () as usize) | ||
143 | } | ||
144 | } | ||
145 | |||
138 | impl<T: Internable + ?Sized> AsRef<T> for Interned<T> { | 146 | impl<T: Internable + ?Sized> AsRef<T> for Interned<T> { |
139 | #[inline] | 147 | #[inline] |
140 | fn as_ref(&self) -> &T { | 148 | fn as_ref(&self) -> &T { |
@@ -183,7 +191,10 @@ pub trait Internable: Hash + Eq + 'static { | |||
183 | fn storage() -> &'static InternStorage<Self>; | 191 | fn storage() -> &'static InternStorage<Self>; |
184 | } | 192 | } |
185 | 193 | ||
186 | macro_rules! impl_internable { | 194 | /// Implements `Internable` for a given list of types, making them usable with `Interned`. |
195 | #[macro_export] | ||
196 | #[doc(hidden)] | ||
197 | macro_rules! _impl_internable { | ||
187 | ( $($t:path),+ $(,)? ) => { $( | 198 | ( $($t:path),+ $(,)? ) => { $( |
188 | impl Internable for $t { | 199 | impl Internable for $t { |
189 | fn storage() -> &'static InternStorage<Self> { | 200 | fn storage() -> &'static InternStorage<Self> { |
@@ -194,4 +205,12 @@ macro_rules! impl_internable { | |||
194 | )+ }; | 205 | )+ }; |
195 | } | 206 | } |
196 | 207 | ||
197 | impl_internable!(crate::type_ref::TypeRef, crate::type_ref::TraitRef, crate::path::ModPath, str); | 208 | pub use crate::_impl_internable as impl_internable; |
209 | |||
210 | impl_internable!( | ||
211 | crate::type_ref::TypeRef, | ||
212 | crate::type_ref::TraitRef, | ||
213 | crate::path::ModPath, | ||
214 | GenericParams, | ||
215 | str, | ||
216 | ); | ||
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs index a8ee5eeac..9014468ea 100644 --- a/crates/hir_def/src/item_scope.rs +++ b/crates/hir_def/src/item_scope.rs | |||
@@ -11,7 +11,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; | |||
11 | use stdx::format_to; | 11 | use stdx::format_to; |
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
14 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, | 14 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, ImplId, |
15 | LocalModuleId, MacroDefId, ModuleDefId, ModuleId, TraitId, | 15 | LocalModuleId, MacroDefId, ModuleDefId, ModuleId, TraitId, |
16 | }; | 16 | }; |
17 | 17 | ||
@@ -37,6 +37,7 @@ pub struct ItemScope { | |||
37 | 37 | ||
38 | defs: Vec<ModuleDefId>, | 38 | defs: Vec<ModuleDefId>, |
39 | impls: Vec<ImplId>, | 39 | impls: Vec<ImplId>, |
40 | unnamed_consts: Vec<ConstId>, | ||
40 | /// Traits imported via `use Trait as _;`. | 41 | /// Traits imported via `use Trait as _;`. |
41 | unnamed_trait_imports: FxHashMap<TraitId, Visibility>, | 42 | unnamed_trait_imports: FxHashMap<TraitId, Visibility>, |
42 | /// Macros visible in current module in legacy textual scope | 43 | /// Macros visible in current module in legacy textual scope |
@@ -106,6 +107,10 @@ impl ItemScope { | |||
106 | .map(|(_, v)| v) | 107 | .map(|(_, v)| v) |
107 | } | 108 | } |
108 | 109 | ||
110 | pub fn unnamed_consts(&self) -> impl Iterator<Item = ConstId> + '_ { | ||
111 | self.unnamed_consts.iter().copied() | ||
112 | } | ||
113 | |||
109 | /// Iterate over all module scoped macros | 114 | /// Iterate over all module scoped macros |
110 | pub(crate) fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a { | 115 | pub(crate) fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a { |
111 | self.entries().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_))) | 116 | self.entries().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_))) |
@@ -156,6 +161,10 @@ impl ItemScope { | |||
156 | self.impls.push(imp) | 161 | self.impls.push(imp) |
157 | } | 162 | } |
158 | 163 | ||
164 | pub(crate) fn define_unnamed_const(&mut self, konst: ConstId) { | ||
165 | self.unnamed_consts.push(konst); | ||
166 | } | ||
167 | |||
159 | pub(crate) fn define_legacy_macro(&mut self, name: Name, mac: MacroDefId) { | 168 | pub(crate) fn define_legacy_macro(&mut self, name: Name, mac: MacroDefId) { |
160 | self.legacy_macros.insert(name, mac); | 169 | self.legacy_macros.insert(name, mac); |
161 | } | 170 | } |
@@ -295,6 +304,7 @@ impl ItemScope { | |||
295 | unresolved, | 304 | unresolved, |
296 | defs, | 305 | defs, |
297 | impls, | 306 | impls, |
307 | unnamed_consts, | ||
298 | unnamed_trait_imports, | 308 | unnamed_trait_imports, |
299 | legacy_macros, | 309 | legacy_macros, |
300 | } = self; | 310 | } = self; |
@@ -304,6 +314,7 @@ impl ItemScope { | |||
304 | unresolved.shrink_to_fit(); | 314 | unresolved.shrink_to_fit(); |
305 | defs.shrink_to_fit(); | 315 | defs.shrink_to_fit(); |
306 | impls.shrink_to_fit(); | 316 | impls.shrink_to_fit(); |
317 | unnamed_consts.shrink_to_fit(); | ||
307 | unnamed_trait_imports.shrink_to_fit(); | 318 | unnamed_trait_imports.shrink_to_fit(); |
308 | legacy_macros.shrink_to_fit(); | 319 | legacy_macros.shrink_to_fit(); |
309 | } | 320 | } |
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 739906778..eaeca01bd 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs | |||
@@ -58,13 +58,6 @@ impl fmt::Debug for RawVisibilityId { | |||
58 | } | 58 | } |
59 | } | 59 | } |
60 | 60 | ||
61 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
62 | pub struct GenericParamsId(u32); | ||
63 | |||
64 | impl GenericParamsId { | ||
65 | pub const EMPTY: Self = GenericParamsId(u32::max_value()); | ||
66 | } | ||
67 | |||
68 | /// The item tree of a source file. | 61 | /// The item tree of a source file. |
69 | #[derive(Debug, Default, Eq, PartialEq)] | 62 | #[derive(Debug, Default, Eq, PartialEq)] |
70 | pub struct ItemTree { | 63 | pub struct ItemTree { |
@@ -106,6 +99,16 @@ impl ItemTree { | |||
106 | // items. | 99 | // items. |
107 | ctx.lower_macro_stmts(stmts) | 100 | ctx.lower_macro_stmts(stmts) |
108 | }, | 101 | }, |
102 | ast::Pat(_pat) => { | ||
103 | // FIXME: This occurs because macros in pattern position are treated as inner | ||
104 | // items and expanded during block DefMap computation | ||
105 | return Default::default(); | ||
106 | }, | ||
107 | ast::Type(ty) => { | ||
108 | // Types can contain inner items. We return an empty item tree in this case, but | ||
109 | // still need to collect inner items. | ||
110 | ctx.lower_inner_items(ty.syntax()) | ||
111 | }, | ||
109 | ast::Expr(e) => { | 112 | ast::Expr(e) => { |
110 | // Macros can expand to expressions. We return an empty item tree in this case, but | 113 | // Macros can expand to expressions. We return an empty item tree in this case, but |
111 | // still need to collect inner items. | 114 | // still need to collect inner items. |
@@ -146,7 +149,6 @@ impl ItemTree { | |||
146 | macro_rules, | 149 | macro_rules, |
147 | macro_defs, | 150 | macro_defs, |
148 | vis, | 151 | vis, |
149 | generics, | ||
150 | inner_items, | 152 | inner_items, |
151 | } = &mut **data; | 153 | } = &mut **data; |
152 | 154 | ||
@@ -170,7 +172,6 @@ impl ItemTree { | |||
170 | macro_defs.shrink_to_fit(); | 172 | macro_defs.shrink_to_fit(); |
171 | 173 | ||
172 | vis.arena.shrink_to_fit(); | 174 | vis.arena.shrink_to_fit(); |
173 | generics.arena.shrink_to_fit(); | ||
174 | 175 | ||
175 | inner_items.shrink_to_fit(); | 176 | inner_items.shrink_to_fit(); |
176 | } | 177 | } |
@@ -195,13 +196,6 @@ impl ItemTree { | |||
195 | self.raw_attrs(of).clone().filter(db, krate) | 196 | self.raw_attrs(of).clone().filter(db, krate) |
196 | } | 197 | } |
197 | 198 | ||
198 | pub fn all_inner_items(&self) -> impl Iterator<Item = ModItem> + '_ { | ||
199 | match &self.data { | ||
200 | Some(data) => Some(data.inner_items.values().flatten().copied()).into_iter().flatten(), | ||
201 | None => None.into_iter().flatten(), | ||
202 | } | ||
203 | } | ||
204 | |||
205 | pub fn inner_items_of_block(&self, block: FileAstId<ast::BlockExpr>) -> &[ModItem] { | 199 | pub fn inner_items_of_block(&self, block: FileAstId<ast::BlockExpr>) -> &[ModItem] { |
206 | match &self.data { | 200 | match &self.data { |
207 | Some(data) => data.inner_items.get(&block).map(|it| &**it).unwrap_or(&[]), | 201 | Some(data) => data.inner_items.get(&block).map(|it| &**it).unwrap_or(&[]), |
@@ -242,32 +236,6 @@ static VIS_PRIV: RawVisibility = RawVisibility::Module(ModPath::from_kind(PathKi | |||
242 | static VIS_PUB_CRATE: RawVisibility = RawVisibility::Module(ModPath::from_kind(PathKind::Crate)); | 236 | static VIS_PUB_CRATE: RawVisibility = RawVisibility::Module(ModPath::from_kind(PathKind::Crate)); |
243 | 237 | ||
244 | #[derive(Default, Debug, Eq, PartialEq)] | 238 | #[derive(Default, Debug, Eq, PartialEq)] |
245 | struct GenericParamsStorage { | ||
246 | arena: Arena<GenericParams>, | ||
247 | } | ||
248 | |||
249 | impl GenericParamsStorage { | ||
250 | fn alloc(&mut self, params: GenericParams) -> GenericParamsId { | ||
251 | if params.types.is_empty() | ||
252 | && params.lifetimes.is_empty() | ||
253 | && params.consts.is_empty() | ||
254 | && params.where_predicates.is_empty() | ||
255 | { | ||
256 | return GenericParamsId::EMPTY; | ||
257 | } | ||
258 | |||
259 | GenericParamsId(self.arena.alloc(params).into_raw().into()) | ||
260 | } | ||
261 | } | ||
262 | |||
263 | static EMPTY_GENERICS: GenericParams = GenericParams { | ||
264 | types: Arena::new(), | ||
265 | lifetimes: Arena::new(), | ||
266 | consts: Arena::new(), | ||
267 | where_predicates: Vec::new(), | ||
268 | }; | ||
269 | |||
270 | #[derive(Default, Debug, Eq, PartialEq)] | ||
271 | struct ItemTreeData { | 239 | struct ItemTreeData { |
272 | imports: Arena<Import>, | 240 | imports: Arena<Import>, |
273 | extern_crates: Arena<ExternCrate>, | 241 | extern_crates: Arena<ExternCrate>, |
@@ -289,7 +257,6 @@ struct ItemTreeData { | |||
289 | macro_defs: Arena<MacroDef>, | 257 | macro_defs: Arena<MacroDef>, |
290 | 258 | ||
291 | vis: ItemVisibilities, | 259 | vis: ItemVisibilities, |
292 | generics: GenericParamsStorage, | ||
293 | 260 | ||
294 | inner_items: FxHashMap<FileAstId<ast::BlockExpr>, SmallVec<[ModItem; 1]>>, | 261 | inner_items: FxHashMap<FileAstId<ast::BlockExpr>, SmallVec<[ModItem; 1]>>, |
295 | } | 262 | } |
@@ -508,17 +475,6 @@ impl Index<RawVisibilityId> for ItemTree { | |||
508 | } | 475 | } |
509 | } | 476 | } |
510 | 477 | ||
511 | impl Index<GenericParamsId> for ItemTree { | ||
512 | type Output = GenericParams; | ||
513 | |||
514 | fn index(&self, index: GenericParamsId) -> &Self::Output { | ||
515 | match index { | ||
516 | GenericParamsId::EMPTY => &EMPTY_GENERICS, | ||
517 | _ => &self.data().generics.arena[Idx::from_raw(index.0.into())], | ||
518 | } | ||
519 | } | ||
520 | } | ||
521 | |||
522 | impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree { | 478 | impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree { |
523 | type Output = N; | 479 | type Output = N; |
524 | fn index(&self, id: FileItemTreeId<N>) -> &N { | 480 | fn index(&self, id: FileItemTreeId<N>) -> &N { |
@@ -555,7 +511,7 @@ pub struct ExternCrate { | |||
555 | pub struct Function { | 511 | pub struct Function { |
556 | pub name: Name, | 512 | pub name: Name, |
557 | pub visibility: RawVisibilityId, | 513 | pub visibility: RawVisibilityId, |
558 | pub generic_params: GenericParamsId, | 514 | pub generic_params: Interned<GenericParams>, |
559 | pub abi: Option<Interned<str>>, | 515 | pub abi: Option<Interned<str>>, |
560 | pub params: IdRange<Param>, | 516 | pub params: IdRange<Param>, |
561 | pub ret_type: Interned<TypeRef>, | 517 | pub ret_type: Interned<TypeRef>, |
@@ -590,7 +546,7 @@ impl FnFlags { | |||
590 | pub struct Struct { | 546 | pub struct Struct { |
591 | pub name: Name, | 547 | pub name: Name, |
592 | pub visibility: RawVisibilityId, | 548 | pub visibility: RawVisibilityId, |
593 | pub generic_params: GenericParamsId, | 549 | pub generic_params: Interned<GenericParams>, |
594 | pub fields: Fields, | 550 | pub fields: Fields, |
595 | pub ast_id: FileAstId<ast::Struct>, | 551 | pub ast_id: FileAstId<ast::Struct>, |
596 | pub kind: StructDefKind, | 552 | pub kind: StructDefKind, |
@@ -610,7 +566,7 @@ pub enum StructDefKind { | |||
610 | pub struct Union { | 566 | pub struct Union { |
611 | pub name: Name, | 567 | pub name: Name, |
612 | pub visibility: RawVisibilityId, | 568 | pub visibility: RawVisibilityId, |
613 | pub generic_params: GenericParamsId, | 569 | pub generic_params: Interned<GenericParams>, |
614 | pub fields: Fields, | 570 | pub fields: Fields, |
615 | pub ast_id: FileAstId<ast::Union>, | 571 | pub ast_id: FileAstId<ast::Union>, |
616 | } | 572 | } |
@@ -619,7 +575,7 @@ pub struct Union { | |||
619 | pub struct Enum { | 575 | pub struct Enum { |
620 | pub name: Name, | 576 | pub name: Name, |
621 | pub visibility: RawVisibilityId, | 577 | pub visibility: RawVisibilityId, |
622 | pub generic_params: GenericParamsId, | 578 | pub generic_params: Interned<GenericParams>, |
623 | pub variants: IdRange<Variant>, | 579 | pub variants: IdRange<Variant>, |
624 | pub ast_id: FileAstId<ast::Enum>, | 580 | pub ast_id: FileAstId<ast::Enum>, |
625 | } | 581 | } |
@@ -648,7 +604,7 @@ pub struct Static { | |||
648 | pub struct Trait { | 604 | pub struct Trait { |
649 | pub name: Name, | 605 | pub name: Name, |
650 | pub visibility: RawVisibilityId, | 606 | pub visibility: RawVisibilityId, |
651 | pub generic_params: GenericParamsId, | 607 | pub generic_params: Interned<GenericParams>, |
652 | pub is_auto: bool, | 608 | pub is_auto: bool, |
653 | pub is_unsafe: bool, | 609 | pub is_unsafe: bool, |
654 | pub bounds: Box<[TypeBound]>, | 610 | pub bounds: Box<[TypeBound]>, |
@@ -658,7 +614,7 @@ pub struct Trait { | |||
658 | 614 | ||
659 | #[derive(Debug, Clone, Eq, PartialEq)] | 615 | #[derive(Debug, Clone, Eq, PartialEq)] |
660 | pub struct Impl { | 616 | pub struct Impl { |
661 | pub generic_params: GenericParamsId, | 617 | pub generic_params: Interned<GenericParams>, |
662 | pub target_trait: Option<Interned<TraitRef>>, | 618 | pub target_trait: Option<Interned<TraitRef>>, |
663 | pub self_ty: Interned<TypeRef>, | 619 | pub self_ty: Interned<TypeRef>, |
664 | pub is_negative: bool, | 620 | pub is_negative: bool, |
@@ -672,7 +628,7 @@ pub struct TypeAlias { | |||
672 | pub visibility: RawVisibilityId, | 628 | pub visibility: RawVisibilityId, |
673 | /// Bounds on the type alias itself. Only valid in trait declarations, eg. `type Assoc: Copy;`. | 629 | /// Bounds on the type alias itself. Only valid in trait declarations, eg. `type Assoc: Copy;`. |
674 | pub bounds: Box<[TypeBound]>, | 630 | pub bounds: Box<[TypeBound]>, |
675 | pub generic_params: GenericParamsId, | 631 | pub generic_params: Interned<GenericParams>, |
676 | pub type_ref: Option<Interned<TypeRef>>, | 632 | pub type_ref: Option<Interned<TypeRef>>, |
677 | pub is_extern: bool, | 633 | pub is_extern: bool, |
678 | pub ast_id: FileAstId<ast::TypeAlias>, | 634 | pub ast_id: FileAstId<ast::TypeAlias>, |
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index ab7ad8310..45b099cf3 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -189,7 +189,7 @@ impl Ctx { | |||
189 | block_stack.push(self.source_ast_id_map.ast_id(&block)); | 189 | block_stack.push(self.source_ast_id_map.ast_id(&block)); |
190 | }, | 190 | }, |
191 | ast::Item(item) => { | 191 | ast::Item(item) => { |
192 | // FIXME: This triggers for macro calls in expression position | 192 | // FIXME: This triggers for macro calls in expression/pattern/type position |
193 | let mod_items = self.lower_mod_item(&item, true); | 193 | let mod_items = self.lower_mod_item(&item, true); |
194 | let current_block = block_stack.last(); | 194 | let current_block = block_stack.last(); |
195 | if let (Some(mod_items), Some(block)) = (mod_items, current_block) { | 195 | if let (Some(mod_items), Some(block)) = (mod_items, current_block) { |
@@ -434,7 +434,7 @@ impl Ctx { | |||
434 | let mut res = Function { | 434 | let mut res = Function { |
435 | name, | 435 | name, |
436 | visibility, | 436 | visibility, |
437 | generic_params: GenericParamsId::EMPTY, | 437 | generic_params: Interned::new(GenericParams::default()), |
438 | abi, | 438 | abi, |
439 | params, | 439 | params, |
440 | ret_type: Interned::new(ret_type), | 440 | ret_type: Interned::new(ret_type), |
@@ -682,7 +682,7 @@ impl Ctx { | |||
682 | &mut self, | 682 | &mut self, |
683 | owner: GenericsOwner<'_>, | 683 | owner: GenericsOwner<'_>, |
684 | node: &impl ast::GenericParamsOwner, | 684 | node: &impl ast::GenericParamsOwner, |
685 | ) -> GenericParamsId { | 685 | ) -> Interned<GenericParams> { |
686 | // Generics are part of item headers and may contain inner items we need to collect. | 686 | // Generics are part of item headers and may contain inner items we need to collect. |
687 | if let Some(params) = node.generic_param_list() { | 687 | if let Some(params) = node.generic_param_list() { |
688 | self.collect_inner_items(params.syntax()); | 688 | self.collect_inner_items(params.syntax()); |
@@ -698,7 +698,7 @@ impl Ctx { | |||
698 | &mut self, | 698 | &mut self, |
699 | owner: GenericsOwner<'_>, | 699 | owner: GenericsOwner<'_>, |
700 | node: &impl ast::GenericParamsOwner, | 700 | node: &impl ast::GenericParamsOwner, |
701 | ) -> GenericParamsId { | 701 | ) -> Interned<GenericParams> { |
702 | let mut sm = &mut Default::default(); | 702 | let mut sm = &mut Default::default(); |
703 | let mut generics = GenericParams::default(); | 703 | let mut generics = GenericParams::default(); |
704 | match owner { | 704 | match owner { |
@@ -739,7 +739,8 @@ impl Ctx { | |||
739 | } | 739 | } |
740 | } | 740 | } |
741 | 741 | ||
742 | self.data().generics.alloc(generics) | 742 | generics.shrink_to_fit(); |
743 | Interned::new(generics) | ||
743 | } | 744 | } |
744 | 745 | ||
745 | fn lower_type_bounds(&mut self, node: &impl ast::TypeBoundsOwner) -> Vec<TypeBound> { | 746 | fn lower_type_bounds(&mut self, node: &impl ast::TypeBoundsOwner) -> Vec<TypeBound> { |
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index f408e510a..25694f037 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs | |||
@@ -27,6 +27,7 @@ pub mod dyn_map; | |||
27 | pub mod keys; | 27 | pub mod keys; |
28 | 28 | ||
29 | pub mod item_tree; | 29 | pub mod item_tree; |
30 | pub mod intern; | ||
30 | 31 | ||
31 | pub mod adt; | 32 | pub mod adt; |
32 | pub mod data; | 33 | pub mod data; |
@@ -49,22 +50,23 @@ pub mod import_map; | |||
49 | 50 | ||
50 | #[cfg(test)] | 51 | #[cfg(test)] |
51 | mod test_db; | 52 | mod test_db; |
52 | mod intern; | ||
53 | 53 | ||
54 | use std::{ | 54 | use std::{ |
55 | hash::{Hash, Hasher}, | 55 | hash::{Hash, Hasher}, |
56 | sync::Arc, | 56 | sync::Arc, |
57 | }; | 57 | }; |
58 | 58 | ||
59 | use adt::VariantData; | ||
59 | use base_db::{impl_intern_key, salsa, CrateId}; | 60 | use base_db::{impl_intern_key, salsa, CrateId}; |
60 | use hir_expand::{ | 61 | use hir_expand::{ |
61 | ast_id_map::FileAstId, | 62 | ast_id_map::FileAstId, |
62 | eager::{expand_eager_macro, ErrorEmitted, ErrorSink}, | 63 | eager::{expand_eager_macro, ErrorEmitted, ErrorSink}, |
63 | hygiene::Hygiene, | 64 | hygiene::Hygiene, |
64 | AstId, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, | 65 | AstId, AttrId, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, |
65 | }; | 66 | }; |
66 | use la_arena::Idx; | 67 | use la_arena::Idx; |
67 | use nameres::DefMap; | 68 | use nameres::DefMap; |
69 | use path::ModPath; | ||
68 | use syntax::ast; | 70 | use syntax::ast; |
69 | 71 | ||
70 | use crate::builtin_type::BuiltinType; | 72 | use crate::builtin_type::BuiltinType; |
@@ -106,6 +108,18 @@ impl ModuleId { | |||
106 | pub fn containing_module(&self, db: &dyn db::DefDatabase) -> Option<ModuleId> { | 108 | pub fn containing_module(&self, db: &dyn db::DefDatabase) -> Option<ModuleId> { |
107 | self.def_map(db).containing_module(self.local_id) | 109 | self.def_map(db).containing_module(self.local_id) |
108 | } | 110 | } |
111 | |||
112 | /// Returns `true` if this module represents a block expression. | ||
113 | /// | ||
114 | /// Returns `false` if this module is a submodule *inside* a block expression | ||
115 | /// (eg. `m` in `{ mod m {} }`). | ||
116 | pub fn is_block_root(&self, db: &dyn db::DefDatabase) -> bool { | ||
117 | if self.block.is_none() { | ||
118 | return false; | ||
119 | } | ||
120 | |||
121 | self.def_map(db)[self.local_id].parent.is_none() | ||
122 | } | ||
109 | } | 123 | } |
110 | 124 | ||
111 | /// An ID of a module, **local** to a specific crate | 125 | /// An ID of a module, **local** to a specific crate |
@@ -434,6 +448,16 @@ impl_from!( | |||
434 | for AttrDefId | 448 | for AttrDefId |
435 | ); | 449 | ); |
436 | 450 | ||
451 | impl From<AssocContainerId> for AttrDefId { | ||
452 | fn from(acid: AssocContainerId) -> Self { | ||
453 | match acid { | ||
454 | AssocContainerId::ModuleId(mid) => AttrDefId::ModuleId(mid), | ||
455 | AssocContainerId::ImplId(iid) => AttrDefId::ImplId(iid), | ||
456 | AssocContainerId::TraitId(tid) => AttrDefId::TraitId(tid), | ||
457 | } | ||
458 | } | ||
459 | } | ||
460 | |||
437 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 461 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
438 | pub enum VariantId { | 462 | pub enum VariantId { |
439 | EnumVariantId(EnumVariantId), | 463 | EnumVariantId(EnumVariantId), |
@@ -442,6 +466,26 @@ pub enum VariantId { | |||
442 | } | 466 | } |
443 | impl_from!(EnumVariantId, StructId, UnionId for VariantId); | 467 | impl_from!(EnumVariantId, StructId, UnionId for VariantId); |
444 | 468 | ||
469 | impl VariantId { | ||
470 | pub fn variant_data(self, db: &dyn db::DefDatabase) -> Arc<VariantData> { | ||
471 | match self { | ||
472 | VariantId::StructId(it) => db.struct_data(it).variant_data.clone(), | ||
473 | VariantId::UnionId(it) => db.union_data(it).variant_data.clone(), | ||
474 | VariantId::EnumVariantId(it) => { | ||
475 | db.enum_data(it.parent).variants[it.local_id].variant_data.clone() | ||
476 | } | ||
477 | } | ||
478 | } | ||
479 | |||
480 | pub fn file_id(self, db: &dyn db::DefDatabase) -> HirFileId { | ||
481 | match self { | ||
482 | VariantId::EnumVariantId(it) => it.parent.lookup(db).id.file_id(), | ||
483 | VariantId::StructId(it) => it.lookup(db).id.file_id(), | ||
484 | VariantId::UnionId(it) => it.lookup(db).id.file_id(), | ||
485 | } | ||
486 | } | ||
487 | } | ||
488 | |||
445 | trait Intern { | 489 | trait Intern { |
446 | type ID; | 490 | type ID; |
447 | fn intern(self, db: &dyn db::DefDatabase) -> Self::ID; | 491 | fn intern(self, db: &dyn db::DefDatabase) -> Self::ID; |
@@ -644,7 +688,10 @@ impl<T: ast::AstNode> AstIdWithPath<T> { | |||
644 | } | 688 | } |
645 | } | 689 | } |
646 | 690 | ||
647 | pub struct UnresolvedMacro; | 691 | #[derive(Debug)] |
692 | pub struct UnresolvedMacro { | ||
693 | pub path: ModPath, | ||
694 | } | ||
648 | 695 | ||
649 | fn macro_call_as_call_id( | 696 | fn macro_call_as_call_id( |
650 | call: &AstIdWithPath<ast::MacroCall>, | 697 | call: &AstIdWithPath<ast::MacroCall>, |
@@ -653,7 +700,8 @@ fn macro_call_as_call_id( | |||
653 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | 700 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, |
654 | error_sink: &mut dyn FnMut(mbe::ExpandError), | 701 | error_sink: &mut dyn FnMut(mbe::ExpandError), |
655 | ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> { | 702 | ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> { |
656 | let def: MacroDefId = resolver(call.path.clone()).ok_or(UnresolvedMacro)?; | 703 | let def: MacroDefId = |
704 | resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?; | ||
657 | 705 | ||
658 | let res = if let MacroDefKind::BuiltInEager(..) = def.kind { | 706 | let res = if let MacroDefKind::BuiltInEager(..) = def.kind { |
659 | let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast())); | 707 | let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast())); |
@@ -669,24 +717,36 @@ fn macro_call_as_call_id( | |||
669 | ) | 717 | ) |
670 | .map(MacroCallId::from) | 718 | .map(MacroCallId::from) |
671 | } else { | 719 | } else { |
672 | Ok(def.as_lazy_macro(db.upcast(), krate, MacroCallKind::FnLike(call.ast_id)).into()) | 720 | Ok(def |
721 | .as_lazy_macro(db.upcast(), krate, MacroCallKind::FnLike { ast_id: call.ast_id }) | ||
722 | .into()) | ||
673 | }; | 723 | }; |
674 | Ok(res) | 724 | Ok(res) |
675 | } | 725 | } |
676 | 726 | ||
677 | fn derive_macro_as_call_id( | 727 | fn derive_macro_as_call_id( |
678 | item_attr: &AstIdWithPath<ast::Item>, | 728 | item_attr: &AstIdWithPath<ast::Item>, |
729 | derive_attr: AttrId, | ||
679 | db: &dyn db::DefDatabase, | 730 | db: &dyn db::DefDatabase, |
680 | krate: CrateId, | 731 | krate: CrateId, |
681 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | 732 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, |
682 | ) -> Result<MacroCallId, UnresolvedMacro> { | 733 | ) -> Result<MacroCallId, UnresolvedMacro> { |
683 | let def: MacroDefId = resolver(item_attr.path.clone()).ok_or(UnresolvedMacro)?; | 734 | let def: MacroDefId = resolver(item_attr.path.clone()) |
684 | let last_segment = item_attr.path.segments().last().ok_or(UnresolvedMacro)?; | 735 | .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?; |
736 | let last_segment = item_attr | ||
737 | .path | ||
738 | .segments() | ||
739 | .last() | ||
740 | .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?; | ||
685 | let res = def | 741 | let res = def |
686 | .as_lazy_macro( | 742 | .as_lazy_macro( |
687 | db.upcast(), | 743 | db.upcast(), |
688 | krate, | 744 | krate, |
689 | MacroCallKind::Derive(item_attr.ast_id, last_segment.to_string()), | 745 | MacroCallKind::Derive { |
746 | ast_id: item_attr.ast_id, | ||
747 | derive_name: last_segment.to_string(), | ||
748 | derive_attr, | ||
749 | }, | ||
690 | ) | 750 | ) |
691 | .into(); | 751 | .into(); |
692 | Ok(res) | 752 | Ok(res) |
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index 7dd68219f..ba027c44a 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -410,6 +410,20 @@ impl DefMap { | |||
410 | } | 410 | } |
411 | } | 411 | } |
412 | 412 | ||
413 | pub fn dump_block_scopes(&self, db: &dyn DefDatabase) -> String { | ||
414 | let mut buf = String::new(); | ||
415 | let mut arc; | ||
416 | let mut current_map = self; | ||
417 | while let Some(block) = ¤t_map.block { | ||
418 | format_to!(buf, "{:?} in {:?}\n", block.block, block.parent); | ||
419 | arc = block.parent.def_map(db); | ||
420 | current_map = &*arc; | ||
421 | } | ||
422 | |||
423 | format_to!(buf, "crate scope\n"); | ||
424 | buf | ||
425 | } | ||
426 | |||
413 | fn shrink_to_fit(&mut self) { | 427 | fn shrink_to_fit(&mut self) { |
414 | // Exhaustive match to require handling new fields. | 428 | // Exhaustive match to require handling new fields. |
415 | let Self { | 429 | let Self { |
@@ -481,7 +495,7 @@ mod diagnostics { | |||
481 | 495 | ||
482 | UnresolvedProcMacro { ast: MacroCallKind }, | 496 | UnresolvedProcMacro { ast: MacroCallKind }, |
483 | 497 | ||
484 | UnresolvedMacroCall { ast: AstId<ast::MacroCall> }, | 498 | UnresolvedMacroCall { ast: AstId<ast::MacroCall>, path: ModPath }, |
485 | 499 | ||
486 | MacroError { ast: MacroCallKind, message: String }, | 500 | MacroError { ast: MacroCallKind, message: String }, |
487 | } | 501 | } |
@@ -546,8 +560,9 @@ mod diagnostics { | |||
546 | pub(super) fn unresolved_macro_call( | 560 | pub(super) fn unresolved_macro_call( |
547 | container: LocalModuleId, | 561 | container: LocalModuleId, |
548 | ast: AstId<ast::MacroCall>, | 562 | ast: AstId<ast::MacroCall>, |
563 | path: ModPath, | ||
549 | ) -> Self { | 564 | ) -> Self { |
550 | Self { in_module: container, kind: DiagnosticKind::UnresolvedMacroCall { ast } } | 565 | Self { in_module: container, kind: DiagnosticKind::UnresolvedMacroCall { ast, path } } |
551 | } | 566 | } |
552 | 567 | ||
553 | pub(super) fn add_to( | 568 | pub(super) fn add_to( |
@@ -613,12 +628,12 @@ mod diagnostics { | |||
613 | DiagnosticKind::UnresolvedProcMacro { ast } => { | 628 | DiagnosticKind::UnresolvedProcMacro { ast } => { |
614 | let mut precise_location = None; | 629 | let mut precise_location = None; |
615 | let (file, ast, name) = match ast { | 630 | let (file, ast, name) = match ast { |
616 | MacroCallKind::FnLike(ast) => { | 631 | MacroCallKind::FnLike { ast_id } => { |
617 | let node = ast.to_node(db.upcast()); | 632 | let node = ast_id.to_node(db.upcast()); |
618 | (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None) | 633 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None) |
619 | } | 634 | } |
620 | MacroCallKind::Derive(ast, name) => { | 635 | MacroCallKind::Derive { ast_id, derive_name, .. } => { |
621 | let node = ast.to_node(db.upcast()); | 636 | let node = ast_id.to_node(db.upcast()); |
622 | 637 | ||
623 | // Compute the precise location of the macro name's token in the derive | 638 | // Compute the precise location of the macro name's token in the derive |
624 | // list. | 639 | // list. |
@@ -639,7 +654,7 @@ mod diagnostics { | |||
639 | }); | 654 | }); |
640 | for token in tokens { | 655 | for token in tokens { |
641 | if token.kind() == SyntaxKind::IDENT | 656 | if token.kind() == SyntaxKind::IDENT |
642 | && token.text() == name.as_str() | 657 | && token.text() == derive_name.as_str() |
643 | { | 658 | { |
644 | precise_location = Some(token.text_range()); | 659 | precise_location = Some(token.text_range()); |
645 | break 'outer; | 660 | break 'outer; |
@@ -648,9 +663,9 @@ mod diagnostics { | |||
648 | } | 663 | } |
649 | 664 | ||
650 | ( | 665 | ( |
651 | ast.file_id, | 666 | ast_id.file_id, |
652 | SyntaxNodePtr::from(AstPtr::new(&node)), | 667 | SyntaxNodePtr::from(AstPtr::new(&node)), |
653 | Some(name.clone()), | 668 | Some(derive_name.clone()), |
654 | ) | 669 | ) |
655 | } | 670 | } |
656 | }; | 671 | }; |
@@ -662,20 +677,24 @@ mod diagnostics { | |||
662 | }); | 677 | }); |
663 | } | 678 | } |
664 | 679 | ||
665 | DiagnosticKind::UnresolvedMacroCall { ast } => { | 680 | DiagnosticKind::UnresolvedMacroCall { ast, path } => { |
666 | let node = ast.to_node(db.upcast()); | 681 | let node = ast.to_node(db.upcast()); |
667 | sink.push(UnresolvedMacroCall { file: ast.file_id, node: AstPtr::new(&node) }); | 682 | sink.push(UnresolvedMacroCall { |
683 | file: ast.file_id, | ||
684 | node: AstPtr::new(&node), | ||
685 | path: path.clone(), | ||
686 | }); | ||
668 | } | 687 | } |
669 | 688 | ||
670 | DiagnosticKind::MacroError { ast, message } => { | 689 | DiagnosticKind::MacroError { ast, message } => { |
671 | let (file, ast) = match ast { | 690 | let (file, ast) = match ast { |
672 | MacroCallKind::FnLike(ast) => { | 691 | MacroCallKind::FnLike { ast_id, .. } => { |
673 | let node = ast.to_node(db.upcast()); | 692 | let node = ast_id.to_node(db.upcast()); |
674 | (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) | 693 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) |
675 | } | 694 | } |
676 | MacroCallKind::Derive(ast, _) => { | 695 | MacroCallKind::Derive { ast_id, .. } => { |
677 | let node = ast.to_node(db.upcast()); | 696 | let node = ast_id.to_node(db.upcast()); |
678 | (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) | 697 | (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) |
679 | } | 698 | } |
680 | }; | 699 | }; |
681 | sink.push(MacroError { file, node: ast, message: message.clone() }); | 700 | sink.push(MacroError { file, node: ast, message: message.clone() }); |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 4ddc791ce..05ceb1efb 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -13,7 +13,7 @@ use hir_expand::{ | |||
13 | builtin_macro::find_builtin_macro, | 13 | builtin_macro::find_builtin_macro, |
14 | name::{AsName, Name}, | 14 | name::{AsName, Name}, |
15 | proc_macro::ProcMacroExpander, | 15 | proc_macro::ProcMacroExpander, |
16 | HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, | 16 | AttrId, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, |
17 | }; | 17 | }; |
18 | use hir_expand::{InFile, MacroCallLoc}; | 18 | use hir_expand::{InFile, MacroCallLoc}; |
19 | use rustc_hash::{FxHashMap, FxHashSet}; | 19 | use rustc_hash::{FxHashMap, FxHashSet}; |
@@ -215,8 +215,8 @@ struct MacroDirective { | |||
215 | 215 | ||
216 | #[derive(Clone, Debug, Eq, PartialEq)] | 216 | #[derive(Clone, Debug, Eq, PartialEq)] |
217 | enum MacroDirectiveKind { | 217 | enum MacroDirectiveKind { |
218 | FnLike { ast_id: AstIdWithPath<ast::MacroCall>, legacy: Option<MacroCallId> }, | 218 | FnLike { ast_id: AstIdWithPath<ast::MacroCall> }, |
219 | Derive { ast_id: AstIdWithPath<ast::Item> }, | 219 | Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId }, |
220 | } | 220 | } |
221 | 221 | ||
222 | struct DefData<'a> { | 222 | struct DefData<'a> { |
@@ -478,7 +478,7 @@ impl DefCollector<'_> { | |||
478 | self.def_map.edition, | 478 | self.def_map.edition, |
479 | ); | 479 | ); |
480 | 480 | ||
481 | let res = self.def_map.resolve_name_in_extern_prelude(&extern_crate.name); | 481 | let res = self.def_map.resolve_name_in_extern_prelude(self.db, &extern_crate.name); |
482 | 482 | ||
483 | if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { | 483 | if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { |
484 | cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); | 484 | cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); |
@@ -534,6 +534,7 @@ impl DefCollector<'_> { | |||
534 | log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition); | 534 | log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition); |
535 | if import.is_extern_crate { | 535 | if import.is_extern_crate { |
536 | let res = self.def_map.resolve_name_in_extern_prelude( | 536 | let res = self.def_map.resolve_name_in_extern_prelude( |
537 | self.db, | ||
537 | &import | 538 | &import |
538 | .path | 539 | .path |
539 | .as_ident() | 540 | .as_ident() |
@@ -806,13 +807,7 @@ impl DefCollector<'_> { | |||
806 | let mut res = ReachedFixedPoint::Yes; | 807 | let mut res = ReachedFixedPoint::Yes; |
807 | macros.retain(|directive| { | 808 | macros.retain(|directive| { |
808 | match &directive.kind { | 809 | match &directive.kind { |
809 | MacroDirectiveKind::FnLike { ast_id, legacy } => { | 810 | MacroDirectiveKind::FnLike { ast_id } => { |
810 | if let Some(call_id) = legacy { | ||
811 | res = ReachedFixedPoint::No; | ||
812 | resolved.push((directive.module_id, *call_id, directive.depth)); | ||
813 | return false; | ||
814 | } | ||
815 | |||
816 | match macro_call_as_call_id( | 811 | match macro_call_as_call_id( |
817 | ast_id, | 812 | ast_id, |
818 | self.db, | 813 | self.db, |
@@ -834,19 +829,23 @@ impl DefCollector<'_> { | |||
834 | res = ReachedFixedPoint::No; | 829 | res = ReachedFixedPoint::No; |
835 | return false; | 830 | return false; |
836 | } | 831 | } |
837 | Err(UnresolvedMacro) | Ok(Err(_)) => {} | 832 | Err(UnresolvedMacro { .. }) | Ok(Err(_)) => {} |
838 | } | 833 | } |
839 | } | 834 | } |
840 | MacroDirectiveKind::Derive { ast_id } => { | 835 | MacroDirectiveKind::Derive { ast_id, derive_attr } => { |
841 | match derive_macro_as_call_id(ast_id, self.db, self.def_map.krate, |path| { | 836 | match derive_macro_as_call_id( |
842 | self.resolve_derive_macro(directive.module_id, &path) | 837 | ast_id, |
843 | }) { | 838 | *derive_attr, |
839 | self.db, | ||
840 | self.def_map.krate, | ||
841 | |path| self.resolve_derive_macro(directive.module_id, &path), | ||
842 | ) { | ||
844 | Ok(call_id) => { | 843 | Ok(call_id) => { |
845 | resolved.push((directive.module_id, call_id, 0)); | 844 | resolved.push((directive.module_id, call_id, directive.depth)); |
846 | res = ReachedFixedPoint::No; | 845 | res = ReachedFixedPoint::No; |
847 | return false; | 846 | return false; |
848 | } | 847 | } |
849 | Err(UnresolvedMacro) => (), | 848 | Err(UnresolvedMacro { .. }) => (), |
850 | } | 849 | } |
851 | } | 850 | } |
852 | } | 851 | } |
@@ -944,10 +943,11 @@ impl DefCollector<'_> { | |||
944 | &mut |_| (), | 943 | &mut |_| (), |
945 | ) { | 944 | ) { |
946 | Ok(_) => (), | 945 | Ok(_) => (), |
947 | Err(UnresolvedMacro) => { | 946 | Err(UnresolvedMacro { path }) => { |
948 | self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call( | 947 | self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call( |
949 | directive.module_id, | 948 | directive.module_id, |
950 | ast_id.ast_id, | 949 | ast_id.ast_id, |
950 | path, | ||
951 | )); | 951 | )); |
952 | } | 952 | } |
953 | }, | 953 | }, |
@@ -1169,19 +1169,27 @@ impl ModCollector<'_, '_> { | |||
1169 | } | 1169 | } |
1170 | ModItem::Const(id) => { | 1170 | ModItem::Const(id) => { |
1171 | let it = &self.item_tree[id]; | 1171 | let it = &self.item_tree[id]; |
1172 | 1172 | let const_id = ConstLoc { | |
1173 | if let Some(name) = &it.name { | 1173 | container: module.into(), |
1174 | def = Some(DefData { | 1174 | id: ItemTreeId::new(self.file_id, id), |
1175 | id: ConstLoc { | 1175 | } |
1176 | container: module.into(), | 1176 | .intern(self.def_collector.db); |
1177 | id: ItemTreeId::new(self.file_id, id), | 1177 | |
1178 | } | 1178 | match &it.name { |
1179 | .intern(self.def_collector.db) | 1179 | Some(name) => { |
1180 | .into(), | 1180 | def = Some(DefData { |
1181 | name, | 1181 | id: const_id.into(), |
1182 | visibility: &self.item_tree[it.visibility], | 1182 | name, |
1183 | has_constructor: false, | 1183 | visibility: &self.item_tree[it.visibility], |
1184 | }); | 1184 | has_constructor: false, |
1185 | }); | ||
1186 | } | ||
1187 | None => { | ||
1188 | // const _: T = ...; | ||
1189 | self.def_collector.def_map.modules[self.module_id] | ||
1190 | .scope | ||
1191 | .define_unnamed_const(const_id); | ||
1192 | } | ||
1185 | } | 1193 | } |
1186 | } | 1194 | } |
1187 | ModItem::Static(id) => { | 1195 | ModItem::Static(id) => { |
@@ -1366,7 +1374,7 @@ impl ModCollector<'_, '_> { | |||
1366 | self.def_collector.unexpanded_macros.push(MacroDirective { | 1374 | self.def_collector.unexpanded_macros.push(MacroDirective { |
1367 | module_id: self.module_id, | 1375 | module_id: self.module_id, |
1368 | depth: self.macro_depth + 1, | 1376 | depth: self.macro_depth + 1, |
1369 | kind: MacroDirectiveKind::Derive { ast_id }, | 1377 | kind: MacroDirectiveKind::Derive { ast_id, derive_attr: derive.id }, |
1370 | }); | 1378 | }); |
1371 | } | 1379 | } |
1372 | } | 1380 | } |
@@ -1518,12 +1526,12 @@ impl ModCollector<'_, '_> { | |||
1518 | // Built-in macro failed eager expansion. | 1526 | // Built-in macro failed eager expansion. |
1519 | self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error( | 1527 | self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error( |
1520 | self.module_id, | 1528 | self.module_id, |
1521 | MacroCallKind::FnLike(ast_id.ast_id), | 1529 | MacroCallKind::FnLike { ast_id: ast_id.ast_id }, |
1522 | error.unwrap().to_string(), | 1530 | error.unwrap().to_string(), |
1523 | )); | 1531 | )); |
1524 | return; | 1532 | return; |
1525 | } | 1533 | } |
1526 | Err(UnresolvedMacro) => (), | 1534 | Err(UnresolvedMacro { .. }) => (), |
1527 | } | 1535 | } |
1528 | 1536 | ||
1529 | // Case 2: resolve in module scope, expand during name resolution. | 1537 | // Case 2: resolve in module scope, expand during name resolution. |
@@ -1535,7 +1543,7 @@ impl ModCollector<'_, '_> { | |||
1535 | self.def_collector.unexpanded_macros.push(MacroDirective { | 1543 | self.def_collector.unexpanded_macros.push(MacroDirective { |
1536 | module_id: self.module_id, | 1544 | module_id: self.module_id, |
1537 | depth: self.macro_depth + 1, | 1545 | depth: self.macro_depth + 1, |
1538 | kind: MacroDirectiveKind::FnLike { ast_id, legacy: None }, | 1546 | kind: MacroDirectiveKind::FnLike { ast_id }, |
1539 | }); | 1547 | }); |
1540 | } | 1548 | } |
1541 | 1549 | ||
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs index 60471937c..c984148c3 100644 --- a/crates/hir_def/src/nameres/path_resolution.rs +++ b/crates/hir_def/src/nameres/path_resolution.rs | |||
@@ -60,12 +60,26 @@ impl ResolvePathResult { | |||
60 | } | 60 | } |
61 | 61 | ||
62 | impl DefMap { | 62 | impl DefMap { |
63 | pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { | 63 | pub(super) fn resolve_name_in_extern_prelude( |
64 | &self, | ||
65 | db: &dyn DefDatabase, | ||
66 | name: &Name, | ||
67 | ) -> PerNs { | ||
64 | if name == &name!(self) { | 68 | if name == &name!(self) { |
65 | cov_mark::hit!(extern_crate_self_as); | 69 | cov_mark::hit!(extern_crate_self_as); |
66 | return PerNs::types(self.module_id(self.root).into(), Visibility::Public); | 70 | return PerNs::types(self.module_id(self.root).into(), Visibility::Public); |
67 | } | 71 | } |
68 | self.extern_prelude | 72 | |
73 | let arc; | ||
74 | let root = match self.block { | ||
75 | Some(_) => { | ||
76 | arc = self.crate_root(db).def_map(db); | ||
77 | &*arc | ||
78 | } | ||
79 | None => self, | ||
80 | }; | ||
81 | |||
82 | root.extern_prelude | ||
69 | .get(name) | 83 | .get(name) |
70 | .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)) | 84 | .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)) |
71 | } | 85 | } |
@@ -191,7 +205,7 @@ impl DefMap { | |||
191 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), | 205 | None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), |
192 | }; | 206 | }; |
193 | log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); | 207 | log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); |
194 | self.resolve_name_in_crate_root_or_extern_prelude(&segment) | 208 | self.resolve_name_in_crate_root_or_extern_prelude(db, &segment) |
195 | } | 209 | } |
196 | PathKind::Plain => { | 210 | PathKind::Plain => { |
197 | let (_, segment) = match segments.next() { | 211 | let (_, segment) = match segments.next() { |
@@ -373,7 +387,13 @@ impl DefMap { | |||
373 | .get_legacy_macro(name) | 387 | .get_legacy_macro(name) |
374 | .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public)); | 388 | .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public)); |
375 | let from_scope = self[module].scope.get(name); | 389 | let from_scope = self[module].scope.get(name); |
376 | let from_builtin = BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none); | 390 | let from_builtin = match self.block { |
391 | Some(_) => { | ||
392 | // Only resolve to builtins in the root `DefMap`. | ||
393 | PerNs::none() | ||
394 | } | ||
395 | None => BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none), | ||
396 | }; | ||
377 | let from_scope_or_builtin = match shadow { | 397 | let from_scope_or_builtin = match shadow { |
378 | BuiltinShadowMode::Module => from_scope.or(from_builtin), | 398 | BuiltinShadowMode::Module => from_scope.or(from_builtin), |
379 | BuiltinShadowMode::Other => { | 399 | BuiltinShadowMode::Other => { |
@@ -384,24 +404,31 @@ impl DefMap { | |||
384 | } | 404 | } |
385 | } | 405 | } |
386 | }; | 406 | }; |
387 | // Give precedence to names in outer `DefMap`s over the extern prelude; only check prelude | 407 | let from_extern_prelude = self |
388 | // from the crate DefMap. | 408 | .extern_prelude |
389 | let from_extern_prelude = match self.block { | 409 | .get(name) |
390 | Some(_) => PerNs::none(), | 410 | .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)); |
391 | None => self | ||
392 | .extern_prelude | ||
393 | .get(name) | ||
394 | .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)), | ||
395 | }; | ||
396 | 411 | ||
397 | let from_prelude = self.resolve_in_prelude(db, name); | 412 | let from_prelude = self.resolve_in_prelude(db, name); |
398 | 413 | ||
399 | from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude) | 414 | from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude) |
400 | } | 415 | } |
401 | 416 | ||
402 | fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs { | 417 | fn resolve_name_in_crate_root_or_extern_prelude( |
403 | let from_crate_root = self[self.root].scope.get(name); | 418 | &self, |
404 | let from_extern_prelude = self.resolve_name_in_extern_prelude(name); | 419 | db: &dyn DefDatabase, |
420 | name: &Name, | ||
421 | ) -> PerNs { | ||
422 | let arc; | ||
423 | let crate_def_map = match self.block { | ||
424 | Some(_) => { | ||
425 | arc = self.crate_root(db).def_map(db); | ||
426 | &arc | ||
427 | } | ||
428 | None => self, | ||
429 | }; | ||
430 | let from_crate_root = crate_def_map[crate_def_map.root].scope.get(name); | ||
431 | let from_extern_prelude = self.resolve_name_in_extern_prelude(db, name); | ||
405 | 432 | ||
406 | from_crate_root.or(from_extern_prelude) | 433 | from_crate_root.or(from_extern_prelude) |
407 | } | 434 | } |
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs index fefdadb22..543975e07 100644 --- a/crates/hir_def/src/nameres/tests/diagnostics.rs +++ b/crates/hir_def/src/nameres/tests/diagnostics.rs | |||
@@ -170,7 +170,7 @@ fn unresolved_legacy_scope_macro() { | |||
170 | 170 | ||
171 | m!(); | 171 | m!(); |
172 | m2!(); | 172 | m2!(); |
173 | //^^^^^^ unresolved macro call | 173 | //^^^^^^ unresolved macro `self::m2!` |
174 | "#, | 174 | "#, |
175 | ); | 175 | ); |
176 | } | 176 | } |
@@ -187,7 +187,7 @@ fn unresolved_module_scope_macro() { | |||
187 | 187 | ||
188 | self::m!(); | 188 | self::m!(); |
189 | self::m2!(); | 189 | self::m2!(); |
190 | //^^^^^^^^^^^^ unresolved macro call | 190 | //^^^^^^^^^^^^ unresolved macro `self::m2!` |
191 | "#, | 191 | "#, |
192 | ); | 192 | ); |
193 | } | 193 | } |
@@ -233,7 +233,7 @@ fn good_out_dir_diagnostic() { | |||
233 | macro_rules! concat { () => {} } | 233 | macro_rules! concat { () => {} } |
234 | 234 | ||
235 | include!(concat!(env!("OUT_DIR"), "/out.rs")); | 235 | include!(concat!(env!("OUT_DIR"), "/out.rs")); |
236 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "load out dirs from check" to fix | 236 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix |
237 | "#, | 237 | "#, |
238 | ); | 238 | ); |
239 | } | 239 | } |
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs index a3e83e2cf..509f77850 100644 --- a/crates/hir_def/src/path.rs +++ b/crates/hir_def/src/path.rs | |||
@@ -48,7 +48,8 @@ pub enum ImportAlias { | |||
48 | 48 | ||
49 | impl ModPath { | 49 | impl ModPath { |
50 | pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> { | 50 | pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> { |
51 | lower::lower_path(path, hygiene).map(|it| (*it.mod_path).clone()) | 51 | let ctx = LowerCtx::with_hygiene(hygiene); |
52 | lower::lower_path(path, &ctx).map(|it| (*it.mod_path).clone()) | ||
52 | } | 53 | } |
53 | 54 | ||
54 | pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath { | 55 | pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath { |
@@ -122,7 +123,7 @@ impl ModPath { | |||
122 | pub struct Path { | 123 | pub struct Path { |
123 | /// Type based path like `<T>::foo`. | 124 | /// Type based path like `<T>::foo`. |
124 | /// Note that paths like `<Type as Trait>::foo` are desugard to `Trait::<Self=Type>::foo`. | 125 | /// Note that paths like `<Type as Trait>::foo` are desugard to `Trait::<Self=Type>::foo`. |
125 | type_anchor: Option<Box<TypeRef>>, | 126 | type_anchor: Option<Interned<TypeRef>>, |
126 | mod_path: Interned<ModPath>, | 127 | mod_path: Interned<ModPath>, |
127 | /// Invariant: the same len as `self.mod_path.segments` | 128 | /// Invariant: the same len as `self.mod_path.segments` |
128 | generic_args: Vec<Option<Arc<GenericArgs>>>, | 129 | generic_args: Vec<Option<Arc<GenericArgs>>>, |
@@ -167,8 +168,8 @@ pub enum GenericArg { | |||
167 | impl Path { | 168 | impl Path { |
168 | /// Converts an `ast::Path` to `Path`. Works with use trees. | 169 | /// Converts an `ast::Path` to `Path`. Works with use trees. |
169 | /// It correctly handles `$crate` based path from macro call. | 170 | /// It correctly handles `$crate` based path from macro call. |
170 | pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<Path> { | 171 | pub fn from_src(path: ast::Path, ctx: &LowerCtx) -> Option<Path> { |
171 | lower::lower_path(path, hygiene) | 172 | lower::lower_path(path, ctx) |
172 | } | 173 | } |
173 | 174 | ||
174 | /// Converts a known mod path to `Path`. | 175 | /// Converts a known mod path to `Path`. |
@@ -289,6 +290,12 @@ impl From<Name> for Path { | |||
289 | } | 290 | } |
290 | } | 291 | } |
291 | 292 | ||
293 | impl From<Name> for Box<Path> { | ||
294 | fn from(name: Name) -> Box<Path> { | ||
295 | Box::new(Path::from(name)) | ||
296 | } | ||
297 | } | ||
298 | |||
292 | impl From<Name> for ModPath { | 299 | impl From<Name> for ModPath { |
293 | fn from(name: Name) -> ModPath { | 300 | fn from(name: Name) -> ModPath { |
294 | ModPath::from_segments(PathKind::Plain, iter::once(name)) | 301 | ModPath::from_segments(PathKind::Plain, iter::once(name)) |
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs index 28f6244da..1df6db525 100644 --- a/crates/hir_def/src/path/lower.rs +++ b/crates/hir_def/src/path/lower.rs | |||
@@ -6,10 +6,7 @@ use crate::intern::Interned; | |||
6 | use std::sync::Arc; | 6 | use std::sync::Arc; |
7 | 7 | ||
8 | use either::Either; | 8 | use either::Either; |
9 | use hir_expand::{ | 9 | use hir_expand::name::{name, AsName}; |
10 | hygiene::Hygiene, | ||
11 | name::{name, AsName}, | ||
12 | }; | ||
13 | use syntax::ast::{self, AstNode, TypeBoundsOwner}; | 10 | use syntax::ast::{self, AstNode, TypeBoundsOwner}; |
14 | 11 | ||
15 | use super::AssociatedTypeBinding; | 12 | use super::AssociatedTypeBinding; |
@@ -23,12 +20,12 @@ pub(super) use lower_use::lower_use_tree; | |||
23 | 20 | ||
24 | /// Converts an `ast::Path` to `Path`. Works with use trees. | 21 | /// Converts an `ast::Path` to `Path`. Works with use trees. |
25 | /// It correctly handles `$crate` based path from macro call. | 22 | /// It correctly handles `$crate` based path from macro call. |
26 | pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> { | 23 | pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx) -> Option<Path> { |
27 | let mut kind = PathKind::Plain; | 24 | let mut kind = PathKind::Plain; |
28 | let mut type_anchor = None; | 25 | let mut type_anchor = None; |
29 | let mut segments = Vec::new(); | 26 | let mut segments = Vec::new(); |
30 | let mut generic_args = Vec::new(); | 27 | let mut generic_args = Vec::new(); |
31 | let ctx = LowerCtx::with_hygiene(hygiene); | 28 | let hygiene = ctx.hygiene(); |
32 | loop { | 29 | loop { |
33 | let segment = path.segment()?; | 30 | let segment = path.segment()?; |
34 | 31 | ||
@@ -43,10 +40,10 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> | |||
43 | Either::Left(name) => { | 40 | Either::Left(name) => { |
44 | let args = segment | 41 | let args = segment |
45 | .generic_arg_list() | 42 | .generic_arg_list() |
46 | .and_then(|it| lower_generic_args(&ctx, it)) | 43 | .and_then(|it| lower_generic_args(ctx, it)) |
47 | .or_else(|| { | 44 | .or_else(|| { |
48 | lower_generic_args_from_fn_path( | 45 | lower_generic_args_from_fn_path( |
49 | &ctx, | 46 | ctx, |
50 | segment.param_list(), | 47 | segment.param_list(), |
51 | segment.ret_type(), | 48 | segment.ret_type(), |
52 | ) | 49 | ) |
@@ -64,17 +61,17 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> | |||
64 | ast::PathSegmentKind::Type { type_ref, trait_ref } => { | 61 | ast::PathSegmentKind::Type { type_ref, trait_ref } => { |
65 | assert!(path.qualifier().is_none()); // this can only occur at the first segment | 62 | assert!(path.qualifier().is_none()); // this can only occur at the first segment |
66 | 63 | ||
67 | let self_type = TypeRef::from_ast(&ctx, type_ref?); | 64 | let self_type = TypeRef::from_ast(ctx, type_ref?); |
68 | 65 | ||
69 | match trait_ref { | 66 | match trait_ref { |
70 | // <T>::foo | 67 | // <T>::foo |
71 | None => { | 68 | None => { |
72 | type_anchor = Some(Box::new(self_type)); | 69 | type_anchor = Some(Interned::new(self_type)); |
73 | kind = PathKind::Plain; | 70 | kind = PathKind::Plain; |
74 | } | 71 | } |
75 | // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo | 72 | // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo |
76 | Some(trait_ref) => { | 73 | Some(trait_ref) => { |
77 | let path = Path::from_src(trait_ref.path()?, hygiene)?; | 74 | let path = Path::from_src(trait_ref.path()?, ctx)?; |
78 | let mod_path = (*path.mod_path).clone(); | 75 | let mod_path = (*path.mod_path).clone(); |
79 | let num_segments = path.mod_path.segments.len(); | 76 | let num_segments = path.mod_path.segments.len(); |
80 | kind = mod_path.kind; | 77 | kind = mod_path.kind; |
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs index a73585ee7..0391cc49b 100644 --- a/crates/hir_def/src/resolver.rs +++ b/crates/hir_def/src/resolver.rs | |||
@@ -14,6 +14,7 @@ use crate::{ | |||
14 | db::DefDatabase, | 14 | db::DefDatabase, |
15 | expr::{ExprId, LabelId, PatId}, | 15 | expr::{ExprId, LabelId, PatId}, |
16 | generics::GenericParams, | 16 | generics::GenericParams, |
17 | intern::Interned, | ||
17 | item_scope::{BuiltinShadowMode, BUILTIN_SCOPE}, | 18 | item_scope::{BuiltinShadowMode, BUILTIN_SCOPE}, |
18 | nameres::DefMap, | 19 | nameres::DefMap, |
19 | path::{ModPath, PathKind}, | 20 | path::{ModPath, PathKind}, |
@@ -50,7 +51,7 @@ enum Scope { | |||
50 | /// All the items and imported names of a module | 51 | /// All the items and imported names of a module |
51 | ModuleScope(ModuleItemMap), | 52 | ModuleScope(ModuleItemMap), |
52 | /// Brings the generic parameters of an item into scope | 53 | /// Brings the generic parameters of an item into scope |
53 | GenericParams { def: GenericDefId, params: Arc<GenericParams> }, | 54 | GenericParams { def: GenericDefId, params: Interned<GenericParams> }, |
54 | /// Brings `Self` in `impl` block into scope | 55 | /// Brings `Self` in `impl` block into scope |
55 | ImplDefScope(ImplId), | 56 | ImplDefScope(ImplId), |
56 | /// Brings `Self` in enum, struct and union definitions into scope | 57 | /// Brings `Self` in enum, struct and union definitions into scope |
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs index dd36106f8..8fa703a57 100644 --- a/crates/hir_def/src/test_db.rs +++ b/crates/hir_def/src/test_db.rs | |||
@@ -15,7 +15,12 @@ use rustc_hash::FxHashSet; | |||
15 | use syntax::{algo, ast, AstNode, TextRange, TextSize}; | 15 | use syntax::{algo, ast, AstNode, TextRange, TextSize}; |
16 | use test_utils::extract_annotations; | 16 | use test_utils::extract_annotations; |
17 | 17 | ||
18 | use crate::{db::DefDatabase, nameres::DefMap, src::HasSource, Lookup, ModuleDefId, ModuleId}; | 18 | use crate::{ |
19 | db::DefDatabase, | ||
20 | nameres::{DefMap, ModuleSource}, | ||
21 | src::HasSource, | ||
22 | LocalModuleId, Lookup, ModuleDefId, ModuleId, | ||
23 | }; | ||
19 | 24 | ||
20 | #[salsa::database( | 25 | #[salsa::database( |
21 | base_db::SourceDatabaseExtStorage, | 26 | base_db::SourceDatabaseExtStorage, |
@@ -87,10 +92,11 @@ impl TestDB { | |||
87 | pub(crate) fn module_at_position(&self, position: FilePosition) -> ModuleId { | 92 | pub(crate) fn module_at_position(&self, position: FilePosition) -> ModuleId { |
88 | let file_module = self.module_for_file(position.file_id); | 93 | let file_module = self.module_for_file(position.file_id); |
89 | let mut def_map = file_module.def_map(self); | 94 | let mut def_map = file_module.def_map(self); |
95 | let module = self.mod_at_position(&def_map, position); | ||
90 | 96 | ||
91 | def_map = match self.block_at_position(&def_map, position) { | 97 | def_map = match self.block_at_position(&def_map, position) { |
92 | Some(it) => it, | 98 | Some(it) => it, |
93 | None => return file_module, | 99 | None => return def_map.module_id(module), |
94 | }; | 100 | }; |
95 | loop { | 101 | loop { |
96 | let new_map = self.block_at_position(&def_map, position); | 102 | let new_map = self.block_at_position(&def_map, position); |
@@ -106,6 +112,47 @@ impl TestDB { | |||
106 | } | 112 | } |
107 | } | 113 | } |
108 | 114 | ||
115 | /// Finds the smallest/innermost module in `def_map` containing `position`. | ||
116 | fn mod_at_position(&self, def_map: &DefMap, position: FilePosition) -> LocalModuleId { | ||
117 | let mut size = None; | ||
118 | let mut res = def_map.root(); | ||
119 | for (module, data) in def_map.modules() { | ||
120 | let src = data.definition_source(self); | ||
121 | if src.file_id != position.file_id.into() { | ||
122 | continue; | ||
123 | } | ||
124 | |||
125 | let range = match src.value { | ||
126 | ModuleSource::SourceFile(it) => it.syntax().text_range(), | ||
127 | ModuleSource::Module(it) => it.syntax().text_range(), | ||
128 | ModuleSource::BlockExpr(it) => it.syntax().text_range(), | ||
129 | }; | ||
130 | |||
131 | if !range.contains(position.offset) { | ||
132 | continue; | ||
133 | } | ||
134 | |||
135 | let new_size = match size { | ||
136 | None => range.len(), | ||
137 | Some(size) => { | ||
138 | if range.len() < size { | ||
139 | range.len() | ||
140 | } else { | ||
141 | size | ||
142 | } | ||
143 | } | ||
144 | }; | ||
145 | |||
146 | if size != Some(new_size) { | ||
147 | cov_mark::hit!(submodule_in_testdb); | ||
148 | size = Some(new_size); | ||
149 | res = module; | ||
150 | } | ||
151 | } | ||
152 | |||
153 | res | ||
154 | } | ||
155 | |||
109 | fn block_at_position(&self, def_map: &DefMap, position: FilePosition) -> Option<Arc<DefMap>> { | 156 | fn block_at_position(&self, def_map: &DefMap, position: FilePosition) -> Option<Arc<DefMap>> { |
110 | // Find the smallest (innermost) function in `def_map` containing the cursor. | 157 | // Find the smallest (innermost) function in `def_map` containing the cursor. |
111 | let mut size = None; | 158 | let mut size = None; |
diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs index 4c24aae94..ea29da5da 100644 --- a/crates/hir_def/src/type_ref.rs +++ b/crates/hir_def/src/type_ref.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! HIR for references to types. Paths in these are not yet resolved. They can | 1 | //! HIR for references to types. Paths in these are not yet resolved. They can |
2 | //! be directly created from an ast::TypeRef, without further queries. | 2 | //! be directly created from an ast::TypeRef, without further queries. |
3 | use hir_expand::name::Name; | 3 | |
4 | use hir_expand::{name::Name, AstId, InFile}; | ||
4 | use syntax::ast; | 5 | use syntax::ast; |
5 | 6 | ||
6 | use crate::{body::LowerCtx, path::Path}; | 7 | use crate::{body::LowerCtx, path::Path}; |
@@ -68,6 +69,7 @@ impl TraitRef { | |||
68 | } | 69 | } |
69 | } | 70 | } |
70 | } | 71 | } |
72 | |||
71 | /// Compare ty::Ty | 73 | /// Compare ty::Ty |
72 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] | 74 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] |
73 | pub enum TypeRef { | 75 | pub enum TypeRef { |
@@ -84,6 +86,7 @@ pub enum TypeRef { | |||
84 | // For | 86 | // For |
85 | ImplTrait(Vec<TypeBound>), | 87 | ImplTrait(Vec<TypeBound>), |
86 | DynTrait(Vec<TypeBound>), | 88 | DynTrait(Vec<TypeBound>), |
89 | Macro(AstId<ast::MacroCall>), | ||
87 | Error, | 90 | Error, |
88 | } | 91 | } |
89 | 92 | ||
@@ -116,7 +119,7 @@ pub enum TypeBound { | |||
116 | 119 | ||
117 | impl TypeRef { | 120 | impl TypeRef { |
118 | /// Converts an `ast::TypeRef` to a `hir::TypeRef`. | 121 | /// Converts an `ast::TypeRef` to a `hir::TypeRef`. |
119 | pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self { | 122 | pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self { |
120 | match node { | 123 | match node { |
121 | ast::Type::ParenType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()), | 124 | ast::Type::ParenType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()), |
122 | ast::Type::TupleType(inner) => { | 125 | ast::Type::TupleType(inner) => { |
@@ -176,8 +179,13 @@ impl TypeRef { | |||
176 | ast::Type::DynTraitType(inner) => { | 179 | ast::Type::DynTraitType(inner) => { |
177 | TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list())) | 180 | TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list())) |
178 | } | 181 | } |
179 | // FIXME: Macros in type position are not yet supported. | 182 | ast::Type::MacroType(mt) => match mt.macro_call() { |
180 | ast::Type::MacroType(_) => TypeRef::Error, | 183 | Some(mc) => ctx |
184 | .ast_id(&mc) | ||
185 | .map(|mc| TypeRef::Macro(InFile::new(ctx.file_id(), mc))) | ||
186 | .unwrap_or(TypeRef::Error), | ||
187 | None => TypeRef::Error, | ||
188 | }, | ||
181 | } | 189 | } |
182 | } | 190 | } |
183 | 191 | ||
@@ -215,7 +223,7 @@ impl TypeRef { | |||
215 | } | 223 | } |
216 | } | 224 | } |
217 | TypeRef::Path(path) => go_path(path, f), | 225 | TypeRef::Path(path) => go_path(path, f), |
218 | TypeRef::Never | TypeRef::Placeholder | TypeRef::Error => {} | 226 | TypeRef::Never | TypeRef::Placeholder | TypeRef::Macro(_) | TypeRef::Error => {} |
219 | }; | 227 | }; |
220 | } | 228 | } |
221 | 229 | ||
diff --git a/crates/hir_def/src/visibility.rs b/crates/hir_def/src/visibility.rs index 7d00a37c4..d4b7c9970 100644 --- a/crates/hir_def/src/visibility.rs +++ b/crates/hir_def/src/visibility.rs | |||
@@ -11,7 +11,7 @@ use crate::{ | |||
11 | nameres::DefMap, | 11 | nameres::DefMap, |
12 | path::{ModPath, PathKind}, | 12 | path::{ModPath, PathKind}, |
13 | resolver::HasResolver, | 13 | resolver::HasResolver, |
14 | FunctionId, HasModule, LocalFieldId, ModuleDefId, ModuleId, VariantId, | 14 | FunctionId, HasModule, LocalFieldId, ModuleId, VariantId, |
15 | }; | 15 | }; |
16 | 16 | ||
17 | /// Visibility of an item, not yet resolved. | 17 | /// Visibility of an item, not yet resolved. |
@@ -25,7 +25,7 @@ pub enum RawVisibility { | |||
25 | } | 25 | } |
26 | 26 | ||
27 | impl RawVisibility { | 27 | impl RawVisibility { |
28 | pub(crate) const fn private() -> RawVisibility { | 28 | pub(crate) fn private() -> RawVisibility { |
29 | RawVisibility::Module(ModPath::from_kind(PathKind::Super(0))) | 29 | RawVisibility::Module(ModPath::from_kind(PathKind::Super(0))) |
30 | } | 30 | } |
31 | 31 | ||
@@ -123,11 +123,19 @@ impl Visibility { | |||
123 | def_map: &DefMap, | 123 | def_map: &DefMap, |
124 | mut from_module: crate::LocalModuleId, | 124 | mut from_module: crate::LocalModuleId, |
125 | ) -> bool { | 125 | ) -> bool { |
126 | let to_module = match self { | 126 | let mut to_module = match self { |
127 | Visibility::Module(m) => m, | 127 | Visibility::Module(m) => m, |
128 | Visibility::Public => return true, | 128 | Visibility::Public => return true, |
129 | }; | 129 | }; |
130 | 130 | ||
131 | // `to_module` might be the root module of a block expression. Those have the same | ||
132 | // visibility as the containing module (even though no items are directly nameable from | ||
133 | // there, getting this right is important for method resolution). | ||
134 | // In that case, we adjust the visibility of `to_module` to point to the containing module. | ||
135 | if to_module.is_block_root(db) { | ||
136 | to_module = to_module.containing_module(db).unwrap(); | ||
137 | } | ||
138 | |||
131 | // from_module needs to be a descendant of to_module | 139 | // from_module needs to be a descendant of to_module |
132 | let mut def_map = def_map; | 140 | let mut def_map = def_map; |
133 | let mut parent_arc; | 141 | let mut parent_arc; |
@@ -217,6 +225,6 @@ pub(crate) fn field_visibilities_query( | |||
217 | 225 | ||
218 | /// Resolve visibility of a function. | 226 | /// Resolve visibility of a function. |
219 | pub(crate) fn function_visibility_query(db: &dyn DefDatabase, def: FunctionId) -> Visibility { | 227 | pub(crate) fn function_visibility_query(db: &dyn DefDatabase, def: FunctionId) -> Visibility { |
220 | let resolver = ModuleDefId::from(def).module(db).unwrap().resolver(db); | 228 | let resolver = def.resolver(db); |
221 | db.function_data(def).visibility.resolve(db, &resolver) | 229 | db.function_data(def).visibility.resolve(db, &resolver) |
222 | } | 230 | } |
diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs index 6ece4b289..537c03028 100644 --- a/crates/hir_expand/src/builtin_derive.rs +++ b/crates/hir_expand/src/builtin_derive.rs | |||
@@ -269,7 +269,7 @@ mod tests { | |||
269 | use expect_test::{expect, Expect}; | 269 | use expect_test::{expect, Expect}; |
270 | use name::AsName; | 270 | use name::AsName; |
271 | 271 | ||
272 | use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc}; | 272 | use crate::{test_db::TestDB, AstId, AttrId, MacroCallId, MacroCallKind, MacroCallLoc}; |
273 | 273 | ||
274 | use super::*; | 274 | use super::*; |
275 | 275 | ||
@@ -308,7 +308,7 @@ $0 | |||
308 | 308 | ||
309 | let expander = BuiltinDeriveExpander::find_by_name(&name).unwrap(); | 309 | let expander = BuiltinDeriveExpander::find_by_name(&name).unwrap(); |
310 | 310 | ||
311 | let attr_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0])); | 311 | let ast_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0])); |
312 | 312 | ||
313 | let loc = MacroCallLoc { | 313 | let loc = MacroCallLoc { |
314 | def: MacroDefId { | 314 | def: MacroDefId { |
@@ -317,7 +317,11 @@ $0 | |||
317 | local_inner: false, | 317 | local_inner: false, |
318 | }, | 318 | }, |
319 | krate: CrateId(0), | 319 | krate: CrateId(0), |
320 | kind: MacroCallKind::Derive(attr_id, name.to_string()), | 320 | kind: MacroCallKind::Derive { |
321 | ast_id, | ||
322 | derive_name: name.to_string(), | ||
323 | derive_attr: AttrId(0), | ||
324 | }, | ||
321 | }; | 325 | }; |
322 | 326 | ||
323 | let id: MacroCallId = db.intern_macro(loc).into(); | 327 | let id: MacroCallId = db.intern_macro(loc).into(); |
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index 75ec4196b..179de61f9 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs | |||
@@ -110,6 +110,7 @@ register_builtin! { | |||
110 | (format_args_nl, FormatArgsNl) => format_args_expand, | 110 | (format_args_nl, FormatArgsNl) => format_args_expand, |
111 | (llvm_asm, LlvmAsm) => asm_expand, | 111 | (llvm_asm, LlvmAsm) => asm_expand, |
112 | (asm, Asm) => asm_expand, | 112 | (asm, Asm) => asm_expand, |
113 | (global_asm, GlobalAsm) => global_asm_expand, | ||
113 | (cfg, Cfg) => cfg_expand, | 114 | (cfg, Cfg) => cfg_expand, |
114 | (core_panic, CorePanic) => panic_expand, | 115 | (core_panic, CorePanic) => panic_expand, |
115 | (std_panic, StdPanic) => panic_expand, | 116 | (std_panic, StdPanic) => panic_expand, |
@@ -274,6 +275,15 @@ fn asm_expand( | |||
274 | ExpandResult::ok(expanded) | 275 | ExpandResult::ok(expanded) |
275 | } | 276 | } |
276 | 277 | ||
278 | fn global_asm_expand( | ||
279 | _db: &dyn AstDatabase, | ||
280 | _id: LazyMacroId, | ||
281 | _tt: &tt::Subtree, | ||
282 | ) -> ExpandResult<tt::Subtree> { | ||
283 | // Expand to nothing (at item-level) | ||
284 | ExpandResult::ok(quote! {}) | ||
285 | } | ||
286 | |||
277 | fn cfg_expand( | 287 | fn cfg_expand( |
278 | db: &dyn AstDatabase, | 288 | db: &dyn AstDatabase, |
279 | id: LazyMacroId, | 289 | id: LazyMacroId, |
@@ -490,7 +500,7 @@ fn env_expand( | |||
490 | // unnecessary diagnostics for eg. `CARGO_PKG_NAME`. | 500 | // unnecessary diagnostics for eg. `CARGO_PKG_NAME`. |
491 | if key == "OUT_DIR" { | 501 | if key == "OUT_DIR" { |
492 | err = Some(mbe::ExpandError::Other( | 502 | err = Some(mbe::ExpandError::Other( |
493 | r#"`OUT_DIR` not set, enable "load out dirs from check" to fix"#.into(), | 503 | r#"`OUT_DIR` not set, enable "run build scripts" to fix"#.into(), |
494 | )); | 504 | )); |
495 | } | 505 | } |
496 | 506 | ||
@@ -566,10 +576,9 @@ mod tests { | |||
566 | let loc = MacroCallLoc { | 576 | let loc = MacroCallLoc { |
567 | def, | 577 | def, |
568 | krate, | 578 | krate, |
569 | kind: MacroCallKind::FnLike(AstId::new( | 579 | kind: MacroCallKind::FnLike { |
570 | file_id.into(), | 580 | ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(¯o_call)), |
571 | ast_id_map.ast_id(¯o_call), | 581 | }, |
572 | )), | ||
573 | }; | 582 | }; |
574 | 583 | ||
575 | let id: MacroCallId = db.intern_macro(loc).into(); | 584 | let id: MacroCallId = db.intern_macro(loc).into(); |
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index 10fe60821..1e4b0cc19 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -439,6 +439,8 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { | |||
439 | match parent.kind() { | 439 | match parent.kind() { |
440 | MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items, | 440 | MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items, |
441 | MACRO_STMTS => FragmentKind::Statements, | 441 | MACRO_STMTS => FragmentKind::Statements, |
442 | MACRO_PAT => FragmentKind::Pattern, | ||
443 | MACRO_TYPE => FragmentKind::Type, | ||
442 | ITEM_LIST => FragmentKind::Items, | 444 | ITEM_LIST => FragmentKind::Items, |
443 | LET_STMT => { | 445 | LET_STMT => { |
444 | // FIXME: Handle LHS Pattern | 446 | // FIXME: Handle LHS Pattern |
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs index 9705526fa..f12132f84 100644 --- a/crates/hir_expand/src/eager.rs +++ b/crates/hir_expand/src/eager.rs | |||
@@ -29,8 +29,9 @@ use base_db::CrateId; | |||
29 | use mbe::ExpandResult; | 29 | use mbe::ExpandResult; |
30 | use parser::FragmentKind; | 30 | use parser::FragmentKind; |
31 | use std::sync::Arc; | 31 | use std::sync::Arc; |
32 | use syntax::{algo::SyntaxRewriter, SyntaxNode}; | 32 | use syntax::{ted, SyntaxNode}; |
33 | 33 | ||
34 | #[derive(Debug)] | ||
34 | pub struct ErrorEmitted { | 35 | pub struct ErrorEmitted { |
35 | _private: (), | 36 | _private: (), |
36 | } | 37 | } |
@@ -174,8 +175,9 @@ fn lazy_expand( | |||
174 | ) -> ExpandResult<Option<InFile<SyntaxNode>>> { | 175 | ) -> ExpandResult<Option<InFile<SyntaxNode>>> { |
175 | let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value); | 176 | let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value); |
176 | 177 | ||
177 | let id: MacroCallId = | 178 | let id: MacroCallId = def |
178 | def.as_lazy_macro(db, krate, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into(); | 179 | .as_lazy_macro(db, krate, MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id) }) |
180 | .into(); | ||
179 | 181 | ||
180 | let err = db.macro_expand_error(id); | 182 | let err = db.macro_expand_error(id); |
181 | let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)); | 183 | let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)); |
@@ -190,10 +192,10 @@ fn eager_macro_recur( | |||
190 | macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, | 192 | macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, |
191 | mut diagnostic_sink: &mut dyn FnMut(mbe::ExpandError), | 193 | mut diagnostic_sink: &mut dyn FnMut(mbe::ExpandError), |
192 | ) -> Result<SyntaxNode, ErrorEmitted> { | 194 | ) -> Result<SyntaxNode, ErrorEmitted> { |
193 | let original = curr.value.clone(); | 195 | let original = curr.value.clone().clone_for_update(); |
194 | 196 | ||
195 | let children = curr.value.descendants().filter_map(ast::MacroCall::cast); | 197 | let children = original.descendants().filter_map(ast::MacroCall::cast); |
196 | let mut rewriter = SyntaxRewriter::default(); | 198 | let mut replacements = Vec::new(); |
197 | 199 | ||
198 | // Collect replacement | 200 | // Collect replacement |
199 | for child in children { | 201 | for child in children { |
@@ -212,6 +214,7 @@ fn eager_macro_recur( | |||
212 | .into(); | 214 | .into(); |
213 | db.parse_or_expand(id.as_file()) | 215 | db.parse_or_expand(id.as_file()) |
214 | .expect("successful macro expansion should be parseable") | 216 | .expect("successful macro expansion should be parseable") |
217 | .clone_for_update() | ||
215 | } | 218 | } |
216 | MacroDefKind::Declarative(_) | 219 | MacroDefKind::Declarative(_) |
217 | | MacroDefKind::BuiltIn(..) | 220 | | MacroDefKind::BuiltIn(..) |
@@ -225,15 +228,14 @@ fn eager_macro_recur( | |||
225 | } | 228 | } |
226 | }; | 229 | }; |
227 | 230 | ||
228 | // check if the whole original sytnax is replaced | 231 | // check if the whole original syntax is replaced |
229 | // Note that SyntaxRewriter cannot replace the root node itself | ||
230 | if child.syntax() == &original { | 232 | if child.syntax() == &original { |
231 | return Ok(insert); | 233 | return Ok(insert); |
232 | } | 234 | } |
233 | 235 | ||
234 | rewriter.replace(child.syntax(), &insert); | 236 | replacements.push((child, insert)); |
235 | } | 237 | } |
236 | 238 | ||
237 | let res = rewriter.rewrite(&original); | 239 | replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new)); |
238 | Ok(res) | 240 | Ok(original) |
239 | } | 241 | } |
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index 3e332ee47..a0e6aec62 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs | |||
@@ -290,22 +290,27 @@ pub struct MacroCallLoc { | |||
290 | 290 | ||
291 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 291 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
292 | pub enum MacroCallKind { | 292 | pub enum MacroCallKind { |
293 | FnLike(AstId<ast::MacroCall>), | 293 | FnLike { ast_id: AstId<ast::MacroCall> }, |
294 | Derive(AstId<ast::Item>, String), | 294 | Derive { ast_id: AstId<ast::Item>, derive_name: String, derive_attr: AttrId }, |
295 | } | 295 | } |
296 | 296 | ||
297 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
298 | pub struct AttrId(pub u32); | ||
299 | |||
297 | impl MacroCallKind { | 300 | impl MacroCallKind { |
298 | fn file_id(&self) -> HirFileId { | 301 | fn file_id(&self) -> HirFileId { |
299 | match self { | 302 | match self { |
300 | MacroCallKind::FnLike(ast_id) => ast_id.file_id, | 303 | MacroCallKind::FnLike { ast_id, .. } => ast_id.file_id, |
301 | MacroCallKind::Derive(ast_id, _) => ast_id.file_id, | 304 | MacroCallKind::Derive { ast_id, .. } => ast_id.file_id, |
302 | } | 305 | } |
303 | } | 306 | } |
304 | 307 | ||
305 | fn node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> { | 308 | fn node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> { |
306 | match self { | 309 | match self { |
307 | MacroCallKind::FnLike(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()), | 310 | MacroCallKind::FnLike { ast_id, .. } => { |
308 | MacroCallKind::Derive(ast_id, _) => { | 311 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) |
312 | } | ||
313 | MacroCallKind::Derive { ast_id, .. } => { | ||
309 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) | 314 | ast_id.with_value(ast_id.to_node(db).syntax().clone()) |
310 | } | 315 | } |
311 | } | 316 | } |
@@ -313,10 +318,10 @@ impl MacroCallKind { | |||
313 | 318 | ||
314 | fn arg(&self, db: &dyn db::AstDatabase) -> Option<SyntaxNode> { | 319 | fn arg(&self, db: &dyn db::AstDatabase) -> Option<SyntaxNode> { |
315 | match self { | 320 | match self { |
316 | MacroCallKind::FnLike(ast_id) => { | 321 | MacroCallKind::FnLike { ast_id, .. } => { |
317 | Some(ast_id.to_node(db).token_tree()?.syntax().clone()) | 322 | Some(ast_id.to_node(db).token_tree()?.syntax().clone()) |
318 | } | 323 | } |
319 | MacroCallKind::Derive(ast_id, _) => Some(ast_id.to_node(db).syntax().clone()), | 324 | MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()), |
320 | } | 325 | } |
321 | } | 326 | } |
322 | } | 327 | } |
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs index a0f8766b0..bcfd3e524 100644 --- a/crates/hir_expand/src/name.rs +++ b/crates/hir_expand/src/name.rs | |||
@@ -221,6 +221,7 @@ pub mod known { | |||
221 | option_env, | 221 | option_env, |
222 | llvm_asm, | 222 | llvm_asm, |
223 | asm, | 223 | asm, |
224 | global_asm, | ||
224 | // Builtin derives | 225 | // Builtin derives |
225 | Copy, | 226 | Copy, |
226 | Clone, | 227 | Clone, |
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml index 030b7eebe..66b3418f2 100644 --- a/crates/hir_ty/Cargo.toml +++ b/crates/hir_ty/Cargo.toml | |||
@@ -12,15 +12,15 @@ doctest = false | |||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = { version = "1.1", features = ["thread-local"] } | 13 | cov-mark = { version = "1.1", features = ["thread-local"] } |
14 | itertools = "0.10.0" | 14 | itertools = "0.10.0" |
15 | arrayvec = "0.6" | 15 | arrayvec = "0.7" |
16 | smallvec = "1.2.0" | 16 | smallvec = "1.2.0" |
17 | ena = "0.14.0" | 17 | ena = "0.14.0" |
18 | log = "0.4.8" | 18 | log = "0.4.8" |
19 | rustc-hash = "1.1.0" | 19 | rustc-hash = "1.1.0" |
20 | scoped-tls = "1" | 20 | scoped-tls = "1" |
21 | chalk-solve = { version = "0.60", default-features = false } | 21 | chalk-solve = { version = "0.64", default-features = false } |
22 | chalk-ir = "0.60" | 22 | chalk-ir = "0.64" |
23 | chalk-recursive = "0.60" | 23 | chalk-recursive = "0.64" |
24 | la-arena = { version = "0.2.0", path = "../../lib/arena" } | 24 | la-arena = { version = "0.2.0", path = "../../lib/arena" } |
25 | 25 | ||
26 | stdx = { path = "../stdx", version = "0.0.0" } | 26 | stdx = { path = "../stdx", version = "0.0.0" } |
diff --git a/crates/hir_ty/src/autoderef.rs b/crates/hir_ty/src/autoderef.rs index 70c56cc45..2c07494a9 100644 --- a/crates/hir_ty/src/autoderef.rs +++ b/crates/hir_ty/src/autoderef.rs | |||
@@ -6,16 +6,15 @@ | |||
6 | use std::iter::successors; | 6 | use std::iter::successors; |
7 | 7 | ||
8 | use base_db::CrateId; | 8 | use base_db::CrateId; |
9 | use chalk_ir::cast::Cast; | 9 | use chalk_ir::{cast::Cast, fold::Fold, interner::HasInterner, VariableKind}; |
10 | use hir_def::lang_item::LangItemTarget; | 10 | use hir_def::lang_item::LangItemTarget; |
11 | use hir_expand::name::name; | 11 | use hir_expand::name::name; |
12 | use log::{info, warn}; | 12 | use log::{info, warn}; |
13 | 13 | ||
14 | use crate::{ | 14 | use crate::{ |
15 | db::HirDatabase, | 15 | db::HirDatabase, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds, |
16 | traits::{InEnvironment, Solution}, | 16 | DebruijnIndex, InEnvironment, Interner, ProjectionTyExt, Solution, Substitution, Ty, TyBuilder, |
17 | AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds, DebruijnIndex, Interner, Ty, | 17 | TyKind, |
18 | TyBuilder, TyKind, | ||
19 | }; | 18 | }; |
20 | 19 | ||
21 | const AUTODEREF_RECURSION_LIMIT: usize = 10; | 20 | const AUTODEREF_RECURSION_LIMIT: usize = 10; |
@@ -37,18 +36,28 @@ pub(crate) fn deref( | |||
37 | krate: CrateId, | 36 | krate: CrateId, |
38 | ty: InEnvironment<&Canonical<Ty>>, | 37 | ty: InEnvironment<&Canonical<Ty>>, |
39 | ) -> Option<Canonical<Ty>> { | 38 | ) -> Option<Canonical<Ty>> { |
40 | if let Some(derefed) = ty.goal.value.builtin_deref() { | 39 | let _p = profile::span("deref"); |
40 | if let Some(derefed) = builtin_deref(&ty.goal.value) { | ||
41 | Some(Canonical { value: derefed, binders: ty.goal.binders.clone() }) | 41 | Some(Canonical { value: derefed, binders: ty.goal.binders.clone() }) |
42 | } else { | 42 | } else { |
43 | deref_by_trait(db, krate, ty) | 43 | deref_by_trait(db, krate, ty) |
44 | } | 44 | } |
45 | } | 45 | } |
46 | 46 | ||
47 | fn builtin_deref(ty: &Ty) -> Option<Ty> { | ||
48 | match ty.kind(&Interner) { | ||
49 | TyKind::Ref(.., ty) => Some(ty.clone()), | ||
50 | TyKind::Raw(.., ty) => Some(ty.clone()), | ||
51 | _ => None, | ||
52 | } | ||
53 | } | ||
54 | |||
47 | fn deref_by_trait( | 55 | fn deref_by_trait( |
48 | db: &dyn HirDatabase, | 56 | db: &dyn HirDatabase, |
49 | krate: CrateId, | 57 | krate: CrateId, |
50 | ty: InEnvironment<&Canonical<Ty>>, | 58 | ty: InEnvironment<&Canonical<Ty>>, |
51 | ) -> Option<Canonical<Ty>> { | 59 | ) -> Option<Canonical<Ty>> { |
60 | let _p = profile::span("deref_by_trait"); | ||
52 | let deref_trait = match db.lang_item(krate, "deref".into())? { | 61 | let deref_trait = match db.lang_item(krate, "deref".into())? { |
53 | LangItemTarget::TraitId(it) => it, | 62 | LangItemTarget::TraitId(it) => it, |
54 | _ => return None, | 63 | _ => return None, |
@@ -97,7 +106,7 @@ fn deref_by_trait( | |||
97 | binders: CanonicalVarKinds::from_iter( | 106 | binders: CanonicalVarKinds::from_iter( |
98 | &Interner, | 107 | &Interner, |
99 | ty.goal.binders.iter(&Interner).cloned().chain(Some(chalk_ir::WithKind::new( | 108 | ty.goal.binders.iter(&Interner).cloned().chain(Some(chalk_ir::WithKind::new( |
100 | chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General), | 109 | VariableKind::Ty(chalk_ir::TyVariableKind::General), |
101 | chalk_ir::UniverseIndex::ROOT, | 110 | chalk_ir::UniverseIndex::ROOT, |
102 | ))), | 111 | ))), |
103 | ), | 112 | ), |
@@ -122,23 +131,25 @@ fn deref_by_trait( | |||
122 | // assumptions will be broken. We would need to properly introduce | 131 | // assumptions will be broken. We would need to properly introduce |
123 | // new variables in that case | 132 | // new variables in that case |
124 | 133 | ||
125 | for i in 1..vars.0.binders.len(&Interner) { | 134 | for i in 1..vars.binders.len(&Interner) { |
126 | if vars.0.value.at(&Interner, i - 1).assert_ty_ref(&Interner).kind(&Interner) | 135 | if vars.value.subst.at(&Interner, i - 1).assert_ty_ref(&Interner).kind(&Interner) |
127 | != &TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, i - 1)) | 136 | != &TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, i - 1)) |
128 | { | 137 | { |
129 | warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.goal, solution); | 138 | warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.goal, solution); |
130 | return None; | 139 | return None; |
131 | } | 140 | } |
132 | } | 141 | } |
133 | Some(Canonical { | 142 | // FIXME: we remove lifetime variables here since they can confuse |
143 | // the method resolution code later | ||
144 | Some(fixup_lifetime_variables(Canonical { | ||
134 | value: vars | 145 | value: vars |
135 | .0 | ||
136 | .value | 146 | .value |
137 | .at(&Interner, vars.0.value.len(&Interner) - 1) | 147 | .subst |
148 | .at(&Interner, vars.value.subst.len(&Interner) - 1) | ||
138 | .assert_ty_ref(&Interner) | 149 | .assert_ty_ref(&Interner) |
139 | .clone(), | 150 | .clone(), |
140 | binders: vars.0.binders.clone(), | 151 | binders: vars.binders.clone(), |
141 | }) | 152 | })) |
142 | } | 153 | } |
143 | Solution::Ambig(_) => { | 154 | Solution::Ambig(_) => { |
144 | info!("Ambiguous solution for derefing {:?}: {:?}", ty.goal, solution); | 155 | info!("Ambiguous solution for derefing {:?}: {:?}", ty.goal, solution); |
@@ -146,3 +157,32 @@ fn deref_by_trait( | |||
146 | } | 157 | } |
147 | } | 158 | } |
148 | } | 159 | } |
160 | |||
161 | fn fixup_lifetime_variables<T: Fold<Interner, Result = T> + HasInterner<Interner = Interner>>( | ||
162 | c: Canonical<T>, | ||
163 | ) -> Canonical<T> { | ||
164 | // Removes lifetime variables from the Canonical, replacing them by static lifetimes. | ||
165 | let mut i = 0; | ||
166 | let subst = Substitution::from_iter( | ||
167 | &Interner, | ||
168 | c.binders.iter(&Interner).map(|vk| match vk.kind { | ||
169 | VariableKind::Ty(_) => { | ||
170 | let index = i; | ||
171 | i += 1; | ||
172 | BoundVar::new(DebruijnIndex::INNERMOST, index).to_ty(&Interner).cast(&Interner) | ||
173 | } | ||
174 | VariableKind::Lifetime => static_lifetime().cast(&Interner), | ||
175 | VariableKind::Const(_) => unimplemented!(), | ||
176 | }), | ||
177 | ); | ||
178 | let binders = CanonicalVarKinds::from_iter( | ||
179 | &Interner, | ||
180 | c.binders.iter(&Interner).filter(|vk| match vk.kind { | ||
181 | VariableKind::Ty(_) => true, | ||
182 | VariableKind::Lifetime => false, | ||
183 | VariableKind::Const(_) => true, | ||
184 | }), | ||
185 | ); | ||
186 | let value = subst.apply(c.value, &Interner); | ||
187 | Canonical { binders, value } | ||
188 | } | ||
diff --git a/crates/hir_ty/src/builder.rs b/crates/hir_ty/src/builder.rs index 4a9a8058f..e25ef866d 100644 --- a/crates/hir_ty/src/builder.rs +++ b/crates/hir_ty/src/builder.rs | |||
@@ -4,6 +4,7 @@ use std::iter; | |||
4 | 4 | ||
5 | use chalk_ir::{ | 5 | use chalk_ir::{ |
6 | cast::{Cast, CastTo, Caster}, | 6 | cast::{Cast, CastTo, Caster}, |
7 | fold::Fold, | ||
7 | interner::HasInterner, | 8 | interner::HasInterner, |
8 | AdtId, BoundVar, DebruijnIndex, Safety, Scalar, | 9 | AdtId, BoundVar, DebruijnIndex, Safety, Scalar, |
9 | }; | 10 | }; |
@@ -12,8 +13,8 @@ use smallvec::SmallVec; | |||
12 | 13 | ||
13 | use crate::{ | 14 | use crate::{ |
14 | db::HirDatabase, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, | 15 | db::HirDatabase, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, |
15 | CallableSig, FnPointer, FnSig, GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, | 16 | CallableSig, FnPointer, FnSig, FnSubst, GenericArg, Interner, ProjectionTy, Substitution, |
16 | TyDefId, TyKind, TypeWalk, ValueTyDefId, | 17 | TraitRef, Ty, TyDefId, TyExt, TyKind, ValueTyDefId, |
17 | }; | 18 | }; |
18 | 19 | ||
19 | /// This is a builder for `Ty` or anything that needs a `Substitution`. | 20 | /// This is a builder for `Ty` or anything that needs a `Substitution`. |
@@ -32,8 +33,7 @@ impl<D> TyBuilder<D> { | |||
32 | 33 | ||
33 | fn build_internal(self) -> (D, Substitution) { | 34 | fn build_internal(self) -> (D, Substitution) { |
34 | assert_eq!(self.vec.len(), self.param_count); | 35 | assert_eq!(self.vec.len(), self.param_count); |
35 | // FIXME: would be good to have a way to construct a chalk_ir::Substitution from the interned form | 36 | let subst = Substitution::from_iter(&Interner, self.vec); |
36 | let subst = Substitution(self.vec); | ||
37 | (self.data, subst) | 37 | (self.data, subst) |
38 | } | 38 | } |
39 | 39 | ||
@@ -54,7 +54,7 @@ impl<D> TyBuilder<D> { | |||
54 | } | 54 | } |
55 | 55 | ||
56 | pub fn fill_with_unknown(self) -> Self { | 56 | pub fn fill_with_unknown(self) -> Self { |
57 | self.fill(iter::repeat(TyKind::Unknown.intern(&Interner))) | 57 | self.fill(iter::repeat(TyKind::Error.intern(&Interner))) |
58 | } | 58 | } |
59 | 59 | ||
60 | pub fn fill(mut self, filler: impl Iterator<Item = impl CastTo<GenericArg>>) -> Self { | 60 | pub fn fill(mut self, filler: impl Iterator<Item = impl CastTo<GenericArg>>) -> Self { |
@@ -78,9 +78,12 @@ impl TyBuilder<()> { | |||
78 | 78 | ||
79 | pub fn fn_ptr(sig: CallableSig) -> Ty { | 79 | pub fn fn_ptr(sig: CallableSig) -> Ty { |
80 | TyKind::Function(FnPointer { | 80 | TyKind::Function(FnPointer { |
81 | num_args: sig.params().len(), | 81 | num_binders: 0, |
82 | sig: FnSig { abi: (), safety: Safety::Safe, variadic: sig.is_varargs }, | 82 | sig: FnSig { abi: (), safety: Safety::Safe, variadic: sig.is_varargs }, |
83 | substs: Substitution::from_iter(&Interner, sig.params_and_return.iter().cloned()), | 83 | substitution: FnSubst(Substitution::from_iter( |
84 | &Interner, | ||
85 | sig.params_and_return.iter().cloned(), | ||
86 | )), | ||
84 | }) | 87 | }) |
85 | .intern(&Interner) | 88 | .intern(&Interner) |
86 | } | 89 | } |
@@ -138,8 +141,9 @@ impl TyBuilder<hir_def::AdtId> { | |||
138 | self.vec.push(fallback().cast(&Interner)); | 141 | self.vec.push(fallback().cast(&Interner)); |
139 | } else { | 142 | } else { |
140 | // each default can depend on the previous parameters | 143 | // each default can depend on the previous parameters |
141 | let subst_so_far = Substitution(self.vec.clone()); | 144 | let subst_so_far = Substitution::from_iter(&Interner, self.vec.clone()); |
142 | self.vec.push(default_ty.clone().subst(&subst_so_far).cast(&Interner)); | 145 | self.vec |
146 | .push(default_ty.clone().substitute(&Interner, &subst_so_far).cast(&Interner)); | ||
143 | } | 147 | } |
144 | } | 148 | } |
145 | self | 149 | self |
@@ -192,15 +196,15 @@ impl TyBuilder<TypeAliasId> { | |||
192 | } | 196 | } |
193 | } | 197 | } |
194 | 198 | ||
195 | impl<T: TypeWalk + HasInterner<Interner = Interner>> TyBuilder<Binders<T>> { | 199 | impl<T: HasInterner<Interner = Interner> + Fold<Interner>> TyBuilder<Binders<T>> { |
196 | fn subst_binders(b: Binders<T>) -> Self { | 200 | fn subst_binders(b: Binders<T>) -> Self { |
197 | let param_count = b.num_binders; | 201 | let param_count = b.binders.len(&Interner); |
198 | TyBuilder::new(b, param_count) | 202 | TyBuilder::new(b, param_count) |
199 | } | 203 | } |
200 | 204 | ||
201 | pub fn build(self) -> T { | 205 | pub fn build(self) -> <T as Fold<Interner>>::Result { |
202 | let (b, subst) = self.build_internal(); | 206 | let (b, subst) = self.build_internal(); |
203 | b.subst(&subst) | 207 | b.substitute(&Interner, &subst) |
204 | } | 208 | } |
205 | } | 209 | } |
206 | 210 | ||
diff --git a/crates/hir_ty/src/chalk_cast.rs b/crates/hir_ty/src/chalk_cast.rs deleted file mode 100644 index df6492113..000000000 --- a/crates/hir_ty/src/chalk_cast.rs +++ /dev/null | |||
@@ -1,73 +0,0 @@ | |||
1 | //! Implementations of the Chalk `Cast` trait for our types. | ||
2 | |||
3 | use chalk_ir::{ | ||
4 | cast::{Cast, CastTo}, | ||
5 | interner::HasInterner, | ||
6 | }; | ||
7 | |||
8 | use crate::{AliasEq, DomainGoal, GenericArg, GenericArgData, Interner, TraitRef, Ty, WhereClause}; | ||
9 | |||
10 | macro_rules! has_interner { | ||
11 | ($t:ty) => { | ||
12 | impl HasInterner for $t { | ||
13 | type Interner = crate::Interner; | ||
14 | } | ||
15 | }; | ||
16 | } | ||
17 | |||
18 | has_interner!(WhereClause); | ||
19 | has_interner!(DomainGoal); | ||
20 | has_interner!(GenericArg); | ||
21 | has_interner!(Ty); | ||
22 | |||
23 | impl CastTo<WhereClause> for TraitRef { | ||
24 | fn cast_to(self, _interner: &Interner) -> WhereClause { | ||
25 | WhereClause::Implemented(self) | ||
26 | } | ||
27 | } | ||
28 | |||
29 | impl CastTo<WhereClause> for AliasEq { | ||
30 | fn cast_to(self, _interner: &Interner) -> WhereClause { | ||
31 | WhereClause::AliasEq(self) | ||
32 | } | ||
33 | } | ||
34 | |||
35 | impl CastTo<DomainGoal> for WhereClause { | ||
36 | fn cast_to(self, _interner: &Interner) -> DomainGoal { | ||
37 | DomainGoal::Holds(self) | ||
38 | } | ||
39 | } | ||
40 | |||
41 | impl CastTo<GenericArg> for Ty { | ||
42 | fn cast_to(self, interner: &Interner) -> GenericArg { | ||
43 | GenericArg::new(interner, GenericArgData::Ty(self)) | ||
44 | } | ||
45 | } | ||
46 | |||
47 | macro_rules! transitive_impl { | ||
48 | ($a:ty, $b:ty, $c:ty) => { | ||
49 | impl CastTo<$c> for $a { | ||
50 | fn cast_to(self, interner: &Interner) -> $c { | ||
51 | self.cast::<$b>(interner).cast(interner) | ||
52 | } | ||
53 | } | ||
54 | }; | ||
55 | } | ||
56 | |||
57 | // In Chalk, these can be done as blanket impls, but that doesn't work here | ||
58 | // because of coherence | ||
59 | |||
60 | transitive_impl!(TraitRef, WhereClause, DomainGoal); | ||
61 | transitive_impl!(AliasEq, WhereClause, DomainGoal); | ||
62 | |||
63 | macro_rules! reflexive_impl { | ||
64 | ($a:ty) => { | ||
65 | impl CastTo<$a> for $a { | ||
66 | fn cast_to(self, _interner: &Interner) -> $a { | ||
67 | self | ||
68 | } | ||
69 | } | ||
70 | }; | ||
71 | } | ||
72 | |||
73 | reflexive_impl!(GenericArg); | ||
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/chalk_db.rs index 541e6082f..8f054d06b 100644 --- a/crates/hir_ty/src/traits/chalk.rs +++ b/crates/hir_ty/src/chalk_db.rs | |||
@@ -1,52 +1,47 @@ | |||
1 | //! Conversion code from/to Chalk. | 1 | //! The implementation of `RustIrDatabase` for Chalk, which provides information |
2 | //! about the code that Chalk needs. | ||
2 | use std::sync::Arc; | 3 | use std::sync::Arc; |
3 | 4 | ||
4 | use log::debug; | 5 | use log::debug; |
5 | 6 | ||
6 | use chalk_ir::{fold::shift::Shift, CanonicalVarKinds}; | 7 | use chalk_ir::{cast::Cast, fold::shift::Shift, CanonicalVarKinds}; |
7 | use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait}; | 8 | use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait}; |
8 | 9 | ||
9 | use base_db::{salsa::InternKey, CrateId}; | 10 | use base_db::CrateId; |
10 | use hir_def::{ | 11 | use hir_def::{ |
11 | lang_item::{lang_attr, LangItemTarget}, | 12 | lang_item::{lang_attr, LangItemTarget}, |
12 | AssocContainerId, AssocItemId, HasModule, Lookup, TypeAliasId, | 13 | AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId, |
13 | }; | 14 | }; |
14 | use hir_expand::name::name; | 15 | use hir_expand::name::name; |
15 | 16 | ||
16 | use super::ChalkContext; | ||
17 | use crate::{ | 17 | use crate::{ |
18 | db::HirDatabase, | 18 | db::HirDatabase, |
19 | display::HirDisplay, | 19 | display::HirDisplay, |
20 | from_assoc_type_id, | 20 | from_assoc_type_id, from_chalk_trait_id, make_only_type_binders, |
21 | mapping::{from_chalk, ToChalk, TypeAliasAsValue}, | ||
21 | method_resolution::{TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS}, | 22 | method_resolution::{TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS}, |
22 | to_assoc_type_id, to_chalk_trait_id, | 23 | to_assoc_type_id, to_chalk_trait_id, |
24 | traits::ChalkContext, | ||
23 | utils::generics, | 25 | utils::generics, |
24 | AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId, ProjectionTy, Substitution, | 26 | AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId, Interner, ProjectionTy, |
25 | TraitRef, Ty, TyBuilder, TyKind, WhereClause, | 27 | ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, |
28 | TyExt, TyKind, WhereClause, | ||
26 | }; | 29 | }; |
27 | use mapping::{ | ||
28 | convert_where_clauses, generic_predicate_to_inline_bound, make_binders, TypeAliasAsValue, | ||
29 | }; | ||
30 | |||
31 | pub use self::interner::Interner; | ||
32 | pub(crate) use self::interner::*; | ||
33 | 30 | ||
34 | pub(super) mod tls; | 31 | pub(crate) type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum<Interner>; |
35 | mod interner; | 32 | pub(crate) type TraitDatum = chalk_solve::rust_ir::TraitDatum<Interner>; |
36 | mod mapping; | 33 | pub(crate) type StructDatum = chalk_solve::rust_ir::AdtDatum<Interner>; |
37 | 34 | pub(crate) type ImplDatum = chalk_solve::rust_ir::ImplDatum<Interner>; | |
38 | pub(crate) trait ToChalk { | 35 | pub(crate) type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum<Interner>; |
39 | type Chalk; | 36 | |
40 | fn to_chalk(self, db: &dyn HirDatabase) -> Self::Chalk; | 37 | pub(crate) type AssocTypeId = chalk_ir::AssocTypeId<Interner>; |
41 | fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self; | 38 | pub(crate) type TraitId = chalk_ir::TraitId<Interner>; |
42 | } | 39 | pub(crate) type AdtId = chalk_ir::AdtId<Interner>; |
43 | 40 | pub(crate) type ImplId = chalk_ir::ImplId<Interner>; | |
44 | pub(crate) fn from_chalk<T, ChalkT>(db: &dyn HirDatabase, chalk: ChalkT) -> T | 41 | pub(crate) type AssociatedTyValueId = chalk_solve::rust_ir::AssociatedTyValueId<Interner>; |
45 | where | 42 | pub(crate) type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue<Interner>; |
46 | T: ToChalk<Chalk = ChalkT>, | 43 | pub(crate) type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>; |
47 | { | 44 | pub(crate) type Variances = chalk_ir::Variances<Interner>; |
48 | T::from_chalk(db, chalk) | ||
49 | } | ||
50 | 45 | ||
51 | impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | 46 | impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { |
52 | fn associated_ty_data(&self, id: AssocTypeId) -> Arc<AssociatedTyDatum> { | 47 | fn associated_ty_data(&self, id: AssocTypeId) -> Arc<AssociatedTyDatum> { |
@@ -84,9 +79,9 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
84 | binders: &CanonicalVarKinds<Interner>, | 79 | binders: &CanonicalVarKinds<Interner>, |
85 | ) -> Vec<ImplId> { | 80 | ) -> Vec<ImplId> { |
86 | debug!("impls_for_trait {:?}", trait_id); | 81 | debug!("impls_for_trait {:?}", trait_id); |
87 | let trait_: hir_def::TraitId = from_chalk(self.db, trait_id); | 82 | let trait_: hir_def::TraitId = from_chalk_trait_id(trait_id); |
88 | 83 | ||
89 | let ty: Ty = from_chalk(self.db, parameters[0].assert_ty_ref(&Interner).clone()); | 84 | let ty: Ty = parameters[0].assert_ty_ref(&Interner).clone(); |
90 | 85 | ||
91 | fn binder_kind( | 86 | fn binder_kind( |
92 | ty: &Ty, | 87 | ty: &Ty, |
@@ -103,7 +98,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
103 | None | 98 | None |
104 | } | 99 | } |
105 | 100 | ||
106 | let self_ty_fp = TyFingerprint::for_impl(&ty); | 101 | let self_ty_fp = TyFingerprint::for_trait_impl(&ty); |
107 | let fps: &[TyFingerprint] = match binder_kind(&ty, binders) { | 102 | let fps: &[TyFingerprint] = match binder_kind(&ty, binders) { |
108 | Some(chalk_ir::TyVariableKind::Integer) => &ALL_INT_FPS, | 103 | Some(chalk_ir::TyVariableKind::Integer) => &ALL_INT_FPS, |
109 | Some(chalk_ir::TyVariableKind::Float) => &ALL_FLOAT_FPS, | 104 | Some(chalk_ir::TyVariableKind::Float) => &ALL_FLOAT_FPS, |
@@ -166,7 +161,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
166 | Some(LangItemTarget::TraitId(trait_)) => trait_, | 161 | Some(LangItemTarget::TraitId(trait_)) => trait_, |
167 | _ => return None, | 162 | _ => return None, |
168 | }; | 163 | }; |
169 | Some(trait_.to_chalk(self.db)) | 164 | Some(to_chalk_trait_id(trait_)) |
170 | } | 165 | } |
171 | 166 | ||
172 | fn program_clauses_for_env( | 167 | fn program_clauses_for_env( |
@@ -184,16 +179,16 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
184 | .db | 179 | .db |
185 | .return_type_impl_traits(func) | 180 | .return_type_impl_traits(func) |
186 | .expect("impl trait id without impl traits"); | 181 | .expect("impl trait id without impl traits"); |
187 | let data = &datas.value.impl_traits[idx as usize]; | 182 | let (datas, binders) = (*datas).as_ref().into_value_and_skipped_binders(); |
183 | let data = &datas.impl_traits[idx as usize]; | ||
188 | let bound = OpaqueTyDatumBound { | 184 | let bound = OpaqueTyDatumBound { |
189 | bounds: make_binders( | 185 | bounds: make_only_type_binders( |
190 | data.bounds.value.iter().cloned().map(|b| b.to_chalk(self.db)).collect(), | ||
191 | 1, | 186 | 1, |
187 | data.bounds.skip_binders().iter().cloned().collect(), | ||
192 | ), | 188 | ), |
193 | where_clauses: make_binders(vec![], 0), | 189 | where_clauses: make_only_type_binders(0, vec![]), |
194 | }; | 190 | }; |
195 | let num_vars = datas.num_binders; | 191 | chalk_ir::Binders::new(binders, bound) |
196 | make_binders(bound, num_vars) | ||
197 | } | 192 | } |
198 | crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => { | 193 | crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => { |
199 | if let Some((future_trait, future_output)) = self | 194 | if let Some((future_trait, future_output)) = self |
@@ -215,7 +210,8 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
215 | let impl_bound = WhereClause::Implemented(TraitRef { | 210 | let impl_bound = WhereClause::Implemented(TraitRef { |
216 | trait_id: to_chalk_trait_id(future_trait), | 211 | trait_id: to_chalk_trait_id(future_trait), |
217 | // Self type as the first parameter. | 212 | // Self type as the first parameter. |
218 | substitution: Substitution::single( | 213 | substitution: Substitution::from1( |
214 | &Interner, | ||
219 | TyKind::BoundVar(BoundVar { | 215 | TyKind::BoundVar(BoundVar { |
220 | debruijn: DebruijnIndex::INNERMOST, | 216 | debruijn: DebruijnIndex::INNERMOST, |
221 | index: 0, | 217 | index: 0, |
@@ -227,7 +223,8 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
227 | alias: AliasTy::Projection(ProjectionTy { | 223 | alias: AliasTy::Projection(ProjectionTy { |
228 | associated_ty_id: to_assoc_type_id(future_output), | 224 | associated_ty_id: to_assoc_type_id(future_output), |
229 | // Self type as the first parameter. | 225 | // Self type as the first parameter. |
230 | substitution: Substitution::single( | 226 | substitution: Substitution::from1( |
227 | &Interner, | ||
231 | TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)) | 228 | TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)) |
232 | .intern(&Interner), | 229 | .intern(&Interner), |
233 | ), | 230 | ), |
@@ -237,25 +234,25 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
237 | .intern(&Interner), | 234 | .intern(&Interner), |
238 | }); | 235 | }); |
239 | let bound = OpaqueTyDatumBound { | 236 | let bound = OpaqueTyDatumBound { |
240 | bounds: make_binders( | 237 | bounds: make_only_type_binders( |
238 | 1, | ||
241 | vec![ | 239 | vec![ |
242 | wrap_in_empty_binders(impl_bound).to_chalk(self.db), | 240 | crate::wrap_empty_binders(impl_bound), |
243 | wrap_in_empty_binders(proj_bound).to_chalk(self.db), | 241 | crate::wrap_empty_binders(proj_bound), |
244 | ], | 242 | ], |
245 | 1, | ||
246 | ), | 243 | ), |
247 | where_clauses: make_binders(vec![], 0), | 244 | where_clauses: make_only_type_binders(0, vec![]), |
248 | }; | 245 | }; |
249 | // The opaque type has 1 parameter. | 246 | // The opaque type has 1 parameter. |
250 | make_binders(bound, 1) | 247 | make_only_type_binders(1, bound) |
251 | } else { | 248 | } else { |
252 | // If failed to find Symbol’s value as variable is void: Future::Output, return empty bounds as fallback. | 249 | // If failed to find Symbol’s value as variable is void: Future::Output, return empty bounds as fallback. |
253 | let bound = OpaqueTyDatumBound { | 250 | let bound = OpaqueTyDatumBound { |
254 | bounds: make_binders(vec![], 0), | 251 | bounds: make_only_type_binders(0, vec![]), |
255 | where_clauses: make_binders(vec![], 0), | 252 | where_clauses: make_only_type_binders(0, vec![]), |
256 | }; | 253 | }; |
257 | // The opaque type has 1 parameter. | 254 | // The opaque type has 1 parameter. |
258 | make_binders(bound, 1) | 255 | make_only_type_binders(1, bound) |
259 | } | 256 | } |
260 | } | 257 | } |
261 | }; | 258 | }; |
@@ -265,7 +262,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
265 | 262 | ||
266 | fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId<Interner>) -> chalk_ir::Ty<Interner> { | 263 | fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId<Interner>) -> chalk_ir::Ty<Interner> { |
267 | // FIXME: actually provide the hidden type; it is relevant for auto traits | 264 | // FIXME: actually provide the hidden type; it is relevant for auto traits |
268 | TyKind::Unknown.intern(&Interner).to_chalk(self.db) | 265 | TyKind::Error.intern(&Interner) |
269 | } | 266 | } |
270 | 267 | ||
271 | fn is_object_safe(&self, _trait_id: chalk_ir::TraitId<Interner>) -> bool { | 268 | fn is_object_safe(&self, _trait_id: chalk_ir::TraitId<Interner>) -> bool { |
@@ -286,33 +283,32 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
286 | _closure_id: chalk_ir::ClosureId<Interner>, | 283 | _closure_id: chalk_ir::ClosureId<Interner>, |
287 | substs: &chalk_ir::Substitution<Interner>, | 284 | substs: &chalk_ir::Substitution<Interner>, |
288 | ) -> chalk_ir::Binders<rust_ir::FnDefInputsAndOutputDatum<Interner>> { | 285 | ) -> chalk_ir::Binders<rust_ir::FnDefInputsAndOutputDatum<Interner>> { |
289 | let sig_ty: Ty = | 286 | let sig_ty = substs.at(&Interner, 0).assert_ty_ref(&Interner).clone(); |
290 | from_chalk(self.db, substs.at(&Interner, 0).assert_ty_ref(&Interner).clone()); | ||
291 | let sig = &sig_ty.callable_sig(self.db).expect("first closure param should be fn ptr"); | 287 | let sig = &sig_ty.callable_sig(self.db).expect("first closure param should be fn ptr"); |
292 | let io = rust_ir::FnDefInputsAndOutputDatum { | 288 | let io = rust_ir::FnDefInputsAndOutputDatum { |
293 | argument_types: sig.params().iter().map(|ty| ty.clone().to_chalk(self.db)).collect(), | 289 | argument_types: sig.params().iter().cloned().collect(), |
294 | return_type: sig.ret().clone().to_chalk(self.db), | 290 | return_type: sig.ret().clone(), |
295 | }; | 291 | }; |
296 | make_binders(io.shifted_in(&Interner), 0) | 292 | make_only_type_binders(0, io.shifted_in(&Interner)) |
297 | } | 293 | } |
298 | fn closure_upvars( | 294 | fn closure_upvars( |
299 | &self, | 295 | &self, |
300 | _closure_id: chalk_ir::ClosureId<Interner>, | 296 | _closure_id: chalk_ir::ClosureId<Interner>, |
301 | _substs: &chalk_ir::Substitution<Interner>, | 297 | _substs: &chalk_ir::Substitution<Interner>, |
302 | ) -> chalk_ir::Binders<chalk_ir::Ty<Interner>> { | 298 | ) -> chalk_ir::Binders<chalk_ir::Ty<Interner>> { |
303 | let ty = TyBuilder::unit().to_chalk(self.db); | 299 | let ty = TyBuilder::unit(); |
304 | make_binders(ty, 0) | 300 | make_only_type_binders(0, ty) |
305 | } | 301 | } |
306 | fn closure_fn_substitution( | 302 | fn closure_fn_substitution( |
307 | &self, | 303 | &self, |
308 | _closure_id: chalk_ir::ClosureId<Interner>, | 304 | _closure_id: chalk_ir::ClosureId<Interner>, |
309 | _substs: &chalk_ir::Substitution<Interner>, | 305 | _substs: &chalk_ir::Substitution<Interner>, |
310 | ) -> chalk_ir::Substitution<Interner> { | 306 | ) -> chalk_ir::Substitution<Interner> { |
311 | Substitution::empty(&Interner).to_chalk(self.db) | 307 | Substitution::empty(&Interner) |
312 | } | 308 | } |
313 | 309 | ||
314 | fn trait_name(&self, trait_id: chalk_ir::TraitId<Interner>) -> String { | 310 | fn trait_name(&self, trait_id: chalk_ir::TraitId<Interner>) -> String { |
315 | let id = from_chalk(self.db, trait_id); | 311 | let id = from_chalk_trait_id(trait_id); |
316 | self.db.trait_data(id).name.to_string() | 312 | self.db.trait_data(id).name.to_string() |
317 | } | 313 | } |
318 | fn adt_name(&self, chalk_ir::AdtId(adt_id): AdtId) -> String { | 314 | fn adt_name(&self, chalk_ir::AdtId(adt_id): AdtId) -> String { |
@@ -403,10 +399,10 @@ pub(crate) fn associated_ty_data_query( | |||
403 | let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars); | 399 | let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars); |
404 | let bound_data = rust_ir::AssociatedTyDatumBound { bounds, where_clauses }; | 400 | let bound_data = rust_ir::AssociatedTyDatumBound { bounds, where_clauses }; |
405 | let datum = AssociatedTyDatum { | 401 | let datum = AssociatedTyDatum { |
406 | trait_id: trait_.to_chalk(db), | 402 | trait_id: to_chalk_trait_id(trait_), |
407 | id, | 403 | id, |
408 | name: type_alias, | 404 | name: type_alias, |
409 | binders: make_binders(bound_data, generic_params.len()), | 405 | binders: make_only_type_binders(generic_params.len(), bound_data), |
410 | }; | 406 | }; |
411 | Arc::new(datum) | 407 | Arc::new(datum) |
412 | } | 408 | } |
@@ -417,7 +413,7 @@ pub(crate) fn trait_datum_query( | |||
417 | trait_id: TraitId, | 413 | trait_id: TraitId, |
418 | ) -> Arc<TraitDatum> { | 414 | ) -> Arc<TraitDatum> { |
419 | debug!("trait_datum {:?}", trait_id); | 415 | debug!("trait_datum {:?}", trait_id); |
420 | let trait_: hir_def::TraitId = from_chalk(db, trait_id); | 416 | let trait_ = from_chalk_trait_id(trait_id); |
421 | let trait_data = db.trait_data(trait_); | 417 | let trait_data = db.trait_data(trait_); |
422 | debug!("trait {:?} = {:?}", trait_id, trait_data.name); | 418 | debug!("trait {:?} = {:?}", trait_id, trait_data.name); |
423 | let generic_params = generics(db.upcast(), trait_.into()); | 419 | let generic_params = generics(db.upcast(), trait_.into()); |
@@ -439,7 +435,7 @@ pub(crate) fn trait_datum_query( | |||
439 | lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name)); | 435 | lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name)); |
440 | let trait_datum = TraitDatum { | 436 | let trait_datum = TraitDatum { |
441 | id: trait_id, | 437 | id: trait_id, |
442 | binders: make_binders(trait_datum_bound, bound_vars.len(&Interner)), | 438 | binders: make_only_type_binders(bound_vars.len(&Interner), trait_datum_bound), |
443 | flags, | 439 | flags, |
444 | associated_ty_ids, | 440 | associated_ty_ids, |
445 | well_known, | 441 | well_known, |
@@ -508,7 +504,7 @@ pub(crate) fn struct_datum_query( | |||
508 | // FIXME set ADT kind | 504 | // FIXME set ADT kind |
509 | kind: rust_ir::AdtKind::Struct, | 505 | kind: rust_ir::AdtKind::Struct, |
510 | id: struct_id, | 506 | id: struct_id, |
511 | binders: make_binders(struct_datum_bound, num_params), | 507 | binders: make_only_type_binders(num_params, struct_datum_bound), |
512 | flags, | 508 | flags, |
513 | }; | 509 | }; |
514 | Arc::new(struct_datum) | 510 | Arc::new(struct_datum) |
@@ -535,7 +531,8 @@ fn impl_def_datum( | |||
535 | .impl_trait(impl_id) | 531 | .impl_trait(impl_id) |
536 | // ImplIds for impls where the trait ref can't be resolved should never reach Chalk | 532 | // ImplIds for impls where the trait ref can't be resolved should never reach Chalk |
537 | .expect("invalid impl passed to Chalk") | 533 | .expect("invalid impl passed to Chalk") |
538 | .value; | 534 | .into_value_and_skipped_binders() |
535 | .0; | ||
539 | let impl_data = db.impl_data(impl_id); | 536 | let impl_data = db.impl_data(impl_id); |
540 | 537 | ||
541 | let generic_params = generics(db.upcast(), impl_id.into()); | 538 | let generic_params = generics(db.upcast(), impl_id.into()); |
@@ -555,7 +552,6 @@ fn impl_def_datum( | |||
555 | trait_ref.display(db), | 552 | trait_ref.display(db), |
556 | where_clauses | 553 | where_clauses |
557 | ); | 554 | ); |
558 | let trait_ref = trait_ref.to_chalk(db); | ||
559 | 555 | ||
560 | let polarity = if negative { rust_ir::Polarity::Negative } else { rust_ir::Polarity::Positive }; | 556 | let polarity = if negative { rust_ir::Polarity::Negative } else { rust_ir::Polarity::Positive }; |
561 | 557 | ||
@@ -577,7 +573,7 @@ fn impl_def_datum( | |||
577 | .collect(); | 573 | .collect(); |
578 | debug!("impl_datum: {:?}", impl_datum_bound); | 574 | debug!("impl_datum: {:?}", impl_datum_bound); |
579 | let impl_datum = ImplDatum { | 575 | let impl_datum = ImplDatum { |
580 | binders: make_binders(impl_datum_bound, bound_vars.len(&Interner)), | 576 | binders: make_only_type_binders(bound_vars.len(&Interner), impl_datum_bound), |
581 | impl_type, | 577 | impl_type, |
582 | polarity, | 578 | polarity, |
583 | associated_ty_value_ids, | 579 | associated_ty_value_ids, |
@@ -605,18 +601,22 @@ fn type_alias_associated_ty_value( | |||
605 | _ => panic!("assoc ty value should be in impl"), | 601 | _ => panic!("assoc ty value should be in impl"), |
606 | }; | 602 | }; |
607 | 603 | ||
608 | let trait_ref = db.impl_trait(impl_id).expect("assoc ty value should not exist").value; // we don't return any assoc ty values if the impl'd trait can't be resolved | 604 | let trait_ref = db |
605 | .impl_trait(impl_id) | ||
606 | .expect("assoc ty value should not exist") | ||
607 | .into_value_and_skipped_binders() | ||
608 | .0; // we don't return any assoc ty values if the impl'd trait can't be resolved | ||
609 | 609 | ||
610 | let assoc_ty = db | 610 | let assoc_ty = db |
611 | .trait_data(trait_ref.hir_trait_id()) | 611 | .trait_data(trait_ref.hir_trait_id()) |
612 | .associated_type_by_name(&type_alias_data.name) | 612 | .associated_type_by_name(&type_alias_data.name) |
613 | .expect("assoc ty value should not exist"); // validated when building the impl data as well | 613 | .expect("assoc ty value should not exist"); // validated when building the impl data as well |
614 | let ty = db.ty(type_alias.into()); | 614 | let (ty, binders) = db.ty(type_alias.into()).into_value_and_skipped_binders(); |
615 | let value_bound = rust_ir::AssociatedTyValueBound { ty: ty.value.to_chalk(db) }; | 615 | let value_bound = rust_ir::AssociatedTyValueBound { ty }; |
616 | let value = rust_ir::AssociatedTyValue { | 616 | let value = rust_ir::AssociatedTyValue { |
617 | impl_id: impl_id.to_chalk(db), | 617 | impl_id: impl_id.to_chalk(db), |
618 | associated_ty_id: to_assoc_type_id(assoc_ty), | 618 | associated_ty_id: to_assoc_type_id(assoc_ty), |
619 | value: make_binders(value_bound, ty.num_binders), | 619 | value: chalk_ir::Binders::new(binders, value_bound), |
620 | }; | 620 | }; |
621 | Arc::new(value) | 621 | Arc::new(value) |
622 | } | 622 | } |
@@ -628,34 +628,25 @@ pub(crate) fn fn_def_datum_query( | |||
628 | ) -> Arc<FnDefDatum> { | 628 | ) -> Arc<FnDefDatum> { |
629 | let callable_def: CallableDefId = from_chalk(db, fn_def_id); | 629 | let callable_def: CallableDefId = from_chalk(db, fn_def_id); |
630 | let generic_params = generics(db.upcast(), callable_def.into()); | 630 | let generic_params = generics(db.upcast(), callable_def.into()); |
631 | let sig = db.callable_item_signature(callable_def); | 631 | let (sig, binders) = db.callable_item_signature(callable_def).into_value_and_skipped_binders(); |
632 | let bound_vars = generic_params.bound_vars_subst(DebruijnIndex::INNERMOST); | 632 | let bound_vars = generic_params.bound_vars_subst(DebruijnIndex::INNERMOST); |
633 | let where_clauses = convert_where_clauses(db, callable_def.into(), &bound_vars); | 633 | let where_clauses = convert_where_clauses(db, callable_def.into(), &bound_vars); |
634 | let bound = rust_ir::FnDefDatumBound { | 634 | let bound = rust_ir::FnDefDatumBound { |
635 | // Note: Chalk doesn't actually use this information yet as far as I am aware, but we provide it anyway | 635 | // Note: Chalk doesn't actually use this information yet as far as I am aware, but we provide it anyway |
636 | inputs_and_output: make_binders( | 636 | inputs_and_output: make_only_type_binders( |
637 | 0, | ||
637 | rust_ir::FnDefInputsAndOutputDatum { | 638 | rust_ir::FnDefInputsAndOutputDatum { |
638 | argument_types: sig | 639 | argument_types: sig.params().iter().cloned().collect(), |
639 | .value | 640 | return_type: sig.ret().clone(), |
640 | .params() | ||
641 | .iter() | ||
642 | .map(|ty| ty.clone().to_chalk(db)) | ||
643 | .collect(), | ||
644 | return_type: sig.value.ret().clone().to_chalk(db), | ||
645 | } | 641 | } |
646 | .shifted_in(&Interner), | 642 | .shifted_in(&Interner), |
647 | 0, | ||
648 | ), | 643 | ), |
649 | where_clauses, | 644 | where_clauses, |
650 | }; | 645 | }; |
651 | let datum = FnDefDatum { | 646 | let datum = FnDefDatum { |
652 | id: fn_def_id, | 647 | id: fn_def_id, |
653 | sig: chalk_ir::FnSig { | 648 | sig: chalk_ir::FnSig { abi: (), safety: chalk_ir::Safety::Safe, variadic: sig.is_varargs }, |
654 | abi: (), | 649 | binders: chalk_ir::Binders::new(binders, bound), |
655 | safety: chalk_ir::Safety::Safe, | ||
656 | variadic: sig.value.is_varargs, | ||
657 | }, | ||
658 | binders: make_binders(bound, sig.num_binders), | ||
659 | }; | 650 | }; |
660 | Arc::new(datum) | 651 | Arc::new(datum) |
661 | } | 652 | } |
@@ -685,42 +676,65 @@ pub(crate) fn adt_variance_query( | |||
685 | ) | 676 | ) |
686 | } | 677 | } |
687 | 678 | ||
688 | impl From<FnDefId> for crate::db::InternedCallableDefId { | 679 | pub(super) fn convert_where_clauses( |
689 | fn from(fn_def_id: FnDefId) -> Self { | 680 | db: &dyn HirDatabase, |
690 | InternKey::from_intern_id(fn_def_id.0) | 681 | def: GenericDefId, |
691 | } | 682 | substs: &Substitution, |
692 | } | 683 | ) -> Vec<chalk_ir::QuantifiedWhereClause<Interner>> { |
693 | 684 | let generic_predicates = db.generic_predicates(def); | |
694 | impl From<crate::db::InternedCallableDefId> for FnDefId { | 685 | let mut result = Vec::with_capacity(generic_predicates.len()); |
695 | fn from(callable_def_id: crate::db::InternedCallableDefId) -> Self { | 686 | for pred in generic_predicates.iter() { |
696 | chalk_ir::FnDefId(callable_def_id.as_intern_id()) | 687 | result.push(pred.clone().substitute(&Interner, substs)); |
697 | } | 688 | } |
698 | } | 689 | result |
699 | |||
700 | impl From<OpaqueTyId> for crate::db::InternedOpaqueTyId { | ||
701 | fn from(id: OpaqueTyId) -> Self { | ||
702 | InternKey::from_intern_id(id.0) | ||
703 | } | ||
704 | } | ||
705 | |||
706 | impl From<crate::db::InternedOpaqueTyId> for OpaqueTyId { | ||
707 | fn from(id: crate::db::InternedOpaqueTyId) -> Self { | ||
708 | chalk_ir::OpaqueTyId(id.as_intern_id()) | ||
709 | } | ||
710 | } | ||
711 | |||
712 | impl From<chalk_ir::ClosureId<Interner>> for crate::db::InternedClosureId { | ||
713 | fn from(id: chalk_ir::ClosureId<Interner>) -> Self { | ||
714 | Self::from_intern_id(id.0) | ||
715 | } | ||
716 | } | 690 | } |
717 | 691 | ||
718 | impl From<crate::db::InternedClosureId> for chalk_ir::ClosureId<Interner> { | 692 | pub(super) fn generic_predicate_to_inline_bound( |
719 | fn from(id: crate::db::InternedClosureId) -> Self { | 693 | db: &dyn HirDatabase, |
720 | chalk_ir::ClosureId(id.as_intern_id()) | 694 | pred: &QuantifiedWhereClause, |
695 | self_ty: &Ty, | ||
696 | ) -> Option<chalk_ir::Binders<rust_ir::InlineBound<Interner>>> { | ||
697 | // An InlineBound is like a GenericPredicate, except the self type is left out. | ||
698 | // We don't have a special type for this, but Chalk does. | ||
699 | let self_ty_shifted_in = self_ty.clone().shifted_in_from(&Interner, DebruijnIndex::ONE); | ||
700 | let (pred, binders) = pred.as_ref().into_value_and_skipped_binders(); | ||
701 | match pred { | ||
702 | WhereClause::Implemented(trait_ref) => { | ||
703 | if trait_ref.self_type_parameter(&Interner) != self_ty_shifted_in { | ||
704 | // we can only convert predicates back to type bounds if they | ||
705 | // have the expected self type | ||
706 | return None; | ||
707 | } | ||
708 | let args_no_self = trait_ref.substitution.as_slice(&Interner)[1..] | ||
709 | .iter() | ||
710 | .map(|ty| ty.clone().cast(&Interner)) | ||
711 | .collect(); | ||
712 | let trait_bound = rust_ir::TraitBound { trait_id: trait_ref.trait_id, args_no_self }; | ||
713 | Some(chalk_ir::Binders::new(binders, rust_ir::InlineBound::TraitBound(trait_bound))) | ||
714 | } | ||
715 | WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => { | ||
716 | if projection_ty.self_type_parameter(&Interner) != self_ty_shifted_in { | ||
717 | return None; | ||
718 | } | ||
719 | let trait_ = projection_ty.trait_(db); | ||
720 | let args_no_self = projection_ty.substitution.as_slice(&Interner)[1..] | ||
721 | .iter() | ||
722 | .map(|ty| ty.clone().cast(&Interner)) | ||
723 | .collect(); | ||
724 | let alias_eq_bound = rust_ir::AliasEqBound { | ||
725 | value: ty.clone(), | ||
726 | trait_bound: rust_ir::TraitBound { | ||
727 | trait_id: to_chalk_trait_id(trait_), | ||
728 | args_no_self, | ||
729 | }, | ||
730 | associated_ty_id: projection_ty.associated_ty_id, | ||
731 | parameters: Vec::new(), // FIXME we don't support generic associated types yet | ||
732 | }; | ||
733 | Some(chalk_ir::Binders::new( | ||
734 | binders, | ||
735 | rust_ir::InlineBound::AliasEqBound(alias_eq_bound), | ||
736 | )) | ||
737 | } | ||
738 | _ => None, | ||
721 | } | 739 | } |
722 | } | 740 | } |
723 | |||
724 | fn wrap_in_empty_binders<T: crate::TypeWalk>(value: T) -> crate::Binders<T> { | ||
725 | crate::Binders::wrap_empty(value) | ||
726 | } | ||
diff --git a/crates/hir_ty/src/chalk_ext.rs b/crates/hir_ty/src/chalk_ext.rs index b7463366b..8c4542956 100644 --- a/crates/hir_ty/src/chalk_ext.rs +++ b/crates/hir_ty/src/chalk_ext.rs | |||
@@ -1,13 +1,305 @@ | |||
1 | //! Various extensions traits for Chalk types. | 1 | //! Various extensions traits for Chalk types. |
2 | 2 | ||
3 | use crate::{Interner, Ty, TyKind}; | 3 | use chalk_ir::Mutability; |
4 | use hir_def::{ | ||
5 | type_ref::Rawness, AssocContainerId, FunctionId, GenericDefId, HasModule, Lookup, TraitId, | ||
6 | }; | ||
7 | |||
8 | use crate::{ | ||
9 | db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, | ||
10 | from_placeholder_idx, to_chalk_trait_id, AdtId, AliasEq, AliasTy, Binders, CallableDefId, | ||
11 | CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy, QuantifiedWhereClause, | ||
12 | Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause, | ||
13 | }; | ||
4 | 14 | ||
5 | pub trait TyExt { | 15 | pub trait TyExt { |
6 | fn is_unit(&self) -> bool; | 16 | fn is_unit(&self) -> bool; |
17 | fn is_never(&self) -> bool; | ||
18 | fn is_unknown(&self) -> bool; | ||
19 | |||
20 | fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>; | ||
21 | fn as_tuple(&self) -> Option<&Substitution>; | ||
22 | fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>; | ||
23 | fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>; | ||
24 | fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>; | ||
25 | fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId>; | ||
26 | |||
27 | fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId>; | ||
28 | fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig>; | ||
29 | |||
30 | fn strip_references(&self) -> &Ty; | ||
31 | |||
32 | /// If this is a `dyn Trait`, returns that trait. | ||
33 | fn dyn_trait(&self) -> Option<TraitId>; | ||
34 | |||
35 | fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>>; | ||
36 | fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId>; | ||
37 | |||
38 | /// FIXME: Get rid of this, it's not a good abstraction | ||
39 | fn equals_ctor(&self, other: &Ty) -> bool; | ||
7 | } | 40 | } |
8 | 41 | ||
9 | impl TyExt for Ty { | 42 | impl TyExt for Ty { |
10 | fn is_unit(&self) -> bool { | 43 | fn is_unit(&self) -> bool { |
11 | matches!(self.kind(&Interner), TyKind::Tuple(0, _)) | 44 | matches!(self.kind(&Interner), TyKind::Tuple(0, _)) |
12 | } | 45 | } |
46 | |||
47 | fn is_never(&self) -> bool { | ||
48 | matches!(self.kind(&Interner), TyKind::Never) | ||
49 | } | ||
50 | |||
51 | fn is_unknown(&self) -> bool { | ||
52 | matches!(self.kind(&Interner), TyKind::Error) | ||
53 | } | ||
54 | |||
55 | fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> { | ||
56 | match self.kind(&Interner) { | ||
57 | TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)), | ||
58 | _ => None, | ||
59 | } | ||
60 | } | ||
61 | |||
62 | fn as_tuple(&self) -> Option<&Substitution> { | ||
63 | match self.kind(&Interner) { | ||
64 | TyKind::Tuple(_, substs) => Some(substs), | ||
65 | _ => None, | ||
66 | } | ||
67 | } | ||
68 | |||
69 | fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId> { | ||
70 | if let Some(CallableDefId::FunctionId(func)) = self.callable_def(db) { | ||
71 | Some(func) | ||
72 | } else { | ||
73 | None | ||
74 | } | ||
75 | } | ||
76 | fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)> { | ||
77 | match self.kind(&Interner) { | ||
78 | TyKind::Ref(mutability, lifetime, ty) => Some((ty, lifetime.clone(), *mutability)), | ||
79 | _ => None, | ||
80 | } | ||
81 | } | ||
82 | |||
83 | fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> { | ||
84 | match self.kind(&Interner) { | ||
85 | TyKind::Ref(mutability, _, ty) => Some((ty, Rawness::Ref, *mutability)), | ||
86 | TyKind::Raw(mutability, ty) => Some((ty, Rawness::RawPtr, *mutability)), | ||
87 | _ => None, | ||
88 | } | ||
89 | } | ||
90 | |||
91 | fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId> { | ||
92 | match *self.kind(&Interner) { | ||
93 | TyKind::Adt(AdtId(adt), ..) => Some(adt.into()), | ||
94 | TyKind::FnDef(callable, ..) => { | ||
95 | Some(db.lookup_intern_callable_def(callable.into()).into()) | ||
96 | } | ||
97 | TyKind::AssociatedType(type_alias, ..) => Some(from_assoc_type_id(type_alias).into()), | ||
98 | TyKind::Foreign(type_alias, ..) => Some(from_foreign_def_id(type_alias).into()), | ||
99 | _ => None, | ||
100 | } | ||
101 | } | ||
102 | |||
103 | fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId> { | ||
104 | match self.kind(&Interner) { | ||
105 | &TyKind::FnDef(def, ..) => Some(db.lookup_intern_callable_def(def.into())), | ||
106 | _ => None, | ||
107 | } | ||
108 | } | ||
109 | |||
110 | fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig> { | ||
111 | match self.kind(&Interner) { | ||
112 | TyKind::Function(fn_ptr) => Some(CallableSig::from_fn_ptr(fn_ptr)), | ||
113 | TyKind::FnDef(def, parameters) => { | ||
114 | let callable_def = db.lookup_intern_callable_def((*def).into()); | ||
115 | let sig = db.callable_item_signature(callable_def); | ||
116 | Some(sig.substitute(&Interner, ¶meters)) | ||
117 | } | ||
118 | TyKind::Closure(.., substs) => { | ||
119 | let sig_param = substs.at(&Interner, 0).assert_ty_ref(&Interner); | ||
120 | sig_param.callable_sig(db) | ||
121 | } | ||
122 | _ => None, | ||
123 | } | ||
124 | } | ||
125 | |||
126 | fn dyn_trait(&self) -> Option<TraitId> { | ||
127 | let trait_ref = match self.kind(&Interner) { | ||
128 | TyKind::Dyn(dyn_ty) => dyn_ty.bounds.skip_binders().interned().get(0).and_then(|b| { | ||
129 | match b.skip_binders() { | ||
130 | WhereClause::Implemented(trait_ref) => Some(trait_ref), | ||
131 | _ => None, | ||
132 | } | ||
133 | }), | ||
134 | _ => None, | ||
135 | }?; | ||
136 | Some(from_chalk_trait_id(trait_ref.trait_id)) | ||
137 | } | ||
138 | |||
139 | fn strip_references(&self) -> &Ty { | ||
140 | let mut t: &Ty = self; | ||
141 | while let TyKind::Ref(_mutability, _lifetime, ty) = t.kind(&Interner) { | ||
142 | t = ty; | ||
143 | } | ||
144 | t | ||
145 | } | ||
146 | |||
147 | fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>> { | ||
148 | match self.kind(&Interner) { | ||
149 | TyKind::OpaqueType(opaque_ty_id, ..) => { | ||
150 | match db.lookup_intern_impl_trait_id((*opaque_ty_id).into()) { | ||
151 | ImplTraitId::AsyncBlockTypeImplTrait(def, _expr) => { | ||
152 | let krate = def.module(db.upcast()).krate(); | ||
153 | if let Some(future_trait) = db | ||
154 | .lang_item(krate, "future_trait".into()) | ||
155 | .and_then(|item| item.as_trait()) | ||
156 | { | ||
157 | // This is only used by type walking. | ||
158 | // Parameters will be walked outside, and projection predicate is not used. | ||
159 | // So just provide the Future trait. | ||
160 | let impl_bound = Binders::empty( | ||
161 | &Interner, | ||
162 | WhereClause::Implemented(TraitRef { | ||
163 | trait_id: to_chalk_trait_id(future_trait), | ||
164 | substitution: Substitution::empty(&Interner), | ||
165 | }), | ||
166 | ); | ||
167 | Some(vec![impl_bound]) | ||
168 | } else { | ||
169 | None | ||
170 | } | ||
171 | } | ||
172 | ImplTraitId::ReturnTypeImplTrait(..) => None, | ||
173 | } | ||
174 | } | ||
175 | TyKind::Alias(AliasTy::Opaque(opaque_ty)) => { | ||
176 | let predicates = match db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into()) | ||
177 | { | ||
178 | ImplTraitId::ReturnTypeImplTrait(func, idx) => { | ||
179 | db.return_type_impl_traits(func).map(|it| { | ||
180 | let data = (*it) | ||
181 | .as_ref() | ||
182 | .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); | ||
183 | data.substitute(&Interner, &opaque_ty.substitution) | ||
184 | }) | ||
185 | } | ||
186 | // It always has an parameter for Future::Output type. | ||
187 | ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(), | ||
188 | }; | ||
189 | |||
190 | predicates.map(|it| it.into_value_and_skipped_binders().0) | ||
191 | } | ||
192 | TyKind::Placeholder(idx) => { | ||
193 | let id = from_placeholder_idx(db, *idx); | ||
194 | let generic_params = db.generic_params(id.parent); | ||
195 | let param_data = &generic_params.types[id.local_id]; | ||
196 | match param_data.provenance { | ||
197 | hir_def::generics::TypeParamProvenance::ArgumentImplTrait => { | ||
198 | let substs = TyBuilder::type_params_subst(db, id.parent); | ||
199 | let predicates = db | ||
200 | .generic_predicates(id.parent) | ||
201 | .into_iter() | ||
202 | .map(|pred| pred.clone().substitute(&Interner, &substs)) | ||
203 | .filter(|wc| match &wc.skip_binders() { | ||
204 | WhereClause::Implemented(tr) => { | ||
205 | &tr.self_type_parameter(&Interner) == self | ||
206 | } | ||
207 | WhereClause::AliasEq(AliasEq { | ||
208 | alias: AliasTy::Projection(proj), | ||
209 | ty: _, | ||
210 | }) => &proj.self_type_parameter(&Interner) == self, | ||
211 | _ => false, | ||
212 | }) | ||
213 | .collect::<Vec<_>>(); | ||
214 | |||
215 | Some(predicates) | ||
216 | } | ||
217 | _ => None, | ||
218 | } | ||
219 | } | ||
220 | _ => None, | ||
221 | } | ||
222 | } | ||
223 | |||
224 | fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId> { | ||
225 | match self.kind(&Interner) { | ||
226 | TyKind::AssociatedType(id, ..) => { | ||
227 | match from_assoc_type_id(*id).lookup(db.upcast()).container { | ||
228 | AssocContainerId::TraitId(trait_id) => Some(trait_id), | ||
229 | _ => None, | ||
230 | } | ||
231 | } | ||
232 | TyKind::Alias(AliasTy::Projection(projection_ty)) => { | ||
233 | match from_assoc_type_id(projection_ty.associated_ty_id) | ||
234 | .lookup(db.upcast()) | ||
235 | .container | ||
236 | { | ||
237 | AssocContainerId::TraitId(trait_id) => Some(trait_id), | ||
238 | _ => None, | ||
239 | } | ||
240 | } | ||
241 | _ => None, | ||
242 | } | ||
243 | } | ||
244 | |||
245 | fn equals_ctor(&self, other: &Ty) -> bool { | ||
246 | match (self.kind(&Interner), other.kind(&Interner)) { | ||
247 | (TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2, | ||
248 | (TyKind::Slice(_), TyKind::Slice(_)) | (TyKind::Array(_, _), TyKind::Array(_, _)) => { | ||
249 | true | ||
250 | } | ||
251 | (TyKind::FnDef(def_id, ..), TyKind::FnDef(def_id2, ..)) => def_id == def_id2, | ||
252 | (TyKind::OpaqueType(ty_id, ..), TyKind::OpaqueType(ty_id2, ..)) => ty_id == ty_id2, | ||
253 | (TyKind::AssociatedType(ty_id, ..), TyKind::AssociatedType(ty_id2, ..)) => { | ||
254 | ty_id == ty_id2 | ||
255 | } | ||
256 | (TyKind::Foreign(ty_id, ..), TyKind::Foreign(ty_id2, ..)) => ty_id == ty_id2, | ||
257 | (TyKind::Closure(id1, _), TyKind::Closure(id2, _)) => id1 == id2, | ||
258 | (TyKind::Ref(mutability, ..), TyKind::Ref(mutability2, ..)) | ||
259 | | (TyKind::Raw(mutability, ..), TyKind::Raw(mutability2, ..)) => { | ||
260 | mutability == mutability2 | ||
261 | } | ||
262 | ( | ||
263 | TyKind::Function(FnPointer { num_binders, sig, .. }), | ||
264 | TyKind::Function(FnPointer { num_binders: num_binders2, sig: sig2, .. }), | ||
265 | ) => num_binders == num_binders2 && sig == sig2, | ||
266 | (TyKind::Tuple(cardinality, _), TyKind::Tuple(cardinality2, _)) => { | ||
267 | cardinality == cardinality2 | ||
268 | } | ||
269 | (TyKind::Str, TyKind::Str) | (TyKind::Never, TyKind::Never) => true, | ||
270 | (TyKind::Scalar(scalar), TyKind::Scalar(scalar2)) => scalar == scalar2, | ||
271 | _ => false, | ||
272 | } | ||
273 | } | ||
274 | } | ||
275 | |||
276 | pub trait ProjectionTyExt { | ||
277 | fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef; | ||
278 | fn trait_(&self, db: &dyn HirDatabase) -> TraitId; | ||
279 | } | ||
280 | |||
281 | impl ProjectionTyExt for ProjectionTy { | ||
282 | fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef { | ||
283 | TraitRef { | ||
284 | trait_id: to_chalk_trait_id(self.trait_(db)), | ||
285 | substitution: self.substitution.clone(), | ||
286 | } | ||
287 | } | ||
288 | |||
289 | fn trait_(&self, db: &dyn HirDatabase) -> TraitId { | ||
290 | match from_assoc_type_id(self.associated_ty_id).lookup(db.upcast()).container { | ||
291 | AssocContainerId::TraitId(it) => it, | ||
292 | _ => panic!("projection ty without parent trait"), | ||
293 | } | ||
294 | } | ||
295 | } | ||
296 | |||
297 | pub trait TraitRefExt { | ||
298 | fn hir_trait_id(&self) -> TraitId; | ||
299 | } | ||
300 | |||
301 | impl TraitRefExt for TraitRef { | ||
302 | fn hir_trait_id(&self) -> TraitId { | ||
303 | from_chalk_trait_id(self.trait_id) | ||
304 | } | ||
13 | } | 305 | } |
diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs index 58e4247c6..cf67d4266 100644 --- a/crates/hir_ty/src/db.rs +++ b/crates/hir_ty/src/db.rs | |||
@@ -1,18 +1,19 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! The home of `HirDatabase`, which is the Salsa database containing all the |
2 | //! type inference-related queries. | ||
2 | 3 | ||
3 | use std::sync::Arc; | 4 | use std::sync::Arc; |
4 | 5 | ||
5 | use base_db::{impl_intern_key, salsa, CrateId, Upcast}; | 6 | use base_db::{impl_intern_key, salsa, CrateId, Upcast}; |
6 | use hir_def::{ | 7 | use hir_def::{ |
7 | db::DefDatabase, expr::ExprId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId, ImplId, | 8 | db::DefDatabase, expr::ExprId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId, ImplId, |
8 | LocalFieldId, TypeParamId, VariantId, | 9 | LifetimeParamId, LocalFieldId, TypeParamId, VariantId, |
9 | }; | 10 | }; |
10 | use la_arena::ArenaMap; | 11 | use la_arena::ArenaMap; |
11 | 12 | ||
12 | use crate::{ | 13 | use crate::{ |
14 | chalk_db, | ||
13 | method_resolution::{InherentImpls, TraitImpls}, | 15 | method_resolution::{InherentImpls, TraitImpls}, |
14 | traits::chalk, | 16 | Binders, CallableDefId, FnDefId, ImplTraitId, InferenceResult, Interner, PolyFnSig, |
15 | Binders, CallableDefId, FnDefId, ImplTraitId, InferenceResult, PolyFnSig, | ||
16 | QuantifiedWhereClause, ReturnTypeImplTraits, TraitRef, Ty, TyDefId, ValueTyDefId, | 17 | QuantifiedWhereClause, ReturnTypeImplTraits, TraitRef, Ty, TyDefId, ValueTyDefId, |
17 | }; | 18 | }; |
18 | use hir_expand::name::Name; | 19 | use hir_expand::name::Name; |
@@ -86,51 +87,68 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { | |||
86 | #[salsa::interned] | 87 | #[salsa::interned] |
87 | fn intern_type_param_id(&self, param_id: TypeParamId) -> InternedTypeParamId; | 88 | fn intern_type_param_id(&self, param_id: TypeParamId) -> InternedTypeParamId; |
88 | #[salsa::interned] | 89 | #[salsa::interned] |
90 | fn intern_lifetime_param_id(&self, param_id: LifetimeParamId) -> InternedLifetimeParamId; | ||
91 | #[salsa::interned] | ||
92 | fn intern_const_param_id(&self, param_id: ConstParamId) -> InternedConstParamId; | ||
93 | #[salsa::interned] | ||
89 | fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId; | 94 | fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId; |
90 | #[salsa::interned] | 95 | #[salsa::interned] |
91 | fn intern_closure(&self, id: (DefWithBodyId, ExprId)) -> InternedClosureId; | 96 | fn intern_closure(&self, id: (DefWithBodyId, ExprId)) -> InternedClosureId; |
92 | 97 | ||
93 | #[salsa::invoke(chalk::associated_ty_data_query)] | 98 | #[salsa::invoke(chalk_db::associated_ty_data_query)] |
94 | fn associated_ty_data(&self, id: chalk::AssocTypeId) -> Arc<chalk::AssociatedTyDatum>; | 99 | fn associated_ty_data(&self, id: chalk_db::AssocTypeId) -> Arc<chalk_db::AssociatedTyDatum>; |
95 | 100 | ||
96 | #[salsa::invoke(chalk::trait_datum_query)] | 101 | #[salsa::invoke(chalk_db::trait_datum_query)] |
97 | fn trait_datum(&self, krate: CrateId, trait_id: chalk::TraitId) -> Arc<chalk::TraitDatum>; | 102 | fn trait_datum(&self, krate: CrateId, trait_id: chalk_db::TraitId) |
103 | -> Arc<chalk_db::TraitDatum>; | ||
98 | 104 | ||
99 | #[salsa::invoke(chalk::struct_datum_query)] | 105 | #[salsa::invoke(chalk_db::struct_datum_query)] |
100 | fn struct_datum(&self, krate: CrateId, struct_id: chalk::AdtId) -> Arc<chalk::StructDatum>; | 106 | fn struct_datum( |
107 | &self, | ||
108 | krate: CrateId, | ||
109 | struct_id: chalk_db::AdtId, | ||
110 | ) -> Arc<chalk_db::StructDatum>; | ||
101 | 111 | ||
102 | #[salsa::invoke(crate::traits::chalk::impl_datum_query)] | 112 | #[salsa::invoke(chalk_db::impl_datum_query)] |
103 | fn impl_datum(&self, krate: CrateId, impl_id: chalk::ImplId) -> Arc<chalk::ImplDatum>; | 113 | fn impl_datum(&self, krate: CrateId, impl_id: chalk_db::ImplId) -> Arc<chalk_db::ImplDatum>; |
104 | 114 | ||
105 | #[salsa::invoke(crate::traits::chalk::fn_def_datum_query)] | 115 | #[salsa::invoke(chalk_db::fn_def_datum_query)] |
106 | fn fn_def_datum(&self, krate: CrateId, fn_def_id: FnDefId) -> Arc<chalk::FnDefDatum>; | 116 | fn fn_def_datum(&self, krate: CrateId, fn_def_id: FnDefId) -> Arc<chalk_db::FnDefDatum>; |
107 | 117 | ||
108 | #[salsa::invoke(crate::traits::chalk::fn_def_variance_query)] | 118 | #[salsa::invoke(chalk_db::fn_def_variance_query)] |
109 | fn fn_def_variance(&self, krate: CrateId, fn_def_id: FnDefId) -> chalk::Variances; | 119 | fn fn_def_variance(&self, krate: CrateId, fn_def_id: FnDefId) -> chalk_db::Variances; |
110 | 120 | ||
111 | #[salsa::invoke(crate::traits::chalk::adt_variance_query)] | 121 | #[salsa::invoke(chalk_db::adt_variance_query)] |
112 | fn adt_variance(&self, krate: CrateId, adt_id: chalk::AdtId) -> chalk::Variances; | 122 | fn adt_variance(&self, krate: CrateId, adt_id: chalk_db::AdtId) -> chalk_db::Variances; |
113 | 123 | ||
114 | #[salsa::invoke(crate::traits::chalk::associated_ty_value_query)] | 124 | #[salsa::invoke(chalk_db::associated_ty_value_query)] |
115 | fn associated_ty_value( | 125 | fn associated_ty_value( |
116 | &self, | 126 | &self, |
117 | krate: CrateId, | 127 | krate: CrateId, |
118 | id: chalk::AssociatedTyValueId, | 128 | id: chalk_db::AssociatedTyValueId, |
119 | ) -> Arc<chalk::AssociatedTyValue>; | 129 | ) -> Arc<chalk_db::AssociatedTyValue>; |
120 | 130 | ||
121 | #[salsa::invoke(crate::traits::trait_solve_query)] | 131 | #[salsa::invoke(trait_solve_wait)] |
132 | #[salsa::transparent] | ||
122 | fn trait_solve( | 133 | fn trait_solve( |
123 | &self, | 134 | &self, |
124 | krate: CrateId, | 135 | krate: CrateId, |
125 | goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>, | 136 | goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>, |
126 | ) -> Option<crate::traits::Solution>; | 137 | ) -> Option<crate::Solution>; |
127 | 138 | ||
128 | #[salsa::invoke(crate::traits::chalk::program_clauses_for_chalk_env_query)] | 139 | #[salsa::invoke(crate::traits::trait_solve_query)] |
140 | fn trait_solve_query( | ||
141 | &self, | ||
142 | krate: CrateId, | ||
143 | goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>, | ||
144 | ) -> Option<crate::Solution>; | ||
145 | |||
146 | #[salsa::invoke(chalk_db::program_clauses_for_chalk_env_query)] | ||
129 | fn program_clauses_for_chalk_env( | 147 | fn program_clauses_for_chalk_env( |
130 | &self, | 148 | &self, |
131 | krate: CrateId, | 149 | krate: CrateId, |
132 | env: chalk_ir::Environment<chalk::Interner>, | 150 | env: chalk_ir::Environment<Interner>, |
133 | ) -> chalk_ir::ProgramClauses<chalk::Interner>; | 151 | ) -> chalk_ir::ProgramClauses<Interner>; |
134 | } | 152 | } |
135 | 153 | ||
136 | fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> { | 154 | fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> { |
@@ -146,6 +164,15 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> | |||
146 | db.infer_query(def) | 164 | db.infer_query(def) |
147 | } | 165 | } |
148 | 166 | ||
167 | fn trait_solve_wait( | ||
168 | db: &dyn HirDatabase, | ||
169 | krate: CrateId, | ||
170 | goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>, | ||
171 | ) -> Option<crate::Solution> { | ||
172 | let _p = profile::span("trait_solve::wait"); | ||
173 | db.trait_solve_query(krate, goal) | ||
174 | } | ||
175 | |||
149 | #[test] | 176 | #[test] |
150 | fn hir_database_is_object_safe() { | 177 | fn hir_database_is_object_safe() { |
151 | fn _assert_object_safe(_: &dyn HirDatabase) {} | 178 | fn _assert_object_safe(_: &dyn HirDatabase) {} |
@@ -156,6 +183,14 @@ pub struct InternedTypeParamId(salsa::InternId); | |||
156 | impl_intern_key!(InternedTypeParamId); | 183 | impl_intern_key!(InternedTypeParamId); |
157 | 184 | ||
158 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 185 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
186 | pub struct InternedLifetimeParamId(salsa::InternId); | ||
187 | impl_intern_key!(InternedLifetimeParamId); | ||
188 | |||
189 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
190 | pub struct InternedConstParamId(salsa::InternId); | ||
191 | impl_intern_key!(InternedConstParamId); | ||
192 | |||
193 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
159 | pub struct InternedOpaqueTyId(salsa::InternId); | 194 | pub struct InternedOpaqueTyId(salsa::InternId); |
160 | impl_intern_key!(InternedOpaqueTyId); | 195 | impl_intern_key!(InternedOpaqueTyId); |
161 | 196 | ||
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs index 86f937e1d..84fc8ce14 100644 --- a/crates/hir_ty/src/diagnostics.rs +++ b/crates/hir_ty/src/diagnostics.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! Type inference-based diagnostics. |
2 | mod expr; | 2 | mod expr; |
3 | mod match_check; | 3 | mod match_check; |
4 | mod unsafe_check; | 4 | mod unsafe_check; |
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs index 1c9f9ede7..075dc4131 100644 --- a/crates/hir_ty/src/diagnostics/decl_check.rs +++ b/crates/hir_ty/src/diagnostics/decl_check.rs | |||
@@ -35,6 +35,8 @@ use crate::{ | |||
35 | }; | 35 | }; |
36 | 36 | ||
37 | mod allow { | 37 | mod allow { |
38 | pub(super) const BAD_STYLE: &str = "bad_style"; | ||
39 | pub(super) const NONSTANDARD_STYLE: &str = "nonstandard_style"; | ||
38 | pub(super) const NON_SNAKE_CASE: &str = "non_snake_case"; | 40 | pub(super) const NON_SNAKE_CASE: &str = "non_snake_case"; |
39 | pub(super) const NON_UPPER_CASE_GLOBAL: &str = "non_upper_case_globals"; | 41 | pub(super) const NON_UPPER_CASE_GLOBAL: &str = "non_upper_case_globals"; |
40 | pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types"; | 42 | pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types"; |
@@ -83,10 +85,39 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
83 | } | 85 | } |
84 | 86 | ||
85 | /// Checks whether not following the convention is allowed for this item. | 87 | /// Checks whether not following the convention is allowed for this item. |
86 | /// | 88 | fn allowed(&self, id: AttrDefId, allow_name: &str, recursing: bool) -> bool { |
87 | /// Currently this method doesn't check parent attributes. | 89 | let is_allowed = |def_id| { |
88 | fn allowed(&self, id: AttrDefId, allow_name: &str) -> bool { | 90 | let attrs = self.db.attrs(def_id); |
89 | self.db.attrs(id).by_key("allow").tt_values().any(|tt| tt.to_string().contains(allow_name)) | 91 | // don't bug the user about directly no_mangle annotated stuff, they can't do anything about it |
92 | (!recursing && attrs.by_key("no_mangle").exists()) | ||
93 | || attrs.by_key("allow").tt_values().any(|tt| { | ||
94 | let allows = tt.to_string(); | ||
95 | allows.contains(allow_name) | ||
96 | || allows.contains(allow::BAD_STYLE) | ||
97 | || allows.contains(allow::NONSTANDARD_STYLE) | ||
98 | }) | ||
99 | }; | ||
100 | |||
101 | is_allowed(id) | ||
102 | // go upwards one step or give up | ||
103 | || match id { | ||
104 | AttrDefId::ModuleId(m) => m.containing_module(self.db.upcast()).map(|v| v.into()), | ||
105 | AttrDefId::FunctionId(f) => Some(f.lookup(self.db.upcast()).container.into()), | ||
106 | AttrDefId::StaticId(sid) => Some(sid.lookup(self.db.upcast()).container.into()), | ||
107 | AttrDefId::ConstId(cid) => Some(cid.lookup(self.db.upcast()).container.into()), | ||
108 | AttrDefId::TraitId(tid) => Some(tid.lookup(self.db.upcast()).container.into()), | ||
109 | AttrDefId::ImplId(iid) => Some(iid.lookup(self.db.upcast()).container.into()), | ||
110 | // These warnings should not explore macro definitions at all | ||
111 | AttrDefId::MacroDefId(_) => None, | ||
112 | // Will never occur under an enum/struct/union/type alias | ||
113 | AttrDefId::AdtId(_) => None, | ||
114 | AttrDefId::FieldId(_) => None, | ||
115 | AttrDefId::EnumVariantId(_) => None, | ||
116 | AttrDefId::TypeAliasId(_) => None, | ||
117 | AttrDefId::GenericParamId(_) => None, | ||
118 | } | ||
119 | .map(|mid| self.allowed(mid, allow_name, true)) | ||
120 | .unwrap_or(false) | ||
90 | } | 121 | } |
91 | 122 | ||
92 | fn validate_func(&mut self, func: FunctionId) { | 123 | fn validate_func(&mut self, func: FunctionId) { |
@@ -109,7 +140,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
109 | } | 140 | } |
110 | 141 | ||
111 | // Check whether non-snake case identifiers are allowed for this function. | 142 | // Check whether non-snake case identifiers are allowed for this function. |
112 | if self.allowed(func.into(), allow::NON_SNAKE_CASE) { | 143 | if self.allowed(func.into(), allow::NON_SNAKE_CASE, false) { |
113 | return; | 144 | return; |
114 | } | 145 | } |
115 | 146 | ||
@@ -328,8 +359,9 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
328 | fn validate_struct(&mut self, struct_id: StructId) { | 359 | fn validate_struct(&mut self, struct_id: StructId) { |
329 | let data = self.db.struct_data(struct_id); | 360 | let data = self.db.struct_data(struct_id); |
330 | 361 | ||
331 | let non_camel_case_allowed = self.allowed(struct_id.into(), allow::NON_CAMEL_CASE_TYPES); | 362 | let non_camel_case_allowed = |
332 | let non_snake_case_allowed = self.allowed(struct_id.into(), allow::NON_SNAKE_CASE); | 363 | self.allowed(struct_id.into(), allow::NON_CAMEL_CASE_TYPES, false); |
364 | let non_snake_case_allowed = self.allowed(struct_id.into(), allow::NON_SNAKE_CASE, false); | ||
333 | 365 | ||
334 | // Check the structure name. | 366 | // Check the structure name. |
335 | let struct_name = data.name.to_string(); | 367 | let struct_name = data.name.to_string(); |
@@ -461,7 +493,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
461 | let data = self.db.enum_data(enum_id); | 493 | let data = self.db.enum_data(enum_id); |
462 | 494 | ||
463 | // Check whether non-camel case names are allowed for this enum. | 495 | // Check whether non-camel case names are allowed for this enum. |
464 | if self.allowed(enum_id.into(), allow::NON_CAMEL_CASE_TYPES) { | 496 | if self.allowed(enum_id.into(), allow::NON_CAMEL_CASE_TYPES, false) { |
465 | return; | 497 | return; |
466 | } | 498 | } |
467 | 499 | ||
@@ -584,7 +616,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
584 | fn validate_const(&mut self, const_id: ConstId) { | 616 | fn validate_const(&mut self, const_id: ConstId) { |
585 | let data = self.db.const_data(const_id); | 617 | let data = self.db.const_data(const_id); |
586 | 618 | ||
587 | if self.allowed(const_id.into(), allow::NON_UPPER_CASE_GLOBAL) { | 619 | if self.allowed(const_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) { |
588 | return; | 620 | return; |
589 | } | 621 | } |
590 | 622 | ||
@@ -632,7 +664,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
632 | return; | 664 | return; |
633 | } | 665 | } |
634 | 666 | ||
635 | if self.allowed(static_id.into(), allow::NON_UPPER_CASE_GLOBAL) { | 667 | if self.allowed(static_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) { |
636 | return; | 668 | return; |
637 | } | 669 | } |
638 | 670 | ||
@@ -867,23 +899,116 @@ fn main() { | |||
867 | fn allow_attributes() { | 899 | fn allow_attributes() { |
868 | check_diagnostics( | 900 | check_diagnostics( |
869 | r#" | 901 | r#" |
870 | #[allow(non_snake_case)] | 902 | #[allow(non_snake_case)] |
871 | fn NonSnakeCaseName(SOME_VAR: u8) -> u8{ | 903 | fn NonSnakeCaseName(SOME_VAR: u8) -> u8{ |
872 | let OtherVar = SOME_VAR + 1; | 904 | // cov_flags generated output from elsewhere in this file |
873 | OtherVar | 905 | extern "C" { |
906 | #[no_mangle] | ||
907 | static lower_case: u8; | ||
874 | } | 908 | } |
875 | 909 | ||
876 | #[allow(non_snake_case, non_camel_case_types)] | 910 | let OtherVar = SOME_VAR + 1; |
877 | pub struct some_type { | 911 | OtherVar |
878 | SOME_FIELD: u8, | 912 | } |
879 | SomeField: u16, | 913 | |
914 | #[allow(nonstandard_style)] | ||
915 | mod CheckNonstandardStyle { | ||
916 | fn HiImABadFnName() {} | ||
917 | } | ||
918 | |||
919 | #[allow(bad_style)] | ||
920 | mod CheckBadStyle { | ||
921 | fn HiImABadFnName() {} | ||
922 | } | ||
923 | |||
924 | mod F { | ||
925 | #![allow(non_snake_case)] | ||
926 | fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {} | ||
927 | } | ||
928 | |||
929 | #[allow(non_snake_case, non_camel_case_types)] | ||
930 | pub struct some_type { | ||
931 | SOME_FIELD: u8, | ||
932 | SomeField: u16, | ||
933 | } | ||
934 | |||
935 | #[allow(non_upper_case_globals)] | ||
936 | pub const some_const: u8 = 10; | ||
937 | |||
938 | #[allow(non_upper_case_globals)] | ||
939 | pub static SomeStatic: u8 = 10; | ||
940 | "#, | ||
941 | ); | ||
880 | } | 942 | } |
881 | 943 | ||
882 | #[allow(non_upper_case_globals)] | 944 | #[test] |
883 | pub const some_const: u8 = 10; | 945 | fn allow_attributes_crate_attr() { |
946 | check_diagnostics( | ||
947 | r#" | ||
948 | #![allow(non_snake_case)] | ||
884 | 949 | ||
885 | #[allow(non_upper_case_globals)] | 950 | mod F { |
886 | pub static SomeStatic: u8 = 10; | 951 | fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {} |
952 | } | ||
953 | "#, | ||
954 | ); | ||
955 | } | ||
956 | |||
957 | #[test] | ||
958 | #[ignore] | ||
959 | fn bug_trait_inside_fn() { | ||
960 | // FIXME: | ||
961 | // This is broken, and in fact, should not even be looked at by this | ||
962 | // lint in the first place. There's weird stuff going on in the | ||
963 | // collection phase. | ||
964 | // It's currently being brought in by: | ||
965 | // * validate_func on `a` recursing into modules | ||
966 | // * then it finds the trait and then the function while iterating | ||
967 | // through modules | ||
968 | // * then validate_func is called on Dirty | ||
969 | // * ... which then proceeds to look at some unknown module taking no | ||
970 | // attrs from either the impl or the fn a, and then finally to the root | ||
971 | // module | ||
972 | // | ||
973 | // It should find the attribute on the trait, but it *doesn't even see | ||
974 | // the trait* as far as I can tell. | ||
975 | |||
976 | check_diagnostics( | ||
977 | r#" | ||
978 | trait T { fn a(); } | ||
979 | struct U {} | ||
980 | impl T for U { | ||
981 | fn a() { | ||
982 | // this comes out of bitflags, mostly | ||
983 | #[allow(non_snake_case)] | ||
984 | trait __BitFlags { | ||
985 | const HiImAlsoBad: u8 = 2; | ||
986 | #[inline] | ||
987 | fn Dirty(&self) -> bool { | ||
988 | false | ||
989 | } | ||
990 | } | ||
991 | |||
992 | } | ||
993 | } | ||
994 | "#, | ||
995 | ); | ||
996 | } | ||
997 | |||
998 | #[test] | ||
999 | #[ignore] | ||
1000 | fn bug_traits_arent_checked() { | ||
1001 | // FIXME: Traits and functions in traits aren't currently checked by | ||
1002 | // r-a, even though rustc will complain about them. | ||
1003 | check_diagnostics( | ||
1004 | r#" | ||
1005 | trait BAD_TRAIT { | ||
1006 | // ^^^^^^^^^ Trait `BAD_TRAIT` should have CamelCase name, e.g. `BadTrait` | ||
1007 | fn BAD_FUNCTION(); | ||
1008 | // ^^^^^^^^^^^^ Function `BAD_FUNCTION` should have snake_case name, e.g. `bad_function` | ||
1009 | fn BadFunction(); | ||
1010 | // ^^^^^^^^^^^^ Function `BadFunction` should have snake_case name, e.g. `bad_function` | ||
1011 | } | ||
887 | "#, | 1012 | "#, |
888 | ); | 1013 | ); |
889 | } | 1014 | } |
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index 8169b759f..79602c3dd 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs | |||
@@ -14,7 +14,6 @@ use crate::{ | |||
14 | MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, | 14 | MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, |
15 | MissingPatFields, RemoveThisSemicolon, | 15 | MissingPatFields, RemoveThisSemicolon, |
16 | }, | 16 | }, |
17 | utils::variant_data, | ||
18 | AdtId, InferenceResult, Interner, TyExt, TyKind, | 17 | AdtId, InferenceResult, Interner, TyExt, TyKind, |
19 | }; | 18 | }; |
20 | 19 | ||
@@ -104,7 +103,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
104 | let root = source_ptr.file_syntax(db.upcast()); | 103 | let root = source_ptr.file_syntax(db.upcast()); |
105 | if let ast::Expr::RecordExpr(record_expr) = &source_ptr.value.to_node(&root) { | 104 | if let ast::Expr::RecordExpr(record_expr) = &source_ptr.value.to_node(&root) { |
106 | if let Some(_) = record_expr.record_expr_field_list() { | 105 | if let Some(_) = record_expr.record_expr_field_list() { |
107 | let variant_data = variant_data(db.upcast(), variant_def); | 106 | let variant_data = variant_def.variant_data(db.upcast()); |
108 | let missed_fields = missed_fields | 107 | let missed_fields = missed_fields |
109 | .into_iter() | 108 | .into_iter() |
110 | .map(|idx| variant_data.fields()[idx].name.clone()) | 109 | .map(|idx| variant_data.fields()[idx].name.clone()) |
@@ -135,7 +134,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
135 | let root = source_ptr.file_syntax(db.upcast()); | 134 | let root = source_ptr.file_syntax(db.upcast()); |
136 | if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) { | 135 | if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) { |
137 | if let Some(_) = record_pat.record_pat_field_list() { | 136 | if let Some(_) = record_pat.record_pat_field_list() { |
138 | let variant_data = variant_data(db.upcast(), variant_def); | 137 | let variant_data = variant_def.variant_data(db.upcast()); |
139 | let missed_fields = missed_fields | 138 | let missed_fields = missed_fields |
140 | .into_iter() | 139 | .into_iter() |
141 | .map(|idx| variant_data.fields()[idx].name.clone()) | 140 | .map(|idx| variant_data.fields()[idx].name.clone()) |
@@ -245,7 +244,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
245 | Some(callee) => callee, | 244 | Some(callee) => callee, |
246 | None => return, | 245 | None => return, |
247 | }; | 246 | }; |
248 | let sig = db.callable_item_signature(callee.into()).value; | 247 | let sig = |
248 | db.callable_item_signature(callee.into()).into_value_and_skipped_binders().0; | ||
249 | 249 | ||
250 | (sig, args) | 250 | (sig, args) |
251 | } | 251 | } |
@@ -314,7 +314,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
314 | if pat_ty == match_expr_ty | 314 | if pat_ty == match_expr_ty |
315 | || match_expr_ty | 315 | || match_expr_ty |
316 | .as_reference() | 316 | .as_reference() |
317 | .map(|(match_expr_ty, _)| match_expr_ty == pat_ty) | 317 | .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty) |
318 | .unwrap_or(false) | 318 | .unwrap_or(false) |
319 | { | 319 | { |
320 | // If we had a NotUsefulMatchArm diagnostic, we could | 320 | // If we had a NotUsefulMatchArm diagnostic, we could |
@@ -452,7 +452,7 @@ pub fn record_literal_missing_fields( | |||
452 | return None; | 452 | return None; |
453 | } | 453 | } |
454 | 454 | ||
455 | let variant_data = variant_data(db.upcast(), variant_def); | 455 | let variant_data = variant_def.variant_data(db.upcast()); |
456 | 456 | ||
457 | let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect(); | 457 | let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect(); |
458 | let missed_fields: Vec<LocalFieldId> = variant_data | 458 | let missed_fields: Vec<LocalFieldId> = variant_data |
@@ -482,7 +482,7 @@ pub fn record_pattern_missing_fields( | |||
482 | return None; | 482 | return None; |
483 | } | 483 | } |
484 | 484 | ||
485 | let variant_data = variant_data(db.upcast(), variant_def); | 485 | let variant_data = variant_def.variant_data(db.upcast()); |
486 | 486 | ||
487 | let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect(); | 487 | let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect(); |
488 | let missed_fields: Vec<LocalFieldId> = variant_data | 488 | let missed_fields: Vec<LocalFieldId> = variant_data |
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs index 34291578a..e9762622f 100644 --- a/crates/hir_ty/src/diagnostics/match_check.rs +++ b/crates/hir_ty/src/diagnostics/match_check.rs | |||
@@ -227,7 +227,7 @@ use hir_def::{ | |||
227 | use la_arena::Idx; | 227 | use la_arena::Idx; |
228 | use smallvec::{smallvec, SmallVec}; | 228 | use smallvec::{smallvec, SmallVec}; |
229 | 229 | ||
230 | use crate::{db::HirDatabase, AdtId, InferenceResult, Interner, TyKind}; | 230 | use crate::{db::HirDatabase, AdtId, InferenceResult, Interner, TyExt, TyKind}; |
231 | 231 | ||
232 | #[derive(Debug, Clone, Copy)] | 232 | #[derive(Debug, Clone, Copy)] |
233 | /// Either a pattern from the source code being analyzed, represented as | 233 | /// Either a pattern from the source code being analyzed, represented as |
diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs index b5efe9df5..ed97dc0e3 100644 --- a/crates/hir_ty/src/diagnostics/unsafe_check.rs +++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs | |||
@@ -11,7 +11,9 @@ use hir_def::{ | |||
11 | }; | 11 | }; |
12 | use hir_expand::diagnostics::DiagnosticSink; | 12 | use hir_expand::diagnostics::DiagnosticSink; |
13 | 13 | ||
14 | use crate::{db::HirDatabase, diagnostics::MissingUnsafe, InferenceResult, Interner, TyKind}; | 14 | use crate::{ |
15 | db::HirDatabase, diagnostics::MissingUnsafe, InferenceResult, Interner, TyExt, TyKind, | ||
16 | }; | ||
15 | 17 | ||
16 | pub(super) struct UnsafeValidator<'a, 'b: 'a> { | 18 | pub(super) struct UnsafeValidator<'a, 'b: 'a> { |
17 | owner: DefWithBodyId, | 19 | owner: DefWithBodyId, |
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index 385bd9405..4fb7d9cf2 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs | |||
@@ -1,9 +1,15 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! The `HirDisplay` trait, which serves two purposes: Turning various bits from |
2 | //! HIR back into source code, and just displaying them for debugging/testing | ||
3 | //! purposes. | ||
2 | 4 | ||
3 | use std::{array, fmt}; | 5 | use std::{ |
6 | array, | ||
7 | fmt::{self, Debug}, | ||
8 | }; | ||
4 | 9 | ||
5 | use chalk_ir::Mutability; | 10 | use chalk_ir::BoundVar; |
6 | use hir_def::{ | 11 | use hir_def::{ |
12 | body, | ||
7 | db::DefDatabase, | 13 | db::DefDatabase, |
8 | find_path, | 14 | find_path, |
9 | generics::TypeParamProvenance, | 15 | generics::TypeParamProvenance, |
@@ -13,13 +19,15 @@ use hir_def::{ | |||
13 | visibility::Visibility, | 19 | visibility::Visibility, |
14 | AssocContainerId, Lookup, ModuleId, TraitId, | 20 | AssocContainerId, Lookup, ModuleId, TraitId, |
15 | }; | 21 | }; |
16 | use hir_expand::name::Name; | 22 | use hir_expand::{hygiene::Hygiene, name::Name}; |
17 | 23 | ||
18 | use crate::{ | 24 | use crate::{ |
19 | db::HirDatabase, from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, primitive, | 25 | const_from_placeholder_idx, db::HirDatabase, from_assoc_type_id, from_foreign_def_id, |
20 | to_assoc_type_id, traits::chalk::from_chalk, utils::generics, AdtId, AliasEq, AliasTy, | 26 | from_placeholder_idx, lt_from_placeholder_idx, mapping::from_chalk, primitive, subst_prefix, |
21 | CallableDefId, CallableSig, DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, OpaqueTy, | 27 | to_assoc_type_id, utils::generics, AdtId, AliasEq, AliasTy, CallableDefId, CallableSig, Const, |
22 | ProjectionTy, QuantifiedWhereClause, Scalar, TraitRef, Ty, TyExt, TyKind, WhereClause, | 28 | ConstValue, DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, |
29 | LifetimeOutlives, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, | ||
30 | Scalar, TraitRef, TraitRefExt, Ty, TyExt, TyKind, WhereClause, | ||
23 | }; | 31 | }; |
24 | 32 | ||
25 | pub struct HirFormatter<'a> { | 33 | pub struct HirFormatter<'a> { |
@@ -46,6 +54,10 @@ pub trait HirDisplay { | |||
46 | where | 54 | where |
47 | Self: Sized, | 55 | Self: Sized, |
48 | { | 56 | { |
57 | assert!( | ||
58 | !matches!(display_target, DisplayTarget::SourceCode { .. }), | ||
59 | "HirDisplayWrapper cannot fail with DisplaySourceCodeError, use HirDisplay::hir_fmt directly instead" | ||
60 | ); | ||
49 | HirDisplayWrapper { db, t: self, max_size, omit_verbose_types, display_target } | 61 | HirDisplayWrapper { db, t: self, max_size, omit_verbose_types, display_target } |
50 | } | 62 | } |
51 | 63 | ||
@@ -230,7 +242,7 @@ where | |||
230 | Err(HirDisplayError::FmtError) => Err(fmt::Error), | 242 | Err(HirDisplayError::FmtError) => Err(fmt::Error), |
231 | Err(HirDisplayError::DisplaySourceCodeError(_)) => { | 243 | Err(HirDisplayError::DisplaySourceCodeError(_)) => { |
232 | // This should never happen | 244 | // This should never happen |
233 | panic!("HirDisplay failed when calling Display::fmt!") | 245 | panic!("HirDisplay::hir_fmt failed with DisplaySourceCodeError when calling Display::fmt!") |
234 | } | 246 | } |
235 | } | 247 | } |
236 | } | 248 | } |
@@ -251,16 +263,12 @@ impl HirDisplay for ProjectionTy { | |||
251 | } | 263 | } |
252 | 264 | ||
253 | let trait_ = f.db.trait_data(self.trait_(f.db)); | 265 | let trait_ = f.db.trait_data(self.trait_(f.db)); |
254 | let first_parameter = self.self_type_parameter().into_displayable( | 266 | write!(f, "<")?; |
255 | f.db, | 267 | self.self_type_parameter(&Interner).hir_fmt(f)?; |
256 | f.max_size, | 268 | write!(f, " as {}", trait_.name)?; |
257 | f.omit_verbose_types, | ||
258 | f.display_target, | ||
259 | ); | ||
260 | write!(f, "<{} as {}", first_parameter, trait_.name)?; | ||
261 | if self.substitution.len(&Interner) > 1 { | 269 | if self.substitution.len(&Interner) > 1 { |
262 | write!(f, "<")?; | 270 | write!(f, "<")?; |
263 | f.write_joined(&self.substitution.interned(&Interner)[1..], ", ")?; | 271 | f.write_joined(&self.substitution.as_slice(&Interner)[1..], ", ")?; |
264 | write!(f, ">")?; | 272 | write!(f, ">")?; |
265 | } | 273 | } |
266 | write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)).name)?; | 274 | write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)).name)?; |
@@ -282,10 +290,35 @@ impl HirDisplay for GenericArg { | |||
282 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | 290 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
283 | match self.interned() { | 291 | match self.interned() { |
284 | crate::GenericArgData::Ty(ty) => ty.hir_fmt(f), | 292 | crate::GenericArgData::Ty(ty) => ty.hir_fmt(f), |
293 | crate::GenericArgData::Lifetime(lt) => lt.hir_fmt(f), | ||
294 | crate::GenericArgData::Const(c) => c.hir_fmt(f), | ||
295 | } | ||
296 | } | ||
297 | } | ||
298 | |||
299 | impl HirDisplay for Const { | ||
300 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
301 | let data = self.interned(); | ||
302 | match data.value { | ||
303 | ConstValue::BoundVar(idx) => idx.hir_fmt(f), | ||
304 | ConstValue::InferenceVar(..) => write!(f, "_"), | ||
305 | ConstValue::Placeholder(idx) => { | ||
306 | let id = const_from_placeholder_idx(f.db, idx); | ||
307 | let generics = generics(f.db.upcast(), id.parent); | ||
308 | let param_data = &generics.params.consts[id.local_id]; | ||
309 | write!(f, "{}", param_data.name) | ||
310 | } | ||
311 | ConstValue::Concrete(_) => write!(f, "_"), | ||
285 | } | 312 | } |
286 | } | 313 | } |
287 | } | 314 | } |
288 | 315 | ||
316 | impl HirDisplay for BoundVar { | ||
317 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
318 | write!(f, "?{}.{}", self.debruijn.depth(), self.index) | ||
319 | } | ||
320 | } | ||
321 | |||
289 | impl HirDisplay for Ty { | 322 | impl HirDisplay for Ty { |
290 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | 323 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
291 | if f.should_truncate() { | 324 | if f.should_truncate() { |
@@ -305,15 +338,14 @@ impl HirDisplay for Ty { | |||
305 | t.hir_fmt(f)?; | 338 | t.hir_fmt(f)?; |
306 | write!(f, "]")?; | 339 | write!(f, "]")?; |
307 | } | 340 | } |
308 | TyKind::Array(t) => { | 341 | TyKind::Array(t, c) => { |
309 | write!(f, "[")?; | 342 | write!(f, "[")?; |
310 | t.hir_fmt(f)?; | 343 | t.hir_fmt(f)?; |
311 | write!(f, "; _]")?; | 344 | write!(f, "; ")?; |
345 | c.hir_fmt(f)?; | ||
346 | write!(f, "]")?; | ||
312 | } | 347 | } |
313 | TyKind::Raw(m, t) | TyKind::Ref(m, t) => { | 348 | TyKind::Raw(m, t) | TyKind::Ref(m, _, t) => { |
314 | let ty_display = | ||
315 | t.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target); | ||
316 | |||
317 | if matches!(self.kind(&Interner), TyKind::Raw(..)) { | 349 | if matches!(self.kind(&Interner), TyKind::Raw(..)) { |
318 | write!( | 350 | write!( |
319 | f, | 351 | f, |
@@ -352,8 +384,8 @@ impl HirDisplay for Ty { | |||
352 | let data = (*datas) | 384 | let data = (*datas) |
353 | .as_ref() | 385 | .as_ref() |
354 | .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); | 386 | .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); |
355 | let bounds = data.subst(parameters); | 387 | let bounds = data.substitute(&Interner, parameters); |
356 | bounds.value | 388 | bounds.into_value_and_skipped_binders().0 |
357 | } else { | 389 | } else { |
358 | Vec::new() | 390 | Vec::new() |
359 | } | 391 | } |
@@ -368,16 +400,16 @@ impl HirDisplay for Ty { | |||
368 | if fn_traits(f.db.upcast(), trait_).any(|it| it == trait_) | 400 | if fn_traits(f.db.upcast(), trait_).any(|it| it == trait_) |
369 | && predicates.len() <= 2 | 401 | && predicates.len() <= 2 |
370 | { | 402 | { |
371 | return write!(f, "{}", ty_display); | 403 | return t.hir_fmt(f); |
372 | } | 404 | } |
373 | } | 405 | } |
374 | 406 | ||
375 | if predicates.len() > 1 { | 407 | if predicates.len() > 1 { |
376 | write!(f, "(")?; | 408 | write!(f, "(")?; |
377 | write!(f, "{}", ty_display)?; | 409 | t.hir_fmt(f)?; |
378 | write!(f, ")")?; | 410 | write!(f, ")")?; |
379 | } else { | 411 | } else { |
380 | write!(f, "{}", ty_display)?; | 412 | t.hir_fmt(f)?; |
381 | } | 413 | } |
382 | } | 414 | } |
383 | TyKind::Tuple(_, substs) => { | 415 | TyKind::Tuple(_, substs) => { |
@@ -387,7 +419,7 @@ impl HirDisplay for Ty { | |||
387 | write!(f, ",)")?; | 419 | write!(f, ",)")?; |
388 | } else { | 420 | } else { |
389 | write!(f, "(")?; | 421 | write!(f, "(")?; |
390 | f.write_joined(&*substs.0, ", ")?; | 422 | f.write_joined(&*substs.as_slice(&Interner), ", ")?; |
391 | write!(f, ")")?; | 423 | write!(f, ")")?; |
392 | } | 424 | } |
393 | } | 425 | } |
@@ -397,7 +429,7 @@ impl HirDisplay for Ty { | |||
397 | } | 429 | } |
398 | TyKind::FnDef(def, parameters) => { | 430 | TyKind::FnDef(def, parameters) => { |
399 | let def = from_chalk(f.db, *def); | 431 | let def = from_chalk(f.db, *def); |
400 | let sig = f.db.callable_item_signature(def).subst(parameters); | 432 | let sig = f.db.callable_item_signature(def).substitute(&Interner, parameters); |
401 | match def { | 433 | match def { |
402 | CallableDefId::FunctionId(ff) => { | 434 | CallableDefId::FunctionId(ff) => { |
403 | write!(f, "fn {}", f.db.function_data(ff).name)? | 435 | write!(f, "fn {}", f.db.function_data(ff).name)? |
@@ -415,7 +447,7 @@ impl HirDisplay for Ty { | |||
415 | // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self? | 447 | // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self? |
416 | if total_len > 0 { | 448 | if total_len > 0 { |
417 | write!(f, "<")?; | 449 | write!(f, "<")?; |
418 | f.write_joined(¶meters.0[..total_len], ", ")?; | 450 | f.write_joined(¶meters.as_slice(&Interner)[..total_len], ", ")?; |
419 | write!(f, ">")?; | 451 | write!(f, ">")?; |
420 | } | 452 | } |
421 | } | 453 | } |
@@ -424,14 +456,8 @@ impl HirDisplay for Ty { | |||
424 | write!(f, ")")?; | 456 | write!(f, ")")?; |
425 | let ret = sig.ret(); | 457 | let ret = sig.ret(); |
426 | if !ret.is_unit() { | 458 | if !ret.is_unit() { |
427 | let ret_display = ret.into_displayable( | 459 | write!(f, " -> ")?; |
428 | f.db, | 460 | ret.hir_fmt(f)?; |
429 | f.max_size, | ||
430 | f.omit_verbose_types, | ||
431 | f.display_target, | ||
432 | ); | ||
433 | |||
434 | write!(f, " -> {}", ret_display)?; | ||
435 | } | 461 | } |
436 | } | 462 | } |
437 | TyKind::Adt(AdtId(def_id), parameters) => { | 463 | TyKind::Adt(AdtId(def_id), parameters) => { |
@@ -468,7 +494,7 @@ impl HirDisplay for Ty { | |||
468 | .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) | 494 | .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) |
469 | .filter(|defaults| !defaults.is_empty()) | 495 | .filter(|defaults| !defaults.is_empty()) |
470 | { | 496 | { |
471 | None => parameters.0.as_ref(), | 497 | None => parameters.as_slice(&Interner), |
472 | Some(default_parameters) => { | 498 | Some(default_parameters) => { |
473 | let mut default_from = 0; | 499 | let mut default_from = 0; |
474 | for (i, parameter) in parameters.iter(&Interner).enumerate() { | 500 | for (i, parameter) in parameters.iter(&Interner).enumerate() { |
@@ -476,13 +502,15 @@ impl HirDisplay for Ty { | |||
476 | parameter.assert_ty_ref(&Interner).kind(&Interner), | 502 | parameter.assert_ty_ref(&Interner).kind(&Interner), |
477 | default_parameters.get(i), | 503 | default_parameters.get(i), |
478 | ) { | 504 | ) { |
479 | (&TyKind::Unknown, _) | (_, None) => { | 505 | (&TyKind::Error, _) | (_, None) => { |
480 | default_from = i + 1; | 506 | default_from = i + 1; |
481 | } | 507 | } |
482 | (_, Some(default_parameter)) => { | 508 | (_, Some(default_parameter)) => { |
483 | let actual_default = default_parameter | 509 | let actual_default = |
484 | .clone() | 510 | default_parameter.clone().substitute( |
485 | .subst(¶meters.prefix(i)); | 511 | &Interner, |
512 | &subst_prefix(parameters, i), | ||
513 | ); | ||
486 | if parameter.assert_ty_ref(&Interner) != &actual_default | 514 | if parameter.assert_ty_ref(&Interner) != &actual_default |
487 | { | 515 | { |
488 | default_from = i + 1; | 516 | default_from = i + 1; |
@@ -490,11 +518,11 @@ impl HirDisplay for Ty { | |||
490 | } | 518 | } |
491 | } | 519 | } |
492 | } | 520 | } |
493 | ¶meters.0[0..default_from] | 521 | ¶meters.as_slice(&Interner)[0..default_from] |
494 | } | 522 | } |
495 | } | 523 | } |
496 | } else { | 524 | } else { |
497 | parameters.0.as_ref() | 525 | parameters.as_slice(&Interner) |
498 | }; | 526 | }; |
499 | if !parameters_to_write.is_empty() { | 527 | if !parameters_to_write.is_empty() { |
500 | write!(f, "<")?; | 528 | write!(f, "<")?; |
@@ -517,7 +545,7 @@ impl HirDisplay for Ty { | |||
517 | write!(f, "{}::{}", trait_.name, type_alias_data.name)?; | 545 | write!(f, "{}::{}", trait_.name, type_alias_data.name)?; |
518 | if parameters.len(&Interner) > 0 { | 546 | if parameters.len(&Interner) > 0 { |
519 | write!(f, "<")?; | 547 | write!(f, "<")?; |
520 | f.write_joined(&*parameters.0, ", ")?; | 548 | f.write_joined(&*parameters.as_slice(&Interner), ", ")?; |
521 | write!(f, ">")?; | 549 | write!(f, ">")?; |
522 | } | 550 | } |
523 | } else { | 551 | } else { |
@@ -529,7 +557,7 @@ impl HirDisplay for Ty { | |||
529 | projection_ty.hir_fmt(f)?; | 557 | projection_ty.hir_fmt(f)?; |
530 | } | 558 | } |
531 | } | 559 | } |
532 | TyKind::ForeignType(type_alias) => { | 560 | TyKind::Foreign(type_alias) => { |
533 | let type_alias = f.db.type_alias_data(from_foreign_def_id(*type_alias)); | 561 | let type_alias = f.db.type_alias_data(from_foreign_def_id(*type_alias)); |
534 | write!(f, "{}", type_alias.name)?; | 562 | write!(f, "{}", type_alias.name)?; |
535 | } | 563 | } |
@@ -542,8 +570,8 @@ impl HirDisplay for Ty { | |||
542 | let data = (*datas) | 570 | let data = (*datas) |
543 | .as_ref() | 571 | .as_ref() |
544 | .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); | 572 | .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); |
545 | let bounds = data.subst(¶meters); | 573 | let bounds = data.substitute(&Interner, ¶meters); |
546 | write_bounds_like_dyn_trait_with_prefix("impl", &bounds.value, f)?; | 574 | write_bounds_like_dyn_trait_with_prefix("impl", bounds.skip_binders(), f)?; |
547 | // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution | 575 | // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution |
548 | } | 576 | } |
549 | ImplTraitId::AsyncBlockTypeImplTrait(..) => { | 577 | ImplTraitId::AsyncBlockTypeImplTrait(..) => { |
@@ -571,13 +599,8 @@ impl HirDisplay for Ty { | |||
571 | write!(f, "|")?; | 599 | write!(f, "|")?; |
572 | }; | 600 | }; |
573 | 601 | ||
574 | let ret_display = sig.ret().into_displayable( | 602 | write!(f, " -> ")?; |
575 | f.db, | 603 | sig.ret().hir_fmt(f)?; |
576 | f.max_size, | ||
577 | f.omit_verbose_types, | ||
578 | f.display_target, | ||
579 | ); | ||
580 | write!(f, " -> {}", ret_display)?; | ||
581 | } else { | 604 | } else { |
582 | write!(f, "{{closure}}")?; | 605 | write!(f, "{{closure}}")?; |
583 | } | 606 | } |
@@ -592,25 +615,26 @@ impl HirDisplay for Ty { | |||
592 | } | 615 | } |
593 | TypeParamProvenance::ArgumentImplTrait => { | 616 | TypeParamProvenance::ArgumentImplTrait => { |
594 | let substs = generics.type_params_subst(f.db); | 617 | let substs = generics.type_params_subst(f.db); |
595 | let bounds = f | 618 | let bounds = |
596 | .db | 619 | f.db.generic_predicates(id.parent) |
597 | .generic_predicates(id.parent) | 620 | .into_iter() |
598 | .into_iter() | 621 | .map(|pred| pred.clone().substitute(&Interner, &substs)) |
599 | .map(|pred| pred.clone().subst(&substs)) | 622 | .filter(|wc| match &wc.skip_binders() { |
600 | .filter(|wc| match &wc.skip_binders() { | 623 | WhereClause::Implemented(tr) => { |
601 | WhereClause::Implemented(tr) => tr.self_type_parameter() == self, | 624 | &tr.self_type_parameter(&Interner) == self |
602 | WhereClause::AliasEq(AliasEq { | 625 | } |
603 | alias: AliasTy::Projection(proj), | 626 | WhereClause::AliasEq(AliasEq { |
604 | ty: _, | 627 | alias: AliasTy::Projection(proj), |
605 | }) => proj.self_type_parameter() == self, | 628 | ty: _, |
606 | _ => false, | 629 | }) => &proj.self_type_parameter(&Interner) == self, |
607 | }) | 630 | _ => false, |
608 | .collect::<Vec<_>>(); | 631 | }) |
632 | .collect::<Vec<_>>(); | ||
609 | write_bounds_like_dyn_trait_with_prefix("impl", &bounds, f)?; | 633 | write_bounds_like_dyn_trait_with_prefix("impl", &bounds, f)?; |
610 | } | 634 | } |
611 | } | 635 | } |
612 | } | 636 | } |
613 | TyKind::BoundVar(idx) => write!(f, "?{}.{}", idx.debruijn.depth(), idx.index)?, | 637 | TyKind::BoundVar(idx) => idx.hir_fmt(f)?, |
614 | TyKind::Dyn(dyn_ty) => { | 638 | TyKind::Dyn(dyn_ty) => { |
615 | write_bounds_like_dyn_trait_with_prefix( | 639 | write_bounds_like_dyn_trait_with_prefix( |
616 | "dyn", | 640 | "dyn", |
@@ -628,15 +652,15 @@ impl HirDisplay for Ty { | |||
628 | let data = (*datas) | 652 | let data = (*datas) |
629 | .as_ref() | 653 | .as_ref() |
630 | .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); | 654 | .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); |
631 | let bounds = data.subst(&opaque_ty.substitution); | 655 | let bounds = data.substitute(&Interner, &opaque_ty.substitution); |
632 | write_bounds_like_dyn_trait_with_prefix("impl", &bounds.value, f)?; | 656 | write_bounds_like_dyn_trait_with_prefix("impl", bounds.skip_binders(), f)?; |
633 | } | 657 | } |
634 | ImplTraitId::AsyncBlockTypeImplTrait(..) => { | 658 | ImplTraitId::AsyncBlockTypeImplTrait(..) => { |
635 | write!(f, "{{async block}}")?; | 659 | write!(f, "{{async block}}")?; |
636 | } | 660 | } |
637 | }; | 661 | }; |
638 | } | 662 | } |
639 | TyKind::Unknown => { | 663 | TyKind::Error => { |
640 | if f.display_target.is_source_code() { | 664 | if f.display_target.is_source_code() { |
641 | return Err(HirDisplayError::DisplaySourceCodeError( | 665 | return Err(HirDisplayError::DisplaySourceCodeError( |
642 | DisplaySourceCodeError::UnknownType, | 666 | DisplaySourceCodeError::UnknownType, |
@@ -645,6 +669,8 @@ impl HirDisplay for Ty { | |||
645 | write!(f, "{{unknown}}")?; | 669 | write!(f, "{{unknown}}")?; |
646 | } | 670 | } |
647 | TyKind::InferenceVar(..) => write!(f, "_")?, | 671 | TyKind::InferenceVar(..) => write!(f, "_")?, |
672 | TyKind::Generator(..) => write!(f, "{{generator}}")?, | ||
673 | TyKind::GeneratorWitness(..) => write!(f, "{{generator witness}}")?, | ||
648 | } | 674 | } |
649 | Ok(()) | 675 | Ok(()) |
650 | } | 676 | } |
@@ -664,9 +690,8 @@ impl HirDisplay for CallableSig { | |||
664 | write!(f, ")")?; | 690 | write!(f, ")")?; |
665 | let ret = self.ret(); | 691 | let ret = self.ret(); |
666 | if !ret.is_unit() { | 692 | if !ret.is_unit() { |
667 | let ret_display = | 693 | write!(f, " -> ")?; |
668 | ret.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target); | 694 | ret.hir_fmt(f)?; |
669 | write!(f, " -> {}", ret_display)?; | ||
670 | } | 695 | } |
671 | Ok(()) | 696 | Ok(()) |
672 | } | 697 | } |
@@ -723,17 +748,17 @@ fn write_bounds_like_dyn_trait( | |||
723 | if !first { | 748 | if !first { |
724 | write!(f, " + ")?; | 749 | write!(f, " + ")?; |
725 | } | 750 | } |
726 | // We assume that the self type is $0 (i.e. the | 751 | // We assume that the self type is ^0.0 (i.e. the |
727 | // existential) here, which is the only thing that's | 752 | // existential) here, which is the only thing that's |
728 | // possible in actual Rust, and hence don't print it | 753 | // possible in actual Rust, and hence don't print it |
729 | write!(f, "{}", f.db.trait_data(trait_).name)?; | 754 | write!(f, "{}", f.db.trait_data(trait_).name)?; |
730 | if let [_, params @ ..] = &*trait_ref.substitution.0 { | 755 | if let [_, params @ ..] = &*trait_ref.substitution.as_slice(&Interner) { |
731 | if is_fn_trait { | 756 | if is_fn_trait { |
732 | if let Some(args) = | 757 | if let Some(args) = |
733 | params.first().and_then(|it| it.assert_ty_ref(&Interner).as_tuple()) | 758 | params.first().and_then(|it| it.assert_ty_ref(&Interner).as_tuple()) |
734 | { | 759 | { |
735 | write!(f, "(")?; | 760 | write!(f, "(")?; |
736 | f.write_joined(&*args.0, ", ")?; | 761 | f.write_joined(args.as_slice(&Interner), ", ")?; |
737 | write!(f, ")")?; | 762 | write!(f, ")")?; |
738 | } | 763 | } |
739 | } else if !params.is_empty() { | 764 | } else if !params.is_empty() { |
@@ -765,6 +790,10 @@ fn write_bounds_like_dyn_trait( | |||
765 | } | 790 | } |
766 | ty.hir_fmt(f)?; | 791 | ty.hir_fmt(f)?; |
767 | } | 792 | } |
793 | |||
794 | // FIXME implement these | ||
795 | WhereClause::LifetimeOutlives(_) => {} | ||
796 | WhereClause::TypeOutlives(_) => {} | ||
768 | } | 797 | } |
769 | first = false; | 798 | first = false; |
770 | } | 799 | } |
@@ -774,31 +803,29 @@ fn write_bounds_like_dyn_trait( | |||
774 | Ok(()) | 803 | Ok(()) |
775 | } | 804 | } |
776 | 805 | ||
777 | impl TraitRef { | 806 | fn fmt_trait_ref(tr: &TraitRef, f: &mut HirFormatter, use_as: bool) -> Result<(), HirDisplayError> { |
778 | fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> Result<(), HirDisplayError> { | 807 | if f.should_truncate() { |
779 | if f.should_truncate() { | 808 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
780 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | 809 | } |
781 | } | ||
782 | 810 | ||
783 | self.self_type_parameter().hir_fmt(f)?; | 811 | tr.self_type_parameter(&Interner).hir_fmt(f)?; |
784 | if use_as { | 812 | if use_as { |
785 | write!(f, " as ")?; | 813 | write!(f, " as ")?; |
786 | } else { | 814 | } else { |
787 | write!(f, ": ")?; | 815 | write!(f, ": ")?; |
788 | } | 816 | } |
789 | write!(f, "{}", f.db.trait_data(self.hir_trait_id()).name)?; | 817 | write!(f, "{}", f.db.trait_data(tr.hir_trait_id()).name)?; |
790 | if self.substitution.len(&Interner) > 1 { | 818 | if tr.substitution.len(&Interner) > 1 { |
791 | write!(f, "<")?; | 819 | write!(f, "<")?; |
792 | f.write_joined(&self.substitution.interned(&Interner)[1..], ", ")?; | 820 | f.write_joined(&tr.substitution.as_slice(&Interner)[1..], ", ")?; |
793 | write!(f, ">")?; | 821 | write!(f, ">")?; |
794 | } | ||
795 | Ok(()) | ||
796 | } | 822 | } |
823 | Ok(()) | ||
797 | } | 824 | } |
798 | 825 | ||
799 | impl HirDisplay for TraitRef { | 826 | impl HirDisplay for TraitRef { |
800 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | 827 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
801 | self.hir_fmt_ext(f, false) | 828 | fmt_trait_ref(self, f, false) |
802 | } | 829 | } |
803 | } | 830 | } |
804 | 831 | ||
@@ -812,7 +839,7 @@ impl HirDisplay for WhereClause { | |||
812 | WhereClause::Implemented(trait_ref) => trait_ref.hir_fmt(f)?, | 839 | WhereClause::Implemented(trait_ref) => trait_ref.hir_fmt(f)?, |
813 | WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => { | 840 | WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => { |
814 | write!(f, "<")?; | 841 | write!(f, "<")?; |
815 | projection_ty.trait_ref(f.db).hir_fmt_ext(f, true)?; | 842 | fmt_trait_ref(&projection_ty.trait_ref(f.db), f, true)?; |
816 | write!( | 843 | write!( |
817 | f, | 844 | f, |
818 | ">::{} = ", | 845 | ">::{} = ", |
@@ -821,20 +848,44 @@ impl HirDisplay for WhereClause { | |||
821 | ty.hir_fmt(f)?; | 848 | ty.hir_fmt(f)?; |
822 | } | 849 | } |
823 | WhereClause::AliasEq(_) => write!(f, "{{error}}")?, | 850 | WhereClause::AliasEq(_) => write!(f, "{{error}}")?, |
851 | |||
852 | // FIXME implement these | ||
853 | WhereClause::TypeOutlives(..) => {} | ||
854 | WhereClause::LifetimeOutlives(..) => {} | ||
824 | } | 855 | } |
825 | Ok(()) | 856 | Ok(()) |
826 | } | 857 | } |
827 | } | 858 | } |
828 | 859 | ||
860 | impl HirDisplay for LifetimeOutlives { | ||
861 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
862 | self.a.hir_fmt(f)?; | ||
863 | write!(f, ": ")?; | ||
864 | self.b.hir_fmt(f) | ||
865 | } | ||
866 | } | ||
867 | |||
829 | impl HirDisplay for Lifetime { | 868 | impl HirDisplay for Lifetime { |
830 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | 869 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
870 | self.interned().hir_fmt(f) | ||
871 | } | ||
872 | } | ||
873 | |||
874 | impl HirDisplay for LifetimeData { | ||
875 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
831 | match self { | 876 | match self { |
832 | Lifetime::Parameter(id) => { | 877 | LifetimeData::BoundVar(idx) => idx.hir_fmt(f), |
878 | LifetimeData::InferenceVar(_) => write!(f, "_"), | ||
879 | LifetimeData::Placeholder(idx) => { | ||
880 | let id = lt_from_placeholder_idx(f.db, *idx); | ||
833 | let generics = generics(f.db.upcast(), id.parent); | 881 | let generics = generics(f.db.upcast(), id.parent); |
834 | let param_data = &generics.params.lifetimes[id.local_id]; | 882 | let param_data = &generics.params.lifetimes[id.local_id]; |
835 | write!(f, "{}", ¶m_data.name) | 883 | write!(f, "{}", param_data.name) |
836 | } | 884 | } |
837 | Lifetime::Static => write!(f, "'static"), | 885 | LifetimeData::Static => write!(f, "'static"), |
886 | LifetimeData::Empty(_) => Ok(()), | ||
887 | LifetimeData::Erased => Ok(()), | ||
888 | LifetimeData::Phantom(_, _) => Ok(()), | ||
838 | } | 889 | } |
839 | } | 890 | } |
840 | } | 891 | } |
@@ -845,9 +896,11 @@ impl HirDisplay for DomainGoal { | |||
845 | DomainGoal::Holds(wc) => { | 896 | DomainGoal::Holds(wc) => { |
846 | write!(f, "Holds(")?; | 897 | write!(f, "Holds(")?; |
847 | wc.hir_fmt(f)?; | 898 | wc.hir_fmt(f)?; |
848 | write!(f, ")") | 899 | write!(f, ")")?; |
849 | } | 900 | } |
901 | _ => write!(f, "?")?, | ||
850 | } | 902 | } |
903 | Ok(()) | ||
851 | } | 904 | } |
852 | } | 905 | } |
853 | 906 | ||
@@ -945,6 +998,18 @@ impl HirDisplay for TypeRef { | |||
945 | write!(f, "dyn ")?; | 998 | write!(f, "dyn ")?; |
946 | f.write_joined(bounds, " + ")?; | 999 | f.write_joined(bounds, " + ")?; |
947 | } | 1000 | } |
1001 | TypeRef::Macro(macro_call) => { | ||
1002 | let macro_call = macro_call.to_node(f.db.upcast()); | ||
1003 | let ctx = body::LowerCtx::with_hygiene(&Hygiene::new_unhygienic()); | ||
1004 | match macro_call.path() { | ||
1005 | Some(path) => match Path::from_src(path, &ctx) { | ||
1006 | Some(path) => path.hir_fmt(f)?, | ||
1007 | None => write!(f, "{{macro}}")?, | ||
1008 | }, | ||
1009 | None => write!(f, "{{macro}}")?, | ||
1010 | } | ||
1011 | write!(f, "!(..)")?; | ||
1012 | } | ||
948 | TypeRef::Error => write!(f, "{{error}}")?, | 1013 | TypeRef::Error => write!(f, "{{error}}")?, |
949 | } | 1014 | } |
950 | Ok(()) | 1015 | Ok(()) |
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 1b1d4458c..bf2da2d4a 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs | |||
@@ -18,7 +18,7 @@ use std::mem; | |||
18 | use std::ops::Index; | 18 | use std::ops::Index; |
19 | use std::sync::Arc; | 19 | use std::sync::Arc; |
20 | 20 | ||
21 | use chalk_ir::{cast::Cast, Mutability}; | 21 | use chalk_ir::{cast::Cast, DebruijnIndex, Mutability}; |
22 | use hir_def::{ | 22 | use hir_def::{ |
23 | body::Body, | 23 | body::Body, |
24 | data::{ConstData, FunctionData, StaticData}, | 24 | data::{ConstData, FunctionData, StaticData}, |
@@ -37,12 +37,12 @@ use stdx::impl_from; | |||
37 | use syntax::SmolStr; | 37 | use syntax::SmolStr; |
38 | 38 | ||
39 | use super::{ | 39 | use super::{ |
40 | traits::{DomainGoal, Guidance, Solution}, | 40 | DomainGoal, Guidance, InEnvironment, ProjectionTy, Solution, TraitEnvironment, TraitRef, Ty, |
41 | InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty, TypeWalk, | ||
42 | }; | 41 | }; |
43 | use crate::{ | 42 | use crate::{ |
44 | db::HirDatabase, infer::diagnostics::InferenceDiagnostic, lower::ImplTraitLoweringMode, | 43 | db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic, |
45 | to_assoc_type_id, AliasEq, AliasTy, Interner, TyBuilder, TyKind, | 44 | lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Canonical, Interner, |
45 | TyBuilder, TyExt, TyKind, | ||
46 | }; | 46 | }; |
47 | 47 | ||
48 | // This lint has a false positive here. See the link below for details. | 48 | // This lint has a false positive here. See the link below for details. |
@@ -120,7 +120,7 @@ struct InternedStandardTypes { | |||
120 | 120 | ||
121 | impl Default for InternedStandardTypes { | 121 | impl Default for InternedStandardTypes { |
122 | fn default() -> Self { | 122 | fn default() -> Self { |
123 | InternedStandardTypes { unknown: TyKind::Unknown.intern(&Interner) } | 123 | InternedStandardTypes { unknown: TyKind::Error.intern(&Interner) } |
124 | } | 124 | } |
125 | } | 125 | } |
126 | 126 | ||
@@ -131,10 +131,7 @@ pub struct InferenceResult { | |||
131 | method_resolutions: FxHashMap<ExprId, FunctionId>, | 131 | method_resolutions: FxHashMap<ExprId, FunctionId>, |
132 | /// For each field access expr, records the field it resolves to. | 132 | /// For each field access expr, records the field it resolves to. |
133 | field_resolutions: FxHashMap<ExprId, FieldId>, | 133 | field_resolutions: FxHashMap<ExprId, FieldId>, |
134 | /// For each field in record literal, records the field it resolves to. | 134 | /// For each struct literal or pattern, records the variant it resolves to. |
135 | record_field_resolutions: FxHashMap<ExprId, FieldId>, | ||
136 | record_pat_field_resolutions: FxHashMap<PatId, FieldId>, | ||
137 | /// For each struct literal, records the variant it resolves to. | ||
138 | variant_resolutions: FxHashMap<ExprOrPatId, VariantId>, | 135 | variant_resolutions: FxHashMap<ExprOrPatId, VariantId>, |
139 | /// For each associated item record what it resolves to | 136 | /// For each associated item record what it resolves to |
140 | assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>, | 137 | assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>, |
@@ -153,12 +150,6 @@ impl InferenceResult { | |||
153 | pub fn field_resolution(&self, expr: ExprId) -> Option<FieldId> { | 150 | pub fn field_resolution(&self, expr: ExprId) -> Option<FieldId> { |
154 | self.field_resolutions.get(&expr).copied() | 151 | self.field_resolutions.get(&expr).copied() |
155 | } | 152 | } |
156 | pub fn record_field_resolution(&self, expr: ExprId) -> Option<FieldId> { | ||
157 | self.record_field_resolutions.get(&expr).copied() | ||
158 | } | ||
159 | pub fn record_pat_field_resolution(&self, pat: PatId) -> Option<FieldId> { | ||
160 | self.record_pat_field_resolutions.get(&pat).copied() | ||
161 | } | ||
162 | pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantId> { | 153 | pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantId> { |
163 | self.variant_resolutions.get(&id.into()).copied() | 154 | self.variant_resolutions.get(&id.into()).copied() |
164 | } | 155 | } |
@@ -247,7 +238,7 @@ impl<'a> InferenceContext<'a> { | |||
247 | table: unify::InferenceTable::new(), | 238 | table: unify::InferenceTable::new(), |
248 | obligations: Vec::default(), | 239 | obligations: Vec::default(), |
249 | last_obligations_check: None, | 240 | last_obligations_check: None, |
250 | return_ty: TyKind::Unknown.intern(&Interner), // set in collect_fn_signature | 241 | return_ty: TyKind::Error.intern(&Interner), // set in collect_fn_signature |
251 | trait_env: owner | 242 | trait_env: owner |
252 | .as_generic_def_id() | 243 | .as_generic_def_id() |
253 | .map_or_else(Default::default, |d| db.trait_environment(d)), | 244 | .map_or_else(Default::default, |d| db.trait_environment(d)), |
@@ -261,7 +252,7 @@ impl<'a> InferenceContext<'a> { | |||
261 | } | 252 | } |
262 | 253 | ||
263 | fn err_ty(&self) -> Ty { | 254 | fn err_ty(&self) -> Ty { |
264 | TyKind::Unknown.intern(&Interner) | 255 | TyKind::Error.intern(&Interner) |
265 | } | 256 | } |
266 | 257 | ||
267 | fn resolve_all(mut self) -> InferenceResult { | 258 | fn resolve_all(mut self) -> InferenceResult { |
@@ -326,13 +317,13 @@ impl<'a> InferenceContext<'a> { | |||
326 | /// Replaces Ty::Unknown by a new type var, so we can maybe still infer it. | 317 | /// Replaces Ty::Unknown by a new type var, so we can maybe still infer it. |
327 | fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { | 318 | fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { |
328 | match ty.kind(&Interner) { | 319 | match ty.kind(&Interner) { |
329 | TyKind::Unknown => self.table.new_type_var(), | 320 | TyKind::Error => self.table.new_type_var(), |
330 | _ => ty, | 321 | _ => ty, |
331 | } | 322 | } |
332 | } | 323 | } |
333 | 324 | ||
334 | fn insert_type_vars(&mut self, ty: Ty) -> Ty { | 325 | fn insert_type_vars(&mut self, ty: Ty) -> Ty { |
335 | ty.fold(&mut |ty| self.insert_type_vars_shallow(ty)) | 326 | fold_tys(ty, |ty, _| self.insert_type_vars_shallow(ty), DebruijnIndex::INNERMOST) |
336 | } | 327 | } |
337 | 328 | ||
338 | fn resolve_obligations_as_possible(&mut self) { | 329 | fn resolve_obligations_as_possible(&mut self) { |
@@ -345,17 +336,24 @@ impl<'a> InferenceContext<'a> { | |||
345 | self.last_obligations_check = Some(self.table.revision); | 336 | self.last_obligations_check = Some(self.table.revision); |
346 | let obligations = mem::replace(&mut self.obligations, Vec::new()); | 337 | let obligations = mem::replace(&mut self.obligations, Vec::new()); |
347 | for obligation in obligations { | 338 | for obligation in obligations { |
348 | let in_env = InEnvironment::new(self.trait_env.env.clone(), obligation.clone()); | 339 | let in_env = InEnvironment::new(&self.trait_env.env, obligation.clone()); |
349 | let canonicalized = self.canonicalizer().canonicalize_obligation(in_env); | 340 | let canonicalized = self.canonicalizer().canonicalize_obligation(in_env); |
350 | let solution = | 341 | let solution = |
351 | self.db.trait_solve(self.resolver.krate().unwrap(), canonicalized.value.clone()); | 342 | self.db.trait_solve(self.resolver.krate().unwrap(), canonicalized.value.clone()); |
352 | 343 | ||
353 | match solution { | 344 | match solution { |
354 | Some(Solution::Unique(substs)) => { | 345 | Some(Solution::Unique(canonical_subst)) => { |
355 | canonicalized.apply_solution(self, substs.0); | 346 | canonicalized.apply_solution( |
347 | self, | ||
348 | Canonical { | ||
349 | binders: canonical_subst.binders, | ||
350 | // FIXME: handle constraints | ||
351 | value: canonical_subst.value.subst, | ||
352 | }, | ||
353 | ); | ||
356 | } | 354 | } |
357 | Some(Solution::Ambig(Guidance::Definite(substs))) => { | 355 | Some(Solution::Ambig(Guidance::Definite(substs))) => { |
358 | canonicalized.apply_solution(self, substs.0); | 356 | canonicalized.apply_solution(self, substs); |
359 | self.obligations.push(obligation); | 357 | self.obligations.push(obligation); |
360 | } | 358 | } |
361 | Some(_) => { | 359 | Some(_) => { |
@@ -436,12 +434,16 @@ impl<'a> InferenceContext<'a> { | |||
436 | /// to do it as well. | 434 | /// to do it as well. |
437 | fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty { | 435 | fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty { |
438 | let ty = self.resolve_ty_as_possible(ty); | 436 | let ty = self.resolve_ty_as_possible(ty); |
439 | ty.fold(&mut |ty| match ty.kind(&Interner) { | 437 | fold_tys( |
440 | TyKind::Alias(AliasTy::Projection(proj_ty)) => { | 438 | ty, |
441 | self.normalize_projection_ty(proj_ty.clone()) | 439 | |ty, _| match ty.kind(&Interner) { |
442 | } | 440 | TyKind::Alias(AliasTy::Projection(proj_ty)) => { |
443 | _ => ty, | 441 | self.normalize_projection_ty(proj_ty.clone()) |
444 | }) | 442 | } |
443 | _ => ty, | ||
444 | }, | ||
445 | DebruijnIndex::INNERMOST, | ||
446 | ) | ||
445 | } | 447 | } |
446 | 448 | ||
447 | fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty { | 449 | fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty { |
@@ -470,55 +472,32 @@ impl<'a> InferenceContext<'a> { | |||
470 | TypeNs::AdtId(AdtId::StructId(strukt)) => { | 472 | TypeNs::AdtId(AdtId::StructId(strukt)) => { |
471 | let substs = ctx.substs_from_path(path, strukt.into(), true); | 473 | let substs = ctx.substs_from_path(path, strukt.into(), true); |
472 | let ty = self.db.ty(strukt.into()); | 474 | let ty = self.db.ty(strukt.into()); |
473 | let ty = self.insert_type_vars(ty.subst(&substs)); | 475 | let ty = self.insert_type_vars(ty.substitute(&Interner, &substs)); |
474 | forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) | 476 | forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) |
475 | } | 477 | } |
476 | TypeNs::AdtId(AdtId::UnionId(u)) => { | 478 | TypeNs::AdtId(AdtId::UnionId(u)) => { |
477 | let substs = ctx.substs_from_path(path, u.into(), true); | 479 | let substs = ctx.substs_from_path(path, u.into(), true); |
478 | let ty = self.db.ty(u.into()); | 480 | let ty = self.db.ty(u.into()); |
479 | let ty = self.insert_type_vars(ty.subst(&substs)); | 481 | let ty = self.insert_type_vars(ty.substitute(&Interner, &substs)); |
480 | forbid_unresolved_segments((ty, Some(u.into())), unresolved) | 482 | forbid_unresolved_segments((ty, Some(u.into())), unresolved) |
481 | } | 483 | } |
482 | TypeNs::EnumVariantId(var) => { | 484 | TypeNs::EnumVariantId(var) => { |
483 | let substs = ctx.substs_from_path(path, var.into(), true); | 485 | let substs = ctx.substs_from_path(path, var.into(), true); |
484 | let ty = self.db.ty(var.parent.into()); | 486 | let ty = self.db.ty(var.parent.into()); |
485 | let ty = self.insert_type_vars(ty.subst(&substs)); | 487 | let ty = self.insert_type_vars(ty.substitute(&Interner, &substs)); |
486 | forbid_unresolved_segments((ty, Some(var.into())), unresolved) | 488 | forbid_unresolved_segments((ty, Some(var.into())), unresolved) |
487 | } | 489 | } |
488 | TypeNs::SelfType(impl_id) => { | 490 | TypeNs::SelfType(impl_id) => { |
489 | let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); | 491 | let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); |
490 | let substs = generics.type_params_subst(self.db); | 492 | let substs = generics.type_params_subst(self.db); |
491 | let ty = self.db.impl_self_ty(impl_id).subst(&substs); | 493 | let ty = self.db.impl_self_ty(impl_id).substitute(&Interner, &substs); |
492 | match unresolved { | 494 | self.resolve_variant_on_alias(ty, unresolved, path) |
493 | None => { | ||
494 | let variant = ty_variant(&ty); | ||
495 | (ty, variant) | ||
496 | } | ||
497 | Some(1) => { | ||
498 | let segment = path.mod_path().segments().last().unwrap(); | ||
499 | // this could be an enum variant or associated type | ||
500 | if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() { | ||
501 | let enum_data = self.db.enum_data(enum_id); | ||
502 | if let Some(local_id) = enum_data.variant(segment) { | ||
503 | let variant = EnumVariantId { parent: enum_id, local_id }; | ||
504 | return (ty, Some(variant.into())); | ||
505 | } | ||
506 | } | ||
507 | // FIXME potentially resolve assoc type | ||
508 | (self.err_ty(), None) | ||
509 | } | ||
510 | Some(_) => { | ||
511 | // FIXME diagnostic | ||
512 | (self.err_ty(), None) | ||
513 | } | ||
514 | } | ||
515 | } | 495 | } |
516 | TypeNs::TypeAliasId(it) => { | 496 | TypeNs::TypeAliasId(it) => { |
517 | let ty = TyBuilder::def_ty(self.db, it.into()) | 497 | let ty = TyBuilder::def_ty(self.db, it.into()) |
518 | .fill(std::iter::repeat_with(|| self.table.new_type_var())) | 498 | .fill(std::iter::repeat_with(|| self.table.new_type_var())) |
519 | .build(); | 499 | .build(); |
520 | let variant = ty_variant(&ty); | 500 | self.resolve_variant_on_alias(ty, unresolved, path) |
521 | forbid_unresolved_segments((ty, variant), unresolved) | ||
522 | } | 501 | } |
523 | TypeNs::AdtSelfType(_) => { | 502 | TypeNs::AdtSelfType(_) => { |
524 | // FIXME this could happen in array size expressions, once we're checking them | 503 | // FIXME this could happen in array size expressions, once we're checking them |
@@ -542,19 +521,46 @@ impl<'a> InferenceContext<'a> { | |||
542 | result | 521 | result |
543 | } else { | 522 | } else { |
544 | // FIXME diagnostic | 523 | // FIXME diagnostic |
545 | (TyKind::Unknown.intern(&Interner), None) | 524 | (TyKind::Error.intern(&Interner), None) |
546 | } | 525 | } |
547 | } | 526 | } |
527 | } | ||
548 | 528 | ||
549 | fn ty_variant(ty: &Ty) -> Option<VariantId> { | 529 | fn resolve_variant_on_alias( |
550 | ty.as_adt().and_then(|(adt_id, _)| match adt_id { | 530 | &mut self, |
551 | AdtId::StructId(s) => Some(VariantId::StructId(s)), | 531 | ty: Ty, |
552 | AdtId::UnionId(u) => Some(VariantId::UnionId(u)), | 532 | unresolved: Option<usize>, |
553 | AdtId::EnumId(_) => { | 533 | path: &Path, |
554 | // FIXME Error E0071, expected struct, variant or union type, found enum `Foo` | 534 | ) -> (Ty, Option<VariantId>) { |
555 | None | 535 | match unresolved { |
536 | None => { | ||
537 | let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id { | ||
538 | AdtId::StructId(s) => Some(VariantId::StructId(s)), | ||
539 | AdtId::UnionId(u) => Some(VariantId::UnionId(u)), | ||
540 | AdtId::EnumId(_) => { | ||
541 | // FIXME Error E0071, expected struct, variant or union type, found enum `Foo` | ||
542 | None | ||
543 | } | ||
544 | }); | ||
545 | (ty, variant) | ||
546 | } | ||
547 | Some(1) => { | ||
548 | let segment = path.mod_path().segments().last().unwrap(); | ||
549 | // this could be an enum variant or associated type | ||
550 | if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() { | ||
551 | let enum_data = self.db.enum_data(enum_id); | ||
552 | if let Some(local_id) = enum_data.variant(segment) { | ||
553 | let variant = EnumVariantId { parent: enum_id, local_id }; | ||
554 | return (ty, Some(variant.into())); | ||
555 | } | ||
556 | } | 556 | } |
557 | }) | 557 | // FIXME potentially resolve assoc type |
558 | (self.err_ty(), None) | ||
559 | } | ||
560 | Some(_) => { | ||
561 | // FIXME diagnostic | ||
562 | (self.err_ty(), None) | ||
563 | } | ||
558 | } | 564 | } |
559 | } | 565 | } |
560 | 566 | ||
@@ -692,25 +698,6 @@ impl<'a> InferenceContext<'a> { | |||
692 | } | 698 | } |
693 | } | 699 | } |
694 | 700 | ||
695 | /// The kinds of placeholders we need during type inference. There's separate | ||
696 | /// values for general types, and for integer and float variables. The latter | ||
697 | /// two are used for inference of literal values (e.g. `100` could be one of | ||
698 | /// several integer types). | ||
699 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] | ||
700 | pub struct InferenceVar { | ||
701 | index: u32, | ||
702 | } | ||
703 | |||
704 | impl InferenceVar { | ||
705 | fn to_inner(self) -> unify::TypeVarId { | ||
706 | unify::TypeVarId(self.index) | ||
707 | } | ||
708 | |||
709 | fn from_inner(unify::TypeVarId(index): unify::TypeVarId) -> Self { | ||
710 | InferenceVar { index } | ||
711 | } | ||
712 | } | ||
713 | |||
714 | /// When inferring an expression, we propagate downward whatever type hint we | 701 | /// When inferring an expression, we propagate downward whatever type hint we |
715 | /// are able in the form of an `Expectation`. | 702 | /// are able in the form of an `Expectation`. |
716 | #[derive(Clone, PartialEq, Eq, Debug)] | 703 | #[derive(Clone, PartialEq, Eq, Debug)] |
@@ -755,7 +742,7 @@ impl Expectation { | |||
755 | fn none() -> Self { | 742 | fn none() -> Self { |
756 | Expectation { | 743 | Expectation { |
757 | // FIXME | 744 | // FIXME |
758 | ty: TyKind::Unknown.intern(&Interner), | 745 | ty: TyKind::Error.intern(&Interner), |
759 | rvalue_hint: false, | 746 | rvalue_hint: false, |
760 | } | 747 | } |
761 | } | 748 | } |
@@ -763,7 +750,7 @@ impl Expectation { | |||
763 | fn coercion_target(&self) -> Ty { | 750 | fn coercion_target(&self) -> Ty { |
764 | if self.rvalue_hint { | 751 | if self.rvalue_hint { |
765 | // FIXME | 752 | // FIXME |
766 | TyKind::Unknown.intern(&Interner) | 753 | TyKind::Error.intern(&Interner) |
767 | } else { | 754 | } else { |
768 | self.ty.clone() | 755 | self.ty.clone() |
769 | } | 756 | } |
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 028a4d568..1f463a425 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs | |||
@@ -7,7 +7,7 @@ | |||
7 | use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; | 7 | use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; |
8 | use hir_def::lang_item::LangItemTarget; | 8 | use hir_def::lang_item::LangItemTarget; |
9 | 9 | ||
10 | use crate::{autoderef, traits::Solution, Interner, Ty, TyBuilder, TyKind}; | 10 | use crate::{autoderef, Canonical, Interner, Solution, Ty, TyBuilder, TyExt, TyKind}; |
11 | 11 | ||
12 | use super::{InEnvironment, InferenceContext}; | 12 | use super::{InEnvironment, InferenceContext}; |
13 | 13 | ||
@@ -71,17 +71,19 @@ impl<'a> InferenceContext<'a> { | |||
71 | } | 71 | } |
72 | 72 | ||
73 | // Pointer weakening and function to pointer | 73 | // Pointer weakening and function to pointer |
74 | match (from_ty.interned_mut(), to_ty.kind(&Interner)) { | 74 | match (from_ty.kind(&Interner), to_ty.kind(&Interner)) { |
75 | // `*mut T` -> `*const T` | 75 | // `*mut T` -> `*const T` |
76 | (TyKind::Raw(_, inner), TyKind::Raw(m2 @ Mutability::Not, ..)) => { | ||
77 | from_ty = TyKind::Raw(*m2, inner.clone()).intern(&Interner); | ||
78 | } | ||
76 | // `&mut T` -> `&T` | 79 | // `&mut T` -> `&T` |
77 | (TyKind::Raw(m1, ..), TyKind::Raw(m2 @ Mutability::Not, ..)) | 80 | (TyKind::Ref(_, lt, inner), TyKind::Ref(m2 @ Mutability::Not, ..)) => { |
78 | | (TyKind::Ref(m1, ..), TyKind::Ref(m2 @ Mutability::Not, ..)) => { | 81 | from_ty = TyKind::Ref(*m2, lt.clone(), inner.clone()).intern(&Interner); |
79 | *m1 = *m2; | ||
80 | } | 82 | } |
81 | // `&T` -> `*const T` | 83 | // `&T` -> `*const T` |
82 | // `&mut T` -> `*mut T`/`*const T` | 84 | // `&mut T` -> `*mut T`/`*const T` |
83 | (TyKind::Ref(.., substs), &TyKind::Raw(m2 @ Mutability::Not, ..)) | 85 | (TyKind::Ref(.., substs), &TyKind::Raw(m2 @ Mutability::Not, ..)) |
84 | | (TyKind::Ref(Mutability::Mut, substs), &TyKind::Raw(m2, ..)) => { | 86 | | (TyKind::Ref(Mutability::Mut, _, substs), &TyKind::Raw(m2, ..)) => { |
85 | from_ty = TyKind::Raw(m2, substs.clone()).intern(&Interner); | 87 | from_ty = TyKind::Raw(m2, substs.clone()).intern(&Interner); |
86 | } | 88 | } |
87 | 89 | ||
@@ -111,7 +113,9 @@ impl<'a> InferenceContext<'a> { | |||
111 | // Auto Deref if cannot coerce | 113 | // Auto Deref if cannot coerce |
112 | match (from_ty.kind(&Interner), to_ty.kind(&Interner)) { | 114 | match (from_ty.kind(&Interner), to_ty.kind(&Interner)) { |
113 | // FIXME: DerefMut | 115 | // FIXME: DerefMut |
114 | (TyKind::Ref(_, st1), TyKind::Ref(_, st2)) => self.unify_autoderef_behind_ref(st1, st2), | 116 | (TyKind::Ref(.., st1), TyKind::Ref(.., st2)) => { |
117 | self.unify_autoderef_behind_ref(st1, st2) | ||
118 | } | ||
115 | 119 | ||
116 | // Otherwise, normal unify | 120 | // Otherwise, normal unify |
117 | _ => self.unify(&from_ty, to_ty), | 121 | _ => self.unify(&from_ty, to_ty), |
@@ -137,7 +141,7 @@ impl<'a> InferenceContext<'a> { | |||
137 | b.push(from_ty.clone()).push(to_ty.clone()).build() | 141 | b.push(from_ty.clone()).push(to_ty.clone()).build() |
138 | }; | 142 | }; |
139 | 143 | ||
140 | let goal = InEnvironment::new(self.trait_env.env.clone(), trait_ref.cast(&Interner)); | 144 | let goal = InEnvironment::new(&self.trait_env.env, trait_ref.cast(&Interner)); |
141 | 145 | ||
142 | let canonicalizer = self.canonicalizer(); | 146 | let canonicalizer = self.canonicalizer(); |
143 | let canonicalized = canonicalizer.canonicalize_obligation(goal); | 147 | let canonicalized = canonicalizer.canonicalize_obligation(goal); |
@@ -146,7 +150,14 @@ impl<'a> InferenceContext<'a> { | |||
146 | 150 | ||
147 | match solution { | 151 | match solution { |
148 | Solution::Unique(v) => { | 152 | Solution::Unique(v) => { |
149 | canonicalized.apply_solution(self, v.0); | 153 | canonicalized.apply_solution( |
154 | self, | ||
155 | Canonical { | ||
156 | binders: v.binders, | ||
157 | // FIXME handle constraints | ||
158 | value: v.value.subst, | ||
159 | }, | ||
160 | ); | ||
150 | } | 161 | } |
151 | _ => return None, | 162 | _ => return None, |
152 | }; | 163 | }; |
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index c584a2c08..50497eecb 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs | |||
@@ -3,7 +3,7 @@ | |||
3 | use std::iter::{repeat, repeat_with}; | 3 | use std::iter::{repeat, repeat_with}; |
4 | use std::{mem, sync::Arc}; | 4 | use std::{mem, sync::Arc}; |
5 | 5 | ||
6 | use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; | 6 | use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind}; |
7 | use hir_def::{ | 7 | use hir_def::{ |
8 | expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp}, | 8 | expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp}, |
9 | path::{GenericArg, GenericArgs}, | 9 | path::{GenericArg, GenericArgs}, |
@@ -15,15 +15,16 @@ use stdx::always; | |||
15 | use syntax::ast::RangeOp; | 15 | use syntax::ast::RangeOp; |
16 | 16 | ||
17 | use crate::{ | 17 | use crate::{ |
18 | autoderef, | 18 | autoderef, dummy_usize_const, |
19 | lower::lower_to_chalk_mutability, | 19 | lower::lower_to_chalk_mutability, |
20 | mapping::from_chalk, | ||
20 | method_resolution, op, | 21 | method_resolution, op, |
21 | primitive::{self, UintTy}, | 22 | primitive::{self, UintTy}, |
22 | to_chalk_trait_id, | 23 | static_lifetime, to_chalk_trait_id, |
23 | traits::{chalk::from_chalk, FnTrait, InEnvironment}, | 24 | traits::FnTrait, |
24 | utils::{generics, variant_data, Generics}, | 25 | utils::{generics, Generics}, |
25 | AdtId, Binders, CallableDefId, FnPointer, FnSig, Interner, Rawness, Scalar, Substitution, | 26 | AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, InEnvironment, Interner, |
26 | TraitRef, Ty, TyBuilder, TyKind, | 27 | ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind, |
27 | }; | 28 | }; |
28 | 29 | ||
29 | use super::{ | 30 | use super::{ |
@@ -180,7 +181,8 @@ impl<'a> InferenceContext<'a> { | |||
180 | let inner_ty = self.infer_expr(*body, &Expectation::none()); | 181 | let inner_ty = self.infer_expr(*body, &Expectation::none()); |
181 | let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, *body); | 182 | let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, *body); |
182 | let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); | 183 | let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); |
183 | TyKind::OpaqueType(opaque_ty_id, Substitution::single(inner_ty)).intern(&Interner) | 184 | TyKind::OpaqueType(opaque_ty_id, Substitution::from1(&Interner, inner_ty)) |
185 | .intern(&Interner) | ||
184 | } | 186 | } |
185 | Expr::Loop { body, label } => { | 187 | Expr::Loop { body, label } => { |
186 | self.breakables.push(BreakableContext { | 188 | self.breakables.push(BreakableContext { |
@@ -259,14 +261,17 @@ impl<'a> InferenceContext<'a> { | |||
259 | }; | 261 | }; |
260 | sig_tys.push(ret_ty.clone()); | 262 | sig_tys.push(ret_ty.clone()); |
261 | let sig_ty = TyKind::Function(FnPointer { | 263 | let sig_ty = TyKind::Function(FnPointer { |
262 | num_args: sig_tys.len() - 1, | 264 | num_binders: 0, |
263 | sig: FnSig { abi: (), safety: chalk_ir::Safety::Safe, variadic: false }, | 265 | sig: FnSig { abi: (), safety: chalk_ir::Safety::Safe, variadic: false }, |
264 | substs: Substitution::from_iter(&Interner, sig_tys.clone()), | 266 | substitution: FnSubst( |
267 | Substitution::from_iter(&Interner, sig_tys.clone()).shifted_in(&Interner), | ||
268 | ), | ||
265 | }) | 269 | }) |
266 | .intern(&Interner); | 270 | .intern(&Interner); |
267 | let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into(); | 271 | let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into(); |
268 | let closure_ty = | 272 | let closure_ty = |
269 | TyKind::Closure(closure_id, Substitution::single(sig_ty)).intern(&Interner); | 273 | TyKind::Closure(closure_id, Substitution::from1(&Interner, sig_ty)) |
274 | .intern(&Interner); | ||
270 | 275 | ||
271 | // Eagerly try to relate the closure type with the expected | 276 | // Eagerly try to relate the closure type with the expected |
272 | // type, otherwise we often won't have enough information to | 277 | // type, otherwise we often won't have enough information to |
@@ -313,7 +318,13 @@ impl<'a> InferenceContext<'a> { | |||
313 | self.normalize_associated_types_in(ret_ty) | 318 | self.normalize_associated_types_in(ret_ty) |
314 | } | 319 | } |
315 | Expr::MethodCall { receiver, args, method_name, generic_args } => self | 320 | Expr::MethodCall { receiver, args, method_name, generic_args } => self |
316 | .infer_method_call(tgt_expr, *receiver, &args, &method_name, generic_args.as_ref()), | 321 | .infer_method_call( |
322 | tgt_expr, | ||
323 | *receiver, | ||
324 | &args, | ||
325 | &method_name, | ||
326 | generic_args.as_deref(), | ||
327 | ), | ||
317 | Expr::Match { expr, arms } => { | 328 | Expr::Match { expr, arms } => { |
318 | let input_ty = self.infer_expr(*expr, &Expectation::none()); | 329 | let input_ty = self.infer_expr(*expr, &Expectation::none()); |
319 | 330 | ||
@@ -394,16 +405,19 @@ impl<'a> InferenceContext<'a> { | |||
394 | TyKind::Never.intern(&Interner) | 405 | TyKind::Never.intern(&Interner) |
395 | } | 406 | } |
396 | Expr::RecordLit { path, fields, spread } => { | 407 | Expr::RecordLit { path, fields, spread } => { |
397 | let (ty, def_id) = self.resolve_variant(path.as_ref()); | 408 | let (ty, def_id) = self.resolve_variant(path.as_deref()); |
398 | if let Some(variant) = def_id { | 409 | if let Some(variant) = def_id { |
399 | self.write_variant_resolution(tgt_expr.into(), variant); | 410 | self.write_variant_resolution(tgt_expr.into(), variant); |
400 | } | 411 | } |
401 | 412 | ||
402 | self.unify(&ty, &expected.ty); | 413 | self.unify(&ty, &expected.ty); |
403 | 414 | ||
404 | let substs = ty.substs().cloned().unwrap_or_else(|| Substitution::empty(&Interner)); | 415 | let substs = ty |
416 | .as_adt() | ||
417 | .map(|(_, s)| s.clone()) | ||
418 | .unwrap_or_else(|| Substitution::empty(&Interner)); | ||
405 | let field_types = def_id.map(|it| self.db.field_types(it)).unwrap_or_default(); | 419 | let field_types = def_id.map(|it| self.db.field_types(it)).unwrap_or_default(); |
406 | let variant_data = def_id.map(|it| variant_data(self.db.upcast(), it)); | 420 | let variant_data = def_id.map(|it| it.variant_data(self.db.upcast())); |
407 | for field in fields.iter() { | 421 | for field in fields.iter() { |
408 | let field_def = | 422 | let field_def = |
409 | variant_data.as_ref().and_then(|it| match it.field(&field.name) { | 423 | variant_data.as_ref().and_then(|it| match it.field(&field.name) { |
@@ -415,11 +429,8 @@ impl<'a> InferenceContext<'a> { | |||
415 | None | 429 | None |
416 | } | 430 | } |
417 | }); | 431 | }); |
418 | if let Some(field_def) = field_def { | ||
419 | self.result.record_field_resolutions.insert(field.expr, field_def); | ||
420 | } | ||
421 | let field_ty = field_def.map_or(self.err_ty(), |it| { | 432 | let field_ty = field_def.map_or(self.err_ty(), |it| { |
422 | field_types[it.local_id].clone().subst(&substs) | 433 | field_types[it.local_id].clone().substitute(&Interner, &substs) |
423 | }); | 434 | }); |
424 | self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty)); | 435 | self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty)); |
425 | } | 436 | } |
@@ -453,7 +464,7 @@ impl<'a> InferenceContext<'a> { | |||
453 | match canonicalized.decanonicalize_ty(derefed_ty.value).kind(&Interner) { | 464 | match canonicalized.decanonicalize_ty(derefed_ty.value).kind(&Interner) { |
454 | TyKind::Tuple(_, substs) => name.as_tuple_index().and_then(|idx| { | 465 | TyKind::Tuple(_, substs) => name.as_tuple_index().and_then(|idx| { |
455 | substs | 466 | substs |
456 | .interned(&Interner) | 467 | .as_slice(&Interner) |
457 | .get(idx) | 468 | .get(idx) |
458 | .map(|a| a.assert_ty_ref(&Interner)) | 469 | .map(|a| a.assert_ty_ref(&Interner)) |
459 | .cloned() | 470 | .cloned() |
@@ -466,7 +477,7 @@ impl<'a> InferenceContext<'a> { | |||
466 | Some( | 477 | Some( |
467 | self.db.field_types((*s).into())[field.local_id] | 478 | self.db.field_types((*s).into())[field.local_id] |
468 | .clone() | 479 | .clone() |
469 | .subst(¶meters), | 480 | .substitute(&Interner, ¶meters), |
470 | ) | 481 | ) |
471 | } else { | 482 | } else { |
472 | None | 483 | None |
@@ -480,7 +491,7 @@ impl<'a> InferenceContext<'a> { | |||
480 | Some( | 491 | Some( |
481 | self.db.field_types((*u).into())[field.local_id] | 492 | self.db.field_types((*u).into())[field.local_id] |
482 | .clone() | 493 | .clone() |
483 | .subst(¶meters), | 494 | .substitute(&Interner, ¶meters), |
484 | ) | 495 | ) |
485 | } else { | 496 | } else { |
486 | None | 497 | None |
@@ -527,7 +538,7 @@ impl<'a> InferenceContext<'a> { | |||
527 | let inner_ty = self.infer_expr_inner(*expr, &expectation); | 538 | let inner_ty = self.infer_expr_inner(*expr, &expectation); |
528 | match rawness { | 539 | match rawness { |
529 | Rawness::RawPtr => TyKind::Raw(mutability, inner_ty), | 540 | Rawness::RawPtr => TyKind::Raw(mutability, inner_ty), |
530 | Rawness::Ref => TyKind::Ref(mutability, inner_ty), | 541 | Rawness::Ref => TyKind::Ref(mutability, static_lifetime(), inner_ty), |
531 | } | 542 | } |
532 | .intern(&Interner) | 543 | .intern(&Interner) |
533 | } | 544 | } |
@@ -702,7 +713,7 @@ impl<'a> InferenceContext<'a> { | |||
702 | } | 713 | } |
703 | Expr::Array(array) => { | 714 | Expr::Array(array) => { |
704 | let elem_ty = match expected.ty.kind(&Interner) { | 715 | let elem_ty = match expected.ty.kind(&Interner) { |
705 | TyKind::Array(st) | TyKind::Slice(st) => st.clone(), | 716 | TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(), |
706 | _ => self.table.new_type_var(), | 717 | _ => self.table.new_type_var(), |
707 | }; | 718 | }; |
708 | 719 | ||
@@ -726,17 +737,19 @@ impl<'a> InferenceContext<'a> { | |||
726 | } | 737 | } |
727 | } | 738 | } |
728 | 739 | ||
729 | TyKind::Array(elem_ty).intern(&Interner) | 740 | TyKind::Array(elem_ty, dummy_usize_const()).intern(&Interner) |
730 | } | 741 | } |
731 | Expr::Literal(lit) => match lit { | 742 | Expr::Literal(lit) => match lit { |
732 | Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner), | 743 | Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner), |
733 | Literal::String(..) => { | 744 | Literal::String(..) => { |
734 | TyKind::Ref(Mutability::Not, TyKind::Str.intern(&Interner)).intern(&Interner) | 745 | TyKind::Ref(Mutability::Not, static_lifetime(), TyKind::Str.intern(&Interner)) |
746 | .intern(&Interner) | ||
735 | } | 747 | } |
736 | Literal::ByteString(..) => { | 748 | Literal::ByteString(..) => { |
737 | let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(&Interner); | 749 | let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(&Interner); |
738 | let array_type = TyKind::Array(byte_type).intern(&Interner); | 750 | let array_type = |
739 | TyKind::Ref(Mutability::Not, array_type).intern(&Interner) | 751 | TyKind::Array(byte_type, dummy_usize_const()).intern(&Interner); |
752 | TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(&Interner) | ||
740 | } | 753 | } |
741 | Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(&Interner), | 754 | Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(&Interner), |
742 | Literal::Int(_v, ty) => match ty { | 755 | Literal::Int(_v, ty) => match ty { |
@@ -853,10 +866,10 @@ impl<'a> InferenceContext<'a> { | |||
853 | self.write_method_resolution(tgt_expr, func); | 866 | self.write_method_resolution(tgt_expr, func); |
854 | (ty, self.db.value_ty(func.into()), Some(generics(self.db.upcast(), func.into()))) | 867 | (ty, self.db.value_ty(func.into()), Some(generics(self.db.upcast(), func.into()))) |
855 | } | 868 | } |
856 | None => (receiver_ty, Binders::new(0, self.err_ty()), None), | 869 | None => (receiver_ty, Binders::empty(&Interner, self.err_ty()), None), |
857 | }; | 870 | }; |
858 | let substs = self.substs_for_method_call(def_generics, generic_args, &derefed_receiver_ty); | 871 | let substs = self.substs_for_method_call(def_generics, generic_args, &derefed_receiver_ty); |
859 | let method_ty = method_ty.subst(&substs); | 872 | let method_ty = method_ty.substitute(&Interner, &substs); |
860 | let method_ty = self.insert_type_vars(method_ty); | 873 | let method_ty = self.insert_type_vars(method_ty); |
861 | self.register_obligations_for_call(&method_ty); | 874 | self.register_obligations_for_call(&method_ty); |
862 | let (expected_receiver_ty, param_tys, ret_ty) = match method_ty.callable_sig(self.db) { | 875 | let (expected_receiver_ty, param_tys, ret_ty) = match method_ty.callable_sig(self.db) { |
@@ -872,7 +885,9 @@ impl<'a> InferenceContext<'a> { | |||
872 | // Apply autoref so the below unification works correctly | 885 | // Apply autoref so the below unification works correctly |
873 | // FIXME: return correct autorefs from lookup_method | 886 | // FIXME: return correct autorefs from lookup_method |
874 | let actual_receiver_ty = match expected_receiver_ty.as_reference() { | 887 | let actual_receiver_ty = match expected_receiver_ty.as_reference() { |
875 | Some((_, mutability)) => TyKind::Ref(mutability, derefed_receiver_ty).intern(&Interner), | 888 | Some((_, lifetime, mutability)) => { |
889 | TyKind::Ref(mutability, lifetime, derefed_receiver_ty).intern(&Interner) | ||
890 | } | ||
876 | _ => derefed_receiver_ty, | 891 | _ => derefed_receiver_ty, |
877 | }; | 892 | }; |
878 | self.unify(&expected_receiver_ty, &actual_receiver_ty); | 893 | self.unify(&expected_receiver_ty, &actual_receiver_ty); |
@@ -953,9 +968,11 @@ impl<'a> InferenceContext<'a> { | |||
953 | let def: CallableDefId = from_chalk(self.db, *fn_def); | 968 | let def: CallableDefId = from_chalk(self.db, *fn_def); |
954 | let generic_predicates = self.db.generic_predicates(def.into()); | 969 | let generic_predicates = self.db.generic_predicates(def.into()); |
955 | for predicate in generic_predicates.iter() { | 970 | for predicate in generic_predicates.iter() { |
956 | let (predicate, binders) = | 971 | let (predicate, binders) = predicate |
957 | predicate.clone().subst(parameters).into_value_and_skipped_binders(); | 972 | .clone() |
958 | always!(binders == 0); // quantified where clauses not yet handled | 973 | .substitute(&Interner, parameters) |
974 | .into_value_and_skipped_binders(); | ||
975 | always!(binders.len(&Interner) == 0); // quantified where clauses not yet handled | ||
959 | self.push_obligation(predicate.cast(&Interner)); | 976 | self.push_obligation(predicate.cast(&Interner)); |
960 | } | 977 | } |
961 | // add obligation for trait implementation, if this is a trait method | 978 | // add obligation for trait implementation, if this is a trait method |
@@ -964,8 +981,10 @@ impl<'a> InferenceContext<'a> { | |||
964 | if let AssocContainerId::TraitId(trait_) = f.lookup(self.db.upcast()).container | 981 | if let AssocContainerId::TraitId(trait_) = f.lookup(self.db.upcast()).container |
965 | { | 982 | { |
966 | // construct a TraitRef | 983 | // construct a TraitRef |
967 | let substs = | 984 | let substs = crate::subst_prefix( |
968 | parameters.prefix(generics(self.db.upcast(), trait_.into()).len()); | 985 | &*parameters, |
986 | generics(self.db.upcast(), trait_.into()).len(), | ||
987 | ); | ||
969 | self.push_obligation( | 988 | self.push_obligation( |
970 | TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: substs } | 989 | TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: substs } |
971 | .cast(&Interner), | 990 | .cast(&Interner), |
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs index 5b70d5e5a..aea354cde 100644 --- a/crates/hir_ty/src/infer/pat.rs +++ b/crates/hir_ty/src/infer/pat.rs | |||
@@ -7,14 +7,13 @@ use chalk_ir::Mutability; | |||
7 | use hir_def::{ | 7 | use hir_def::{ |
8 | expr::{BindingAnnotation, Expr, Literal, Pat, PatId, RecordFieldPat}, | 8 | expr::{BindingAnnotation, Expr, Literal, Pat, PatId, RecordFieldPat}, |
9 | path::Path, | 9 | path::Path, |
10 | FieldId, | ||
11 | }; | 10 | }; |
12 | use hir_expand::name::Name; | 11 | use hir_expand::name::Name; |
13 | 12 | ||
14 | use super::{BindingMode, Expectation, InferenceContext}; | 13 | use super::{BindingMode, Expectation, InferenceContext}; |
15 | use crate::{ | 14 | use crate::{ |
16 | lower::lower_to_chalk_mutability, utils::variant_data, Interner, Substitution, Ty, TyBuilder, | 15 | lower::lower_to_chalk_mutability, static_lifetime, Interner, Substitution, Ty, TyBuilder, |
17 | TyKind, | 16 | TyExt, TyKind, |
18 | }; | 17 | }; |
19 | 18 | ||
20 | impl<'a> InferenceContext<'a> { | 19 | impl<'a> InferenceContext<'a> { |
@@ -28,13 +27,14 @@ impl<'a> InferenceContext<'a> { | |||
28 | ellipsis: Option<usize>, | 27 | ellipsis: Option<usize>, |
29 | ) -> Ty { | 28 | ) -> Ty { |
30 | let (ty, def) = self.resolve_variant(path); | 29 | let (ty, def) = self.resolve_variant(path); |
31 | let var_data = def.map(|it| variant_data(self.db.upcast(), it)); | 30 | let var_data = def.map(|it| it.variant_data(self.db.upcast())); |
32 | if let Some(variant) = def { | 31 | if let Some(variant) = def { |
33 | self.write_variant_resolution(id.into(), variant); | 32 | self.write_variant_resolution(id.into(), variant); |
34 | } | 33 | } |
35 | self.unify(&ty, expected); | 34 | self.unify(&ty, expected); |
36 | 35 | ||
37 | let substs = ty.substs().cloned().unwrap_or_else(|| Substitution::empty(&Interner)); | 36 | let substs = |
37 | ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(&Interner)); | ||
38 | 38 | ||
39 | let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); | 39 | let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); |
40 | let (pre, post) = match ellipsis { | 40 | let (pre, post) = match ellipsis { |
@@ -49,7 +49,9 @@ impl<'a> InferenceContext<'a> { | |||
49 | let expected_ty = var_data | 49 | let expected_ty = var_data |
50 | .as_ref() | 50 | .as_ref() |
51 | .and_then(|d| d.field(&Name::new_tuple_field(i))) | 51 | .and_then(|d| d.field(&Name::new_tuple_field(i))) |
52 | .map_or(self.err_ty(), |field| field_tys[field].clone().subst(&substs)); | 52 | .map_or(self.err_ty(), |field| { |
53 | field_tys[field].clone().substitute(&Interner, &substs) | ||
54 | }); | ||
53 | let expected_ty = self.normalize_associated_types_in(expected_ty); | 55 | let expected_ty = self.normalize_associated_types_in(expected_ty); |
54 | self.infer_pat(subpat, &expected_ty, default_bm); | 56 | self.infer_pat(subpat, &expected_ty, default_bm); |
55 | } | 57 | } |
@@ -66,25 +68,22 @@ impl<'a> InferenceContext<'a> { | |||
66 | id: PatId, | 68 | id: PatId, |
67 | ) -> Ty { | 69 | ) -> Ty { |
68 | let (ty, def) = self.resolve_variant(path); | 70 | let (ty, def) = self.resolve_variant(path); |
69 | let var_data = def.map(|it| variant_data(self.db.upcast(), it)); | 71 | let var_data = def.map(|it| it.variant_data(self.db.upcast())); |
70 | if let Some(variant) = def { | 72 | if let Some(variant) = def { |
71 | self.write_variant_resolution(id.into(), variant); | 73 | self.write_variant_resolution(id.into(), variant); |
72 | } | 74 | } |
73 | 75 | ||
74 | self.unify(&ty, expected); | 76 | self.unify(&ty, expected); |
75 | 77 | ||
76 | let substs = ty.substs().cloned().unwrap_or_else(|| Substitution::empty(&Interner)); | 78 | let substs = |
79 | ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(&Interner)); | ||
77 | 80 | ||
78 | let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); | 81 | let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); |
79 | for subpat in subpats { | 82 | for subpat in subpats { |
80 | let matching_field = var_data.as_ref().and_then(|it| it.field(&subpat.name)); | 83 | let matching_field = var_data.as_ref().and_then(|it| it.field(&subpat.name)); |
81 | if let Some(local_id) = matching_field { | 84 | let expected_ty = matching_field.map_or(self.err_ty(), |field| { |
82 | let field_def = FieldId { parent: def.unwrap(), local_id }; | 85 | field_tys[field].clone().substitute(&Interner, &substs) |
83 | self.result.record_pat_field_resolutions.insert(subpat.pat, field_def); | 86 | }); |
84 | } | ||
85 | |||
86 | let expected_ty = matching_field | ||
87 | .map_or(self.err_ty(), |field| field_tys[field].clone().subst(&substs)); | ||
88 | let expected_ty = self.normalize_associated_types_in(expected_ty); | 87 | let expected_ty = self.normalize_associated_types_in(expected_ty); |
89 | self.infer_pat(subpat.pat, &expected_ty, default_bm); | 88 | self.infer_pat(subpat.pat, &expected_ty, default_bm); |
90 | } | 89 | } |
@@ -101,7 +100,7 @@ impl<'a> InferenceContext<'a> { | |||
101 | let body = Arc::clone(&self.body); // avoid borrow checker problem | 100 | let body = Arc::clone(&self.body); // avoid borrow checker problem |
102 | 101 | ||
103 | if is_non_ref_pat(&body, pat) { | 102 | if is_non_ref_pat(&body, pat) { |
104 | while let Some((inner, mutability)) = expected.as_reference() { | 103 | while let Some((inner, _lifetime, mutability)) = expected.as_reference() { |
105 | expected = inner; | 104 | expected = inner; |
106 | default_bm = match default_bm { | 105 | default_bm = match default_bm { |
107 | BindingMode::Move => BindingMode::Ref(mutability), | 106 | BindingMode::Move => BindingMode::Ref(mutability), |
@@ -123,7 +122,7 @@ impl<'a> InferenceContext<'a> { | |||
123 | let ty = match &body[pat] { | 122 | let ty = match &body[pat] { |
124 | &Pat::Tuple { ref args, ellipsis } => { | 123 | &Pat::Tuple { ref args, ellipsis } => { |
125 | let expectations = match expected.as_tuple() { | 124 | let expectations = match expected.as_tuple() { |
126 | Some(parameters) => &*parameters.0, | 125 | Some(parameters) => &*parameters.as_slice(&Interner), |
127 | _ => &[], | 126 | _ => &[], |
128 | }; | 127 | }; |
129 | 128 | ||
@@ -159,7 +158,7 @@ impl<'a> InferenceContext<'a> { | |||
159 | Pat::Ref { pat, mutability } => { | 158 | Pat::Ref { pat, mutability } => { |
160 | let mutability = lower_to_chalk_mutability(*mutability); | 159 | let mutability = lower_to_chalk_mutability(*mutability); |
161 | let expectation = match expected.as_reference() { | 160 | let expectation = match expected.as_reference() { |
162 | Some((inner_ty, exp_mut)) => { | 161 | Some((inner_ty, _lifetime, exp_mut)) => { |
163 | if mutability != exp_mut { | 162 | if mutability != exp_mut { |
164 | // FIXME: emit type error? | 163 | // FIXME: emit type error? |
165 | } | 164 | } |
@@ -168,10 +167,10 @@ impl<'a> InferenceContext<'a> { | |||
168 | _ => self.result.standard_types.unknown.clone(), | 167 | _ => self.result.standard_types.unknown.clone(), |
169 | }; | 168 | }; |
170 | let subty = self.infer_pat(*pat, &expectation, default_bm); | 169 | let subty = self.infer_pat(*pat, &expectation, default_bm); |
171 | TyKind::Ref(mutability, subty).intern(&Interner) | 170 | TyKind::Ref(mutability, static_lifetime(), subty).intern(&Interner) |
172 | } | 171 | } |
173 | Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat( | 172 | Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat( |
174 | p.as_ref(), | 173 | p.as_deref(), |
175 | subpats, | 174 | subpats, |
176 | expected, | 175 | expected, |
177 | default_bm, | 176 | default_bm, |
@@ -179,7 +178,7 @@ impl<'a> InferenceContext<'a> { | |||
179 | *ellipsis, | 178 | *ellipsis, |
180 | ), | 179 | ), |
181 | Pat::Record { path: p, args: fields, ellipsis: _ } => { | 180 | Pat::Record { path: p, args: fields, ellipsis: _ } => { |
182 | self.infer_record_pat(p.as_ref(), fields, expected, default_bm, pat) | 181 | self.infer_record_pat(p.as_deref(), fields, expected, default_bm, pat) |
183 | } | 182 | } |
184 | Pat::Path(path) => { | 183 | Pat::Path(path) => { |
185 | // FIXME use correct resolver for the surrounding expression | 184 | // FIXME use correct resolver for the surrounding expression |
@@ -201,7 +200,8 @@ impl<'a> InferenceContext<'a> { | |||
201 | 200 | ||
202 | let bound_ty = match mode { | 201 | let bound_ty = match mode { |
203 | BindingMode::Ref(mutability) => { | 202 | BindingMode::Ref(mutability) => { |
204 | TyKind::Ref(mutability, inner_ty.clone()).intern(&Interner) | 203 | TyKind::Ref(mutability, static_lifetime(), inner_ty.clone()) |
204 | .intern(&Interner) | ||
205 | } | 205 | } |
206 | BindingMode::Move => inner_ty.clone(), | 206 | BindingMode::Move => inner_ty.clone(), |
207 | }; | 207 | }; |
@@ -210,17 +210,20 @@ impl<'a> InferenceContext<'a> { | |||
210 | return inner_ty; | 210 | return inner_ty; |
211 | } | 211 | } |
212 | Pat::Slice { prefix, slice, suffix } => { | 212 | Pat::Slice { prefix, slice, suffix } => { |
213 | let (container_ty, elem_ty): (fn(_) -> _, _) = match expected.kind(&Interner) { | 213 | let elem_ty = match expected.kind(&Interner) { |
214 | TyKind::Array(st) => (TyKind::Array, st.clone()), | 214 | TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(), |
215 | TyKind::Slice(st) => (TyKind::Slice, st.clone()), | 215 | _ => self.err_ty(), |
216 | _ => (TyKind::Slice, self.err_ty()), | ||
217 | }; | 216 | }; |
218 | 217 | ||
219 | for pat_id in prefix.iter().chain(suffix) { | 218 | for pat_id in prefix.iter().chain(suffix) { |
220 | self.infer_pat(*pat_id, &elem_ty, default_bm); | 219 | self.infer_pat(*pat_id, &elem_ty, default_bm); |
221 | } | 220 | } |
222 | 221 | ||
223 | let pat_ty = container_ty(elem_ty).intern(&Interner); | 222 | let pat_ty = match expected.kind(&Interner) { |
223 | TyKind::Array(_, const_) => TyKind::Array(elem_ty, const_.clone()), | ||
224 | _ => TyKind::Slice(elem_ty), | ||
225 | } | ||
226 | .intern(&Interner); | ||
224 | if let Some(slice_pat_id) = slice { | 227 | if let Some(slice_pat_id) = slice { |
225 | self.infer_pat(*slice_pat_id, &pat_ty, default_bm); | 228 | self.infer_pat(*slice_pat_id, &pat_ty, default_bm); |
226 | } | 229 | } |
@@ -239,7 +242,7 @@ impl<'a> InferenceContext<'a> { | |||
239 | let (inner_ty, alloc_ty) = match expected.as_adt() { | 242 | let (inner_ty, alloc_ty) = match expected.as_adt() { |
240 | Some((adt, subst)) if adt == box_adt => ( | 243 | Some((adt, subst)) if adt == box_adt => ( |
241 | subst.at(&Interner, 0).assert_ty_ref(&Interner).clone(), | 244 | subst.at(&Interner, 0).assert_ty_ref(&Interner).clone(), |
242 | subst.interned(&Interner).get(1).and_then(|a| a.ty(&Interner).cloned()), | 245 | subst.as_slice(&Interner).get(1).and_then(|a| a.ty(&Interner).cloned()), |
243 | ), | 246 | ), |
244 | _ => (self.result.standard_types.unknown.clone(), None), | 247 | _ => (self.result.standard_types.unknown.clone(), None), |
245 | }; | 248 | }; |
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs index 671ea355f..495282eba 100644 --- a/crates/hir_ty/src/infer/path.rs +++ b/crates/hir_ty/src/infer/path.rs | |||
@@ -10,7 +10,10 @@ use hir_def::{ | |||
10 | }; | 10 | }; |
11 | use hir_expand::name::Name; | 11 | use hir_expand::name::Name; |
12 | 12 | ||
13 | use crate::{method_resolution, Interner, Substitution, Ty, TyBuilder, TyKind, ValueTyDefId}; | 13 | use crate::{ |
14 | method_resolution, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, | ||
15 | ValueTyDefId, | ||
16 | }; | ||
14 | 17 | ||
15 | use super::{ExprOrPatId, InferenceContext, TraitRef}; | 18 | use super::{ExprOrPatId, InferenceContext, TraitRef}; |
16 | 19 | ||
@@ -81,9 +84,9 @@ impl<'a> InferenceContext<'a> { | |||
81 | ValueNs::ImplSelf(impl_id) => { | 84 | ValueNs::ImplSelf(impl_id) => { |
82 | let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); | 85 | let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); |
83 | let substs = generics.type_params_subst(self.db); | 86 | let substs = generics.type_params_subst(self.db); |
84 | let ty = self.db.impl_self_ty(impl_id).subst(&substs); | 87 | let ty = self.db.impl_self_ty(impl_id).substitute(&Interner, &substs); |
85 | if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() { | 88 | if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() { |
86 | let ty = self.db.value_ty(struct_id.into()).subst(&substs); | 89 | let ty = self.db.value_ty(struct_id.into()).substitute(&Interner, &substs); |
87 | return Some(ty); | 90 | return Some(ty); |
88 | } else { | 91 | } else { |
89 | // FIXME: diagnostic, invalid Self reference | 92 | // FIXME: diagnostic, invalid Self reference |
@@ -98,7 +101,7 @@ impl<'a> InferenceContext<'a> { | |||
98 | let substs = ctx.substs_from_path(path, typable, true); | 101 | let substs = ctx.substs_from_path(path, typable, true); |
99 | let ty = TyBuilder::value_ty(self.db, typable) | 102 | let ty = TyBuilder::value_ty(self.db, typable) |
100 | .use_parent_substs(&parent_substs) | 103 | .use_parent_substs(&parent_substs) |
101 | .fill(substs.interned(&Interner)[parent_substs.len(&Interner)..].iter().cloned()) | 104 | .fill(substs.as_slice(&Interner)[parent_substs.len(&Interner)..].iter().cloned()) |
102 | .build(); | 105 | .build(); |
103 | Some(ty) | 106 | Some(ty) |
104 | } | 107 | } |
@@ -142,7 +145,7 @@ impl<'a> InferenceContext<'a> { | |||
142 | remaining_segments_for_ty, | 145 | remaining_segments_for_ty, |
143 | true, | 146 | true, |
144 | ); | 147 | ); |
145 | if let TyKind::Unknown = ty.kind(&Interner) { | 148 | if let TyKind::Error = ty.kind(&Interner) { |
146 | return None; | 149 | return None; |
147 | } | 150 | } |
148 | 151 | ||
@@ -207,7 +210,7 @@ impl<'a> InferenceContext<'a> { | |||
207 | name: &Name, | 210 | name: &Name, |
208 | id: ExprOrPatId, | 211 | id: ExprOrPatId, |
209 | ) -> Option<(ValueNs, Option<Substitution>)> { | 212 | ) -> Option<(ValueNs, Option<Substitution>)> { |
210 | if let TyKind::Unknown = ty.kind(&Interner) { | 213 | if let TyKind::Error = ty.kind(&Interner) { |
211 | return None; | 214 | return None; |
212 | } | 215 | } |
213 | 216 | ||
@@ -243,7 +246,8 @@ impl<'a> InferenceContext<'a> { | |||
243 | let impl_substs = TyBuilder::subst_for_def(self.db, impl_id) | 246 | let impl_substs = TyBuilder::subst_for_def(self.db, impl_id) |
244 | .fill(iter::repeat_with(|| self.table.new_type_var())) | 247 | .fill(iter::repeat_with(|| self.table.new_type_var())) |
245 | .build(); | 248 | .build(); |
246 | let impl_self_ty = self.db.impl_self_ty(impl_id).subst(&impl_substs); | 249 | let impl_self_ty = |
250 | self.db.impl_self_ty(impl_id).substitute(&Interner, &impl_substs); | ||
247 | self.unify(&impl_self_ty, &ty); | 251 | self.unify(&impl_self_ty, &ty); |
248 | Some(impl_substs) | 252 | Some(impl_substs) |
249 | } | 253 | } |
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index a04b935ef..a887e20b0 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs | |||
@@ -2,13 +2,17 @@ | |||
2 | 2 | ||
3 | use std::borrow::Cow; | 3 | use std::borrow::Cow; |
4 | 4 | ||
5 | use chalk_ir::{FloatTy, IntTy, TyVariableKind, UniverseIndex, VariableKind}; | 5 | use chalk_ir::{ |
6 | cast::Cast, fold::Fold, interner::HasInterner, FloatTy, IntTy, TyVariableKind, UniverseIndex, | ||
7 | VariableKind, | ||
8 | }; | ||
6 | use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; | 9 | use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; |
7 | 10 | ||
8 | use super::{DomainGoal, InferenceContext}; | 11 | use super::{DomainGoal, InferenceContext}; |
9 | use crate::{ | 12 | use crate::{ |
10 | AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds, DebruijnIndex, FnPointer, | 13 | fold_tys, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds, |
11 | InEnvironment, InferenceVar, Interner, Scalar, Substitution, Ty, TyKind, TypeWalk, WhereClause, | 14 | DebruijnIndex, FnPointer, FnSubst, InEnvironment, InferenceVar, Interner, Scalar, Substitution, |
15 | Ty, TyExt, TyKind, WhereClause, | ||
12 | }; | 16 | }; |
13 | 17 | ||
14 | impl<'a> InferenceContext<'a> { | 18 | impl<'a> InferenceContext<'a> { |
@@ -33,7 +37,10 @@ where | |||
33 | } | 37 | } |
34 | 38 | ||
35 | #[derive(Debug)] | 39 | #[derive(Debug)] |
36 | pub(super) struct Canonicalized<T> { | 40 | pub(super) struct Canonicalized<T> |
41 | where | ||
42 | T: HasInterner<Interner = Interner>, | ||
43 | { | ||
37 | pub(super) value: Canonical<T>, | 44 | pub(super) value: Canonical<T>, |
38 | free_vars: Vec<(InferenceVar, TyVariableKind)>, | 45 | free_vars: Vec<(InferenceVar, TyVariableKind)>, |
39 | } | 46 | } |
@@ -47,11 +54,16 @@ impl<'a, 'b> Canonicalizer<'a, 'b> { | |||
47 | }) | 54 | }) |
48 | } | 55 | } |
49 | 56 | ||
50 | fn do_canonicalize<T: TypeWalk>(&mut self, t: T, binders: DebruijnIndex) -> T { | 57 | fn do_canonicalize<T: Fold<Interner, Result = T> + HasInterner<Interner = Interner>>( |
51 | t.fold_binders( | 58 | &mut self, |
52 | &mut |ty, binders| match ty.kind(&Interner) { | 59 | t: T, |
60 | binders: DebruijnIndex, | ||
61 | ) -> T { | ||
62 | fold_tys( | ||
63 | t, | ||
64 | |ty, binders| match ty.kind(&Interner) { | ||
53 | &TyKind::InferenceVar(var, kind) => { | 65 | &TyKind::InferenceVar(var, kind) => { |
54 | let inner = var.to_inner(); | 66 | let inner = from_inference_var(var); |
55 | if self.var_stack.contains(&inner) { | 67 | if self.var_stack.contains(&inner) { |
56 | // recursive type | 68 | // recursive type |
57 | return self.ctx.table.type_variable_table.fallback_value(var, kind); | 69 | return self.ctx.table.type_variable_table.fallback_value(var, kind); |
@@ -65,7 +77,7 @@ impl<'a, 'b> Canonicalizer<'a, 'b> { | |||
65 | result | 77 | result |
66 | } else { | 78 | } else { |
67 | let root = self.ctx.table.var_unification_table.find(inner); | 79 | let root = self.ctx.table.var_unification_table.find(inner); |
68 | let position = self.add(InferenceVar::from_inner(root), kind); | 80 | let position = self.add(to_inference_var(root), kind); |
69 | TyKind::BoundVar(BoundVar::new(binders, position)).intern(&Interner) | 81 | TyKind::BoundVar(BoundVar::new(binders, position)).intern(&Interner) |
70 | } | 82 | } |
71 | } | 83 | } |
@@ -75,7 +87,10 @@ impl<'a, 'b> Canonicalizer<'a, 'b> { | |||
75 | ) | 87 | ) |
76 | } | 88 | } |
77 | 89 | ||
78 | fn into_canonicalized<T>(self, result: T) -> Canonicalized<T> { | 90 | fn into_canonicalized<T: HasInterner<Interner = Interner>>( |
91 | self, | ||
92 | result: T, | ||
93 | ) -> Canonicalized<T> { | ||
79 | let kinds = self | 94 | let kinds = self |
80 | .free_vars | 95 | .free_vars |
81 | .iter() | 96 | .iter() |
@@ -102,25 +117,18 @@ impl<'a, 'b> Canonicalizer<'a, 'b> { | |||
102 | DomainGoal::Holds(wc) => { | 117 | DomainGoal::Holds(wc) => { |
103 | DomainGoal::Holds(self.do_canonicalize(wc, DebruijnIndex::INNERMOST)) | 118 | DomainGoal::Holds(self.do_canonicalize(wc, DebruijnIndex::INNERMOST)) |
104 | } | 119 | } |
120 | _ => unimplemented!(), | ||
105 | }; | 121 | }; |
106 | self.into_canonicalized(InEnvironment { goal: result, environment: obligation.environment }) | 122 | self.into_canonicalized(InEnvironment { goal: result, environment: obligation.environment }) |
107 | } | 123 | } |
108 | } | 124 | } |
109 | 125 | ||
110 | impl<T> Canonicalized<T> { | 126 | impl<T: HasInterner<Interner = Interner>> Canonicalized<T> { |
111 | pub(super) fn decanonicalize_ty(&self, mut ty: Ty) -> Ty { | 127 | pub(super) fn decanonicalize_ty(&self, ty: Ty) -> Ty { |
112 | ty.walk_mut_binders( | 128 | crate::fold_free_vars(ty, |bound, _binders| { |
113 | &mut |ty, binders| { | 129 | let (v, k) = self.free_vars[bound.index]; |
114 | if let &mut TyKind::BoundVar(bound) = ty.interned_mut() { | 130 | TyKind::InferenceVar(v, k).intern(&Interner) |
115 | if bound.debruijn >= binders { | 131 | }) |
116 | let (v, k) = self.free_vars[bound.index]; | ||
117 | *ty = TyKind::InferenceVar(v, k).intern(&Interner); | ||
118 | } | ||
119 | } | ||
120 | }, | ||
121 | DebruijnIndex::INNERMOST, | ||
122 | ); | ||
123 | ty | ||
124 | } | 132 | } |
125 | 133 | ||
126 | pub(super) fn apply_solution( | 134 | pub(super) fn apply_solution( |
@@ -132,15 +140,17 @@ impl<T> Canonicalized<T> { | |||
132 | let new_vars = Substitution::from_iter( | 140 | let new_vars = Substitution::from_iter( |
133 | &Interner, | 141 | &Interner, |
134 | solution.binders.iter(&Interner).map(|k| match k.kind { | 142 | solution.binders.iter(&Interner).map(|k| match k.kind { |
135 | VariableKind::Ty(TyVariableKind::General) => ctx.table.new_type_var(), | 143 | VariableKind::Ty(TyVariableKind::General) => { |
136 | VariableKind::Ty(TyVariableKind::Integer) => ctx.table.new_integer_var(), | 144 | ctx.table.new_type_var().cast(&Interner) |
137 | VariableKind::Ty(TyVariableKind::Float) => ctx.table.new_float_var(), | 145 | } |
138 | // HACK: Chalk can sometimes return new lifetime variables. We | 146 | VariableKind::Ty(TyVariableKind::Integer) => { |
139 | // want to just skip them, but to not mess up the indices of | 147 | ctx.table.new_integer_var().cast(&Interner) |
140 | // other variables, we'll just create a new type variable in | 148 | } |
141 | // their place instead. This should not matter (we never see the | 149 | VariableKind::Ty(TyVariableKind::Float) => { |
142 | // actual *uses* of the lifetime variable). | 150 | ctx.table.new_float_var().cast(&Interner) |
143 | VariableKind::Lifetime => ctx.table.new_type_var(), | 151 | } |
152 | // Chalk can sometimes return new lifetime variables. We just use the static lifetime everywhere | ||
153 | VariableKind::Lifetime => static_lifetime().cast(&Interner), | ||
144 | _ => panic!("const variable in solution"), | 154 | _ => panic!("const variable in solution"), |
145 | }), | 155 | }), |
146 | ); | 156 | ); |
@@ -149,7 +159,7 @@ impl<T> Canonicalized<T> { | |||
149 | // eagerly replace projections in the type; we may be getting types | 159 | // eagerly replace projections in the type; we may be getting types |
150 | // e.g. from where clauses where this hasn't happened yet | 160 | // e.g. from where clauses where this hasn't happened yet |
151 | let ty = ctx.normalize_associated_types_in( | 161 | let ty = ctx.normalize_associated_types_in( |
152 | ty.assert_ty_ref(&Interner).clone().subst_bound_vars(&new_vars), | 162 | new_vars.apply(ty.assert_ty_ref(&Interner).clone(), &Interner), |
153 | ); | 163 | ); |
154 | ctx.table.unify(&TyKind::InferenceVar(v, k).intern(&Interner), &ty); | 164 | ctx.table.unify(&TyKind::InferenceVar(v, k).intern(&Interner), &ty); |
155 | } | 165 | } |
@@ -170,8 +180,8 @@ pub(crate) fn unify(tys: &Canonical<(Ty, Ty)>) -> Option<Substitution> { | |||
170 | // fallback to Unknown in the end (kind of hacky, as below) | 180 | // fallback to Unknown in the end (kind of hacky, as below) |
171 | .map(|_| table.new_type_var()), | 181 | .map(|_| table.new_type_var()), |
172 | ); | 182 | ); |
173 | let ty1_with_vars = tys.value.0.clone().subst_bound_vars(&vars); | 183 | let ty1_with_vars = vars.apply(tys.value.0.clone(), &Interner); |
174 | let ty2_with_vars = tys.value.1.clone().subst_bound_vars(&vars); | 184 | let ty2_with_vars = vars.apply(tys.value.1.clone(), &Interner); |
175 | if !table.unify(&ty1_with_vars, &ty2_with_vars) { | 185 | if !table.unify(&ty1_with_vars, &ty2_with_vars) { |
176 | return None; | 186 | return None; |
177 | } | 187 | } |
@@ -204,17 +214,17 @@ impl TypeVariableTable { | |||
204 | } | 214 | } |
205 | 215 | ||
206 | pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) { | 216 | pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) { |
207 | self.inner[iv.to_inner().0 as usize].diverging = diverging; | 217 | self.inner[from_inference_var(iv).0 as usize].diverging = diverging; |
208 | } | 218 | } |
209 | 219 | ||
210 | fn is_diverging(&mut self, iv: InferenceVar) -> bool { | 220 | fn is_diverging(&mut self, iv: InferenceVar) -> bool { |
211 | self.inner[iv.to_inner().0 as usize].diverging | 221 | self.inner[from_inference_var(iv).0 as usize].diverging |
212 | } | 222 | } |
213 | 223 | ||
214 | fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { | 224 | fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { |
215 | match kind { | 225 | match kind { |
216 | _ if self.inner[iv.to_inner().0 as usize].diverging => TyKind::Never, | 226 | _ if self.inner[from_inference_var(iv).0 as usize].diverging => TyKind::Never, |
217 | TyVariableKind::General => TyKind::Unknown, | 227 | TyVariableKind::General => TyKind::Error, |
218 | TyVariableKind::Integer => TyKind::Scalar(Scalar::Int(IntTy::I32)), | 228 | TyVariableKind::Integer => TyKind::Scalar(Scalar::Int(IntTy::I32)), |
219 | TyVariableKind::Float => TyKind::Scalar(Scalar::Float(FloatTy::F64)), | 229 | TyVariableKind::Float => TyKind::Scalar(Scalar::Float(FloatTy::F64)), |
220 | } | 230 | } |
@@ -247,7 +257,7 @@ impl InferenceTable { | |||
247 | self.type_variable_table.push(TypeVariableData { diverging }); | 257 | self.type_variable_table.push(TypeVariableData { diverging }); |
248 | let key = self.var_unification_table.new_key(TypeVarValue::Unknown); | 258 | let key = self.var_unification_table.new_key(TypeVarValue::Unknown); |
249 | assert_eq!(key.0 as usize, self.type_variable_table.inner.len() - 1); | 259 | assert_eq!(key.0 as usize, self.type_variable_table.inner.len() - 1); |
250 | TyKind::InferenceVar(InferenceVar::from_inner(key), kind).intern(&Interner) | 260 | TyKind::InferenceVar(to_inference_var(key), kind).intern(&Interner) |
251 | } | 261 | } |
252 | 262 | ||
253 | pub(crate) fn new_type_var(&mut self) -> Ty { | 263 | pub(crate) fn new_type_var(&mut self) -> Ty { |
@@ -284,7 +294,7 @@ impl InferenceTable { | |||
284 | substs2: &Substitution, | 294 | substs2: &Substitution, |
285 | depth: usize, | 295 | depth: usize, |
286 | ) -> bool { | 296 | ) -> bool { |
287 | substs1.0.iter().zip(substs2.0.iter()).all(|(t1, t2)| { | 297 | substs1.iter(&Interner).zip(substs2.iter(&Interner)).all(|(t1, t2)| { |
288 | self.unify_inner(t1.assert_ty_ref(&Interner), t2.assert_ty_ref(&Interner), depth) | 298 | self.unify_inner(t1.assert_ty_ref(&Interner), t2.assert_ty_ref(&Interner), depth) |
289 | }) | 299 | }) |
290 | } | 300 | } |
@@ -305,8 +315,8 @@ impl InferenceTable { | |||
305 | (TyKind::Adt(_, substs1), TyKind::Adt(_, substs2)) | 315 | (TyKind::Adt(_, substs1), TyKind::Adt(_, substs2)) |
306 | | (TyKind::FnDef(_, substs1), TyKind::FnDef(_, substs2)) | 316 | | (TyKind::FnDef(_, substs1), TyKind::FnDef(_, substs2)) |
307 | | ( | 317 | | ( |
308 | TyKind::Function(FnPointer { substs: substs1, .. }), | 318 | TyKind::Function(FnPointer { substitution: FnSubst(substs1), .. }), |
309 | TyKind::Function(FnPointer { substs: substs2, .. }), | 319 | TyKind::Function(FnPointer { substitution: FnSubst(substs2), .. }), |
310 | ) | 320 | ) |
311 | | (TyKind::Tuple(_, substs1), TyKind::Tuple(_, substs2)) | 321 | | (TyKind::Tuple(_, substs1), TyKind::Tuple(_, substs2)) |
312 | | (TyKind::OpaqueType(_, substs1), TyKind::OpaqueType(_, substs2)) | 322 | | (TyKind::OpaqueType(_, substs1), TyKind::OpaqueType(_, substs2)) |
@@ -314,9 +324,11 @@ impl InferenceTable { | |||
314 | | (TyKind::Closure(.., substs1), TyKind::Closure(.., substs2)) => { | 324 | | (TyKind::Closure(.., substs1), TyKind::Closure(.., substs2)) => { |
315 | self.unify_substs(substs1, substs2, depth + 1) | 325 | self.unify_substs(substs1, substs2, depth + 1) |
316 | } | 326 | } |
317 | (TyKind::Ref(_, ty1), TyKind::Ref(_, ty2)) | 327 | (TyKind::Array(ty1, c1), TyKind::Array(ty2, c2)) if c1 == c2 => { |
328 | self.unify_inner(ty1, ty2, depth + 1) | ||
329 | } | ||
330 | (TyKind::Ref(_, _, ty1), TyKind::Ref(_, _, ty2)) | ||
318 | | (TyKind::Raw(_, ty1), TyKind::Raw(_, ty2)) | 331 | | (TyKind::Raw(_, ty1), TyKind::Raw(_, ty2)) |
319 | | (TyKind::Array(ty1), TyKind::Array(ty2)) | ||
320 | | (TyKind::Slice(ty1), TyKind::Slice(ty2)) => self.unify_inner(ty1, ty2, depth + 1), | 332 | | (TyKind::Slice(ty1), TyKind::Slice(ty2)) => self.unify_inner(ty1, ty2, depth + 1), |
321 | _ => true, /* we checked equals_ctor already */ | 333 | _ => true, /* we checked equals_ctor already */ |
322 | } | 334 | } |
@@ -327,7 +339,7 @@ impl InferenceTable { | |||
327 | 339 | ||
328 | pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -> bool { | 340 | pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -> bool { |
329 | match (ty1.kind(&Interner), ty2.kind(&Interner)) { | 341 | match (ty1.kind(&Interner), ty2.kind(&Interner)) { |
330 | (TyKind::Unknown, _) | (_, TyKind::Unknown) => true, | 342 | (TyKind::Error, _) | (_, TyKind::Error) => true, |
331 | 343 | ||
332 | (TyKind::Placeholder(p1), TyKind::Placeholder(p2)) if *p1 == *p2 => true, | 344 | (TyKind::Placeholder(p1), TyKind::Placeholder(p2)) if *p1 == *p2 => true, |
333 | 345 | ||
@@ -364,8 +376,12 @@ impl InferenceTable { | |||
364 | == self.type_variable_table.is_diverging(*tv2) => | 376 | == self.type_variable_table.is_diverging(*tv2) => |
365 | { | 377 | { |
366 | // both type vars are unknown since we tried to resolve them | 378 | // both type vars are unknown since we tried to resolve them |
367 | if !self.var_unification_table.unioned(tv1.to_inner(), tv2.to_inner()) { | 379 | if !self |
368 | self.var_unification_table.union(tv1.to_inner(), tv2.to_inner()); | 380 | .var_unification_table |
381 | .unioned(from_inference_var(*tv1), from_inference_var(*tv2)) | ||
382 | { | ||
383 | self.var_unification_table | ||
384 | .union(from_inference_var(*tv1), from_inference_var(*tv2)); | ||
369 | self.revision += 1; | 385 | self.revision += 1; |
370 | } | 386 | } |
371 | true | 387 | true |
@@ -402,7 +418,7 @@ impl InferenceTable { | |||
402 | ) => { | 418 | ) => { |
403 | // the type var is unknown since we tried to resolve it | 419 | // the type var is unknown since we tried to resolve it |
404 | self.var_unification_table.union_value( | 420 | self.var_unification_table.union_value( |
405 | tv.to_inner(), | 421 | from_inference_var(*tv), |
406 | TypeVarValue::Known(other.clone().intern(&Interner)), | 422 | TypeVarValue::Known(other.clone().intern(&Interner)), |
407 | ); | 423 | ); |
408 | self.revision += 1; | 424 | self.revision += 1; |
@@ -457,7 +473,7 @@ impl InferenceTable { | |||
457 | } | 473 | } |
458 | match ty.kind(&Interner) { | 474 | match ty.kind(&Interner) { |
459 | TyKind::InferenceVar(tv, _) => { | 475 | TyKind::InferenceVar(tv, _) => { |
460 | let inner = tv.to_inner(); | 476 | let inner = from_inference_var(*tv); |
461 | match self.var_unification_table.inlined_probe_value(inner).known() { | 477 | match self.var_unification_table.inlined_probe_value(inner).known() { |
462 | Some(known_ty) => { | 478 | Some(known_ty) => { |
463 | // The known_ty can't be a type var itself | 479 | // The known_ty can't be a type var itself |
@@ -478,55 +494,63 @@ impl InferenceTable { | |||
478 | /// be resolved as far as possible, i.e. contain no type variables with | 494 | /// be resolved as far as possible, i.e. contain no type variables with |
479 | /// known type. | 495 | /// known type. |
480 | fn resolve_ty_as_possible_inner(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty { | 496 | fn resolve_ty_as_possible_inner(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty { |
481 | ty.fold(&mut |ty| match ty.kind(&Interner) { | 497 | fold_tys( |
482 | &TyKind::InferenceVar(tv, kind) => { | 498 | ty, |
483 | let inner = tv.to_inner(); | 499 | |ty, _| match ty.kind(&Interner) { |
484 | if tv_stack.contains(&inner) { | 500 | &TyKind::InferenceVar(tv, kind) => { |
485 | cov_mark::hit!(type_var_cycles_resolve_as_possible); | 501 | let inner = from_inference_var(tv); |
486 | // recursive type | 502 | if tv_stack.contains(&inner) { |
487 | return self.type_variable_table.fallback_value(tv, kind); | 503 | cov_mark::hit!(type_var_cycles_resolve_as_possible); |
488 | } | 504 | // recursive type |
489 | if let Some(known_ty) = | 505 | return self.type_variable_table.fallback_value(tv, kind); |
490 | self.var_unification_table.inlined_probe_value(inner).known() | 506 | } |
491 | { | 507 | if let Some(known_ty) = |
492 | // known_ty may contain other variables that are known by now | 508 | self.var_unification_table.inlined_probe_value(inner).known() |
493 | tv_stack.push(inner); | 509 | { |
494 | let result = self.resolve_ty_as_possible_inner(tv_stack, known_ty.clone()); | 510 | // known_ty may contain other variables that are known by now |
495 | tv_stack.pop(); | 511 | tv_stack.push(inner); |
496 | result | 512 | let result = self.resolve_ty_as_possible_inner(tv_stack, known_ty.clone()); |
497 | } else { | 513 | tv_stack.pop(); |
498 | ty | 514 | result |
515 | } else { | ||
516 | ty | ||
517 | } | ||
499 | } | 518 | } |
500 | } | 519 | _ => ty, |
501 | _ => ty, | 520 | }, |
502 | }) | 521 | DebruijnIndex::INNERMOST, |
522 | ) | ||
503 | } | 523 | } |
504 | 524 | ||
505 | /// Resolves the type completely; type variables without known type are | 525 | /// Resolves the type completely; type variables without known type are |
506 | /// replaced by TyKind::Unknown. | 526 | /// replaced by TyKind::Unknown. |
507 | fn resolve_ty_completely_inner(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty { | 527 | fn resolve_ty_completely_inner(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty { |
508 | ty.fold(&mut |ty| match ty.kind(&Interner) { | 528 | fold_tys( |
509 | &TyKind::InferenceVar(tv, kind) => { | 529 | ty, |
510 | let inner = tv.to_inner(); | 530 | |ty, _| match ty.kind(&Interner) { |
511 | if tv_stack.contains(&inner) { | 531 | &TyKind::InferenceVar(tv, kind) => { |
512 | cov_mark::hit!(type_var_cycles_resolve_completely); | 532 | let inner = from_inference_var(tv); |
513 | // recursive type | 533 | if tv_stack.contains(&inner) { |
514 | return self.type_variable_table.fallback_value(tv, kind); | 534 | cov_mark::hit!(type_var_cycles_resolve_completely); |
515 | } | 535 | // recursive type |
516 | if let Some(known_ty) = | 536 | return self.type_variable_table.fallback_value(tv, kind); |
517 | self.var_unification_table.inlined_probe_value(inner).known() | 537 | } |
518 | { | 538 | if let Some(known_ty) = |
519 | // known_ty may contain other variables that are known by now | 539 | self.var_unification_table.inlined_probe_value(inner).known() |
520 | tv_stack.push(inner); | 540 | { |
521 | let result = self.resolve_ty_completely_inner(tv_stack, known_ty.clone()); | 541 | // known_ty may contain other variables that are known by now |
522 | tv_stack.pop(); | 542 | tv_stack.push(inner); |
523 | result | 543 | let result = self.resolve_ty_completely_inner(tv_stack, known_ty.clone()); |
524 | } else { | 544 | tv_stack.pop(); |
525 | self.type_variable_table.fallback_value(tv, kind) | 545 | result |
546 | } else { | ||
547 | self.type_variable_table.fallback_value(tv, kind) | ||
548 | } | ||
526 | } | 549 | } |
527 | } | 550 | _ => ty, |
528 | _ => ty, | 551 | }, |
529 | }) | 552 | DebruijnIndex::INNERMOST, |
553 | ) | ||
530 | } | 554 | } |
531 | } | 555 | } |
532 | 556 | ||
@@ -550,6 +574,14 @@ impl UnifyKey for TypeVarId { | |||
550 | } | 574 | } |
551 | } | 575 | } |
552 | 576 | ||
577 | fn from_inference_var(var: InferenceVar) -> TypeVarId { | ||
578 | TypeVarId(var.index()) | ||
579 | } | ||
580 | |||
581 | fn to_inference_var(TypeVarId(index): TypeVarId) -> InferenceVar { | ||
582 | index.into() | ||
583 | } | ||
584 | |||
553 | /// The value of a type variable: either we already know the type, or we don't | 585 | /// The value of a type variable: either we already know the type, or we don't |
554 | /// know it yet. | 586 | /// know it yet. |
555 | #[derive(Clone, PartialEq, Eq, Debug)] | 587 | #[derive(Clone, PartialEq, Eq, Debug)] |
diff --git a/crates/hir_ty/src/traits/chalk/interner.rs b/crates/hir_ty/src/interner.rs index 94e94a26d..a1656115d 100644 --- a/crates/hir_ty/src/traits/chalk/interner.rs +++ b/crates/hir_ty/src/interner.rs | |||
@@ -1,61 +1,83 @@ | |||
1 | //! Implementation of the Chalk `Interner` trait, which allows customizing the | 1 | //! Implementation of the Chalk `Interner` trait, which allows customizing the |
2 | //! representation of the various objects Chalk deals with (types, goals etc.). | 2 | //! representation of the various objects Chalk deals with (types, goals etc.). |
3 | 3 | ||
4 | use super::tls; | 4 | use crate::{chalk_db, tls, GenericArg}; |
5 | use base_db::salsa::InternId; | 5 | use base_db::salsa::InternId; |
6 | use chalk_ir::{GenericArg, Goal, GoalData}; | 6 | use chalk_ir::{Goal, GoalData}; |
7 | use hir_def::TypeAliasId; | 7 | use hir_def::{ |
8 | intern::{impl_internable, InternStorage, Internable, Interned}, | ||
9 | TypeAliasId, | ||
10 | }; | ||
8 | use smallvec::SmallVec; | 11 | use smallvec::SmallVec; |
9 | use std::{fmt, sync::Arc}; | 12 | use std::{fmt, sync::Arc}; |
10 | 13 | ||
11 | #[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] | 14 | #[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] |
12 | pub struct Interner; | 15 | pub struct Interner; |
13 | 16 | ||
14 | pub(crate) type AssocTypeId = chalk_ir::AssocTypeId<Interner>; | 17 | #[derive(PartialEq, Eq, Hash, Debug)] |
15 | pub(crate) type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum<Interner>; | 18 | pub struct InternedWrapper<T>(T); |
16 | pub(crate) type TraitId = chalk_ir::TraitId<Interner>; | 19 | |
17 | pub(crate) type TraitDatum = chalk_solve::rust_ir::TraitDatum<Interner>; | 20 | impl<T> std::ops::Deref for InternedWrapper<T> { |
18 | pub(crate) type AdtId = chalk_ir::AdtId<Interner>; | 21 | type Target = T; |
19 | pub(crate) type StructDatum = chalk_solve::rust_ir::AdtDatum<Interner>; | 22 | |
20 | pub(crate) type ImplId = chalk_ir::ImplId<Interner>; | 23 | fn deref(&self) -> &Self::Target { |
21 | pub(crate) type ImplDatum = chalk_solve::rust_ir::ImplDatum<Interner>; | 24 | &self.0 |
22 | pub(crate) type AssociatedTyValueId = chalk_solve::rust_ir::AssociatedTyValueId<Interner>; | 25 | } |
23 | pub(crate) type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue<Interner>; | 26 | } |
24 | pub(crate) type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>; | 27 | |
25 | pub(crate) type OpaqueTyId = chalk_ir::OpaqueTyId<Interner>; | 28 | impl_internable!( |
26 | pub(crate) type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum<Interner>; | 29 | InternedWrapper<Vec<chalk_ir::VariableKind<Interner>>>, |
27 | pub(crate) type Variances = chalk_ir::Variances<Interner>; | 30 | InternedWrapper<SmallVec<[GenericArg; 2]>>, |
31 | InternedWrapper<chalk_ir::TyData<Interner>>, | ||
32 | InternedWrapper<chalk_ir::LifetimeData<Interner>>, | ||
33 | InternedWrapper<chalk_ir::ConstData<Interner>>, | ||
34 | InternedWrapper<Vec<chalk_ir::CanonicalVarKind<Interner>>>, | ||
35 | InternedWrapper<Vec<chalk_ir::ProgramClause<Interner>>>, | ||
36 | InternedWrapper<Vec<chalk_ir::QuantifiedWhereClause<Interner>>>, | ||
37 | InternedWrapper<Vec<chalk_ir::Variance>>, | ||
38 | ); | ||
28 | 39 | ||
29 | impl chalk_ir::interner::Interner for Interner { | 40 | impl chalk_ir::interner::Interner for Interner { |
30 | type InternedType = Arc<chalk_ir::TyData<Self>>; | 41 | type InternedType = Interned<InternedWrapper<chalk_ir::TyData<Interner>>>; |
31 | type InternedLifetime = chalk_ir::LifetimeData<Self>; | 42 | type InternedLifetime = Interned<InternedWrapper<chalk_ir::LifetimeData<Self>>>; |
32 | type InternedConst = Arc<chalk_ir::ConstData<Self>>; | 43 | type InternedConst = Interned<InternedWrapper<chalk_ir::ConstData<Self>>>; |
33 | type InternedConcreteConst = (); | 44 | type InternedConcreteConst = (); |
34 | type InternedGenericArg = chalk_ir::GenericArgData<Self>; | 45 | type InternedGenericArg = chalk_ir::GenericArgData<Self>; |
35 | type InternedGoal = Arc<GoalData<Self>>; | 46 | type InternedGoal = Arc<GoalData<Self>>; |
36 | type InternedGoals = Vec<Goal<Self>>; | 47 | type InternedGoals = Vec<Goal<Self>>; |
37 | type InternedSubstitution = SmallVec<[GenericArg<Self>; 2]>; | 48 | type InternedSubstitution = Interned<InternedWrapper<SmallVec<[GenericArg; 2]>>>; |
38 | type InternedProgramClause = Arc<chalk_ir::ProgramClauseData<Self>>; | 49 | type InternedProgramClause = chalk_ir::ProgramClauseData<Self>; |
39 | type InternedProgramClauses = Arc<[chalk_ir::ProgramClause<Self>]>; | 50 | type InternedProgramClauses = Interned<InternedWrapper<Vec<chalk_ir::ProgramClause<Self>>>>; |
40 | type InternedQuantifiedWhereClauses = Vec<chalk_ir::QuantifiedWhereClause<Self>>; | 51 | type InternedQuantifiedWhereClauses = |
41 | type InternedVariableKinds = Vec<chalk_ir::VariableKind<Self>>; | 52 | Interned<InternedWrapper<Vec<chalk_ir::QuantifiedWhereClause<Self>>>>; |
42 | type InternedCanonicalVarKinds = Vec<chalk_ir::CanonicalVarKind<Self>>; | 53 | type InternedVariableKinds = Interned<InternedWrapper<Vec<chalk_ir::VariableKind<Interner>>>>; |
54 | type InternedCanonicalVarKinds = | ||
55 | Interned<InternedWrapper<Vec<chalk_ir::CanonicalVarKind<Self>>>>; | ||
43 | type InternedConstraints = Vec<chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>>; | 56 | type InternedConstraints = Vec<chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>>; |
44 | type InternedVariances = Arc<[chalk_ir::Variance]>; | 57 | type InternedVariances = Interned<InternedWrapper<Vec<chalk_ir::Variance>>>; |
45 | type DefId = InternId; | 58 | type DefId = InternId; |
46 | type InternedAdtId = hir_def::AdtId; | 59 | type InternedAdtId = hir_def::AdtId; |
47 | type Identifier = TypeAliasId; | 60 | type Identifier = TypeAliasId; |
48 | type FnAbi = (); | 61 | type FnAbi = (); |
49 | 62 | ||
50 | fn debug_adt_id(type_kind_id: AdtId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> { | 63 | fn debug_adt_id( |
64 | type_kind_id: chalk_db::AdtId, | ||
65 | fmt: &mut fmt::Formatter<'_>, | ||
66 | ) -> Option<fmt::Result> { | ||
51 | tls::with_current_program(|prog| Some(prog?.debug_struct_id(type_kind_id, fmt))) | 67 | tls::with_current_program(|prog| Some(prog?.debug_struct_id(type_kind_id, fmt))) |
52 | } | 68 | } |
53 | 69 | ||
54 | fn debug_trait_id(type_kind_id: TraitId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> { | 70 | fn debug_trait_id( |
71 | type_kind_id: chalk_db::TraitId, | ||
72 | fmt: &mut fmt::Formatter<'_>, | ||
73 | ) -> Option<fmt::Result> { | ||
55 | tls::with_current_program(|prog| Some(prog?.debug_trait_id(type_kind_id, fmt))) | 74 | tls::with_current_program(|prog| Some(prog?.debug_trait_id(type_kind_id, fmt))) |
56 | } | 75 | } |
57 | 76 | ||
58 | fn debug_assoc_type_id(id: AssocTypeId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> { | 77 | fn debug_assoc_type_id( |
78 | id: chalk_db::AssocTypeId, | ||
79 | fmt: &mut fmt::Formatter<'_>, | ||
80 | ) -> Option<fmt::Result> { | ||
59 | tls::with_current_program(|prog| Some(prog?.debug_assoc_type_id(id, fmt))) | 81 | tls::with_current_program(|prog| Some(prog?.debug_assoc_type_id(id, fmt))) |
60 | } | 82 | } |
61 | 83 | ||
@@ -99,7 +121,7 @@ impl chalk_ir::interner::Interner for Interner { | |||
99 | } | 121 | } |
100 | 122 | ||
101 | fn debug_generic_arg( | 123 | fn debug_generic_arg( |
102 | parameter: &GenericArg<Interner>, | 124 | parameter: &GenericArg, |
103 | fmt: &mut fmt::Formatter<'_>, | 125 | fmt: &mut fmt::Formatter<'_>, |
104 | ) -> Option<fmt::Result> { | 126 | ) -> Option<fmt::Result> { |
105 | tls::with_current_program(|prog| Some(prog?.debug_generic_arg(parameter, fmt))) | 127 | tls::with_current_program(|prog| Some(prog?.debug_generic_arg(parameter, fmt))) |
@@ -192,59 +214,58 @@ impl chalk_ir::interner::Interner for Interner { | |||
192 | tls::with_current_program(|prog| Some(prog?.debug_quantified_where_clauses(clauses, fmt))) | 214 | tls::with_current_program(|prog| Some(prog?.debug_quantified_where_clauses(clauses, fmt))) |
193 | } | 215 | } |
194 | 216 | ||
195 | fn intern_ty(&self, kind: chalk_ir::TyKind<Self>) -> Arc<chalk_ir::TyData<Self>> { | 217 | fn intern_ty(&self, kind: chalk_ir::TyKind<Self>) -> Self::InternedType { |
196 | let flags = kind.compute_flags(self); | 218 | let flags = kind.compute_flags(self); |
197 | Arc::new(chalk_ir::TyData { kind, flags }) | 219 | Interned::new(InternedWrapper(chalk_ir::TyData { kind, flags })) |
198 | } | 220 | } |
199 | 221 | ||
200 | fn ty_data<'a>(&self, ty: &'a Arc<chalk_ir::TyData<Self>>) -> &'a chalk_ir::TyData<Self> { | 222 | fn ty_data<'a>(&self, ty: &'a Self::InternedType) -> &'a chalk_ir::TyData<Self> { |
201 | ty | 223 | &ty.0 |
202 | } | 224 | } |
203 | 225 | ||
204 | fn intern_lifetime( | 226 | fn intern_lifetime(&self, lifetime: chalk_ir::LifetimeData<Self>) -> Self::InternedLifetime { |
205 | &self, | 227 | Interned::new(InternedWrapper(lifetime)) |
206 | lifetime: chalk_ir::LifetimeData<Self>, | ||
207 | ) -> chalk_ir::LifetimeData<Self> { | ||
208 | lifetime | ||
209 | } | 228 | } |
210 | 229 | ||
211 | fn lifetime_data<'a>( | 230 | fn lifetime_data<'a>( |
212 | &self, | 231 | &self, |
213 | lifetime: &'a chalk_ir::LifetimeData<Self>, | 232 | lifetime: &'a Self::InternedLifetime, |
214 | ) -> &'a chalk_ir::LifetimeData<Self> { | 233 | ) -> &'a chalk_ir::LifetimeData<Self> { |
215 | lifetime | 234 | &lifetime.0 |
216 | } | 235 | } |
217 | 236 | ||
218 | fn intern_const(&self, constant: chalk_ir::ConstData<Self>) -> Arc<chalk_ir::ConstData<Self>> { | 237 | fn intern_const(&self, constant: chalk_ir::ConstData<Self>) -> Self::InternedConst { |
219 | Arc::new(constant) | 238 | Interned::new(InternedWrapper(constant)) |
220 | } | 239 | } |
221 | 240 | ||
222 | fn const_data<'a>( | 241 | fn const_data<'a>(&self, constant: &'a Self::InternedConst) -> &'a chalk_ir::ConstData<Self> { |
223 | &self, | 242 | &constant.0 |
224 | constant: &'a Arc<chalk_ir::ConstData<Self>>, | ||
225 | ) -> &'a chalk_ir::ConstData<Self> { | ||
226 | constant | ||
227 | } | 243 | } |
228 | 244 | ||
229 | fn const_eq(&self, _ty: &Arc<chalk_ir::TyData<Self>>, _c1: &(), _c2: &()) -> bool { | 245 | fn const_eq( |
246 | &self, | ||
247 | _ty: &Self::InternedType, | ||
248 | _c1: &Self::InternedConcreteConst, | ||
249 | _c2: &Self::InternedConcreteConst, | ||
250 | ) -> bool { | ||
230 | true | 251 | true |
231 | } | 252 | } |
232 | 253 | ||
233 | fn intern_generic_arg( | 254 | fn intern_generic_arg( |
234 | &self, | 255 | &self, |
235 | parameter: chalk_ir::GenericArgData<Self>, | 256 | parameter: chalk_ir::GenericArgData<Self>, |
236 | ) -> chalk_ir::GenericArgData<Self> { | 257 | ) -> Self::InternedGenericArg { |
237 | parameter | 258 | parameter |
238 | } | 259 | } |
239 | 260 | ||
240 | fn generic_arg_data<'a>( | 261 | fn generic_arg_data<'a>( |
241 | &self, | 262 | &self, |
242 | parameter: &'a chalk_ir::GenericArgData<Self>, | 263 | parameter: &'a Self::InternedGenericArg, |
243 | ) -> &'a chalk_ir::GenericArgData<Self> { | 264 | ) -> &'a chalk_ir::GenericArgData<Self> { |
244 | parameter | 265 | parameter |
245 | } | 266 | } |
246 | 267 | ||
247 | fn intern_goal(&self, goal: GoalData<Self>) -> Arc<GoalData<Self>> { | 268 | fn intern_goal(&self, goal: GoalData<Self>) -> Self::InternedGoal { |
248 | Arc::new(goal) | 269 | Arc::new(goal) |
249 | } | 270 | } |
250 | 271 | ||
@@ -255,38 +276,38 @@ impl chalk_ir::interner::Interner for Interner { | |||
255 | data.into_iter().collect() | 276 | data.into_iter().collect() |
256 | } | 277 | } |
257 | 278 | ||
258 | fn goal_data<'a>(&self, goal: &'a Arc<GoalData<Self>>) -> &'a GoalData<Self> { | 279 | fn goal_data<'a>(&self, goal: &'a Self::InternedGoal) -> &'a GoalData<Self> { |
259 | goal | 280 | goal |
260 | } | 281 | } |
261 | 282 | ||
262 | fn goals_data<'a>(&self, goals: &'a Vec<Goal<Interner>>) -> &'a [Goal<Interner>] { | 283 | fn goals_data<'a>(&self, goals: &'a Self::InternedGoals) -> &'a [Goal<Interner>] { |
263 | goals | 284 | goals |
264 | } | 285 | } |
265 | 286 | ||
266 | fn intern_substitution<E>( | 287 | fn intern_substitution<E>( |
267 | &self, | 288 | &self, |
268 | data: impl IntoIterator<Item = Result<GenericArg<Self>, E>>, | 289 | data: impl IntoIterator<Item = Result<GenericArg, E>>, |
269 | ) -> Result<Self::InternedSubstitution, E> { | 290 | ) -> Result<Self::InternedSubstitution, E> { |
270 | data.into_iter().collect() | 291 | Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?))) |
271 | } | 292 | } |
272 | 293 | ||
273 | fn substitution_data<'a>( | 294 | fn substitution_data<'a>( |
274 | &self, | 295 | &self, |
275 | substitution: &'a Self::InternedSubstitution, | 296 | substitution: &'a Self::InternedSubstitution, |
276 | ) -> &'a [GenericArg<Self>] { | 297 | ) -> &'a [GenericArg] { |
277 | substitution | 298 | &substitution.as_ref().0 |
278 | } | 299 | } |
279 | 300 | ||
280 | fn intern_program_clause( | 301 | fn intern_program_clause( |
281 | &self, | 302 | &self, |
282 | data: chalk_ir::ProgramClauseData<Self>, | 303 | data: chalk_ir::ProgramClauseData<Self>, |
283 | ) -> Arc<chalk_ir::ProgramClauseData<Self>> { | 304 | ) -> Self::InternedProgramClause { |
284 | Arc::new(data) | 305 | data |
285 | } | 306 | } |
286 | 307 | ||
287 | fn program_clause_data<'a>( | 308 | fn program_clause_data<'a>( |
288 | &self, | 309 | &self, |
289 | clause: &'a Arc<chalk_ir::ProgramClauseData<Self>>, | 310 | clause: &'a Self::InternedProgramClause, |
290 | ) -> &'a chalk_ir::ProgramClauseData<Self> { | 311 | ) -> &'a chalk_ir::ProgramClauseData<Self> { |
291 | clause | 312 | clause |
292 | } | 313 | } |
@@ -294,13 +315,13 @@ impl chalk_ir::interner::Interner for Interner { | |||
294 | fn intern_program_clauses<E>( | 315 | fn intern_program_clauses<E>( |
295 | &self, | 316 | &self, |
296 | data: impl IntoIterator<Item = Result<chalk_ir::ProgramClause<Self>, E>>, | 317 | data: impl IntoIterator<Item = Result<chalk_ir::ProgramClause<Self>, E>>, |
297 | ) -> Result<Arc<[chalk_ir::ProgramClause<Self>]>, E> { | 318 | ) -> Result<Self::InternedProgramClauses, E> { |
298 | data.into_iter().collect() | 319 | Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?))) |
299 | } | 320 | } |
300 | 321 | ||
301 | fn program_clauses_data<'a>( | 322 | fn program_clauses_data<'a>( |
302 | &self, | 323 | &self, |
303 | clauses: &'a Arc<[chalk_ir::ProgramClause<Self>]>, | 324 | clauses: &'a Self::InternedProgramClauses, |
304 | ) -> &'a [chalk_ir::ProgramClause<Self>] { | 325 | ) -> &'a [chalk_ir::ProgramClause<Self>] { |
305 | &clauses | 326 | &clauses |
306 | } | 327 | } |
@@ -309,7 +330,7 @@ impl chalk_ir::interner::Interner for Interner { | |||
309 | &self, | 330 | &self, |
310 | data: impl IntoIterator<Item = Result<chalk_ir::QuantifiedWhereClause<Self>, E>>, | 331 | data: impl IntoIterator<Item = Result<chalk_ir::QuantifiedWhereClause<Self>, E>>, |
311 | ) -> Result<Self::InternedQuantifiedWhereClauses, E> { | 332 | ) -> Result<Self::InternedQuantifiedWhereClauses, E> { |
312 | data.into_iter().collect() | 333 | Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?))) |
313 | } | 334 | } |
314 | 335 | ||
315 | fn quantified_where_clauses_data<'a>( | 336 | fn quantified_where_clauses_data<'a>( |
@@ -323,21 +344,21 @@ impl chalk_ir::interner::Interner for Interner { | |||
323 | &self, | 344 | &self, |
324 | data: impl IntoIterator<Item = Result<chalk_ir::VariableKind<Self>, E>>, | 345 | data: impl IntoIterator<Item = Result<chalk_ir::VariableKind<Self>, E>>, |
325 | ) -> Result<Self::InternedVariableKinds, E> { | 346 | ) -> Result<Self::InternedVariableKinds, E> { |
326 | data.into_iter().collect() | 347 | Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?))) |
327 | } | 348 | } |
328 | 349 | ||
329 | fn variable_kinds_data<'a>( | 350 | fn variable_kinds_data<'a>( |
330 | &self, | 351 | &self, |
331 | parameter_kinds: &'a Self::InternedVariableKinds, | 352 | parameter_kinds: &'a Self::InternedVariableKinds, |
332 | ) -> &'a [chalk_ir::VariableKind<Self>] { | 353 | ) -> &'a [chalk_ir::VariableKind<Self>] { |
333 | ¶meter_kinds | 354 | ¶meter_kinds.as_ref().0 |
334 | } | 355 | } |
335 | 356 | ||
336 | fn intern_canonical_var_kinds<E>( | 357 | fn intern_canonical_var_kinds<E>( |
337 | &self, | 358 | &self, |
338 | data: impl IntoIterator<Item = Result<chalk_ir::CanonicalVarKind<Self>, E>>, | 359 | data: impl IntoIterator<Item = Result<chalk_ir::CanonicalVarKind<Self>, E>>, |
339 | ) -> Result<Self::InternedCanonicalVarKinds, E> { | 360 | ) -> Result<Self::InternedCanonicalVarKinds, E> { |
340 | data.into_iter().collect() | 361 | Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?))) |
341 | } | 362 | } |
342 | 363 | ||
343 | fn canonical_var_kinds_data<'a>( | 364 | fn canonical_var_kinds_data<'a>( |
@@ -377,7 +398,7 @@ impl chalk_ir::interner::Interner for Interner { | |||
377 | &self, | 398 | &self, |
378 | data: impl IntoIterator<Item = Result<chalk_ir::Variance, E>>, | 399 | data: impl IntoIterator<Item = Result<chalk_ir::Variance, E>>, |
379 | ) -> Result<Self::InternedVariances, E> { | 400 | ) -> Result<Self::InternedVariances, E> { |
380 | data.into_iter().collect() | 401 | Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?))) |
381 | } | 402 | } |
382 | 403 | ||
383 | fn variances_data<'a>( | 404 | fn variances_data<'a>( |
@@ -391,3 +412,12 @@ impl chalk_ir::interner::Interner for Interner { | |||
391 | impl chalk_ir::interner::HasInterner for Interner { | 412 | impl chalk_ir::interner::HasInterner for Interner { |
392 | type Interner = Self; | 413 | type Interner = Self; |
393 | } | 414 | } |
415 | |||
416 | #[macro_export] | ||
417 | macro_rules! has_interner { | ||
418 | ($t:ty) => { | ||
419 | impl HasInterner for $t { | ||
420 | type Interner = crate::Interner; | ||
421 | } | ||
422 | }; | ||
423 | } | ||
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index a8c87eadf..0505fa4ae 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs | |||
@@ -1,65 +1,68 @@ | |||
1 | //! The type system. We currently use this to infer types for completion, hover | 1 | //! The type system. We currently use this to infer types for completion, hover |
2 | //! information and various assists. | 2 | //! information and various assists. |
3 | |||
3 | #[allow(unused)] | 4 | #[allow(unused)] |
4 | macro_rules! eprintln { | 5 | macro_rules! eprintln { |
5 | ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; | 6 | ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; |
6 | } | 7 | } |
7 | 8 | ||
8 | mod autoderef; | 9 | mod autoderef; |
9 | pub mod primitive; | ||
10 | pub mod traits; | ||
11 | pub mod method_resolution; | ||
12 | mod op; | ||
13 | mod lower; | ||
14 | pub(crate) mod infer; | ||
15 | pub(crate) mod utils; | ||
16 | mod chalk_cast; | ||
17 | mod chalk_ext; | ||
18 | mod builder; | 10 | mod builder; |
19 | 11 | mod chalk_db; | |
20 | pub mod display; | 12 | mod chalk_ext; |
13 | mod infer; | ||
14 | mod interner; | ||
15 | mod lower; | ||
16 | mod mapping; | ||
17 | mod op; | ||
18 | mod tls; | ||
19 | mod utils; | ||
20 | mod walk; | ||
21 | pub mod db; | 21 | pub mod db; |
22 | pub mod diagnostics; | 22 | pub mod diagnostics; |
23 | pub mod display; | ||
24 | pub mod method_resolution; | ||
25 | pub mod primitive; | ||
26 | pub mod traits; | ||
23 | 27 | ||
24 | #[cfg(test)] | 28 | #[cfg(test)] |
25 | mod tests; | 29 | mod tests; |
26 | #[cfg(test)] | 30 | #[cfg(test)] |
27 | mod test_db; | 31 | mod test_db; |
28 | 32 | ||
29 | use std::{mem, sync::Arc}; | 33 | use std::sync::Arc; |
30 | |||
31 | use chalk_ir::cast::{CastTo, Caster}; | ||
32 | use itertools::Itertools; | ||
33 | use smallvec::SmallVec; | ||
34 | 34 | ||
35 | use base_db::salsa; | 35 | use chalk_ir::{ |
36 | use hir_def::{ | 36 | fold::{Fold, Shift}, |
37 | expr::ExprId, type_ref::Rawness, AssocContainerId, FunctionId, GenericDefId, HasModule, | 37 | interner::HasInterner, |
38 | LifetimeParamId, Lookup, TraitId, TypeAliasId, TypeParamId, | 38 | UintTy, |
39 | }; | 39 | }; |
40 | use hir_def::{expr::ExprId, type_ref::Rawness, TypeParamId}; | ||
40 | 41 | ||
41 | use crate::{ | 42 | use crate::{db::HirDatabase, display::HirDisplay, utils::generics}; |
42 | db::HirDatabase, | ||
43 | display::HirDisplay, | ||
44 | utils::{generics, make_mut_slice}, | ||
45 | }; | ||
46 | 43 | ||
47 | pub use autoderef::autoderef; | 44 | pub use autoderef::autoderef; |
48 | pub use builder::TyBuilder; | 45 | pub use builder::TyBuilder; |
49 | pub use chalk_ext::TyExt; | 46 | pub use chalk_ext::*; |
50 | pub use infer::{could_unify, InferenceResult, InferenceVar}; | 47 | pub use infer::{could_unify, InferenceResult}; |
48 | pub use interner::Interner; | ||
51 | pub use lower::{ | 49 | pub use lower::{ |
52 | associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode, | 50 | associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode, |
53 | TyDefId, TyLoweringContext, ValueTyDefId, | 51 | TyDefId, TyLoweringContext, ValueTyDefId, |
54 | }; | 52 | }; |
55 | pub use traits::{AliasEq, DomainGoal, InEnvironment, TraitEnvironment}; | 53 | pub use mapping::{ |
54 | const_from_placeholder_idx, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, | ||
55 | from_placeholder_idx, lt_from_placeholder_idx, to_assoc_type_id, to_chalk_trait_id, | ||
56 | to_foreign_def_id, to_placeholder_idx, | ||
57 | }; | ||
58 | pub use traits::TraitEnvironment; | ||
59 | pub use utils::all_super_traits; | ||
60 | pub use walk::TypeWalk; | ||
56 | 61 | ||
57 | pub use chalk_ir::{ | 62 | pub use chalk_ir::{ |
58 | cast::Cast, AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind, | 63 | cast::Cast, AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind, |
59 | }; | 64 | }; |
60 | 65 | ||
61 | pub use crate::traits::chalk::Interner; | ||
62 | |||
63 | pub type ForeignDefId = chalk_ir::ForeignDefId<Interner>; | 66 | pub type ForeignDefId = chalk_ir::ForeignDefId<Interner>; |
64 | pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>; | 67 | pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>; |
65 | pub type FnDefId = chalk_ir::FnDefId<Interner>; | 68 | pub type FnDefId = chalk_ir::FnDefId<Interner>; |
@@ -67,401 +70,56 @@ pub type ClosureId = chalk_ir::ClosureId<Interner>; | |||
67 | pub type OpaqueTyId = chalk_ir::OpaqueTyId<Interner>; | 70 | pub type OpaqueTyId = chalk_ir::OpaqueTyId<Interner>; |
68 | pub type PlaceholderIndex = chalk_ir::PlaceholderIndex; | 71 | pub type PlaceholderIndex = chalk_ir::PlaceholderIndex; |
69 | 72 | ||
73 | pub type VariableKind = chalk_ir::VariableKind<Interner>; | ||
74 | pub type VariableKinds = chalk_ir::VariableKinds<Interner>; | ||
70 | pub type CanonicalVarKinds = chalk_ir::CanonicalVarKinds<Interner>; | 75 | pub type CanonicalVarKinds = chalk_ir::CanonicalVarKinds<Interner>; |
76 | pub type Binders<T> = chalk_ir::Binders<T>; | ||
77 | pub type Substitution = chalk_ir::Substitution<Interner>; | ||
78 | pub type GenericArg = chalk_ir::GenericArg<Interner>; | ||
79 | pub type GenericArgData = chalk_ir::GenericArgData<Interner>; | ||
80 | |||
81 | pub type Ty = chalk_ir::Ty<Interner>; | ||
82 | pub type TyKind = chalk_ir::TyKind<Interner>; | ||
83 | pub type DynTy = chalk_ir::DynTy<Interner>; | ||
84 | pub type FnPointer = chalk_ir::FnPointer<Interner>; | ||
85 | // pub type FnSubst = chalk_ir::FnSubst<Interner>; | ||
86 | pub use chalk_ir::FnSubst; | ||
87 | pub type ProjectionTy = chalk_ir::ProjectionTy<Interner>; | ||
88 | pub type AliasTy = chalk_ir::AliasTy<Interner>; | ||
89 | pub type OpaqueTy = chalk_ir::OpaqueTy<Interner>; | ||
90 | pub type InferenceVar = chalk_ir::InferenceVar; | ||
91 | |||
92 | pub type Lifetime = chalk_ir::Lifetime<Interner>; | ||
93 | pub type LifetimeData = chalk_ir::LifetimeData<Interner>; | ||
94 | pub type LifetimeOutlives = chalk_ir::LifetimeOutlives<Interner>; | ||
95 | |||
96 | pub type Const = chalk_ir::Const<Interner>; | ||
97 | pub type ConstData = chalk_ir::ConstData<Interner>; | ||
98 | pub type ConstValue = chalk_ir::ConstValue<Interner>; | ||
99 | pub type ConcreteConst = chalk_ir::ConcreteConst<Interner>; | ||
71 | 100 | ||
72 | pub type ChalkTraitId = chalk_ir::TraitId<Interner>; | 101 | pub type ChalkTraitId = chalk_ir::TraitId<Interner>; |
73 | 102 | pub type TraitRef = chalk_ir::TraitRef<Interner>; | |
74 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | 103 | pub type QuantifiedWhereClause = Binders<WhereClause>; |
75 | pub enum Lifetime { | 104 | pub type QuantifiedWhereClauses = chalk_ir::QuantifiedWhereClauses<Interner>; |
76 | Parameter(LifetimeParamId), | 105 | pub type Canonical<T> = chalk_ir::Canonical<T>; |
77 | Static, | ||
78 | } | ||
79 | |||
80 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | ||
81 | pub struct OpaqueTy { | ||
82 | pub opaque_ty_id: OpaqueTyId, | ||
83 | pub substitution: Substitution, | ||
84 | } | ||
85 | |||
86 | impl TypeWalk for OpaqueTy { | ||
87 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
88 | self.substitution.walk(f); | ||
89 | } | ||
90 | |||
91 | fn walk_mut_binders( | ||
92 | &mut self, | ||
93 | f: &mut impl FnMut(&mut Ty, DebruijnIndex), | ||
94 | binders: DebruijnIndex, | ||
95 | ) { | ||
96 | self.substitution.walk_mut_binders(f, binders); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | /// A "projection" type corresponds to an (unnormalized) | ||
101 | /// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the | ||
102 | /// trait and all its parameters are fully known. | ||
103 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | ||
104 | pub struct ProjectionTy { | ||
105 | pub associated_ty_id: AssocTypeId, | ||
106 | pub substitution: Substitution, | ||
107 | } | ||
108 | |||
109 | impl ProjectionTy { | ||
110 | pub fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef { | ||
111 | TraitRef { | ||
112 | trait_id: to_chalk_trait_id(self.trait_(db)), | ||
113 | substitution: self.substitution.clone(), | ||
114 | } | ||
115 | } | ||
116 | |||
117 | pub fn self_type_parameter(&self) -> &Ty { | ||
118 | &self.substitution.interned(&Interner)[0].assert_ty_ref(&Interner) | ||
119 | } | ||
120 | |||
121 | fn trait_(&self, db: &dyn HirDatabase) -> TraitId { | ||
122 | match from_assoc_type_id(self.associated_ty_id).lookup(db.upcast()).container { | ||
123 | AssocContainerId::TraitId(it) => it, | ||
124 | _ => panic!("projection ty without parent trait"), | ||
125 | } | ||
126 | } | ||
127 | } | ||
128 | |||
129 | impl TypeWalk for ProjectionTy { | ||
130 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
131 | self.substitution.walk(f); | ||
132 | } | ||
133 | |||
134 | fn walk_mut_binders( | ||
135 | &mut self, | ||
136 | f: &mut impl FnMut(&mut Ty, DebruijnIndex), | ||
137 | binders: DebruijnIndex, | ||
138 | ) { | ||
139 | self.substitution.walk_mut_binders(f, binders); | ||
140 | } | ||
141 | } | ||
142 | |||
143 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | ||
144 | pub struct DynTy { | ||
145 | /// The unknown self type. | ||
146 | pub bounds: Binders<QuantifiedWhereClauses>, | ||
147 | } | ||
148 | 106 | ||
149 | pub type FnSig = chalk_ir::FnSig<Interner>; | 107 | pub type FnSig = chalk_ir::FnSig<Interner>; |
150 | 108 | ||
151 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | 109 | pub type InEnvironment<T> = chalk_ir::InEnvironment<T>; |
152 | pub struct FnPointer { | 110 | pub type DomainGoal = chalk_ir::DomainGoal<Interner>; |
153 | pub num_args: usize, | 111 | pub type AliasEq = chalk_ir::AliasEq<Interner>; |
154 | pub sig: FnSig, | 112 | pub type Solution = chalk_solve::Solution<Interner>; |
155 | pub substs: Substitution, | 113 | pub type ConstrainedSubst = chalk_ir::ConstrainedSubst<Interner>; |
156 | } | 114 | pub type Guidance = chalk_solve::Guidance<Interner>; |
157 | 115 | pub type WhereClause = chalk_ir::WhereClause<Interner>; | |
158 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | ||
159 | pub enum AliasTy { | ||
160 | /// A "projection" type corresponds to an (unnormalized) | ||
161 | /// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the | ||
162 | /// trait and all its parameters are fully known. | ||
163 | Projection(ProjectionTy), | ||
164 | /// An opaque type (`impl Trait`). | ||
165 | /// | ||
166 | /// This is currently only used for return type impl trait; each instance of | ||
167 | /// `impl Trait` in a return type gets its own ID. | ||
168 | Opaque(OpaqueTy), | ||
169 | } | ||
170 | |||
171 | impl TypeWalk for AliasTy { | ||
172 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
173 | match self { | ||
174 | AliasTy::Projection(it) => it.walk(f), | ||
175 | AliasTy::Opaque(it) => it.walk(f), | ||
176 | } | ||
177 | } | ||
178 | 116 | ||
179 | fn walk_mut_binders( | 117 | // FIXME: get rid of this |
180 | &mut self, | 118 | pub fn subst_prefix(s: &Substitution, n: usize) -> Substitution { |
181 | f: &mut impl FnMut(&mut Ty, DebruijnIndex), | 119 | Substitution::from_iter( |
182 | binders: DebruijnIndex, | 120 | &Interner, |
183 | ) { | 121 | s.as_slice(&Interner)[..std::cmp::min(s.len(&Interner), n)].iter().cloned(), |
184 | match self { | 122 | ) |
185 | AliasTy::Projection(it) => it.walk_mut_binders(f, binders), | ||
186 | AliasTy::Opaque(it) => it.walk_mut_binders(f, binders), | ||
187 | } | ||
188 | } | ||
189 | } | ||
190 | /// A type. | ||
191 | /// | ||
192 | /// See also the `TyKind` enum in rustc (librustc/ty/sty.rs), which represents | ||
193 | /// the same thing (but in a different way). | ||
194 | /// | ||
195 | /// This should be cheap to clone. | ||
196 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | ||
197 | pub enum TyKind { | ||
198 | /// Structures, enumerations and unions. | ||
199 | Adt(AdtId<Interner>, Substitution), | ||
200 | |||
201 | /// Represents an associated item like `Iterator::Item`. This is used | ||
202 | /// when we have tried to normalize a projection like `T::Item` but | ||
203 | /// couldn't find a better representation. In that case, we generate | ||
204 | /// an **application type** like `(Iterator::Item)<T>`. | ||
205 | AssociatedType(AssocTypeId, Substitution), | ||
206 | |||
207 | /// a scalar type like `bool` or `u32` | ||
208 | Scalar(Scalar), | ||
209 | |||
210 | /// A tuple type. For example, `(i32, bool)`. | ||
211 | Tuple(usize, Substitution), | ||
212 | |||
213 | /// An array with the given length. Written as `[T; n]`. | ||
214 | Array(Ty), | ||
215 | |||
216 | /// The pointee of an array slice. Written as `[T]`. | ||
217 | Slice(Ty), | ||
218 | |||
219 | /// A raw pointer. Written as `*mut T` or `*const T` | ||
220 | Raw(Mutability, Ty), | ||
221 | |||
222 | /// A reference; a pointer with an associated lifetime. Written as | ||
223 | /// `&'a mut T` or `&'a T`. | ||
224 | Ref(Mutability, Ty), | ||
225 | |||
226 | /// This represents a placeholder for an opaque type in situations where we | ||
227 | /// don't know the hidden type (i.e. currently almost always). This is | ||
228 | /// analogous to the `AssociatedType` type constructor. | ||
229 | /// It is also used as the type of async block, with one type parameter | ||
230 | /// representing the Future::Output type. | ||
231 | OpaqueType(OpaqueTyId, Substitution), | ||
232 | |||
233 | /// The anonymous type of a function declaration/definition. Each | ||
234 | /// function has a unique type, which is output (for a function | ||
235 | /// named `foo` returning an `i32`) as `fn() -> i32 {foo}`. | ||
236 | /// | ||
237 | /// This includes tuple struct / enum variant constructors as well. | ||
238 | /// | ||
239 | /// For example the type of `bar` here: | ||
240 | /// | ||
241 | /// ``` | ||
242 | /// fn foo() -> i32 { 1 } | ||
243 | /// let bar = foo; // bar: fn() -> i32 {foo} | ||
244 | /// ``` | ||
245 | FnDef(FnDefId, Substitution), | ||
246 | |||
247 | /// The pointee of a string slice. Written as `str`. | ||
248 | Str, | ||
249 | |||
250 | /// The never type `!`. | ||
251 | Never, | ||
252 | |||
253 | /// The type of a specific closure. | ||
254 | /// | ||
255 | /// The closure signature is stored in a `FnPtr` type in the first type | ||
256 | /// parameter. | ||
257 | Closure(ClosureId, Substitution), | ||
258 | |||
259 | /// Represents a foreign type declared in external blocks. | ||
260 | ForeignType(ForeignDefId), | ||
261 | |||
262 | /// A pointer to a function. Written as `fn() -> i32`. | ||
263 | /// | ||
264 | /// For example the type of `bar` here: | ||
265 | /// | ||
266 | /// ``` | ||
267 | /// fn foo() -> i32 { 1 } | ||
268 | /// let bar: fn() -> i32 = foo; | ||
269 | /// ``` | ||
270 | Function(FnPointer), | ||
271 | |||
272 | /// An "alias" type represents some form of type alias, such as: | ||
273 | /// - An associated type projection like `<T as Iterator>::Item` | ||
274 | /// - `impl Trait` types | ||
275 | /// - Named type aliases like `type Foo<X> = Vec<X>` | ||
276 | Alias(AliasTy), | ||
277 | |||
278 | /// A placeholder for a type parameter; for example, `T` in `fn f<T>(x: T) | ||
279 | /// {}` when we're type-checking the body of that function. In this | ||
280 | /// situation, we know this stands for *some* type, but don't know the exact | ||
281 | /// type. | ||
282 | Placeholder(PlaceholderIndex), | ||
283 | |||
284 | /// A bound type variable. This is used in various places: when representing | ||
285 | /// some polymorphic type like the type of function `fn f<T>`, the type | ||
286 | /// parameters get turned into variables; during trait resolution, inference | ||
287 | /// variables get turned into bound variables and back; and in `Dyn` the | ||
288 | /// `Self` type is represented with a bound variable as well. | ||
289 | BoundVar(BoundVar), | ||
290 | |||
291 | /// A type variable used during type checking. | ||
292 | InferenceVar(InferenceVar, TyVariableKind), | ||
293 | |||
294 | /// A trait object (`dyn Trait` or bare `Trait` in pre-2018 Rust). | ||
295 | /// | ||
296 | /// The predicates are quantified over the `Self` type, i.e. `Ty::Bound(0)` | ||
297 | /// represents the `Self` type inside the bounds. This is currently | ||
298 | /// implicit; Chalk has the `Binders` struct to make it explicit, but it | ||
299 | /// didn't seem worth the overhead yet. | ||
300 | Dyn(DynTy), | ||
301 | |||
302 | /// A placeholder for a type which could not be computed; this is propagated | ||
303 | /// to avoid useless error messages. Doubles as a placeholder where type | ||
304 | /// variables are inserted before type checking, since we want to try to | ||
305 | /// infer a better type here anyway -- for the IDE use case, we want to try | ||
306 | /// to infer as much as possible even in the presence of type errors. | ||
307 | Unknown, | ||
308 | } | ||
309 | |||
310 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | ||
311 | pub struct Ty(Arc<TyKind>); | ||
312 | |||
313 | impl TyKind { | ||
314 | pub fn intern(self, _interner: &Interner) -> Ty { | ||
315 | Ty(Arc::new(self)) | ||
316 | } | ||
317 | } | ||
318 | |||
319 | impl Ty { | ||
320 | pub fn kind(&self, _interner: &Interner) -> &TyKind { | ||
321 | &self.0 | ||
322 | } | ||
323 | |||
324 | pub fn interned_mut(&mut self) -> &mut TyKind { | ||
325 | Arc::make_mut(&mut self.0) | ||
326 | } | ||
327 | |||
328 | pub fn into_inner(self) -> TyKind { | ||
329 | Arc::try_unwrap(self.0).unwrap_or_else(|a| (*a).clone()) | ||
330 | } | ||
331 | } | ||
332 | |||
333 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | ||
334 | pub struct GenericArg { | ||
335 | interned: GenericArgData, | ||
336 | } | ||
337 | |||
338 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | ||
339 | pub enum GenericArgData { | ||
340 | Ty(Ty), | ||
341 | } | ||
342 | |||
343 | impl GenericArg { | ||
344 | /// Constructs a generic argument using `GenericArgData`. | ||
345 | pub fn new(_interner: &Interner, data: GenericArgData) -> Self { | ||
346 | GenericArg { interned: data } | ||
347 | } | ||
348 | |||
349 | /// Gets the interned value. | ||
350 | pub fn interned(&self) -> &GenericArgData { | ||
351 | &self.interned | ||
352 | } | ||
353 | |||
354 | /// Asserts that this is a type argument. | ||
355 | pub fn assert_ty_ref(&self, interner: &Interner) -> &Ty { | ||
356 | self.ty(interner).unwrap() | ||
357 | } | ||
358 | |||
359 | /// Checks whether the generic argument is a type. | ||
360 | pub fn is_ty(&self, _interner: &Interner) -> bool { | ||
361 | match self.interned() { | ||
362 | GenericArgData::Ty(_) => true, | ||
363 | } | ||
364 | } | ||
365 | |||
366 | /// Returns the type if it is one, `None` otherwise. | ||
367 | pub fn ty(&self, _interner: &Interner) -> Option<&Ty> { | ||
368 | match self.interned() { | ||
369 | GenericArgData::Ty(t) => Some(t), | ||
370 | } | ||
371 | } | ||
372 | } | ||
373 | |||
374 | impl TypeWalk for GenericArg { | ||
375 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
376 | match &self.interned { | ||
377 | GenericArgData::Ty(ty) => { | ||
378 | ty.walk(f); | ||
379 | } | ||
380 | } | ||
381 | } | ||
382 | |||
383 | fn walk_mut_binders( | ||
384 | &mut self, | ||
385 | f: &mut impl FnMut(&mut Ty, DebruijnIndex), | ||
386 | binders: DebruijnIndex, | ||
387 | ) { | ||
388 | match &mut self.interned { | ||
389 | GenericArgData::Ty(ty) => { | ||
390 | ty.walk_mut_binders(f, binders); | ||
391 | } | ||
392 | } | ||
393 | } | ||
394 | } | ||
395 | |||
396 | /// A list of substitutions for generic parameters. | ||
397 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | ||
398 | pub struct Substitution(SmallVec<[GenericArg; 2]>); | ||
399 | |||
400 | impl TypeWalk for Substitution { | ||
401 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
402 | for t in self.0.iter() { | ||
403 | t.walk(f); | ||
404 | } | ||
405 | } | ||
406 | |||
407 | fn walk_mut_binders( | ||
408 | &mut self, | ||
409 | f: &mut impl FnMut(&mut Ty, DebruijnIndex), | ||
410 | binders: DebruijnIndex, | ||
411 | ) { | ||
412 | for t in &mut self.0 { | ||
413 | t.walk_mut_binders(f, binders); | ||
414 | } | ||
415 | } | ||
416 | } | ||
417 | |||
418 | impl Substitution { | ||
419 | pub fn interned(&self, _: &Interner) -> &[GenericArg] { | ||
420 | &self.0 | ||
421 | } | ||
422 | |||
423 | pub fn len(&self, _: &Interner) -> usize { | ||
424 | self.0.len() | ||
425 | } | ||
426 | |||
427 | pub fn is_empty(&self, _: &Interner) -> bool { | ||
428 | self.0.is_empty() | ||
429 | } | ||
430 | |||
431 | pub fn at(&self, _: &Interner, i: usize) -> &GenericArg { | ||
432 | &self.0[i] | ||
433 | } | ||
434 | |||
435 | pub fn empty(_: &Interner) -> Substitution { | ||
436 | Substitution(SmallVec::new()) | ||
437 | } | ||
438 | |||
439 | pub fn iter(&self, _: &Interner) -> std::slice::Iter<'_, GenericArg> { | ||
440 | self.0.iter() | ||
441 | } | ||
442 | |||
443 | pub fn single(ty: Ty) -> Substitution { | ||
444 | Substitution({ | ||
445 | let mut v = SmallVec::new(); | ||
446 | v.push(ty.cast(&Interner)); | ||
447 | v | ||
448 | }) | ||
449 | } | ||
450 | |||
451 | pub fn prefix(&self, n: usize) -> Substitution { | ||
452 | Substitution(self.0[..std::cmp::min(self.0.len(), n)].into()) | ||
453 | } | ||
454 | |||
455 | pub fn suffix(&self, n: usize) -> Substitution { | ||
456 | Substitution(self.0[self.0.len() - std::cmp::min(self.0.len(), n)..].into()) | ||
457 | } | ||
458 | |||
459 | pub fn from_iter( | ||
460 | interner: &Interner, | ||
461 | elements: impl IntoIterator<Item = impl CastTo<GenericArg>>, | ||
462 | ) -> Self { | ||
463 | Substitution(elements.into_iter().casted(interner).collect()) | ||
464 | } | ||
465 | } | 123 | } |
466 | 124 | ||
467 | /// Return an index of a parameter in the generic type parameter list by it's id. | 125 | /// Return an index of a parameter in the generic type parameter list by it's id. |
@@ -469,190 +127,39 @@ pub fn param_idx(db: &dyn HirDatabase, id: TypeParamId) -> Option<usize> { | |||
469 | generics(db.upcast(), id.parent).param_idx(id) | 127 | generics(db.upcast(), id.parent).param_idx(id) |
470 | } | 128 | } |
471 | 129 | ||
472 | #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] | 130 | pub(crate) fn wrap_empty_binders<T>(value: T) -> Binders<T> |
473 | pub struct Binders<T> { | 131 | where |
474 | pub num_binders: usize, | 132 | T: Fold<Interner, Result = T> + HasInterner<Interner = Interner>, |
475 | pub value: T, | 133 | { |
476 | } | 134 | Binders::empty(&Interner, value.shifted_in_from(&Interner, DebruijnIndex::ONE)) |
477 | 135 | } | |
478 | impl<T> Binders<T> { | 136 | |
479 | pub fn new(num_binders: usize, value: T) -> Self { | 137 | pub(crate) fn make_only_type_binders<T: HasInterner<Interner = Interner>>( |
480 | Self { num_binders, value } | 138 | num_vars: usize, |
481 | } | 139 | value: T, |
482 | 140 | ) -> Binders<T> { | |
483 | pub fn wrap_empty(value: T) -> Self | 141 | Binders::new( |
484 | where | 142 | VariableKinds::from_iter( |
485 | T: TypeWalk, | 143 | &Interner, |
486 | { | 144 | std::iter::repeat(chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)) |
487 | Self { num_binders: 0, value: value.shift_bound_vars(DebruijnIndex::ONE) } | 145 | .take(num_vars), |
488 | } | 146 | ), |
489 | 147 | value, | |
490 | pub fn as_ref(&self) -> Binders<&T> { | 148 | ) |
491 | Binders { num_binders: self.num_binders, value: &self.value } | 149 | } |
492 | } | 150 | |
493 | 151 | // FIXME: get rid of this | |
494 | pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Binders<U> { | 152 | pub fn make_canonical<T: HasInterner<Interner = Interner>>( |
495 | Binders { num_binders: self.num_binders, value: f(self.value) } | 153 | value: T, |
496 | } | 154 | kinds: impl IntoIterator<Item = TyVariableKind>, |
497 | 155 | ) -> Canonical<T> { | |
498 | pub fn filter_map<U>(self, f: impl FnOnce(T) -> Option<U>) -> Option<Binders<U>> { | 156 | let kinds = kinds.into_iter().map(|tk| { |
499 | Some(Binders { num_binders: self.num_binders, value: f(self.value)? }) | 157 | chalk_ir::CanonicalVarKind::new( |
500 | } | 158 | chalk_ir::VariableKind::Ty(tk), |
501 | 159 | chalk_ir::UniverseIndex::ROOT, | |
502 | pub fn skip_binders(&self) -> &T { | 160 | ) |
503 | &self.value | 161 | }); |
504 | } | 162 | Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(&Interner, kinds) } |
505 | |||
506 | pub fn into_value_and_skipped_binders(self) -> (T, usize) { | ||
507 | (self.value, self.num_binders) | ||
508 | } | ||
509 | } | ||
510 | |||
511 | impl<T: Clone> Binders<&T> { | ||
512 | pub fn cloned(&self) -> Binders<T> { | ||
513 | Binders { num_binders: self.num_binders, value: self.value.clone() } | ||
514 | } | ||
515 | } | ||
516 | |||
517 | impl<T: TypeWalk> Binders<T> { | ||
518 | /// Substitutes all variables. | ||
519 | pub fn subst(self, subst: &Substitution) -> T { | ||
520 | assert_eq!(subst.len(&Interner), self.num_binders); | ||
521 | self.value.subst_bound_vars(subst) | ||
522 | } | ||
523 | } | ||
524 | |||
525 | impl<T: TypeWalk> TypeWalk for Binders<T> { | ||
526 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
527 | self.value.walk(f); | ||
528 | } | ||
529 | |||
530 | fn walk_mut_binders( | ||
531 | &mut self, | ||
532 | f: &mut impl FnMut(&mut Ty, DebruijnIndex), | ||
533 | binders: DebruijnIndex, | ||
534 | ) { | ||
535 | self.value.walk_mut_binders(f, binders.shifted_in()) | ||
536 | } | ||
537 | } | ||
538 | |||
539 | /// A trait with type parameters. This includes the `Self`, so this represents a concrete type implementing the trait. | ||
540 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | ||
541 | pub struct TraitRef { | ||
542 | pub trait_id: ChalkTraitId, | ||
543 | pub substitution: Substitution, | ||
544 | } | ||
545 | |||
546 | impl TraitRef { | ||
547 | pub fn self_type_parameter(&self) -> &Ty { | ||
548 | &self.substitution.at(&Interner, 0).assert_ty_ref(&Interner) | ||
549 | } | ||
550 | |||
551 | pub fn hir_trait_id(&self) -> TraitId { | ||
552 | from_chalk_trait_id(self.trait_id) | ||
553 | } | ||
554 | } | ||
555 | |||
556 | impl TypeWalk for TraitRef { | ||
557 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
558 | self.substitution.walk(f); | ||
559 | } | ||
560 | |||
561 | fn walk_mut_binders( | ||
562 | &mut self, | ||
563 | f: &mut impl FnMut(&mut Ty, DebruijnIndex), | ||
564 | binders: DebruijnIndex, | ||
565 | ) { | ||
566 | self.substitution.walk_mut_binders(f, binders); | ||
567 | } | ||
568 | } | ||
569 | |||
570 | /// Like `generics::WherePredicate`, but with resolved types: A condition on the | ||
571 | /// parameters of a generic item. | ||
572 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
573 | pub enum WhereClause { | ||
574 | /// The given trait needs to be implemented for its type parameters. | ||
575 | Implemented(TraitRef), | ||
576 | /// An associated type bindings like in `Iterator<Item = T>`. | ||
577 | AliasEq(AliasEq), | ||
578 | } | ||
579 | |||
580 | impl WhereClause { | ||
581 | pub fn is_implemented(&self) -> bool { | ||
582 | matches!(self, WhereClause::Implemented(_)) | ||
583 | } | ||
584 | |||
585 | pub fn trait_ref(&self, db: &dyn HirDatabase) -> Option<TraitRef> { | ||
586 | match self { | ||
587 | WhereClause::Implemented(tr) => Some(tr.clone()), | ||
588 | WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(proj), .. }) => { | ||
589 | Some(proj.trait_ref(db)) | ||
590 | } | ||
591 | WhereClause::AliasEq(_) => None, | ||
592 | } | ||
593 | } | ||
594 | } | ||
595 | |||
596 | impl TypeWalk for WhereClause { | ||
597 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
598 | match self { | ||
599 | WhereClause::Implemented(trait_ref) => trait_ref.walk(f), | ||
600 | WhereClause::AliasEq(alias_eq) => alias_eq.walk(f), | ||
601 | } | ||
602 | } | ||
603 | |||
604 | fn walk_mut_binders( | ||
605 | &mut self, | ||
606 | f: &mut impl FnMut(&mut Ty, DebruijnIndex), | ||
607 | binders: DebruijnIndex, | ||
608 | ) { | ||
609 | match self { | ||
610 | WhereClause::Implemented(trait_ref) => trait_ref.walk_mut_binders(f, binders), | ||
611 | WhereClause::AliasEq(alias_eq) => alias_eq.walk_mut_binders(f, binders), | ||
612 | } | ||
613 | } | ||
614 | } | ||
615 | |||
616 | pub type QuantifiedWhereClause = Binders<WhereClause>; | ||
617 | |||
618 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
619 | pub struct QuantifiedWhereClauses(Arc<[QuantifiedWhereClause]>); | ||
620 | |||
621 | impl QuantifiedWhereClauses { | ||
622 | pub fn from_iter( | ||
623 | _interner: &Interner, | ||
624 | elements: impl IntoIterator<Item = QuantifiedWhereClause>, | ||
625 | ) -> Self { | ||
626 | QuantifiedWhereClauses(elements.into_iter().collect()) | ||
627 | } | ||
628 | |||
629 | pub fn interned(&self) -> &Arc<[QuantifiedWhereClause]> { | ||
630 | &self.0 | ||
631 | } | ||
632 | } | ||
633 | |||
634 | /// Basically a claim (currently not validated / checked) that the contained | ||
635 | /// type / trait ref contains no inference variables; any inference variables it | ||
636 | /// contained have been replaced by bound variables, and `kinds` tells us how | ||
637 | /// many there are and whether they were normal or float/int variables. This is | ||
638 | /// used to erase irrelevant differences between types before using them in | ||
639 | /// queries. | ||
640 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
641 | pub struct Canonical<T> { | ||
642 | pub value: T, | ||
643 | pub binders: CanonicalVarKinds, | ||
644 | } | ||
645 | |||
646 | impl<T> Canonical<T> { | ||
647 | pub fn new(value: T, kinds: impl IntoIterator<Item = TyVariableKind>) -> Self { | ||
648 | let kinds = kinds.into_iter().map(|tk| { | ||
649 | chalk_ir::CanonicalVarKind::new( | ||
650 | chalk_ir::VariableKind::Ty(tk), | ||
651 | chalk_ir::UniverseIndex::ROOT, | ||
652 | ) | ||
653 | }); | ||
654 | Self { value, binders: chalk_ir::CanonicalVarKinds::from_iter(&Interner, kinds) } | ||
655 | } | ||
656 | } | 163 | } |
657 | 164 | ||
658 | /// A function signature as seen by type inference: Several parameter types and | 165 | /// A function signature as seen by type inference: Several parameter types and |
@@ -663,6 +170,8 @@ pub struct CallableSig { | |||
663 | is_varargs: bool, | 170 | is_varargs: bool, |
664 | } | 171 | } |
665 | 172 | ||
173 | has_interner!(CallableSig); | ||
174 | |||
666 | /// A polymorphic function signature. | 175 | /// A polymorphic function signature. |
667 | pub type PolyFnSig = Binders<CallableSig>; | 176 | pub type PolyFnSig = Binders<CallableSig>; |
668 | 177 | ||
@@ -676,10 +185,12 @@ impl CallableSig { | |||
676 | CallableSig { | 185 | CallableSig { |
677 | // FIXME: what to do about lifetime params? -> return PolyFnSig | 186 | // FIXME: what to do about lifetime params? -> return PolyFnSig |
678 | params_and_return: fn_ptr | 187 | params_and_return: fn_ptr |
679 | .substs | 188 | .substitution |
680 | .clone() | 189 | .clone() |
681 | .shift_bound_vars_out(DebruijnIndex::ONE) | 190 | .shifted_out_to(&Interner, DebruijnIndex::ONE) |
682 | .interned(&Interner) | 191 | .expect("unexpected lifetime vars in fn ptr") |
192 | .0 | ||
193 | .as_slice(&Interner) | ||
683 | .iter() | 194 | .iter() |
684 | .map(|arg| arg.assert_ty_ref(&Interner).clone()) | 195 | .map(|arg| arg.assert_ty_ref(&Interner).clone()) |
685 | .collect(), | 196 | .collect(), |
@@ -696,485 +207,20 @@ impl CallableSig { | |||
696 | } | 207 | } |
697 | } | 208 | } |
698 | 209 | ||
699 | impl TypeWalk for CallableSig { | 210 | impl Fold<Interner> for CallableSig { |
700 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | 211 | type Result = CallableSig; |
701 | for t in self.params_and_return.iter() { | ||
702 | t.walk(f); | ||
703 | } | ||
704 | } | ||
705 | |||
706 | fn walk_mut_binders( | ||
707 | &mut self, | ||
708 | f: &mut impl FnMut(&mut Ty, DebruijnIndex), | ||
709 | binders: DebruijnIndex, | ||
710 | ) { | ||
711 | for t in make_mut_slice(&mut self.params_and_return) { | ||
712 | t.walk_mut_binders(f, binders); | ||
713 | } | ||
714 | } | ||
715 | } | ||
716 | |||
717 | impl Ty { | ||
718 | pub fn as_reference(&self) -> Option<(&Ty, Mutability)> { | ||
719 | match self.kind(&Interner) { | ||
720 | TyKind::Ref(mutability, ty) => Some((ty, *mutability)), | ||
721 | _ => None, | ||
722 | } | ||
723 | } | ||
724 | |||
725 | pub fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> { | ||
726 | match self.kind(&Interner) { | ||
727 | TyKind::Ref(mutability, ty) => Some((ty, Rawness::Ref, *mutability)), | ||
728 | TyKind::Raw(mutability, ty) => Some((ty, Rawness::RawPtr, *mutability)), | ||
729 | _ => None, | ||
730 | } | ||
731 | } | ||
732 | |||
733 | pub fn strip_references(&self) -> &Ty { | ||
734 | let mut t: &Ty = self; | ||
735 | |||
736 | while let TyKind::Ref(_mutability, ty) = t.kind(&Interner) { | ||
737 | t = ty; | ||
738 | } | ||
739 | |||
740 | t | ||
741 | } | ||
742 | |||
743 | pub fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> { | ||
744 | match self.kind(&Interner) { | ||
745 | TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)), | ||
746 | _ => None, | ||
747 | } | ||
748 | } | ||
749 | |||
750 | pub fn as_tuple(&self) -> Option<&Substitution> { | ||
751 | match self.kind(&Interner) { | ||
752 | TyKind::Tuple(_, substs) => Some(substs), | ||
753 | _ => None, | ||
754 | } | ||
755 | } | ||
756 | |||
757 | pub fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId> { | ||
758 | match *self.kind(&Interner) { | ||
759 | TyKind::Adt(AdtId(adt), ..) => Some(adt.into()), | ||
760 | TyKind::FnDef(callable, ..) => { | ||
761 | Some(db.lookup_intern_callable_def(callable.into()).into()) | ||
762 | } | ||
763 | TyKind::AssociatedType(type_alias, ..) => Some(from_assoc_type_id(type_alias).into()), | ||
764 | TyKind::ForeignType(type_alias, ..) => Some(from_foreign_def_id(type_alias).into()), | ||
765 | _ => None, | ||
766 | } | ||
767 | } | ||
768 | |||
769 | pub fn is_never(&self) -> bool { | ||
770 | matches!(self.kind(&Interner), TyKind::Never) | ||
771 | } | ||
772 | |||
773 | pub fn is_unknown(&self) -> bool { | ||
774 | matches!(self.kind(&Interner), TyKind::Unknown) | ||
775 | } | ||
776 | |||
777 | pub fn equals_ctor(&self, other: &Ty) -> bool { | ||
778 | match (self.kind(&Interner), other.kind(&Interner)) { | ||
779 | (TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2, | ||
780 | (TyKind::Slice(_), TyKind::Slice(_)) | (TyKind::Array(_), TyKind::Array(_)) => true, | ||
781 | (TyKind::FnDef(def_id, ..), TyKind::FnDef(def_id2, ..)) => def_id == def_id2, | ||
782 | (TyKind::OpaqueType(ty_id, ..), TyKind::OpaqueType(ty_id2, ..)) => ty_id == ty_id2, | ||
783 | (TyKind::AssociatedType(ty_id, ..), TyKind::AssociatedType(ty_id2, ..)) => { | ||
784 | ty_id == ty_id2 | ||
785 | } | ||
786 | (TyKind::ForeignType(ty_id, ..), TyKind::ForeignType(ty_id2, ..)) => ty_id == ty_id2, | ||
787 | (TyKind::Closure(id1, _), TyKind::Closure(id2, _)) => id1 == id2, | ||
788 | (TyKind::Ref(mutability, ..), TyKind::Ref(mutability2, ..)) | ||
789 | | (TyKind::Raw(mutability, ..), TyKind::Raw(mutability2, ..)) => { | ||
790 | mutability == mutability2 | ||
791 | } | ||
792 | ( | ||
793 | TyKind::Function(FnPointer { num_args, sig, .. }), | ||
794 | TyKind::Function(FnPointer { num_args: num_args2, sig: sig2, .. }), | ||
795 | ) => num_args == num_args2 && sig == sig2, | ||
796 | (TyKind::Tuple(cardinality, _), TyKind::Tuple(cardinality2, _)) => { | ||
797 | cardinality == cardinality2 | ||
798 | } | ||
799 | (TyKind::Str, TyKind::Str) | (TyKind::Never, TyKind::Never) => true, | ||
800 | (TyKind::Scalar(scalar), TyKind::Scalar(scalar2)) => scalar == scalar2, | ||
801 | _ => false, | ||
802 | } | ||
803 | } | ||
804 | |||
805 | /// If this is a `dyn Trait` type, this returns the `Trait` part. | ||
806 | fn dyn_trait_ref(&self) -> Option<&TraitRef> { | ||
807 | match self.kind(&Interner) { | ||
808 | TyKind::Dyn(dyn_ty) => { | ||
809 | dyn_ty.bounds.value.interned().get(0).and_then(|b| match b.skip_binders() { | ||
810 | WhereClause::Implemented(trait_ref) => Some(trait_ref), | ||
811 | _ => None, | ||
812 | }) | ||
813 | } | ||
814 | _ => None, | ||
815 | } | ||
816 | } | ||
817 | |||
818 | /// If this is a `dyn Trait`, returns that trait. | ||
819 | pub fn dyn_trait(&self) -> Option<TraitId> { | ||
820 | self.dyn_trait_ref().map(|it| it.trait_id).map(from_chalk_trait_id) | ||
821 | } | ||
822 | |||
823 | fn builtin_deref(&self) -> Option<Ty> { | ||
824 | match self.kind(&Interner) { | ||
825 | TyKind::Ref(.., ty) => Some(ty.clone()), | ||
826 | TyKind::Raw(.., ty) => Some(ty.clone()), | ||
827 | _ => None, | ||
828 | } | ||
829 | } | ||
830 | |||
831 | pub fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId> { | ||
832 | match self.kind(&Interner) { | ||
833 | &TyKind::FnDef(def, ..) => Some(db.lookup_intern_callable_def(def.into())), | ||
834 | _ => None, | ||
835 | } | ||
836 | } | ||
837 | |||
838 | pub fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId> { | ||
839 | if let Some(CallableDefId::FunctionId(func)) = self.callable_def(db) { | ||
840 | Some(func) | ||
841 | } else { | ||
842 | None | ||
843 | } | ||
844 | } | ||
845 | |||
846 | pub fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig> { | ||
847 | match self.kind(&Interner) { | ||
848 | TyKind::Function(fn_ptr) => Some(CallableSig::from_fn_ptr(fn_ptr)), | ||
849 | TyKind::FnDef(def, parameters) => { | ||
850 | let callable_def = db.lookup_intern_callable_def((*def).into()); | ||
851 | let sig = db.callable_item_signature(callable_def); | ||
852 | Some(sig.subst(¶meters)) | ||
853 | } | ||
854 | TyKind::Closure(.., substs) => { | ||
855 | let sig_param = substs.at(&Interner, 0).assert_ty_ref(&Interner); | ||
856 | sig_param.callable_sig(db) | ||
857 | } | ||
858 | _ => None, | ||
859 | } | ||
860 | } | ||
861 | |||
862 | /// Returns the type parameters of this type if it has some (i.e. is an ADT | ||
863 | /// or function); so if `self` is `Option<u32>`, this returns the `u32`. | ||
864 | pub fn substs(&self) -> Option<&Substitution> { | ||
865 | match self.kind(&Interner) { | ||
866 | TyKind::Adt(_, substs) | ||
867 | | TyKind::FnDef(_, substs) | ||
868 | | TyKind::Function(FnPointer { substs, .. }) | ||
869 | | TyKind::Tuple(_, substs) | ||
870 | | TyKind::OpaqueType(_, substs) | ||
871 | | TyKind::AssociatedType(_, substs) | ||
872 | | TyKind::Closure(.., substs) => Some(substs), | ||
873 | _ => None, | ||
874 | } | ||
875 | } | ||
876 | |||
877 | fn substs_mut(&mut self) -> Option<&mut Substitution> { | ||
878 | match self.interned_mut() { | ||
879 | TyKind::Adt(_, substs) | ||
880 | | TyKind::FnDef(_, substs) | ||
881 | | TyKind::Function(FnPointer { substs, .. }) | ||
882 | | TyKind::Tuple(_, substs) | ||
883 | | TyKind::OpaqueType(_, substs) | ||
884 | | TyKind::AssociatedType(_, substs) | ||
885 | | TyKind::Closure(.., substs) => Some(substs), | ||
886 | _ => None, | ||
887 | } | ||
888 | } | ||
889 | |||
890 | pub fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>> { | ||
891 | match self.kind(&Interner) { | ||
892 | TyKind::OpaqueType(opaque_ty_id, ..) => { | ||
893 | match db.lookup_intern_impl_trait_id((*opaque_ty_id).into()) { | ||
894 | ImplTraitId::AsyncBlockTypeImplTrait(def, _expr) => { | ||
895 | let krate = def.module(db.upcast()).krate(); | ||
896 | if let Some(future_trait) = db | ||
897 | .lang_item(krate, "future_trait".into()) | ||
898 | .and_then(|item| item.as_trait()) | ||
899 | { | ||
900 | // This is only used by type walking. | ||
901 | // Parameters will be walked outside, and projection predicate is not used. | ||
902 | // So just provide the Future trait. | ||
903 | let impl_bound = Binders::new( | ||
904 | 0, | ||
905 | WhereClause::Implemented(TraitRef { | ||
906 | trait_id: to_chalk_trait_id(future_trait), | ||
907 | substitution: Substitution::empty(&Interner), | ||
908 | }), | ||
909 | ); | ||
910 | Some(vec![impl_bound]) | ||
911 | } else { | ||
912 | None | ||
913 | } | ||
914 | } | ||
915 | ImplTraitId::ReturnTypeImplTrait(..) => None, | ||
916 | } | ||
917 | } | ||
918 | TyKind::Alias(AliasTy::Opaque(opaque_ty)) => { | ||
919 | let predicates = match db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into()) | ||
920 | { | ||
921 | ImplTraitId::ReturnTypeImplTrait(func, idx) => { | ||
922 | db.return_type_impl_traits(func).map(|it| { | ||
923 | let data = (*it) | ||
924 | .as_ref() | ||
925 | .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); | ||
926 | data.subst(&opaque_ty.substitution) | ||
927 | }) | ||
928 | } | ||
929 | // It always has an parameter for Future::Output type. | ||
930 | ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(), | ||
931 | }; | ||
932 | |||
933 | predicates.map(|it| it.value) | ||
934 | } | ||
935 | TyKind::Placeholder(idx) => { | ||
936 | let id = from_placeholder_idx(db, *idx); | ||
937 | let generic_params = db.generic_params(id.parent); | ||
938 | let param_data = &generic_params.types[id.local_id]; | ||
939 | match param_data.provenance { | ||
940 | hir_def::generics::TypeParamProvenance::ArgumentImplTrait => { | ||
941 | let substs = TyBuilder::type_params_subst(db, id.parent); | ||
942 | let predicates = db | ||
943 | .generic_predicates(id.parent) | ||
944 | .into_iter() | ||
945 | .map(|pred| pred.clone().subst(&substs)) | ||
946 | .filter(|wc| match &wc.skip_binders() { | ||
947 | WhereClause::Implemented(tr) => tr.self_type_parameter() == self, | ||
948 | WhereClause::AliasEq(AliasEq { | ||
949 | alias: AliasTy::Projection(proj), | ||
950 | ty: _, | ||
951 | }) => proj.self_type_parameter() == self, | ||
952 | _ => false, | ||
953 | }) | ||
954 | .collect_vec(); | ||
955 | |||
956 | Some(predicates) | ||
957 | } | ||
958 | _ => None, | ||
959 | } | ||
960 | } | ||
961 | _ => None, | ||
962 | } | ||
963 | } | ||
964 | |||
965 | pub fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId> { | ||
966 | match self.kind(&Interner) { | ||
967 | TyKind::AssociatedType(id, ..) => { | ||
968 | match from_assoc_type_id(*id).lookup(db.upcast()).container { | ||
969 | AssocContainerId::TraitId(trait_id) => Some(trait_id), | ||
970 | _ => None, | ||
971 | } | ||
972 | } | ||
973 | TyKind::Alias(AliasTy::Projection(projection_ty)) => { | ||
974 | match from_assoc_type_id(projection_ty.associated_ty_id) | ||
975 | .lookup(db.upcast()) | ||
976 | .container | ||
977 | { | ||
978 | AssocContainerId::TraitId(trait_id) => Some(trait_id), | ||
979 | _ => None, | ||
980 | } | ||
981 | } | ||
982 | _ => None, | ||
983 | } | ||
984 | } | ||
985 | } | ||
986 | |||
987 | /// This allows walking structures that contain types to do something with those | ||
988 | /// types, similar to Chalk's `Fold` trait. | ||
989 | pub trait TypeWalk { | ||
990 | fn walk(&self, f: &mut impl FnMut(&Ty)); | ||
991 | fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { | ||
992 | self.walk_mut_binders(&mut |ty, _binders| f(ty), DebruijnIndex::INNERMOST); | ||
993 | } | ||
994 | /// Walk the type, counting entered binders. | ||
995 | /// | ||
996 | /// `TyKind::Bound` variables use DeBruijn indexing, which means that 0 refers | ||
997 | /// to the innermost binder, 1 to the next, etc.. So when we want to | ||
998 | /// substitute a certain bound variable, we can't just walk the whole type | ||
999 | /// and blindly replace each instance of a certain index; when we 'enter' | ||
1000 | /// things that introduce new bound variables, we have to keep track of | ||
1001 | /// that. Currently, the only thing that introduces bound variables on our | ||
1002 | /// side are `TyKind::Dyn` and `TyKind::Opaque`, which each introduce a bound | ||
1003 | /// variable for the self type. | ||
1004 | fn walk_mut_binders( | ||
1005 | &mut self, | ||
1006 | f: &mut impl FnMut(&mut Ty, DebruijnIndex), | ||
1007 | binders: DebruijnIndex, | ||
1008 | ); | ||
1009 | |||
1010 | fn fold_binders( | ||
1011 | mut self, | ||
1012 | f: &mut impl FnMut(Ty, DebruijnIndex) -> Ty, | ||
1013 | binders: DebruijnIndex, | ||
1014 | ) -> Self | ||
1015 | where | ||
1016 | Self: Sized, | ||
1017 | { | ||
1018 | self.walk_mut_binders( | ||
1019 | &mut |ty_mut, binders| { | ||
1020 | let ty = mem::replace(ty_mut, TyKind::Unknown.intern(&Interner)); | ||
1021 | *ty_mut = f(ty, binders); | ||
1022 | }, | ||
1023 | binders, | ||
1024 | ); | ||
1025 | self | ||
1026 | } | ||
1027 | |||
1028 | fn fold(mut self, f: &mut impl FnMut(Ty) -> Ty) -> Self | ||
1029 | where | ||
1030 | Self: Sized, | ||
1031 | { | ||
1032 | self.walk_mut(&mut |ty_mut| { | ||
1033 | let ty = mem::replace(ty_mut, TyKind::Unknown.intern(&Interner)); | ||
1034 | *ty_mut = f(ty); | ||
1035 | }); | ||
1036 | self | ||
1037 | } | ||
1038 | 212 | ||
1039 | /// Substitutes `TyKind::Bound` vars with the given substitution. | 213 | fn fold_with<'i>( |
1040 | fn subst_bound_vars(self, substs: &Substitution) -> Self | 214 | self, |
215 | folder: &mut dyn chalk_ir::fold::Folder<'i, Interner>, | ||
216 | outer_binder: DebruijnIndex, | ||
217 | ) -> chalk_ir::Fallible<Self::Result> | ||
1041 | where | 218 | where |
1042 | Self: Sized, | 219 | Interner: 'i, |
1043 | { | 220 | { |
1044 | self.subst_bound_vars_at_depth(substs, DebruijnIndex::INNERMOST) | 221 | let vec = self.params_and_return.to_vec(); |
1045 | } | 222 | let folded = vec.fold_with(folder, outer_binder)?; |
1046 | 223 | Ok(CallableSig { params_and_return: folded.into(), is_varargs: self.is_varargs }) | |
1047 | /// Substitutes `TyKind::Bound` vars with the given substitution. | ||
1048 | fn subst_bound_vars_at_depth(mut self, substs: &Substitution, depth: DebruijnIndex) -> Self | ||
1049 | where | ||
1050 | Self: Sized, | ||
1051 | { | ||
1052 | self.walk_mut_binders( | ||
1053 | &mut |ty, binders| { | ||
1054 | if let &mut TyKind::BoundVar(bound) = ty.interned_mut() { | ||
1055 | if bound.debruijn >= binders { | ||
1056 | *ty = substs.0[bound.index] | ||
1057 | .assert_ty_ref(&Interner) | ||
1058 | .clone() | ||
1059 | .shift_bound_vars(binders); | ||
1060 | } | ||
1061 | } | ||
1062 | }, | ||
1063 | depth, | ||
1064 | ); | ||
1065 | self | ||
1066 | } | ||
1067 | |||
1068 | /// Shifts up debruijn indices of `TyKind::Bound` vars by `n`. | ||
1069 | fn shift_bound_vars(self, n: DebruijnIndex) -> Self | ||
1070 | where | ||
1071 | Self: Sized, | ||
1072 | { | ||
1073 | self.fold_binders( | ||
1074 | &mut |ty, binders| match ty.kind(&Interner) { | ||
1075 | TyKind::BoundVar(bound) if bound.debruijn >= binders => { | ||
1076 | TyKind::BoundVar(bound.shifted_in_from(n)).intern(&Interner) | ||
1077 | } | ||
1078 | _ => ty, | ||
1079 | }, | ||
1080 | DebruijnIndex::INNERMOST, | ||
1081 | ) | ||
1082 | } | ||
1083 | |||
1084 | /// Shifts debruijn indices of `TyKind::Bound` vars out (down) by `n`. | ||
1085 | fn shift_bound_vars_out(self, n: DebruijnIndex) -> Self | ||
1086 | where | ||
1087 | Self: Sized + std::fmt::Debug, | ||
1088 | { | ||
1089 | self.fold_binders( | ||
1090 | &mut |ty, binders| match ty.kind(&Interner) { | ||
1091 | TyKind::BoundVar(bound) if bound.debruijn >= binders => { | ||
1092 | TyKind::BoundVar(bound.shifted_out_to(n).unwrap_or(bound.clone())) | ||
1093 | .intern(&Interner) | ||
1094 | } | ||
1095 | _ => ty, | ||
1096 | }, | ||
1097 | DebruijnIndex::INNERMOST, | ||
1098 | ) | ||
1099 | } | ||
1100 | } | ||
1101 | |||
1102 | impl TypeWalk for Ty { | ||
1103 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
1104 | match self.kind(&Interner) { | ||
1105 | TyKind::Alias(AliasTy::Projection(p_ty)) => { | ||
1106 | for t in p_ty.substitution.iter(&Interner) { | ||
1107 | t.walk(f); | ||
1108 | } | ||
1109 | } | ||
1110 | TyKind::Alias(AliasTy::Opaque(o_ty)) => { | ||
1111 | for t in o_ty.substitution.iter(&Interner) { | ||
1112 | t.walk(f); | ||
1113 | } | ||
1114 | } | ||
1115 | TyKind::Dyn(dyn_ty) => { | ||
1116 | for p in dyn_ty.bounds.value.interned().iter() { | ||
1117 | p.walk(f); | ||
1118 | } | ||
1119 | } | ||
1120 | TyKind::Slice(ty) | TyKind::Array(ty) | TyKind::Ref(_, ty) | TyKind::Raw(_, ty) => { | ||
1121 | ty.walk(f); | ||
1122 | } | ||
1123 | _ => { | ||
1124 | if let Some(substs) = self.substs() { | ||
1125 | for t in substs.iter(&Interner) { | ||
1126 | t.walk(f); | ||
1127 | } | ||
1128 | } | ||
1129 | } | ||
1130 | } | ||
1131 | f(self); | ||
1132 | } | ||
1133 | |||
1134 | fn walk_mut_binders( | ||
1135 | &mut self, | ||
1136 | f: &mut impl FnMut(&mut Ty, DebruijnIndex), | ||
1137 | binders: DebruijnIndex, | ||
1138 | ) { | ||
1139 | match self.interned_mut() { | ||
1140 | TyKind::Alias(AliasTy::Projection(p_ty)) => { | ||
1141 | p_ty.substitution.walk_mut_binders(f, binders); | ||
1142 | } | ||
1143 | TyKind::Dyn(dyn_ty) => { | ||
1144 | for p in make_mut_slice(&mut dyn_ty.bounds.value.0) { | ||
1145 | p.walk_mut_binders(f, binders.shifted_in()); | ||
1146 | } | ||
1147 | } | ||
1148 | TyKind::Alias(AliasTy::Opaque(o_ty)) => { | ||
1149 | o_ty.substitution.walk_mut_binders(f, binders); | ||
1150 | } | ||
1151 | TyKind::Slice(ty) | TyKind::Array(ty) | TyKind::Ref(_, ty) | TyKind::Raw(_, ty) => { | ||
1152 | ty.walk_mut_binders(f, binders); | ||
1153 | } | ||
1154 | _ => { | ||
1155 | if let Some(substs) = self.substs_mut() { | ||
1156 | substs.walk_mut_binders(f, binders); | ||
1157 | } | ||
1158 | } | ||
1159 | } | ||
1160 | f(self, binders); | ||
1161 | } | ||
1162 | } | ||
1163 | |||
1164 | impl<T: TypeWalk> TypeWalk for Vec<T> { | ||
1165 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
1166 | for t in self { | ||
1167 | t.walk(f); | ||
1168 | } | ||
1169 | } | ||
1170 | fn walk_mut_binders( | ||
1171 | &mut self, | ||
1172 | f: &mut impl FnMut(&mut Ty, DebruijnIndex), | ||
1173 | binders: DebruijnIndex, | ||
1174 | ) { | ||
1175 | for t in self { | ||
1176 | t.walk_mut_binders(f, binders); | ||
1177 | } | ||
1178 | } | 224 | } |
1179 | } | 225 | } |
1180 | 226 | ||
@@ -1189,45 +235,75 @@ pub struct ReturnTypeImplTraits { | |||
1189 | pub(crate) impl_traits: Vec<ReturnTypeImplTrait>, | 235 | pub(crate) impl_traits: Vec<ReturnTypeImplTrait>, |
1190 | } | 236 | } |
1191 | 237 | ||
238 | has_interner!(ReturnTypeImplTraits); | ||
239 | |||
1192 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | 240 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] |
1193 | pub(crate) struct ReturnTypeImplTrait { | 241 | pub(crate) struct ReturnTypeImplTrait { |
1194 | pub(crate) bounds: Binders<Vec<QuantifiedWhereClause>>, | 242 | pub(crate) bounds: Binders<Vec<QuantifiedWhereClause>>, |
1195 | } | 243 | } |
1196 | 244 | ||
1197 | pub fn to_foreign_def_id(id: TypeAliasId) -> ForeignDefId { | 245 | pub fn static_lifetime() -> Lifetime { |
1198 | chalk_ir::ForeignDefId(salsa::InternKey::as_intern_id(&id)) | 246 | LifetimeData::Static.intern(&Interner) |
1199 | } | ||
1200 | |||
1201 | pub fn from_foreign_def_id(id: ForeignDefId) -> TypeAliasId { | ||
1202 | salsa::InternKey::from_intern_id(id.0) | ||
1203 | } | 247 | } |
1204 | 248 | ||
1205 | pub fn to_assoc_type_id(id: TypeAliasId) -> AssocTypeId { | 249 | pub fn dummy_usize_const() -> Const { |
1206 | chalk_ir::AssocTypeId(salsa::InternKey::as_intern_id(&id)) | 250 | let usize_ty = chalk_ir::TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner); |
251 | chalk_ir::ConstData { | ||
252 | ty: usize_ty, | ||
253 | value: chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { interned: () }), | ||
254 | } | ||
255 | .intern(&Interner) | ||
1207 | } | 256 | } |
1208 | 257 | ||
1209 | pub fn from_assoc_type_id(id: AssocTypeId) -> TypeAliasId { | 258 | pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + Fold<Interner>>( |
1210 | salsa::InternKey::from_intern_id(id.0) | 259 | t: T, |
1211 | } | 260 | f: impl FnMut(BoundVar, DebruijnIndex) -> Ty, |
261 | ) -> T::Result { | ||
262 | use chalk_ir::{fold::Folder, Fallible}; | ||
263 | struct FreeVarFolder<F>(F); | ||
264 | impl<'i, F: FnMut(BoundVar, DebruijnIndex) -> Ty + 'i> Folder<'i, Interner> for FreeVarFolder<F> { | ||
265 | fn as_dyn(&mut self) -> &mut dyn Folder<'i, Interner> { | ||
266 | self | ||
267 | } | ||
1212 | 268 | ||
1213 | pub fn from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> TypeParamId { | 269 | fn interner(&self) -> &'i Interner { |
1214 | assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT); | 270 | &Interner |
1215 | let interned_id = salsa::InternKey::from_intern_id(salsa::InternId::from(idx.idx)); | 271 | } |
1216 | db.lookup_intern_type_param_id(interned_id) | ||
1217 | } | ||
1218 | 272 | ||
1219 | pub fn to_placeholder_idx(db: &dyn HirDatabase, id: TypeParamId) -> PlaceholderIndex { | 273 | fn fold_free_var_ty( |
1220 | let interned_id = db.intern_type_param_id(id); | 274 | &mut self, |
1221 | PlaceholderIndex { | 275 | bound_var: BoundVar, |
1222 | ui: chalk_ir::UniverseIndex::ROOT, | 276 | outer_binder: DebruijnIndex, |
1223 | idx: salsa::InternKey::as_intern_id(&interned_id).as_usize(), | 277 | ) -> Fallible<Ty> { |
278 | Ok(self.0(bound_var, outer_binder)) | ||
279 | } | ||
1224 | } | 280 | } |
281 | t.fold_with(&mut FreeVarFolder(f), DebruijnIndex::INNERMOST).expect("fold failed unexpectedly") | ||
1225 | } | 282 | } |
1226 | 283 | ||
1227 | pub fn to_chalk_trait_id(id: TraitId) -> ChalkTraitId { | 284 | pub(crate) fn fold_tys<T: HasInterner<Interner = Interner> + Fold<Interner>>( |
1228 | chalk_ir::TraitId(salsa::InternKey::as_intern_id(&id)) | 285 | t: T, |
1229 | } | 286 | f: impl FnMut(Ty, DebruijnIndex) -> Ty, |
287 | binders: DebruijnIndex, | ||
288 | ) -> T::Result { | ||
289 | use chalk_ir::{ | ||
290 | fold::{Folder, SuperFold}, | ||
291 | Fallible, | ||
292 | }; | ||
293 | struct TyFolder<F>(F); | ||
294 | impl<'i, F: FnMut(Ty, DebruijnIndex) -> Ty + 'i> Folder<'i, Interner> for TyFolder<F> { | ||
295 | fn as_dyn(&mut self) -> &mut dyn Folder<'i, Interner> { | ||
296 | self | ||
297 | } | ||
298 | |||
299 | fn interner(&self) -> &'i Interner { | ||
300 | &Interner | ||
301 | } | ||
1230 | 302 | ||
1231 | pub fn from_chalk_trait_id(id: ChalkTraitId) -> TraitId { | 303 | fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Fallible<Ty> { |
1232 | salsa::InternKey::from_intern_id(id.0) | 304 | let ty = ty.super_fold_with(self.as_dyn(), outer_binder)?; |
305 | Ok(self.0(ty, outer_binder)) | ||
306 | } | ||
307 | } | ||
308 | t.fold_with(&mut TyFolder(f), binders).expect("fold failed unexpectedly") | ||
1233 | } | 309 | } |
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index 214655807..7fd46becd 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs | |||
@@ -5,12 +5,14 @@ | |||
5 | //! - Building the type for an item: This happens through the `type_for_def` query. | 5 | //! - Building the type for an item: This happens through the `type_for_def` query. |
6 | //! | 6 | //! |
7 | //! This usually involves resolving names, collecting generic arguments etc. | 7 | //! This usually involves resolving names, collecting generic arguments etc. |
8 | use std::cell::{Cell, RefCell}; | ||
8 | use std::{iter, sync::Arc}; | 9 | use std::{iter, sync::Arc}; |
9 | 10 | ||
10 | use base_db::CrateId; | 11 | use base_db::CrateId; |
11 | use chalk_ir::{cast::Cast, Mutability, Safety}; | 12 | use chalk_ir::{cast::Cast, fold::Shift, interner::HasInterner, Mutability, Safety}; |
12 | use hir_def::{ | 13 | use hir_def::{ |
13 | adt::StructKind, | 14 | adt::StructKind, |
15 | body::{Expander, LowerCtx}, | ||
14 | builtin_type::BuiltinType, | 16 | builtin_type::BuiltinType, |
15 | generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget}, | 17 | generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget}, |
16 | path::{GenericArg, Path, PathSegment, PathSegments}, | 18 | path::{GenericArg, Path, PathSegment, PathSegments}, |
@@ -20,23 +22,24 @@ use hir_def::{ | |||
20 | GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, | 22 | GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, |
21 | TypeAliasId, TypeParamId, UnionId, VariantId, | 23 | TypeAliasId, TypeParamId, UnionId, VariantId, |
22 | }; | 24 | }; |
23 | use hir_expand::name::Name; | 25 | use hir_expand::{name::Name, ExpandResult}; |
24 | use la_arena::ArenaMap; | 26 | use la_arena::ArenaMap; |
25 | use smallvec::SmallVec; | 27 | use smallvec::SmallVec; |
26 | use stdx::impl_from; | 28 | use stdx::impl_from; |
29 | use syntax::ast; | ||
27 | 30 | ||
28 | use crate::{ | 31 | use crate::{ |
29 | db::HirDatabase, | 32 | db::HirDatabase, |
30 | to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, | 33 | dummy_usize_const, |
31 | traits::chalk::{Interner, ToChalk}, | 34 | mapping::ToChalk, |
35 | static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, | ||
32 | utils::{ | 36 | utils::{ |
33 | all_super_trait_refs, associated_type_by_name_including_super_traits, generics, | 37 | all_super_trait_refs, associated_type_by_name_including_super_traits, generics, Generics, |
34 | variant_data, | ||
35 | }, | 38 | }, |
36 | AliasEq, AliasTy, Binders, BoundVar, CallableSig, DebruijnIndex, DynTy, FnPointer, FnSig, | 39 | AliasEq, AliasTy, Binders, BoundVar, CallableSig, DebruijnIndex, DynTy, FnPointer, FnSig, |
37 | ImplTraitId, OpaqueTy, PolyFnSig, ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses, | 40 | FnSubst, ImplTraitId, Interner, OpaqueTy, PolyFnSig, ProjectionTy, QuantifiedWhereClause, |
38 | ReturnTypeImplTrait, ReturnTypeImplTraits, Substitution, TraitEnvironment, TraitRef, Ty, | 41 | QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits, Substitution, |
39 | TyBuilder, TyKind, TypeWalk, WhereClause, | 42 | TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, |
40 | }; | 43 | }; |
41 | 44 | ||
42 | #[derive(Debug)] | 45 | #[derive(Debug)] |
@@ -50,7 +53,7 @@ pub struct TyLoweringContext<'a> { | |||
50 | /// possible currently, so this should be fine for now. | 53 | /// possible currently, so this should be fine for now. |
51 | pub type_param_mode: TypeParamLoweringMode, | 54 | pub type_param_mode: TypeParamLoweringMode, |
52 | pub impl_trait_mode: ImplTraitLoweringMode, | 55 | pub impl_trait_mode: ImplTraitLoweringMode, |
53 | impl_trait_counter: std::cell::Cell<u16>, | 56 | impl_trait_counter: Cell<u16>, |
54 | /// When turning `impl Trait` into opaque types, we have to collect the | 57 | /// When turning `impl Trait` into opaque types, we have to collect the |
55 | /// bounds at the same time to get the IDs correct (without becoming too | 58 | /// bounds at the same time to get the IDs correct (without becoming too |
56 | /// complicated). I don't like using interior mutability (as for the | 59 | /// complicated). I don't like using interior mutability (as for the |
@@ -59,16 +62,17 @@ pub struct TyLoweringContext<'a> { | |||
59 | /// we're grouping the mutable data (the counter and this field) together | 62 | /// we're grouping the mutable data (the counter and this field) together |
60 | /// with the immutable context (the references to the DB and resolver). | 63 | /// with the immutable context (the references to the DB and resolver). |
61 | /// Splitting this up would be a possible fix. | 64 | /// Splitting this up would be a possible fix. |
62 | opaque_type_data: std::cell::RefCell<Vec<ReturnTypeImplTrait>>, | 65 | opaque_type_data: RefCell<Vec<ReturnTypeImplTrait>>, |
66 | expander: RefCell<Option<Expander>>, | ||
63 | } | 67 | } |
64 | 68 | ||
65 | impl<'a> TyLoweringContext<'a> { | 69 | impl<'a> TyLoweringContext<'a> { |
66 | pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver) -> Self { | 70 | pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver) -> Self { |
67 | let impl_trait_counter = std::cell::Cell::new(0); | 71 | let impl_trait_counter = Cell::new(0); |
68 | let impl_trait_mode = ImplTraitLoweringMode::Disallowed; | 72 | let impl_trait_mode = ImplTraitLoweringMode::Disallowed; |
69 | let type_param_mode = TypeParamLoweringMode::Placeholder; | 73 | let type_param_mode = TypeParamLoweringMode::Placeholder; |
70 | let in_binders = DebruijnIndex::INNERMOST; | 74 | let in_binders = DebruijnIndex::INNERMOST; |
71 | let opaque_type_data = std::cell::RefCell::new(Vec::new()); | 75 | let opaque_type_data = RefCell::new(Vec::new()); |
72 | Self { | 76 | Self { |
73 | db, | 77 | db, |
74 | resolver, | 78 | resolver, |
@@ -77,6 +81,7 @@ impl<'a> TyLoweringContext<'a> { | |||
77 | impl_trait_counter, | 81 | impl_trait_counter, |
78 | type_param_mode, | 82 | type_param_mode, |
79 | opaque_type_data, | 83 | opaque_type_data, |
84 | expander: RefCell::new(None), | ||
80 | } | 85 | } |
81 | } | 86 | } |
82 | 87 | ||
@@ -86,15 +91,18 @@ impl<'a> TyLoweringContext<'a> { | |||
86 | f: impl FnOnce(&TyLoweringContext) -> T, | 91 | f: impl FnOnce(&TyLoweringContext) -> T, |
87 | ) -> T { | 92 | ) -> T { |
88 | let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new()); | 93 | let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new()); |
94 | let expander = self.expander.replace(None); | ||
89 | let new_ctx = Self { | 95 | let new_ctx = Self { |
90 | in_binders: debruijn, | 96 | in_binders: debruijn, |
91 | impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()), | 97 | impl_trait_counter: Cell::new(self.impl_trait_counter.get()), |
92 | opaque_type_data: std::cell::RefCell::new(opaque_ty_data_vec), | 98 | opaque_type_data: RefCell::new(opaque_ty_data_vec), |
99 | expander: RefCell::new(expander), | ||
93 | ..*self | 100 | ..*self |
94 | }; | 101 | }; |
95 | let result = f(&new_ctx); | 102 | let result = f(&new_ctx); |
96 | self.impl_trait_counter.set(new_ctx.impl_trait_counter.get()); | 103 | self.impl_trait_counter.set(new_ctx.impl_trait_counter.get()); |
97 | self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner()); | 104 | self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner()); |
105 | self.expander.replace(new_ctx.expander.into_inner()); | ||
98 | result | 106 | result |
99 | } | 107 | } |
100 | 108 | ||
@@ -166,7 +174,7 @@ impl<'a> TyLoweringContext<'a> { | |||
166 | } | 174 | } |
167 | TypeRef::Array(inner) => { | 175 | TypeRef::Array(inner) => { |
168 | let inner_ty = self.lower_ty(inner); | 176 | let inner_ty = self.lower_ty(inner); |
169 | TyKind::Array(inner_ty).intern(&Interner) | 177 | TyKind::Array(inner_ty, dummy_usize_const()).intern(&Interner) |
170 | } | 178 | } |
171 | TypeRef::Slice(inner) => { | 179 | TypeRef::Slice(inner) => { |
172 | let inner_ty = self.lower_ty(inner); | 180 | let inner_ty = self.lower_ty(inner); |
@@ -174,16 +182,19 @@ impl<'a> TyLoweringContext<'a> { | |||
174 | } | 182 | } |
175 | TypeRef::Reference(inner, _, mutability) => { | 183 | TypeRef::Reference(inner, _, mutability) => { |
176 | let inner_ty = self.lower_ty(inner); | 184 | let inner_ty = self.lower_ty(inner); |
177 | TyKind::Ref(lower_to_chalk_mutability(*mutability), inner_ty).intern(&Interner) | 185 | let lifetime = static_lifetime(); |
186 | TyKind::Ref(lower_to_chalk_mutability(*mutability), lifetime, inner_ty) | ||
187 | .intern(&Interner) | ||
178 | } | 188 | } |
179 | TypeRef::Placeholder => TyKind::Unknown.intern(&Interner), | 189 | TypeRef::Placeholder => TyKind::Error.intern(&Interner), |
180 | TypeRef::Fn(params, is_varargs) => { | 190 | TypeRef::Fn(params, is_varargs) => { |
181 | let substs = | 191 | let substs = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { |
182 | Substitution::from_iter(&Interner, params.iter().map(|tr| self.lower_ty(tr))); | 192 | Substitution::from_iter(&Interner, params.iter().map(|tr| ctx.lower_ty(tr))) |
193 | }); | ||
183 | TyKind::Function(FnPointer { | 194 | TyKind::Function(FnPointer { |
184 | num_args: substs.len(&Interner) - 1, | 195 | num_binders: 0, // FIXME lower `for<'a> fn()` correctly |
185 | sig: FnSig { abi: (), safety: Safety::Safe, variadic: *is_varargs }, | 196 | sig: FnSig { abi: (), safety: Safety::Safe, variadic: *is_varargs }, |
186 | substs, | 197 | substitution: FnSubst(substs), |
187 | }) | 198 | }) |
188 | .intern(&Interner) | 199 | .intern(&Interner) |
189 | } | 200 | } |
@@ -196,8 +207,8 @@ impl<'a> TyLoweringContext<'a> { | |||
196 | bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)), | 207 | bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)), |
197 | ) | 208 | ) |
198 | }); | 209 | }); |
199 | let bounds = Binders::new(1, bounds); | 210 | let bounds = crate::make_only_type_binders(1, bounds); |
200 | TyKind::Dyn(DynTy { bounds }).intern(&Interner) | 211 | TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(&Interner) |
201 | } | 212 | } |
202 | TypeRef::ImplTrait(bounds) => { | 213 | TypeRef::ImplTrait(bounds) => { |
203 | match self.impl_trait_mode { | 214 | match self.impl_trait_mode { |
@@ -209,9 +220,9 @@ impl<'a> TyLoweringContext<'a> { | |||
209 | // this dance is to make sure the data is in the right | 220 | // this dance is to make sure the data is in the right |
210 | // place even if we encounter more opaque types while | 221 | // place even if we encounter more opaque types while |
211 | // lowering the bounds | 222 | // lowering the bounds |
212 | self.opaque_type_data | 223 | self.opaque_type_data.borrow_mut().push(ReturnTypeImplTrait { |
213 | .borrow_mut() | 224 | bounds: crate::make_only_type_binders(1, Vec::new()), |
214 | .push(ReturnTypeImplTrait { bounds: Binders::new(1, Vec::new()) }); | 225 | }); |
215 | // We don't want to lower the bounds inside the binders | 226 | // We don't want to lower the bounds inside the binders |
216 | // we're currently in, because they don't end up inside | 227 | // we're currently in, because they don't end up inside |
217 | // those binders. E.g. when we have `impl Trait<impl | 228 | // those binders. E.g. when we have `impl Trait<impl |
@@ -253,12 +264,12 @@ impl<'a> TyLoweringContext<'a> { | |||
253 | data.provenance == TypeParamProvenance::ArgumentImplTrait | 264 | data.provenance == TypeParamProvenance::ArgumentImplTrait |
254 | }) | 265 | }) |
255 | .nth(idx as usize) | 266 | .nth(idx as usize) |
256 | .map_or(TyKind::Unknown, |(id, _)| { | 267 | .map_or(TyKind::Error, |(id, _)| { |
257 | TyKind::Placeholder(to_placeholder_idx(self.db, id)) | 268 | TyKind::Placeholder(to_placeholder_idx(self.db, id)) |
258 | }); | 269 | }); |
259 | param.intern(&Interner) | 270 | param.intern(&Interner) |
260 | } else { | 271 | } else { |
261 | TyKind::Unknown.intern(&Interner) | 272 | TyKind::Error.intern(&Interner) |
262 | } | 273 | } |
263 | } | 274 | } |
264 | ImplTraitLoweringMode::Variable => { | 275 | ImplTraitLoweringMode::Variable => { |
@@ -280,11 +291,58 @@ impl<'a> TyLoweringContext<'a> { | |||
280 | } | 291 | } |
281 | ImplTraitLoweringMode::Disallowed => { | 292 | ImplTraitLoweringMode::Disallowed => { |
282 | // FIXME: report error | 293 | // FIXME: report error |
283 | TyKind::Unknown.intern(&Interner) | 294 | TyKind::Error.intern(&Interner) |
295 | } | ||
296 | } | ||
297 | } | ||
298 | TypeRef::Macro(macro_call) => { | ||
299 | let (expander, recursion_start) = { | ||
300 | let mut expander = self.expander.borrow_mut(); | ||
301 | if expander.is_some() { | ||
302 | (Some(expander), false) | ||
303 | } else { | ||
304 | if let Some(module_id) = self.resolver.module() { | ||
305 | *expander = Some(Expander::new( | ||
306 | self.db.upcast(), | ||
307 | macro_call.file_id, | ||
308 | module_id, | ||
309 | )); | ||
310 | (Some(expander), true) | ||
311 | } else { | ||
312 | (None, false) | ||
313 | } | ||
284 | } | 314 | } |
315 | }; | ||
316 | let ty = if let Some(mut expander) = expander { | ||
317 | let expander_mut = expander.as_mut().unwrap(); | ||
318 | let macro_call = macro_call.to_node(self.db.upcast()); | ||
319 | match expander_mut.enter_expand::<ast::Type>(self.db.upcast(), macro_call) { | ||
320 | Ok(ExpandResult { value: Some((mark, expanded)), .. }) => { | ||
321 | let ctx = | ||
322 | LowerCtx::new(self.db.upcast(), expander_mut.current_file_id()); | ||
323 | let type_ref = TypeRef::from_ast(&ctx, expanded); | ||
324 | |||
325 | drop(expander); | ||
326 | let ty = self.lower_ty(&type_ref); | ||
327 | |||
328 | self.expander | ||
329 | .borrow_mut() | ||
330 | .as_mut() | ||
331 | .unwrap() | ||
332 | .exit(self.db.upcast(), mark); | ||
333 | Some(ty) | ||
334 | } | ||
335 | _ => None, | ||
336 | } | ||
337 | } else { | ||
338 | None | ||
339 | }; | ||
340 | if recursion_start { | ||
341 | *self.expander.borrow_mut() = None; | ||
285 | } | 342 | } |
343 | ty.unwrap_or_else(|| TyKind::Error.intern(&Interner)) | ||
286 | } | 344 | } |
287 | TypeRef::Error => TyKind::Unknown.intern(&Interner), | 345 | TypeRef::Error => TyKind::Error.intern(&Interner), |
288 | }; | 346 | }; |
289 | (ty, res) | 347 | (ty, res) |
290 | } | 348 | } |
@@ -328,7 +386,7 @@ impl<'a> TyLoweringContext<'a> { | |||
328 | (self.select_associated_type(res, segment), None) | 386 | (self.select_associated_type(res, segment), None) |
329 | } else if remaining_segments.len() > 1 { | 387 | } else if remaining_segments.len() > 1 { |
330 | // FIXME report error (ambiguous associated type) | 388 | // FIXME report error (ambiguous associated type) |
331 | (TyKind::Unknown.intern(&Interner), None) | 389 | (TyKind::Error.intern(&Interner), None) |
332 | } else { | 390 | } else { |
333 | (ty, res) | 391 | (ty, res) |
334 | } | 392 | } |
@@ -372,21 +430,24 @@ impl<'a> TyLoweringContext<'a> { | |||
372 | } | 430 | } |
373 | None => { | 431 | None => { |
374 | // FIXME: report error (associated type not found) | 432 | // FIXME: report error (associated type not found) |
375 | TyKind::Unknown.intern(&Interner) | 433 | TyKind::Error.intern(&Interner) |
376 | } | 434 | } |
377 | } | 435 | } |
378 | } else if remaining_segments.len() > 1 { | 436 | } else if remaining_segments.len() > 1 { |
379 | // FIXME report error (ambiguous associated type) | 437 | // FIXME report error (ambiguous associated type) |
380 | TyKind::Unknown.intern(&Interner) | 438 | TyKind::Error.intern(&Interner) |
381 | } else { | 439 | } else { |
382 | let dyn_ty = DynTy { | 440 | let dyn_ty = DynTy { |
383 | bounds: Binders::new( | 441 | bounds: crate::make_only_type_binders( |
384 | 1, | 442 | 1, |
385 | QuantifiedWhereClauses::from_iter( | 443 | QuantifiedWhereClauses::from_iter( |
386 | &Interner, | 444 | &Interner, |
387 | Some(Binders::wrap_empty(WhereClause::Implemented(trait_ref))), | 445 | Some(crate::wrap_empty_binders(WhereClause::Implemented( |
446 | trait_ref, | ||
447 | ))), | ||
388 | ), | 448 | ), |
389 | ), | 449 | ), |
450 | lifetime: static_lifetime(), | ||
390 | }; | 451 | }; |
391 | TyKind::Dyn(dyn_ty).intern(&Interner) | 452 | TyKind::Dyn(dyn_ty).intern(&Interner) |
392 | }; | 453 | }; |
@@ -414,7 +475,7 @@ impl<'a> TyLoweringContext<'a> { | |||
414 | TypeParamLoweringMode::Placeholder => generics.type_params_subst(self.db), | 475 | TypeParamLoweringMode::Placeholder => generics.type_params_subst(self.db), |
415 | TypeParamLoweringMode::Variable => generics.bound_vars_subst(self.in_binders), | 476 | TypeParamLoweringMode::Variable => generics.bound_vars_subst(self.in_binders), |
416 | }; | 477 | }; |
417 | self.db.impl_self_ty(impl_id).subst(&substs) | 478 | self.db.impl_self_ty(impl_id).substitute(&Interner, &substs) |
418 | } | 479 | } |
419 | TypeNs::AdtSelfType(adt) => { | 480 | TypeNs::AdtSelfType(adt) => { |
420 | let generics = generics(self.db.upcast(), adt.into()); | 481 | let generics = generics(self.db.upcast(), adt.into()); |
@@ -422,7 +483,7 @@ impl<'a> TyLoweringContext<'a> { | |||
422 | TypeParamLoweringMode::Placeholder => generics.type_params_subst(self.db), | 483 | TypeParamLoweringMode::Placeholder => generics.type_params_subst(self.db), |
423 | TypeParamLoweringMode::Variable => generics.bound_vars_subst(self.in_binders), | 484 | TypeParamLoweringMode::Variable => generics.bound_vars_subst(self.in_binders), |
424 | }; | 485 | }; |
425 | self.db.ty(adt.into()).subst(&substs) | 486 | self.db.ty(adt.into()).substitute(&Interner, &substs) |
426 | } | 487 | } |
427 | 488 | ||
428 | TypeNs::AdtId(it) => self.lower_path_inner(resolved_segment, it.into(), infer_args), | 489 | TypeNs::AdtId(it) => self.lower_path_inner(resolved_segment, it.into(), infer_args), |
@@ -433,7 +494,7 @@ impl<'a> TyLoweringContext<'a> { | |||
433 | self.lower_path_inner(resolved_segment, it.into(), infer_args) | 494 | self.lower_path_inner(resolved_segment, it.into(), infer_args) |
434 | } | 495 | } |
435 | // FIXME: report error | 496 | // FIXME: report error |
436 | TypeNs::EnumVariantId(_) => return (TyKind::Unknown.intern(&Interner), None), | 497 | TypeNs::EnumVariantId(_) => return (TyKind::Error.intern(&Interner), None), |
437 | }; | 498 | }; |
438 | self.lower_ty_relative_path(ty, Some(resolution), remaining_segments) | 499 | self.lower_ty_relative_path(ty, Some(resolution), remaining_segments) |
439 | } | 500 | } |
@@ -447,7 +508,7 @@ impl<'a> TyLoweringContext<'a> { | |||
447 | let (resolution, remaining_index) = | 508 | let (resolution, remaining_index) = |
448 | match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { | 509 | match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { |
449 | Some(it) => it, | 510 | Some(it) => it, |
450 | None => return (TyKind::Unknown.intern(&Interner), None), | 511 | None => return (TyKind::Error.intern(&Interner), None), |
451 | }; | 512 | }; |
452 | let (resolved_segment, remaining_segments) = match remaining_index { | 513 | let (resolved_segment, remaining_segments) = match remaining_index { |
453 | None => ( | 514 | None => ( |
@@ -477,13 +538,13 @@ impl<'a> TyLoweringContext<'a> { | |||
477 | ), | 538 | ), |
478 | ); | 539 | ); |
479 | let s = generics.type_params_subst(self.db); | 540 | let s = generics.type_params_subst(self.db); |
480 | t.substitution.clone().subst_bound_vars(&s) | 541 | s.apply(t.substitution.clone(), &Interner) |
481 | } | 542 | } |
482 | TypeParamLoweringMode::Variable => t.substitution.clone(), | 543 | TypeParamLoweringMode::Variable => t.substitution.clone(), |
483 | }; | 544 | }; |
484 | // We need to shift in the bound vars, since | 545 | // We need to shift in the bound vars, since |
485 | // associated_type_shorthand_candidates does not do that | 546 | // associated_type_shorthand_candidates does not do that |
486 | let substs = substs.shift_bound_vars(self.in_binders); | 547 | let substs = substs.shifted_in_from(&Interner, self.in_binders); |
487 | // FIXME handle type parameters on the segment | 548 | // FIXME handle type parameters on the segment |
488 | return Some( | 549 | return Some( |
489 | TyKind::Alias(AliasTy::Projection(ProjectionTy { | 550 | TyKind::Alias(AliasTy::Projection(ProjectionTy { |
@@ -498,9 +559,9 @@ impl<'a> TyLoweringContext<'a> { | |||
498 | }, | 559 | }, |
499 | ); | 560 | ); |
500 | 561 | ||
501 | ty.unwrap_or(TyKind::Unknown.intern(&Interner)) | 562 | ty.unwrap_or(TyKind::Error.intern(&Interner)) |
502 | } else { | 563 | } else { |
503 | TyKind::Unknown.intern(&Interner) | 564 | TyKind::Error.intern(&Interner) |
504 | } | 565 | } |
505 | } | 566 | } |
506 | 567 | ||
@@ -516,7 +577,7 @@ impl<'a> TyLoweringContext<'a> { | |||
516 | TyDefId::TypeAliasId(it) => Some(it.into()), | 577 | TyDefId::TypeAliasId(it) => Some(it.into()), |
517 | }; | 578 | }; |
518 | let substs = self.substs_from_path_segment(segment, generic_def, infer_args, None); | 579 | let substs = self.substs_from_path_segment(segment, generic_def, infer_args, None); |
519 | self.db.ty(typeable).subst(&substs) | 580 | self.db.ty(typeable).substitute(&Interner, &substs) |
520 | } | 581 | } |
521 | 582 | ||
522 | /// Collect generic arguments from a path into a `Substs`. See also | 583 | /// Collect generic arguments from a path into a `Substs`. See also |
@@ -569,13 +630,13 @@ impl<'a> TyLoweringContext<'a> { | |||
569 | def_generics.map_or((0, 0, 0, 0), |g| g.provenance_split()); | 630 | def_generics.map_or((0, 0, 0, 0), |g| g.provenance_split()); |
570 | let total_len = parent_params + self_params + type_params + impl_trait_params; | 631 | let total_len = parent_params + self_params + type_params + impl_trait_params; |
571 | 632 | ||
572 | substs.extend(iter::repeat(TyKind::Unknown.intern(&Interner)).take(parent_params)); | 633 | substs.extend(iter::repeat(TyKind::Error.intern(&Interner)).take(parent_params)); |
573 | 634 | ||
574 | let fill_self_params = || { | 635 | let fill_self_params = || { |
575 | substs.extend( | 636 | substs.extend( |
576 | explicit_self_ty | 637 | explicit_self_ty |
577 | .into_iter() | 638 | .into_iter() |
578 | .chain(iter::repeat(TyKind::Unknown.intern(&Interner))) | 639 | .chain(iter::repeat(TyKind::Error.intern(&Interner))) |
579 | .take(self_params), | 640 | .take(self_params), |
580 | ) | 641 | ) |
581 | }; | 642 | }; |
@@ -620,7 +681,7 @@ impl<'a> TyLoweringContext<'a> { | |||
620 | for default_ty in defaults.iter().skip(substs.len()) { | 681 | for default_ty in defaults.iter().skip(substs.len()) { |
621 | // each default can depend on the previous parameters | 682 | // each default can depend on the previous parameters |
622 | let substs_so_far = Substitution::from_iter(&Interner, substs.clone()); | 683 | let substs_so_far = Substitution::from_iter(&Interner, substs.clone()); |
623 | substs.push(default_ty.clone().subst(&substs_so_far)); | 684 | substs.push(default_ty.clone().substitute(&Interner, &substs_so_far)); |
624 | } | 685 | } |
625 | } | 686 | } |
626 | } | 687 | } |
@@ -628,7 +689,7 @@ impl<'a> TyLoweringContext<'a> { | |||
628 | // add placeholders for args that were not provided | 689 | // add placeholders for args that were not provided |
629 | // FIXME: emit diagnostics in contexts where this is not allowed | 690 | // FIXME: emit diagnostics in contexts where this is not allowed |
630 | for _ in substs.len()..total_len { | 691 | for _ in substs.len()..total_len { |
631 | substs.push(TyKind::Unknown.intern(&Interner)); | 692 | substs.push(TyKind::Error.intern(&Interner)); |
632 | } | 693 | } |
633 | assert_eq!(substs.len(), total_len); | 694 | assert_eq!(substs.len(), total_len); |
634 | 695 | ||
@@ -720,7 +781,7 @@ impl<'a> TyLoweringContext<'a> { | |||
720 | let trait_ref = match bound { | 781 | let trait_ref = match bound { |
721 | TypeBound::Path(path) => { | 782 | TypeBound::Path(path) => { |
722 | bindings = self.lower_trait_ref_from_path(path, Some(self_ty)); | 783 | bindings = self.lower_trait_ref_from_path(path, Some(self_ty)); |
723 | bindings.clone().map(WhereClause::Implemented).map(|b| Binders::wrap_empty(b)) | 784 | bindings.clone().map(WhereClause::Implemented).map(|b| crate::wrap_empty_binders(b)) |
724 | } | 785 | } |
725 | TypeBound::Lifetime(_) => None, | 786 | TypeBound::Lifetime(_) => None, |
726 | TypeBound::Error => None, | 787 | TypeBound::Error => None, |
@@ -767,7 +828,7 @@ impl<'a> TyLoweringContext<'a> { | |||
767 | let ty = self.lower_ty(type_ref); | 828 | let ty = self.lower_ty(type_ref); |
768 | let alias_eq = | 829 | let alias_eq = |
769 | AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; | 830 | AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; |
770 | preds.push(Binders::wrap_empty(WhereClause::AliasEq(alias_eq))); | 831 | preds.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq))); |
771 | } | 832 | } |
772 | for bound in &binding.bounds { | 833 | for bound in &binding.bounds { |
773 | preds.extend(self.lower_type_bound( | 834 | preds.extend(self.lower_type_bound( |
@@ -787,7 +848,7 @@ impl<'a> TyLoweringContext<'a> { | |||
787 | let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { | 848 | let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { |
788 | bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)).collect() | 849 | bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)).collect() |
789 | }); | 850 | }); |
790 | ReturnTypeImplTrait { bounds: Binders::new(1, predicates) } | 851 | ReturnTypeImplTrait { bounds: crate::make_only_type_binders(1, predicates) } |
791 | } | 852 | } |
792 | } | 853 | } |
793 | 854 | ||
@@ -831,17 +892,20 @@ pub fn associated_type_shorthand_candidates<R>( | |||
831 | }; | 892 | }; |
832 | 893 | ||
833 | match res { | 894 | match res { |
834 | // FIXME: how to correctly handle higher-ranked bounds here? | 895 | TypeNs::SelfType(impl_id) => search( |
835 | TypeNs::SelfType(impl_id) => { | 896 | // we're _in_ the impl -- the binders get added back later. Correct, |
836 | search(db.impl_trait(impl_id)?.value.shift_bound_vars_out(DebruijnIndex::ONE)) | 897 | // but it would be nice to make this more explicit |
837 | } | 898 | db.impl_trait(impl_id)?.into_value_and_skipped_binders().0, |
899 | ), | ||
838 | TypeNs::GenericParam(param_id) => { | 900 | TypeNs::GenericParam(param_id) => { |
839 | let predicates = db.generic_predicates_for_param(param_id); | 901 | let predicates = db.generic_predicates_for_param(param_id); |
840 | let res = predicates.iter().find_map(|pred| match &pred.value.value { | 902 | let res = predicates.iter().find_map(|pred| match pred.skip_binders().skip_binders() { |
841 | // FIXME: how to correctly handle higher-ranked bounds here? | 903 | // FIXME: how to correctly handle higher-ranked bounds here? |
842 | WhereClause::Implemented(tr) => { | 904 | WhereClause::Implemented(tr) => search( |
843 | search(tr.clone().shift_bound_vars_out(DebruijnIndex::ONE)) | 905 | tr.clone() |
844 | } | 906 | .shifted_out_to(&Interner, DebruijnIndex::ONE) |
907 | .expect("FIXME unexpected higher-ranked trait bound"), | ||
908 | ), | ||
845 | _ => None, | 909 | _ => None, |
846 | }); | 910 | }); |
847 | if let res @ Some(_) = res { | 911 | if let res @ Some(_) = res { |
@@ -870,7 +934,7 @@ pub(crate) fn field_types_query( | |||
870 | db: &dyn HirDatabase, | 934 | db: &dyn HirDatabase, |
871 | variant_id: VariantId, | 935 | variant_id: VariantId, |
872 | ) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>> { | 936 | ) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>> { |
873 | let var_data = variant_data(db.upcast(), variant_id); | 937 | let var_data = variant_id.variant_data(db.upcast()); |
874 | let (resolver, def): (_, GenericDefId) = match variant_id { | 938 | let (resolver, def): (_, GenericDefId) = match variant_id { |
875 | VariantId::StructId(it) => (it.resolver(db.upcast()), it.into()), | 939 | VariantId::StructId(it) => (it.resolver(db.upcast()), it.into()), |
876 | VariantId::UnionId(it) => (it.resolver(db.upcast()), it.into()), | 940 | VariantId::UnionId(it) => (it.resolver(db.upcast()), it.into()), |
@@ -881,7 +945,7 @@ pub(crate) fn field_types_query( | |||
881 | let ctx = | 945 | let ctx = |
882 | TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); | 946 | TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); |
883 | for (field_id, field_data) in var_data.fields().iter() { | 947 | for (field_id, field_data) in var_data.fields().iter() { |
884 | res.insert(field_id, Binders::new(generics.len(), ctx.lower_ty(&field_data.type_ref))) | 948 | res.insert(field_id, make_binders(&generics, ctx.lower_ty(&field_data.type_ref))) |
885 | } | 949 | } |
886 | Arc::new(res) | 950 | Arc::new(res) |
887 | } | 951 | } |
@@ -915,9 +979,7 @@ pub(crate) fn generic_predicates_for_param_query( | |||
915 | }, | 979 | }, |
916 | WherePredicate::Lifetime { .. } => false, | 980 | WherePredicate::Lifetime { .. } => false, |
917 | }) | 981 | }) |
918 | .flat_map(|pred| { | 982 | .flat_map(|pred| ctx.lower_where_predicate(pred, true).map(|p| make_binders(&generics, p))) |
919 | ctx.lower_where_predicate(pred, true).map(|p| Binders::new(generics.len(), p)) | ||
920 | }) | ||
921 | .collect() | 983 | .collect() |
922 | } | 984 | } |
923 | 985 | ||
@@ -941,10 +1003,10 @@ pub(crate) fn trait_environment_query( | |||
941 | for pred in resolver.where_predicates_in_scope() { | 1003 | for pred in resolver.where_predicates_in_scope() { |
942 | for pred in ctx.lower_where_predicate(pred, false) { | 1004 | for pred in ctx.lower_where_predicate(pred, false) { |
943 | if let WhereClause::Implemented(tr) = &pred.skip_binders() { | 1005 | if let WhereClause::Implemented(tr) = &pred.skip_binders() { |
944 | traits_in_scope.push((tr.self_type_parameter().clone(), tr.hir_trait_id())); | 1006 | traits_in_scope |
1007 | .push((tr.self_type_parameter(&Interner).clone(), tr.hir_trait_id())); | ||
945 | } | 1008 | } |
946 | let program_clause: chalk_ir::ProgramClause<Interner> = | 1009 | let program_clause: chalk_ir::ProgramClause<Interner> = pred.clone().cast(&Interner); |
947 | pred.clone().to_chalk(db).cast(&Interner); | ||
948 | clauses.push(program_clause.into_from_env_clause(&Interner)); | 1010 | clauses.push(program_clause.into_from_env_clause(&Interner)); |
949 | } | 1011 | } |
950 | } | 1012 | } |
@@ -967,7 +1029,7 @@ pub(crate) fn trait_environment_query( | |||
967 | let substs = TyBuilder::type_params_subst(db, trait_id); | 1029 | let substs = TyBuilder::type_params_subst(db, trait_id); |
968 | let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution: substs }; | 1030 | let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution: substs }; |
969 | let pred = WhereClause::Implemented(trait_ref); | 1031 | let pred = WhereClause::Implemented(trait_ref); |
970 | let program_clause: chalk_ir::ProgramClause<Interner> = pred.to_chalk(db).cast(&Interner); | 1032 | let program_clause: chalk_ir::ProgramClause<Interner> = pred.cast(&Interner); |
971 | clauses.push(program_clause.into_from_env_clause(&Interner)); | 1033 | clauses.push(program_clause.into_from_env_clause(&Interner)); |
972 | } | 1034 | } |
973 | 1035 | ||
@@ -987,9 +1049,7 @@ pub(crate) fn generic_predicates_query( | |||
987 | let generics = generics(db.upcast(), def); | 1049 | let generics = generics(db.upcast(), def); |
988 | resolver | 1050 | resolver |
989 | .where_predicates_in_scope() | 1051 | .where_predicates_in_scope() |
990 | .flat_map(|pred| { | 1052 | .flat_map(|pred| ctx.lower_where_predicate(pred, false).map(|p| make_binders(&generics, p))) |
991 | ctx.lower_where_predicate(pred, false).map(|p| Binders::new(generics.len(), p)) | ||
992 | }) | ||
993 | .collect() | 1053 | .collect() |
994 | } | 1054 | } |
995 | 1055 | ||
@@ -1008,25 +1068,21 @@ pub(crate) fn generic_defaults_query( | |||
1008 | .enumerate() | 1068 | .enumerate() |
1009 | .map(|(idx, (_, p))| { | 1069 | .map(|(idx, (_, p))| { |
1010 | let mut ty = | 1070 | let mut ty = |
1011 | p.default.as_ref().map_or(TyKind::Unknown.intern(&Interner), |t| ctx.lower_ty(t)); | 1071 | p.default.as_ref().map_or(TyKind::Error.intern(&Interner), |t| ctx.lower_ty(t)); |
1012 | 1072 | ||
1013 | // Each default can only refer to previous parameters. | 1073 | // Each default can only refer to previous parameters. |
1014 | ty.walk_mut_binders( | 1074 | ty = crate::fold_free_vars(ty, |bound, binders| { |
1015 | &mut |ty, binders| match ty.interned_mut() { | 1075 | if bound.index >= idx && bound.debruijn == DebruijnIndex::INNERMOST { |
1016 | TyKind::BoundVar(BoundVar { debruijn, index }) if *debruijn == binders => { | 1076 | // type variable default referring to parameter coming |
1017 | if *index >= idx { | 1077 | // after it. This is forbidden (FIXME: report |
1018 | // type variable default referring to parameter coming | 1078 | // diagnostic) |
1019 | // after it. This is forbidden (FIXME: report | 1079 | TyKind::Error.intern(&Interner) |
1020 | // diagnostic) | 1080 | } else { |
1021 | *ty = TyKind::Unknown.intern(&Interner); | 1081 | bound.shifted_in_from(binders).to_ty(&Interner) |
1022 | } | 1082 | } |
1023 | } | 1083 | }); |
1024 | _ => {} | ||
1025 | }, | ||
1026 | DebruijnIndex::INNERMOST, | ||
1027 | ); | ||
1028 | 1084 | ||
1029 | Binders::new(idx, ty) | 1085 | crate::make_only_type_binders(idx, ty) |
1030 | }) | 1086 | }) |
1031 | .collect(); | 1087 | .collect(); |
1032 | 1088 | ||
@@ -1039,14 +1095,13 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { | |||
1039 | let ctx_params = TyLoweringContext::new(db, &resolver) | 1095 | let ctx_params = TyLoweringContext::new(db, &resolver) |
1040 | .with_impl_trait_mode(ImplTraitLoweringMode::Variable) | 1096 | .with_impl_trait_mode(ImplTraitLoweringMode::Variable) |
1041 | .with_type_param_mode(TypeParamLoweringMode::Variable); | 1097 | .with_type_param_mode(TypeParamLoweringMode::Variable); |
1042 | let params = data.params.iter().map(|tr| (&ctx_params).lower_ty(tr)).collect::<Vec<_>>(); | 1098 | let params = data.params.iter().map(|tr| ctx_params.lower_ty(tr)).collect::<Vec<_>>(); |
1043 | let ctx_ret = TyLoweringContext::new(db, &resolver) | 1099 | let ctx_ret = TyLoweringContext::new(db, &resolver) |
1044 | .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) | 1100 | .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) |
1045 | .with_type_param_mode(TypeParamLoweringMode::Variable); | 1101 | .with_type_param_mode(TypeParamLoweringMode::Variable); |
1046 | let ret = (&ctx_ret).lower_ty(&data.ret_type); | 1102 | let ret = ctx_ret.lower_ty(&data.ret_type); |
1047 | let generics = generics(db.upcast(), def.into()); | 1103 | let generics = generics(db.upcast(), def.into()); |
1048 | let num_binders = generics.len(); | 1104 | make_binders(&generics, CallableSig::from_params_and_return(params, ret, data.is_varargs())) |
1049 | Binders::new(num_binders, CallableSig::from_params_and_return(params, ret, data.is_varargs())) | ||
1050 | } | 1105 | } |
1051 | 1106 | ||
1052 | /// Build the declared type of a function. This should not need to look at the | 1107 | /// Build the declared type of a function. This should not need to look at the |
@@ -1054,8 +1109,8 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { | |||
1054 | fn type_for_fn(db: &dyn HirDatabase, def: FunctionId) -> Binders<Ty> { | 1109 | fn type_for_fn(db: &dyn HirDatabase, def: FunctionId) -> Binders<Ty> { |
1055 | let generics = generics(db.upcast(), def.into()); | 1110 | let generics = generics(db.upcast(), def.into()); |
1056 | let substs = generics.bound_vars_subst(DebruijnIndex::INNERMOST); | 1111 | let substs = generics.bound_vars_subst(DebruijnIndex::INNERMOST); |
1057 | Binders::new( | 1112 | make_binders( |
1058 | substs.len(&Interner), | 1113 | &generics, |
1059 | TyKind::FnDef(CallableDefId::FunctionId(def).to_chalk(db), substs).intern(&Interner), | 1114 | TyKind::FnDef(CallableDefId::FunctionId(def).to_chalk(db), substs).intern(&Interner), |
1060 | ) | 1115 | ) |
1061 | } | 1116 | } |
@@ -1068,7 +1123,7 @@ fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders<Ty> { | |||
1068 | let ctx = | 1123 | let ctx = |
1069 | TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); | 1124 | TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); |
1070 | 1125 | ||
1071 | Binders::new(generics.len(), ctx.lower_ty(&data.type_ref)) | 1126 | make_binders(&generics, ctx.lower_ty(&data.type_ref)) |
1072 | } | 1127 | } |
1073 | 1128 | ||
1074 | /// Build the declared type of a static. | 1129 | /// Build the declared type of a static. |
@@ -1077,7 +1132,7 @@ fn type_for_static(db: &dyn HirDatabase, def: StaticId) -> Binders<Ty> { | |||
1077 | let resolver = def.resolver(db.upcast()); | 1132 | let resolver = def.resolver(db.upcast()); |
1078 | let ctx = TyLoweringContext::new(db, &resolver); | 1133 | let ctx = TyLoweringContext::new(db, &resolver); |
1079 | 1134 | ||
1080 | Binders::new(0, ctx.lower_ty(&data.type_ref)) | 1135 | Binders::empty(&Interner, ctx.lower_ty(&data.type_ref)) |
1081 | } | 1136 | } |
1082 | 1137 | ||
1083 | fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnSig { | 1138 | fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnSig { |
@@ -1087,8 +1142,8 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS | |||
1087 | let ctx = | 1142 | let ctx = |
1088 | TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); | 1143 | TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); |
1089 | let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>(); | 1144 | let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>(); |
1090 | let ret = type_for_adt(db, def.into()); | 1145 | let (ret, binders) = type_for_adt(db, def.into()).into_value_and_skipped_binders(); |
1091 | Binders::new(ret.num_binders, CallableSig::from_params_and_return(params, ret.value, false)) | 1146 | Binders::new(binders, CallableSig::from_params_and_return(params, ret, false)) |
1092 | } | 1147 | } |
1093 | 1148 | ||
1094 | /// Build the type of a tuple struct constructor. | 1149 | /// Build the type of a tuple struct constructor. |
@@ -1099,8 +1154,8 @@ fn type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Binders<T | |||
1099 | } | 1154 | } |
1100 | let generics = generics(db.upcast(), def.into()); | 1155 | let generics = generics(db.upcast(), def.into()); |
1101 | let substs = generics.bound_vars_subst(DebruijnIndex::INNERMOST); | 1156 | let substs = generics.bound_vars_subst(DebruijnIndex::INNERMOST); |
1102 | Binders::new( | 1157 | make_binders( |
1103 | substs.len(&Interner), | 1158 | &generics, |
1104 | TyKind::FnDef(CallableDefId::StructId(def).to_chalk(db), substs).intern(&Interner), | 1159 | TyKind::FnDef(CallableDefId::StructId(def).to_chalk(db), substs).intern(&Interner), |
1105 | ) | 1160 | ) |
1106 | } | 1161 | } |
@@ -1113,8 +1168,8 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) | |||
1113 | let ctx = | 1168 | let ctx = |
1114 | TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); | 1169 | TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); |
1115 | let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>(); | 1170 | let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>(); |
1116 | let ret = type_for_adt(db, def.parent.into()); | 1171 | let (ret, binders) = type_for_adt(db, def.parent.into()).into_value_and_skipped_binders(); |
1117 | Binders::new(ret.num_binders, CallableSig::from_params_and_return(params, ret.value, false)) | 1172 | Binders::new(binders, CallableSig::from_params_and_return(params, ret, false)) |
1118 | } | 1173 | } |
1119 | 1174 | ||
1120 | /// Build the type of a tuple enum variant constructor. | 1175 | /// Build the type of a tuple enum variant constructor. |
@@ -1126,17 +1181,17 @@ fn type_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) - | |||
1126 | } | 1181 | } |
1127 | let generics = generics(db.upcast(), def.parent.into()); | 1182 | let generics = generics(db.upcast(), def.parent.into()); |
1128 | let substs = generics.bound_vars_subst(DebruijnIndex::INNERMOST); | 1183 | let substs = generics.bound_vars_subst(DebruijnIndex::INNERMOST); |
1129 | Binders::new( | 1184 | make_binders( |
1130 | substs.len(&Interner), | 1185 | &generics, |
1131 | TyKind::FnDef(CallableDefId::EnumVariantId(def).to_chalk(db), substs).intern(&Interner), | 1186 | TyKind::FnDef(CallableDefId::EnumVariantId(def).to_chalk(db), substs).intern(&Interner), |
1132 | ) | 1187 | ) |
1133 | } | 1188 | } |
1134 | 1189 | ||
1135 | fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> { | 1190 | fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> { |
1191 | let generics = generics(db.upcast(), adt.into()); | ||
1136 | let b = TyBuilder::adt(db, adt); | 1192 | let b = TyBuilder::adt(db, adt); |
1137 | let num_binders = b.remaining(); | ||
1138 | let ty = b.fill_with_bound_vars(DebruijnIndex::INNERMOST, 0).build(); | 1193 | let ty = b.fill_with_bound_vars(DebruijnIndex::INNERMOST, 0).build(); |
1139 | Binders::new(num_binders, ty) | 1194 | make_binders(&generics, ty) |
1140 | } | 1195 | } |
1141 | 1196 | ||
1142 | fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> { | 1197 | fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> { |
@@ -1145,11 +1200,11 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> { | |||
1145 | let ctx = | 1200 | let ctx = |
1146 | TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); | 1201 | TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); |
1147 | if db.type_alias_data(t).is_extern { | 1202 | if db.type_alias_data(t).is_extern { |
1148 | Binders::new(0, TyKind::ForeignType(crate::to_foreign_def_id(t)).intern(&Interner)) | 1203 | Binders::empty(&Interner, TyKind::Foreign(crate::to_foreign_def_id(t)).intern(&Interner)) |
1149 | } else { | 1204 | } else { |
1150 | let type_ref = &db.type_alias_data(t).type_ref; | 1205 | let type_ref = &db.type_alias_data(t).type_ref; |
1151 | let inner = ctx.lower_ty(type_ref.as_deref().unwrap_or(&TypeRef::Error)); | 1206 | let inner = ctx.lower_ty(type_ref.as_deref().unwrap_or(&TypeRef::Error)); |
1152 | Binders::new(generics.len(), inner) | 1207 | make_binders(&generics, inner) |
1153 | } | 1208 | } |
1154 | } | 1209 | } |
1155 | 1210 | ||
@@ -1208,19 +1263,21 @@ impl_from!(FunctionId, StructId, UnionId, EnumVariantId, ConstId, StaticId for V | |||
1208 | /// namespace. | 1263 | /// namespace. |
1209 | pub(crate) fn ty_query(db: &dyn HirDatabase, def: TyDefId) -> Binders<Ty> { | 1264 | pub(crate) fn ty_query(db: &dyn HirDatabase, def: TyDefId) -> Binders<Ty> { |
1210 | match def { | 1265 | match def { |
1211 | TyDefId::BuiltinType(it) => Binders::new(0, TyBuilder::builtin(it)), | 1266 | TyDefId::BuiltinType(it) => Binders::empty(&Interner, TyBuilder::builtin(it)), |
1212 | TyDefId::AdtId(it) => type_for_adt(db, it), | 1267 | TyDefId::AdtId(it) => type_for_adt(db, it), |
1213 | TyDefId::TypeAliasId(it) => type_for_type_alias(db, it), | 1268 | TyDefId::TypeAliasId(it) => type_for_type_alias(db, it), |
1214 | } | 1269 | } |
1215 | } | 1270 | } |
1216 | 1271 | ||
1217 | pub(crate) fn ty_recover(db: &dyn HirDatabase, _cycle: &[String], def: &TyDefId) -> Binders<Ty> { | 1272 | pub(crate) fn ty_recover(db: &dyn HirDatabase, _cycle: &[String], def: &TyDefId) -> Binders<Ty> { |
1218 | let num_binders = match *def { | 1273 | let generics = match *def { |
1219 | TyDefId::BuiltinType(_) => 0, | 1274 | TyDefId::BuiltinType(_) => { |
1220 | TyDefId::AdtId(it) => generics(db.upcast(), it.into()).len(), | 1275 | return Binders::empty(&Interner, TyKind::Error.intern(&Interner)) |
1221 | TyDefId::TypeAliasId(it) => generics(db.upcast(), it.into()).len(), | 1276 | } |
1277 | TyDefId::AdtId(it) => generics(db.upcast(), it.into()), | ||
1278 | TyDefId::TypeAliasId(it) => generics(db.upcast(), it.into()), | ||
1222 | }; | 1279 | }; |
1223 | Binders::new(num_binders, TyKind::Unknown.intern(&Interner)) | 1280 | make_binders(&generics, TyKind::Error.intern(&Interner)) |
1224 | } | 1281 | } |
1225 | 1282 | ||
1226 | pub(crate) fn value_ty_query(db: &dyn HirDatabase, def: ValueTyDefId) -> Binders<Ty> { | 1283 | pub(crate) fn value_ty_query(db: &dyn HirDatabase, def: ValueTyDefId) -> Binders<Ty> { |
@@ -1240,7 +1297,7 @@ pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binde | |||
1240 | let generics = generics(db.upcast(), impl_id.into()); | 1297 | let generics = generics(db.upcast(), impl_id.into()); |
1241 | let ctx = | 1298 | let ctx = |
1242 | TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); | 1299 | TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); |
1243 | Binders::new(generics.len(), ctx.lower_ty(&impl_data.self_ty)) | 1300 | make_binders(&generics, ctx.lower_ty(&impl_data.self_ty)) |
1244 | } | 1301 | } |
1245 | 1302 | ||
1246 | pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty { | 1303 | pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty { |
@@ -1258,7 +1315,7 @@ pub(crate) fn impl_self_ty_recover( | |||
1258 | impl_id: &ImplId, | 1315 | impl_id: &ImplId, |
1259 | ) -> Binders<Ty> { | 1316 | ) -> Binders<Ty> { |
1260 | let generics = generics(db.upcast(), (*impl_id).into()); | 1317 | let generics = generics(db.upcast(), (*impl_id).into()); |
1261 | Binders::new(generics.len(), TyKind::Unknown.intern(&Interner)) | 1318 | make_binders(&generics, TyKind::Error.intern(&Interner)) |
1262 | } | 1319 | } |
1263 | 1320 | ||
1264 | pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<Binders<TraitRef>> { | 1321 | pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<Binders<TraitRef>> { |
@@ -1266,9 +1323,9 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option< | |||
1266 | let resolver = impl_id.resolver(db.upcast()); | 1323 | let resolver = impl_id.resolver(db.upcast()); |
1267 | let ctx = | 1324 | let ctx = |
1268 | TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); | 1325 | TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); |
1269 | let self_ty = db.impl_self_ty(impl_id); | 1326 | let (self_ty, binders) = db.impl_self_ty(impl_id).into_value_and_skipped_binders(); |
1270 | let target_trait = impl_data.target_trait.as_ref()?; | 1327 | let target_trait = impl_data.target_trait.as_ref()?; |
1271 | Some(Binders::new(self_ty.num_binders, ctx.lower_trait_ref(target_trait, Some(self_ty.value))?)) | 1328 | Some(Binders::new(binders, ctx.lower_trait_ref(target_trait, Some(self_ty))?)) |
1272 | } | 1329 | } |
1273 | 1330 | ||
1274 | pub(crate) fn return_type_impl_traits( | 1331 | pub(crate) fn return_type_impl_traits( |
@@ -1283,13 +1340,12 @@ pub(crate) fn return_type_impl_traits( | |||
1283 | .with_type_param_mode(TypeParamLoweringMode::Variable); | 1340 | .with_type_param_mode(TypeParamLoweringMode::Variable); |
1284 | let _ret = (&ctx_ret).lower_ty(&data.ret_type); | 1341 | let _ret = (&ctx_ret).lower_ty(&data.ret_type); |
1285 | let generics = generics(db.upcast(), def.into()); | 1342 | let generics = generics(db.upcast(), def.into()); |
1286 | let num_binders = generics.len(); | ||
1287 | let return_type_impl_traits = | 1343 | let return_type_impl_traits = |
1288 | ReturnTypeImplTraits { impl_traits: ctx_ret.opaque_type_data.into_inner() }; | 1344 | ReturnTypeImplTraits { impl_traits: ctx_ret.opaque_type_data.into_inner() }; |
1289 | if return_type_impl_traits.impl_traits.is_empty() { | 1345 | if return_type_impl_traits.impl_traits.is_empty() { |
1290 | None | 1346 | None |
1291 | } else { | 1347 | } else { |
1292 | Some(Arc::new(Binders::new(num_binders, return_type_impl_traits))) | 1348 | Some(Arc::new(make_binders(&generics, return_type_impl_traits))) |
1293 | } | 1349 | } |
1294 | } | 1350 | } |
1295 | 1351 | ||
@@ -1299,3 +1355,7 @@ pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mut | |||
1299 | hir_def::type_ref::Mutability::Mut => Mutability::Mut, | 1355 | hir_def::type_ref::Mutability::Mut => Mutability::Mut, |
1300 | } | 1356 | } |
1301 | } | 1357 | } |
1358 | |||
1359 | fn make_binders<T: HasInterner<Interner = Interner>>(generics: &Generics, value: T) -> Binders<T> { | ||
1360 | crate::make_only_type_binders(generics.len(), value) | ||
1361 | } | ||
diff --git a/crates/hir_ty/src/mapping.rs b/crates/hir_ty/src/mapping.rs new file mode 100644 index 000000000..5e86fafe5 --- /dev/null +++ b/crates/hir_ty/src/mapping.rs | |||
@@ -0,0 +1,154 @@ | |||
1 | //! This module contains the implementations of the `ToChalk` trait, which | ||
2 | //! handles conversion between our data types and their corresponding types in | ||
3 | //! Chalk (in both directions); plus some helper functions for more specialized | ||
4 | //! conversions. | ||
5 | |||
6 | use chalk_solve::rust_ir; | ||
7 | |||
8 | use base_db::salsa::{self, InternKey}; | ||
9 | use hir_def::{ConstParamId, LifetimeParamId, TraitId, TypeAliasId, TypeParamId}; | ||
10 | |||
11 | use crate::{ | ||
12 | chalk_db, db::HirDatabase, AssocTypeId, CallableDefId, ChalkTraitId, FnDefId, ForeignDefId, | ||
13 | Interner, OpaqueTyId, PlaceholderIndex, | ||
14 | }; | ||
15 | |||
16 | pub(crate) trait ToChalk { | ||
17 | type Chalk; | ||
18 | fn to_chalk(self, db: &dyn HirDatabase) -> Self::Chalk; | ||
19 | fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self; | ||
20 | } | ||
21 | |||
22 | pub(crate) fn from_chalk<T, ChalkT>(db: &dyn HirDatabase, chalk: ChalkT) -> T | ||
23 | where | ||
24 | T: ToChalk<Chalk = ChalkT>, | ||
25 | { | ||
26 | T::from_chalk(db, chalk) | ||
27 | } | ||
28 | |||
29 | impl ToChalk for hir_def::ImplId { | ||
30 | type Chalk = chalk_db::ImplId; | ||
31 | |||
32 | fn to_chalk(self, _db: &dyn HirDatabase) -> chalk_db::ImplId { | ||
33 | chalk_ir::ImplId(self.as_intern_id()) | ||
34 | } | ||
35 | |||
36 | fn from_chalk(_db: &dyn HirDatabase, impl_id: chalk_db::ImplId) -> hir_def::ImplId { | ||
37 | InternKey::from_intern_id(impl_id.0) | ||
38 | } | ||
39 | } | ||
40 | |||
41 | impl ToChalk for CallableDefId { | ||
42 | type Chalk = FnDefId; | ||
43 | |||
44 | fn to_chalk(self, db: &dyn HirDatabase) -> FnDefId { | ||
45 | db.intern_callable_def(self).into() | ||
46 | } | ||
47 | |||
48 | fn from_chalk(db: &dyn HirDatabase, fn_def_id: FnDefId) -> CallableDefId { | ||
49 | db.lookup_intern_callable_def(fn_def_id.into()) | ||
50 | } | ||
51 | } | ||
52 | |||
53 | pub(crate) struct TypeAliasAsValue(pub(crate) TypeAliasId); | ||
54 | |||
55 | impl ToChalk for TypeAliasAsValue { | ||
56 | type Chalk = chalk_db::AssociatedTyValueId; | ||
57 | |||
58 | fn to_chalk(self, _db: &dyn HirDatabase) -> chalk_db::AssociatedTyValueId { | ||
59 | rust_ir::AssociatedTyValueId(self.0.as_intern_id()) | ||
60 | } | ||
61 | |||
62 | fn from_chalk( | ||
63 | _db: &dyn HirDatabase, | ||
64 | assoc_ty_value_id: chalk_db::AssociatedTyValueId, | ||
65 | ) -> TypeAliasAsValue { | ||
66 | TypeAliasAsValue(TypeAliasId::from_intern_id(assoc_ty_value_id.0)) | ||
67 | } | ||
68 | } | ||
69 | |||
70 | impl From<FnDefId> for crate::db::InternedCallableDefId { | ||
71 | fn from(fn_def_id: FnDefId) -> Self { | ||
72 | InternKey::from_intern_id(fn_def_id.0) | ||
73 | } | ||
74 | } | ||
75 | |||
76 | impl From<crate::db::InternedCallableDefId> for FnDefId { | ||
77 | fn from(callable_def_id: crate::db::InternedCallableDefId) -> Self { | ||
78 | chalk_ir::FnDefId(callable_def_id.as_intern_id()) | ||
79 | } | ||
80 | } | ||
81 | |||
82 | impl From<OpaqueTyId> for crate::db::InternedOpaqueTyId { | ||
83 | fn from(id: OpaqueTyId) -> Self { | ||
84 | InternKey::from_intern_id(id.0) | ||
85 | } | ||
86 | } | ||
87 | |||
88 | impl From<crate::db::InternedOpaqueTyId> for OpaqueTyId { | ||
89 | fn from(id: crate::db::InternedOpaqueTyId) -> Self { | ||
90 | chalk_ir::OpaqueTyId(id.as_intern_id()) | ||
91 | } | ||
92 | } | ||
93 | |||
94 | impl From<chalk_ir::ClosureId<Interner>> for crate::db::InternedClosureId { | ||
95 | fn from(id: chalk_ir::ClosureId<Interner>) -> Self { | ||
96 | Self::from_intern_id(id.0) | ||
97 | } | ||
98 | } | ||
99 | |||
100 | impl From<crate::db::InternedClosureId> for chalk_ir::ClosureId<Interner> { | ||
101 | fn from(id: crate::db::InternedClosureId) -> Self { | ||
102 | chalk_ir::ClosureId(id.as_intern_id()) | ||
103 | } | ||
104 | } | ||
105 | |||
106 | pub fn to_foreign_def_id(id: TypeAliasId) -> ForeignDefId { | ||
107 | chalk_ir::ForeignDefId(salsa::InternKey::as_intern_id(&id)) | ||
108 | } | ||
109 | |||
110 | pub fn from_foreign_def_id(id: ForeignDefId) -> TypeAliasId { | ||
111 | salsa::InternKey::from_intern_id(id.0) | ||
112 | } | ||
113 | |||
114 | pub fn to_assoc_type_id(id: TypeAliasId) -> AssocTypeId { | ||
115 | chalk_ir::AssocTypeId(salsa::InternKey::as_intern_id(&id)) | ||
116 | } | ||
117 | |||
118 | pub fn from_assoc_type_id(id: AssocTypeId) -> TypeAliasId { | ||
119 | salsa::InternKey::from_intern_id(id.0) | ||
120 | } | ||
121 | |||
122 | pub fn from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> TypeParamId { | ||
123 | assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT); | ||
124 | let interned_id = salsa::InternKey::from_intern_id(salsa::InternId::from(idx.idx)); | ||
125 | db.lookup_intern_type_param_id(interned_id) | ||
126 | } | ||
127 | |||
128 | pub fn to_placeholder_idx(db: &dyn HirDatabase, id: TypeParamId) -> PlaceholderIndex { | ||
129 | let interned_id = db.intern_type_param_id(id); | ||
130 | PlaceholderIndex { | ||
131 | ui: chalk_ir::UniverseIndex::ROOT, | ||
132 | idx: salsa::InternKey::as_intern_id(&interned_id).as_usize(), | ||
133 | } | ||
134 | } | ||
135 | |||
136 | pub fn lt_from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> LifetimeParamId { | ||
137 | assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT); | ||
138 | let interned_id = salsa::InternKey::from_intern_id(salsa::InternId::from(idx.idx)); | ||
139 | db.lookup_intern_lifetime_param_id(interned_id) | ||
140 | } | ||
141 | |||
142 | pub fn const_from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> ConstParamId { | ||
143 | assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT); | ||
144 | let interned_id = salsa::InternKey::from_intern_id(salsa::InternId::from(idx.idx)); | ||
145 | db.lookup_intern_const_param_id(interned_id) | ||
146 | } | ||
147 | |||
148 | pub fn to_chalk_trait_id(id: TraitId) -> ChalkTraitId { | ||
149 | chalk_ir::TraitId(salsa::InternKey::as_intern_id(&id)) | ||
150 | } | ||
151 | |||
152 | pub fn from_chalk_trait_id(id: ChalkTraitId) -> TraitId { | ||
153 | salsa::InternKey::from_intern_id(id.0) | ||
154 | } | ||
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index a76586f0c..48bbcfd9f 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs | |||
@@ -8,8 +8,8 @@ use arrayvec::ArrayVec; | |||
8 | use base_db::CrateId; | 8 | use base_db::CrateId; |
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, AssocContainerId, AssocItemId, FunctionId, GenericDefId, HasModule, | 11 | lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, FunctionId, |
12 | 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; |
15 | use rustc_hash::{FxHashMap, FxHashSet}; | 15 | use rustc_hash::{FxHashMap, FxHashSet}; |
@@ -19,51 +19,91 @@ use crate::{ | |||
19 | db::HirDatabase, | 19 | db::HirDatabase, |
20 | from_foreign_def_id, | 20 | from_foreign_def_id, |
21 | primitive::{self, FloatTy, IntTy, UintTy}, | 21 | primitive::{self, FloatTy, IntTy, UintTy}, |
22 | static_lifetime, | ||
22 | utils::all_super_traits, | 23 | utils::all_super_traits, |
23 | AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, FnPointer, FnSig, ForeignDefId, | 24 | AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, ForeignDefId, InEnvironment, Interner, |
24 | InEnvironment, Interner, Scalar, Substitution, TraitEnvironment, Ty, TyBuilder, TyKind, | 25 | Scalar, Substitution, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, |
25 | TypeWalk, | ||
26 | }; | 26 | }; |
27 | 27 | ||
28 | /// This is used as a key for indexing impls. | 28 | /// This is used as a key for indexing impls. |
29 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | 29 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
30 | pub enum TyFingerprint { | 30 | pub enum TyFingerprint { |
31 | // These are lang item impls: | ||
31 | Str, | 32 | Str, |
32 | Slice, | 33 | Slice, |
33 | Array, | 34 | Array, |
34 | Never, | 35 | Never, |
35 | RawPtr(Mutability), | 36 | RawPtr(Mutability), |
36 | Scalar(Scalar), | 37 | Scalar(Scalar), |
38 | // These can have user-defined impls: | ||
37 | Adt(hir_def::AdtId), | 39 | Adt(hir_def::AdtId), |
38 | Dyn(TraitId), | 40 | Dyn(TraitId), |
39 | Tuple(usize), | ||
40 | ForeignType(ForeignDefId), | 41 | ForeignType(ForeignDefId), |
41 | FnPtr(usize, FnSig), | 42 | // These only exist for trait impls |
43 | Unit, | ||
44 | Unnameable, | ||
45 | Function(u32), | ||
42 | } | 46 | } |
43 | 47 | ||
44 | impl TyFingerprint { | 48 | impl TyFingerprint { |
45 | /// Creates a TyFingerprint for looking up an impl. Only certain types can | 49 | /// Creates a TyFingerprint for looking up an inherent impl. Only certain |
46 | /// have impls: if we have some `struct S`, we can have an `impl S`, but not | 50 | /// types can have inherent impls: if we have some `struct S`, we can have |
47 | /// `impl &S`. Hence, this will return `None` for reference types and such. | 51 | /// an `impl S`, but not `impl &S`. Hence, this will return `None` for |
48 | pub fn for_impl(ty: &Ty) -> Option<TyFingerprint> { | 52 | /// reference types and such. |
49 | let fp = match *ty.kind(&Interner) { | 53 | pub fn for_inherent_impl(ty: &Ty) -> Option<TyFingerprint> { |
54 | let fp = match ty.kind(&Interner) { | ||
50 | TyKind::Str => TyFingerprint::Str, | 55 | TyKind::Str => TyFingerprint::Str, |
51 | TyKind::Never => TyFingerprint::Never, | 56 | TyKind::Never => TyFingerprint::Never, |
52 | TyKind::Slice(..) => TyFingerprint::Slice, | 57 | TyKind::Slice(..) => TyFingerprint::Slice, |
53 | TyKind::Array(..) => TyFingerprint::Array, | 58 | TyKind::Array(..) => TyFingerprint::Array, |
54 | TyKind::Scalar(scalar) => TyFingerprint::Scalar(scalar), | 59 | TyKind::Scalar(scalar) => TyFingerprint::Scalar(*scalar), |
55 | TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(adt), | 60 | TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt), |
56 | TyKind::Tuple(cardinality, _) => TyFingerprint::Tuple(cardinality), | 61 | TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability), |
57 | TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(mutability), | 62 | TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id), |
58 | TyKind::ForeignType(alias_id, ..) => TyFingerprint::ForeignType(alias_id), | ||
59 | TyKind::Function(FnPointer { num_args, sig, .. }) => { | ||
60 | TyFingerprint::FnPtr(num_args, sig) | ||
61 | } | ||
62 | TyKind::Dyn(_) => ty.dyn_trait().map(|trait_| TyFingerprint::Dyn(trait_))?, | 63 | TyKind::Dyn(_) => ty.dyn_trait().map(|trait_| TyFingerprint::Dyn(trait_))?, |
63 | _ => return None, | 64 | _ => return None, |
64 | }; | 65 | }; |
65 | Some(fp) | 66 | Some(fp) |
66 | } | 67 | } |
68 | |||
69 | /// Creates a TyFingerprint for looking up a trait impl. | ||
70 | pub fn for_trait_impl(ty: &Ty) -> Option<TyFingerprint> { | ||
71 | let fp = match ty.kind(&Interner) { | ||
72 | TyKind::Str => TyFingerprint::Str, | ||
73 | TyKind::Never => TyFingerprint::Never, | ||
74 | TyKind::Slice(..) => TyFingerprint::Slice, | ||
75 | TyKind::Array(..) => TyFingerprint::Array, | ||
76 | TyKind::Scalar(scalar) => TyFingerprint::Scalar(*scalar), | ||
77 | TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt), | ||
78 | TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability), | ||
79 | TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id), | ||
80 | TyKind::Dyn(_) => ty.dyn_trait().map(|trait_| TyFingerprint::Dyn(trait_))?, | ||
81 | TyKind::Ref(_, _, ty) => return TyFingerprint::for_trait_impl(ty), | ||
82 | TyKind::Tuple(_, subst) => { | ||
83 | let first_ty = subst.interned().get(0).map(|arg| arg.assert_ty_ref(&Interner)); | ||
84 | if let Some(ty) = first_ty { | ||
85 | return TyFingerprint::for_trait_impl(ty); | ||
86 | } else { | ||
87 | TyFingerprint::Unit | ||
88 | } | ||
89 | } | ||
90 | TyKind::AssociatedType(_, _) | ||
91 | | TyKind::OpaqueType(_, _) | ||
92 | | TyKind::FnDef(_, _) | ||
93 | | TyKind::Closure(_, _) | ||
94 | | TyKind::Generator(..) | ||
95 | | TyKind::GeneratorWitness(..) => TyFingerprint::Unnameable, | ||
96 | TyKind::Function(fn_ptr) => { | ||
97 | TyFingerprint::Function(fn_ptr.substitution.0.len(&Interner) as u32) | ||
98 | } | ||
99 | TyKind::Alias(_) | ||
100 | | TyKind::Placeholder(_) | ||
101 | | TyKind::BoundVar(_) | ||
102 | | TyKind::InferenceVar(_, _) | ||
103 | | TyKind::Error => return None, | ||
104 | }; | ||
105 | Some(fp) | ||
106 | } | ||
67 | } | 107 | } |
68 | 108 | ||
69 | pub(crate) const ALL_INT_FPS: [TyFingerprint; 12] = [ | 109 | pub(crate) const ALL_INT_FPS: [TyFingerprint; 12] = [ |
@@ -99,25 +139,38 @@ impl TraitImpls { | |||
99 | let mut impls = Self { map: FxHashMap::default() }; | 139 | let mut impls = Self { map: FxHashMap::default() }; |
100 | 140 | ||
101 | let crate_def_map = db.crate_def_map(krate); | 141 | let crate_def_map = db.crate_def_map(krate); |
102 | for (_module_id, module_data) in crate_def_map.modules() { | 142 | collect_def_map(db, &crate_def_map, &mut impls); |
103 | for impl_id in module_data.scope.impls() { | 143 | |
104 | let target_trait = match db.impl_trait(impl_id) { | 144 | return Arc::new(impls); |
105 | Some(tr) => tr.value.hir_trait_id(), | 145 | |
106 | None => continue, | 146 | fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap, impls: &mut TraitImpls) { |
107 | }; | 147 | for (_module_id, module_data) in def_map.modules() { |
108 | let self_ty = db.impl_self_ty(impl_id); | 148 | for impl_id in module_data.scope.impls() { |
109 | let self_ty_fp = TyFingerprint::for_impl(&self_ty.value); | 149 | let target_trait = match db.impl_trait(impl_id) { |
110 | impls | 150 | Some(tr) => tr.skip_binders().hir_trait_id(), |
111 | .map | 151 | None => continue, |
112 | .entry(target_trait) | 152 | }; |
113 | .or_default() | 153 | let self_ty = db.impl_self_ty(impl_id); |
114 | .entry(self_ty_fp) | 154 | let self_ty_fp = TyFingerprint::for_trait_impl(self_ty.skip_binders()); |
115 | .or_default() | 155 | impls |
116 | .push(impl_id); | 156 | .map |
157 | .entry(target_trait) | ||
158 | .or_default() | ||
159 | .entry(self_ty_fp) | ||
160 | .or_default() | ||
161 | .push(impl_id); | ||
162 | } | ||
163 | |||
164 | // To better support custom derives, collect impls in all unnamed const items. | ||
165 | // const _: () = { ... }; | ||
166 | for konst in module_data.scope.unnamed_consts() { | ||
167 | let body = db.body(konst.into()); | ||
168 | for (_, block_def_map) in body.blocks(db.upcast()) { | ||
169 | collect_def_map(db, &block_def_map, impls); | ||
170 | } | ||
171 | } | ||
117 | } | 172 | } |
118 | } | 173 | } |
119 | |||
120 | Arc::new(impls) | ||
121 | } | 174 | } |
122 | 175 | ||
123 | pub(crate) fn trait_impls_in_deps_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> { | 176 | pub(crate) fn trait_impls_in_deps_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> { |
@@ -143,10 +196,13 @@ impl TraitImpls { | |||
143 | } | 196 | } |
144 | 197 | ||
145 | /// Queries all trait impls for the given type. | 198 | /// Queries all trait impls for the given type. |
146 | pub fn for_self_ty(&self, fp: TyFingerprint) -> impl Iterator<Item = ImplId> + '_ { | 199 | pub fn for_self_ty_without_blanket_impls( |
200 | &self, | ||
201 | fp: TyFingerprint, | ||
202 | ) -> impl Iterator<Item = ImplId> + '_ { | ||
147 | self.map | 203 | self.map |
148 | .values() | 204 | .values() |
149 | .flat_map(move |impls| impls.get(&None).into_iter().chain(impls.get(&Some(fp)))) | 205 | .flat_map(move |impls| impls.get(&Some(fp)).into_iter()) |
150 | .flat_map(|it| it.iter().copied()) | 206 | .flat_map(|it| it.iter().copied()) |
151 | } | 207 | } |
152 | 208 | ||
@@ -190,28 +246,43 @@ pub struct InherentImpls { | |||
190 | 246 | ||
191 | impl InherentImpls { | 247 | impl InherentImpls { |
192 | pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> { | 248 | pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> { |
193 | let mut map: FxHashMap<_, Vec<_>> = FxHashMap::default(); | 249 | let mut impls = Self { map: FxHashMap::default() }; |
194 | 250 | ||
195 | let crate_def_map = db.crate_def_map(krate); | 251 | let crate_def_map = db.crate_def_map(krate); |
196 | for (_module_id, module_data) in crate_def_map.modules() { | 252 | collect_def_map(db, &crate_def_map, &mut impls); |
197 | for impl_id in module_data.scope.impls() { | 253 | |
198 | let data = db.impl_data(impl_id); | 254 | return Arc::new(impls); |
199 | if data.target_trait.is_some() { | 255 | |
200 | continue; | 256 | fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap, impls: &mut InherentImpls) { |
257 | for (_module_id, module_data) in def_map.modules() { | ||
258 | for impl_id in module_data.scope.impls() { | ||
259 | let data = db.impl_data(impl_id); | ||
260 | if data.target_trait.is_some() { | ||
261 | continue; | ||
262 | } | ||
263 | |||
264 | let self_ty = db.impl_self_ty(impl_id); | ||
265 | let fp = TyFingerprint::for_inherent_impl(self_ty.skip_binders()); | ||
266 | if let Some(fp) = fp { | ||
267 | impls.map.entry(fp).or_default().push(impl_id); | ||
268 | } | ||
269 | // `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution) | ||
201 | } | 270 | } |
202 | 271 | ||
203 | let self_ty = db.impl_self_ty(impl_id); | 272 | // To better support custom derives, collect impls in all unnamed const items. |
204 | if let Some(fp) = TyFingerprint::for_impl(&self_ty.value) { | 273 | // const _: () = { ... }; |
205 | map.entry(fp).or_default().push(impl_id); | 274 | for konst in module_data.scope.unnamed_consts() { |
275 | let body = db.body(konst.into()); | ||
276 | for (_, block_def_map) in body.blocks(db.upcast()) { | ||
277 | collect_def_map(db, &block_def_map, impls); | ||
278 | } | ||
206 | } | 279 | } |
207 | } | 280 | } |
208 | } | 281 | } |
209 | |||
210 | Arc::new(Self { map }) | ||
211 | } | 282 | } |
212 | 283 | ||
213 | pub fn for_self_ty(&self, self_ty: &Ty) -> &[ImplId] { | 284 | pub fn for_self_ty(&self, self_ty: &Ty) -> &[ImplId] { |
214 | match TyFingerprint::for_impl(self_ty) { | 285 | match TyFingerprint::for_inherent_impl(self_ty) { |
215 | Some(fp) => self.map.get(&fp).map(|vec| vec.as_ref()).unwrap_or(&[]), | 286 | Some(fp) => self.map.get(&fp).map(|vec| vec.as_ref()).unwrap_or(&[]), |
216 | None => &[], | 287 | None => &[], |
217 | } | 288 | } |
@@ -222,15 +293,14 @@ impl InherentImpls { | |||
222 | } | 293 | } |
223 | } | 294 | } |
224 | 295 | ||
225 | impl Ty { | 296 | pub fn def_crates( |
226 | pub fn def_crates( | 297 | db: &dyn HirDatabase, |
227 | &self, | 298 | ty: &Ty, |
228 | db: &dyn HirDatabase, | 299 | cur_crate: CrateId, |
229 | cur_crate: CrateId, | 300 | ) -> Option<ArrayVec<CrateId, 2>> { |
230 | ) -> Option<ArrayVec<CrateId, 2>> { | 301 | // Types like slice can have inherent impls in several crates, (core and alloc). |
231 | // Types like slice can have inherent impls in several crates, (core and alloc). | 302 | // The corresponding impls are marked with lang items, so we can use them to find the required crates. |
232 | // The corresponding impls are marked with lang items, so we can use them to find the required crates. | 303 | macro_rules! lang_item_crate { |
233 | macro_rules! lang_item_crate { | ||
234 | ($($name:expr),+ $(,)?) => {{ | 304 | ($($name:expr),+ $(,)?) => {{ |
235 | let mut v = ArrayVec::<LangItemTarget, 2>::new(); | 305 | let mut v = ArrayVec::<LangItemTarget, 2>::new(); |
236 | $( | 306 | $( |
@@ -240,51 +310,50 @@ impl Ty { | |||
240 | }}; | 310 | }}; |
241 | } | 311 | } |
242 | 312 | ||
243 | let mod_to_crate_ids = |module: ModuleId| Some(std::iter::once(module.krate()).collect()); | 313 | let mod_to_crate_ids = |module: ModuleId| Some(std::iter::once(module.krate()).collect()); |
244 | 314 | ||
245 | let lang_item_targets = match self.kind(&Interner) { | 315 | let lang_item_targets = match ty.kind(&Interner) { |
246 | TyKind::Adt(AdtId(def_id), _) => { | 316 | TyKind::Adt(AdtId(def_id), _) => { |
247 | return mod_to_crate_ids(def_id.module(db.upcast())); | 317 | return mod_to_crate_ids(def_id.module(db.upcast())); |
248 | } | 318 | } |
249 | TyKind::ForeignType(id) => { | 319 | TyKind::Foreign(id) => { |
250 | return mod_to_crate_ids( | 320 | return mod_to_crate_ids( |
251 | from_foreign_def_id(*id).lookup(db.upcast()).module(db.upcast()), | 321 | from_foreign_def_id(*id).lookup(db.upcast()).module(db.upcast()), |
252 | ); | 322 | ); |
253 | } | 323 | } |
254 | TyKind::Scalar(Scalar::Bool) => lang_item_crate!("bool"), | 324 | TyKind::Scalar(Scalar::Bool) => lang_item_crate!("bool"), |
255 | TyKind::Scalar(Scalar::Char) => lang_item_crate!("char"), | 325 | TyKind::Scalar(Scalar::Char) => lang_item_crate!("char"), |
256 | TyKind::Scalar(Scalar::Float(f)) => match f { | 326 | TyKind::Scalar(Scalar::Float(f)) => match f { |
257 | // There are two lang items: one in libcore (fXX) and one in libstd (fXX_runtime) | 327 | // There are two lang items: one in libcore (fXX) and one in libstd (fXX_runtime) |
258 | FloatTy::F32 => lang_item_crate!("f32", "f32_runtime"), | 328 | FloatTy::F32 => lang_item_crate!("f32", "f32_runtime"), |
259 | FloatTy::F64 => lang_item_crate!("f64", "f64_runtime"), | 329 | FloatTy::F64 => lang_item_crate!("f64", "f64_runtime"), |
260 | }, | 330 | }, |
261 | &TyKind::Scalar(Scalar::Int(t)) => { | 331 | &TyKind::Scalar(Scalar::Int(t)) => { |
262 | lang_item_crate!(primitive::int_ty_to_string(t)) | 332 | lang_item_crate!(primitive::int_ty_to_string(t)) |
263 | } | 333 | } |
264 | &TyKind::Scalar(Scalar::Uint(t)) => { | 334 | &TyKind::Scalar(Scalar::Uint(t)) => { |
265 | lang_item_crate!(primitive::uint_ty_to_string(t)) | 335 | lang_item_crate!(primitive::uint_ty_to_string(t)) |
266 | } | 336 | } |
267 | TyKind::Str => lang_item_crate!("str_alloc", "str"), | 337 | TyKind::Str => lang_item_crate!("str_alloc", "str"), |
268 | TyKind::Slice(_) => lang_item_crate!("slice_alloc", "slice"), | 338 | TyKind::Slice(_) => lang_item_crate!("slice_alloc", "slice"), |
269 | TyKind::Raw(Mutability::Not, _) => lang_item_crate!("const_ptr"), | 339 | TyKind::Raw(Mutability::Not, _) => lang_item_crate!("const_ptr"), |
270 | TyKind::Raw(Mutability::Mut, _) => lang_item_crate!("mut_ptr"), | 340 | TyKind::Raw(Mutability::Mut, _) => lang_item_crate!("mut_ptr"), |
271 | TyKind::Dyn(_) => { | 341 | TyKind::Dyn(_) => { |
272 | return self.dyn_trait().and_then(|trait_| { | 342 | return ty.dyn_trait().and_then(|trait_| { |
273 | mod_to_crate_ids(GenericDefId::TraitId(trait_).module(db.upcast())) | 343 | mod_to_crate_ids(GenericDefId::TraitId(trait_).module(db.upcast())) |
274 | }); | 344 | }); |
275 | } | 345 | } |
276 | _ => return None, | 346 | _ => return None, |
277 | }; | 347 | }; |
278 | let res = lang_item_targets | 348 | let res = lang_item_targets |
279 | .into_iter() | 349 | .into_iter() |
280 | .filter_map(|it| match it { | 350 | .filter_map(|it| match it { |
281 | LangItemTarget::ImplDefId(it) => Some(it), | 351 | LangItemTarget::ImplDefId(it) => Some(it), |
282 | _ => None, | 352 | _ => None, |
283 | }) | 353 | }) |
284 | .map(|it| it.lookup(db.upcast()).container.krate()) | 354 | .map(|it| it.lookup(db.upcast()).container.krate()) |
285 | .collect(); | 355 | .collect(); |
286 | Some(res) | 356 | Some(res) |
287 | } | ||
288 | } | 357 | } |
289 | 358 | ||
290 | /// Look up the method with the given name, returning the actual autoderefed | 359 | /// Look up the method with the given name, returning the actual autoderefed |
@@ -453,7 +522,8 @@ fn iterate_method_candidates_with_autoref( | |||
453 | } | 522 | } |
454 | let refed = Canonical { | 523 | let refed = Canonical { |
455 | binders: deref_chain[0].binders.clone(), | 524 | binders: deref_chain[0].binders.clone(), |
456 | value: TyKind::Ref(Mutability::Not, deref_chain[0].value.clone()).intern(&Interner), | 525 | value: TyKind::Ref(Mutability::Not, static_lifetime(), deref_chain[0].value.clone()) |
526 | .intern(&Interner), | ||
457 | }; | 527 | }; |
458 | if iterate_method_candidates_by_receiver( | 528 | if iterate_method_candidates_by_receiver( |
459 | &refed, | 529 | &refed, |
@@ -470,7 +540,8 @@ fn iterate_method_candidates_with_autoref( | |||
470 | } | 540 | } |
471 | let ref_muted = Canonical { | 541 | let ref_muted = Canonical { |
472 | binders: deref_chain[0].binders.clone(), | 542 | binders: deref_chain[0].binders.clone(), |
473 | value: TyKind::Ref(Mutability::Mut, deref_chain[0].value.clone()).intern(&Interner), | 543 | value: TyKind::Ref(Mutability::Mut, static_lifetime(), deref_chain[0].value.clone()) |
544 | .intern(&Interner), | ||
474 | }; | 545 | }; |
475 | if iterate_method_candidates_by_receiver( | 546 | if iterate_method_candidates_by_receiver( |
476 | &ref_muted, | 547 | &ref_muted, |
@@ -592,6 +663,7 @@ fn iterate_trait_method_candidates( | |||
592 | } | 663 | } |
593 | } | 664 | } |
594 | known_implemented = true; | 665 | known_implemented = true; |
666 | // FIXME: we shouldn't be ignoring the binders here | ||
595 | if callback(&self_ty.value, *item) { | 667 | if callback(&self_ty.value, *item) { |
596 | return true; | 668 | return true; |
597 | } | 669 | } |
@@ -609,7 +681,7 @@ fn iterate_inherent_methods( | |||
609 | visible_from_module: Option<ModuleId>, | 681 | visible_from_module: Option<ModuleId>, |
610 | callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool, | 682 | callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool, |
611 | ) -> bool { | 683 | ) -> bool { |
612 | let def_crates = match self_ty.value.def_crates(db, krate) { | 684 | let def_crates = match def_crates(db, &self_ty.value, krate) { |
613 | Some(k) => k, | 685 | Some(k) => k, |
614 | None => return false, | 686 | None => return false, |
615 | }; | 687 | }; |
@@ -709,10 +781,11 @@ pub(crate) fn inherent_impl_substs( | |||
709 | ) -> Option<Substitution> { | 781 | ) -> Option<Substitution> { |
710 | // we create a var for each type parameter of the impl; we need to keep in | 782 | // we create a var for each type parameter of the impl; we need to keep in |
711 | // mind here that `self_ty` might have vars of its own | 783 | // mind here that `self_ty` might have vars of its own |
784 | let self_ty_vars = self_ty.binders.len(&Interner); | ||
712 | let vars = TyBuilder::subst_for_def(db, impl_id) | 785 | let vars = TyBuilder::subst_for_def(db, impl_id) |
713 | .fill_with_bound_vars(DebruijnIndex::INNERMOST, self_ty.binders.len(&Interner)) | 786 | .fill_with_bound_vars(DebruijnIndex::INNERMOST, self_ty_vars) |
714 | .build(); | 787 | .build(); |
715 | let self_ty_with_vars = db.impl_self_ty(impl_id).subst(&vars); | 788 | let self_ty_with_vars = db.impl_self_ty(impl_id).substitute(&Interner, &vars); |
716 | let mut kinds = self_ty.binders.interned().to_vec(); | 789 | let mut kinds = self_ty.binders.interned().to_vec(); |
717 | kinds.extend( | 790 | kinds.extend( |
718 | iter::repeat(chalk_ir::WithKind::new( | 791 | iter::repeat(chalk_ir::WithKind::new( |
@@ -725,33 +798,27 @@ pub(crate) fn inherent_impl_substs( | |||
725 | binders: CanonicalVarKinds::from_iter(&Interner, kinds), | 798 | binders: CanonicalVarKinds::from_iter(&Interner, kinds), |
726 | value: (self_ty_with_vars, self_ty.value.clone()), | 799 | value: (self_ty_with_vars, self_ty.value.clone()), |
727 | }; | 800 | }; |
728 | let substs = super::infer::unify(&tys); | 801 | let substs = super::infer::unify(&tys)?; |
729 | // We only want the substs for the vars we added, not the ones from self_ty. | 802 | // We only want the substs for the vars we added, not the ones from self_ty. |
730 | // Also, if any of the vars we added are still in there, we replace them by | 803 | // Also, if any of the vars we added are still in there, we replace them by |
731 | // Unknown. I think this can only really happen if self_ty contained | 804 | // Unknown. I think this can only really happen if self_ty contained |
732 | // Unknown, and in that case we want the result to contain Unknown in those | 805 | // Unknown, and in that case we want the result to contain Unknown in those |
733 | // places again. | 806 | // places again. |
734 | substs | 807 | let suffix = |
735 | .map(|s| fallback_bound_vars(s.suffix(vars.len(&Interner)), self_ty.binders.len(&Interner))) | 808 | Substitution::from_iter(&Interner, substs.iter(&Interner).cloned().skip(self_ty_vars)); |
809 | Some(fallback_bound_vars(suffix, self_ty_vars)) | ||
736 | } | 810 | } |
737 | 811 | ||
738 | /// This replaces any 'free' Bound vars in `s` (i.e. those with indices past | 812 | /// This replaces any 'free' Bound vars in `s` (i.e. those with indices past |
739 | /// num_vars_to_keep) by `TyKind::Unknown`. | 813 | /// num_vars_to_keep) by `TyKind::Unknown`. |
740 | fn fallback_bound_vars(s: Substitution, num_vars_to_keep: usize) -> Substitution { | 814 | fn fallback_bound_vars(s: Substitution, num_vars_to_keep: usize) -> Substitution { |
741 | s.fold_binders( | 815 | crate::fold_free_vars(s, |bound, binders| { |
742 | &mut |ty, binders| { | 816 | if bound.index >= num_vars_to_keep && bound.debruijn == DebruijnIndex::INNERMOST { |
743 | if let TyKind::BoundVar(bound) = ty.kind(&Interner) { | 817 | TyKind::Error.intern(&Interner) |
744 | if bound.index >= num_vars_to_keep && bound.debruijn >= binders { | 818 | } else { |
745 | TyKind::Unknown.intern(&Interner) | 819 | bound.shifted_in_from(binders).to_ty(&Interner) |
746 | } else { | 820 | } |
747 | ty | 821 | }) |
748 | } | ||
749 | } else { | ||
750 | ty | ||
751 | } | ||
752 | }, | ||
753 | DebruijnIndex::INNERMOST, | ||
754 | ) | ||
755 | } | 822 | } |
756 | 823 | ||
757 | fn transform_receiver_ty( | 824 | fn transform_receiver_ty( |
@@ -774,7 +841,7 @@ fn transform_receiver_ty( | |||
774 | AssocContainerId::ModuleId(_) => unreachable!(), | 841 | AssocContainerId::ModuleId(_) => unreachable!(), |
775 | }; | 842 | }; |
776 | let sig = db.callable_item_signature(function_id.into()); | 843 | let sig = db.callable_item_signature(function_id.into()); |
777 | Some(sig.value.params()[0].clone().subst_bound_vars(&substs)) | 844 | Some(sig.map(|s| s.params()[0].clone()).substitute(&Interner, &substs)) |
778 | } | 845 | } |
779 | 846 | ||
780 | pub fn implements_trait( | 847 | pub fn implements_trait( |
@@ -800,7 +867,7 @@ pub fn implements_trait_unique( | |||
800 | let goal = generic_implements_goal(db, env, trait_, ty.clone()); | 867 | let goal = generic_implements_goal(db, env, trait_, ty.clone()); |
801 | let solution = db.trait_solve(krate, goal); | 868 | let solution = db.trait_solve(krate, goal); |
802 | 869 | ||
803 | matches!(solution, Some(crate::traits::Solution::Unique(_))) | 870 | matches!(solution, Some(crate::Solution::Unique(_))) |
804 | } | 871 | } |
805 | 872 | ||
806 | /// This creates Substs for a trait with the given Self type and type variables | 873 | /// This creates Substs for a trait with the given Self type and type variables |
@@ -826,7 +893,7 @@ fn generic_implements_goal( | |||
826 | let obligation = trait_ref.cast(&Interner); | 893 | let obligation = trait_ref.cast(&Interner); |
827 | Canonical { | 894 | Canonical { |
828 | binders: CanonicalVarKinds::from_iter(&Interner, kinds), | 895 | binders: CanonicalVarKinds::from_iter(&Interner, kinds), |
829 | value: InEnvironment::new(env.env.clone(), obligation), | 896 | value: InEnvironment::new(&env.env, obligation), |
830 | } | 897 | } |
831 | } | 898 | } |
832 | 899 | ||
@@ -837,7 +904,9 @@ fn autoderef_method_receiver( | |||
837 | ) -> Vec<Canonical<Ty>> { | 904 | ) -> Vec<Canonical<Ty>> { |
838 | let mut deref_chain: Vec<_> = autoderef::autoderef(db, Some(krate), ty).collect(); | 905 | let mut deref_chain: Vec<_> = autoderef::autoderef(db, Some(krate), ty).collect(); |
839 | // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!) | 906 | // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!) |
840 | if let Some(TyKind::Array(parameters)) = deref_chain.last().map(|ty| ty.value.kind(&Interner)) { | 907 | if let Some(TyKind::Array(parameters, _)) = |
908 | deref_chain.last().map(|ty| ty.value.kind(&Interner)) | ||
909 | { | ||
841 | let kinds = deref_chain.last().unwrap().binders.clone(); | 910 | let kinds = deref_chain.last().unwrap().binders.clone(); |
842 | let unsized_ty = TyKind::Slice(parameters.clone()).intern(&Interner); | 911 | let unsized_ty = TyKind::Slice(parameters.clone()).intern(&Interner); |
843 | deref_chain.push(Canonical { value: unsized_ty, binders: kinds }) | 912 | deref_chain.push(Canonical { value: unsized_ty, binders: kinds }) |
diff --git a/crates/hir_ty/src/op.rs b/crates/hir_ty/src/op.rs index 90dd31a35..0222de2bc 100644 --- a/crates/hir_ty/src/op.rs +++ b/crates/hir_ty/src/op.rs | |||
@@ -9,22 +9,56 @@ pub(super) fn binary_op_return_ty(op: BinaryOp, lhs_ty: Ty, rhs_ty: Ty) -> Ty { | |||
9 | BinaryOp::LogicOp(_) | BinaryOp::CmpOp(_) => TyKind::Scalar(Scalar::Bool).intern(&Interner), | 9 | BinaryOp::LogicOp(_) | BinaryOp::CmpOp(_) => TyKind::Scalar(Scalar::Bool).intern(&Interner), |
10 | BinaryOp::Assignment { .. } => TyBuilder::unit(), | 10 | BinaryOp::Assignment { .. } => TyBuilder::unit(), |
11 | BinaryOp::ArithOp(ArithOp::Shl) | BinaryOp::ArithOp(ArithOp::Shr) => { | 11 | BinaryOp::ArithOp(ArithOp::Shl) | BinaryOp::ArithOp(ArithOp::Shr) => { |
12 | match lhs_ty.kind(&Interner) { | 12 | // all integer combinations are valid here |
13 | if matches!( | ||
14 | lhs_ty.kind(&Interner), | ||
13 | TyKind::Scalar(Scalar::Int(_)) | 15 | TyKind::Scalar(Scalar::Int(_)) |
14 | | TyKind::Scalar(Scalar::Uint(_)) | 16 | | TyKind::Scalar(Scalar::Uint(_)) |
15 | | TyKind::Scalar(Scalar::Float(_)) => lhs_ty, | 17 | | TyKind::InferenceVar(_, TyVariableKind::Integer) |
16 | TyKind::InferenceVar(_, TyVariableKind::Integer) | 18 | ) && matches!( |
17 | | TyKind::InferenceVar(_, TyVariableKind::Float) => lhs_ty, | 19 | rhs_ty.kind(&Interner), |
18 | _ => TyKind::Unknown.intern(&Interner), | 20 | TyKind::Scalar(Scalar::Int(_)) |
21 | | TyKind::Scalar(Scalar::Uint(_)) | ||
22 | | TyKind::InferenceVar(_, TyVariableKind::Integer) | ||
23 | ) { | ||
24 | lhs_ty | ||
25 | } else { | ||
26 | TyKind::Error.intern(&Interner) | ||
19 | } | 27 | } |
20 | } | 28 | } |
21 | BinaryOp::ArithOp(_) => match rhs_ty.kind(&Interner) { | 29 | BinaryOp::ArithOp(_) => match (lhs_ty.kind(&Interner), rhs_ty.kind(&Interner)) { |
22 | TyKind::Scalar(Scalar::Int(_)) | 30 | // (int, int) | (uint, uint) | (float, float) |
23 | | TyKind::Scalar(Scalar::Uint(_)) | 31 | (TyKind::Scalar(Scalar::Int(_)), TyKind::Scalar(Scalar::Int(_))) |
24 | | TyKind::Scalar(Scalar::Float(_)) => rhs_ty, | 32 | | (TyKind::Scalar(Scalar::Uint(_)), TyKind::Scalar(Scalar::Uint(_))) |
25 | TyKind::InferenceVar(_, TyVariableKind::Integer) | 33 | | (TyKind::Scalar(Scalar::Float(_)), TyKind::Scalar(Scalar::Float(_))) => rhs_ty, |
26 | | TyKind::InferenceVar(_, TyVariableKind::Float) => rhs_ty, | 34 | // ({int}, int) | ({int}, uint) |
27 | _ => TyKind::Unknown.intern(&Interner), | 35 | (TyKind::InferenceVar(_, TyVariableKind::Integer), TyKind::Scalar(Scalar::Int(_))) |
36 | | (TyKind::InferenceVar(_, TyVariableKind::Integer), TyKind::Scalar(Scalar::Uint(_))) => { | ||
37 | rhs_ty | ||
38 | } | ||
39 | // (int, {int}) | (uint, {int}) | ||
40 | (TyKind::Scalar(Scalar::Int(_)), TyKind::InferenceVar(_, TyVariableKind::Integer)) | ||
41 | | (TyKind::Scalar(Scalar::Uint(_)), TyKind::InferenceVar(_, TyVariableKind::Integer)) => { | ||
42 | lhs_ty | ||
43 | } | ||
44 | // ({float} | float) | ||
45 | (TyKind::InferenceVar(_, TyVariableKind::Float), TyKind::Scalar(Scalar::Float(_))) => { | ||
46 | rhs_ty | ||
47 | } | ||
48 | // (float, {float}) | ||
49 | (TyKind::Scalar(Scalar::Float(_)), TyKind::InferenceVar(_, TyVariableKind::Float)) => { | ||
50 | lhs_ty | ||
51 | } | ||
52 | // ({int}, {int}) | ({float}, {float}) | ||
53 | ( | ||
54 | TyKind::InferenceVar(_, TyVariableKind::Integer), | ||
55 | TyKind::InferenceVar(_, TyVariableKind::Integer), | ||
56 | ) | ||
57 | | ( | ||
58 | TyKind::InferenceVar(_, TyVariableKind::Float), | ||
59 | TyKind::InferenceVar(_, TyVariableKind::Float), | ||
60 | ) => rhs_ty, | ||
61 | _ => TyKind::Error.intern(&Interner), | ||
28 | }, | 62 | }, |
29 | } | 63 | } |
30 | } | 64 | } |
@@ -37,10 +71,10 @@ pub(super) fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty { | |||
37 | TyKind::Scalar(_) | TyKind::Str => lhs_ty, | 71 | TyKind::Scalar(_) | TyKind::Str => lhs_ty, |
38 | TyKind::InferenceVar(_, TyVariableKind::Integer) | 72 | TyKind::InferenceVar(_, TyVariableKind::Integer) |
39 | | TyKind::InferenceVar(_, TyVariableKind::Float) => lhs_ty, | 73 | | TyKind::InferenceVar(_, TyVariableKind::Float) => lhs_ty, |
40 | _ => TyKind::Unknown.intern(&Interner), | 74 | _ => TyKind::Error.intern(&Interner), |
41 | }, | 75 | }, |
42 | BinaryOp::ArithOp(ArithOp::Shl) | BinaryOp::ArithOp(ArithOp::Shr) => { | 76 | BinaryOp::ArithOp(ArithOp::Shl) | BinaryOp::ArithOp(ArithOp::Shr) => { |
43 | TyKind::Unknown.intern(&Interner) | 77 | TyKind::Error.intern(&Interner) |
44 | } | 78 | } |
45 | BinaryOp::CmpOp(CmpOp::Ord { .. }) | 79 | BinaryOp::CmpOp(CmpOp::Ord { .. }) |
46 | | BinaryOp::Assignment { op: Some(_) } | 80 | | BinaryOp::Assignment { op: Some(_) } |
@@ -50,7 +84,7 @@ pub(super) fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty { | |||
50 | | TyKind::Scalar(Scalar::Float(_)) => lhs_ty, | 84 | | TyKind::Scalar(Scalar::Float(_)) => lhs_ty, |
51 | TyKind::InferenceVar(_, TyVariableKind::Integer) | 85 | TyKind::InferenceVar(_, TyVariableKind::Integer) |
52 | | TyKind::InferenceVar(_, TyVariableKind::Float) => lhs_ty, | 86 | | TyKind::InferenceVar(_, TyVariableKind::Float) => lhs_ty, |
53 | _ => TyKind::Unknown.intern(&Interner), | 87 | _ => TyKind::Error.intern(&Interner), |
54 | }, | 88 | }, |
55 | } | 89 | } |
56 | } | 90 | } |
diff --git a/crates/hir_ty/src/primitive.rs b/crates/hir_ty/src/primitive.rs index 2449addfb..d7f48c69a 100644 --- a/crates/hir_ty/src/primitive.rs +++ b/crates/hir_ty/src/primitive.rs | |||
@@ -1,7 +1,4 @@ | |||
1 | //! Defines primitive types, which have a couple of peculiarities: | 1 | //! A few helper functions for dealing with primitives. |
2 | //! | ||
3 | //! * during type inference, they can be uncertain (ie, `let x = 92;`) | ||
4 | //! * they don't belong to any particular crate. | ||
5 | 2 | ||
6 | pub use chalk_ir::{FloatTy, IntTy, UintTy}; | 3 | pub use chalk_ir::{FloatTy, IntTy, UintTy}; |
7 | pub use hir_def::builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint}; | 4 | pub use hir_def::builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint}; |
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs index 86e3d8b86..6588aa46c 100644 --- a/crates/hir_ty/src/tests/macros.rs +++ b/crates/hir_ty/src/tests/macros.rs | |||
@@ -1065,12 +1065,211 @@ fn macro_in_arm() { | |||
1065 | } | 1065 | } |
1066 | "#, | 1066 | "#, |
1067 | expect![[r#" | 1067 | expect![[r#" |
1068 | !0..2 '()': () | ||
1068 | 51..110 '{ ... }; }': () | 1069 | 51..110 '{ ... }; }': () |
1069 | 61..62 'x': u32 | 1070 | 61..62 'x': u32 |
1070 | 65..107 'match ... }': u32 | 1071 | 65..107 'match ... }': u32 |
1071 | 71..73 '()': () | 1072 | 71..73 '()': () |
1072 | 84..91 'unit!()': () | ||
1073 | 95..100 '92u32': u32 | 1073 | 95..100 '92u32': u32 |
1074 | "#]], | 1074 | "#]], |
1075 | ); | 1075 | ); |
1076 | } | 1076 | } |
1077 | |||
1078 | #[test] | ||
1079 | fn macro_in_type_alias_position() { | ||
1080 | check_infer( | ||
1081 | r#" | ||
1082 | macro_rules! U32 { | ||
1083 | () => { u32 }; | ||
1084 | } | ||
1085 | |||
1086 | trait Foo { | ||
1087 | type Ty; | ||
1088 | } | ||
1089 | |||
1090 | impl<T> Foo for T { | ||
1091 | type Ty = U32!(); | ||
1092 | } | ||
1093 | |||
1094 | type TayTo = U32!(); | ||
1095 | |||
1096 | fn testy() { | ||
1097 | let a: <() as Foo>::Ty; | ||
1098 | let b: TayTo; | ||
1099 | } | ||
1100 | "#, | ||
1101 | expect![[r#" | ||
1102 | 147..196 '{ ...yTo; }': () | ||
1103 | 157..158 'a': u32 | ||
1104 | 185..186 'b': u32 | ||
1105 | "#]], | ||
1106 | ); | ||
1107 | } | ||
1108 | |||
1109 | #[test] | ||
1110 | fn nested_macro_in_type_alias_position() { | ||
1111 | check_infer( | ||
1112 | r#" | ||
1113 | macro_rules! U32Inner2 { | ||
1114 | () => { u32 }; | ||
1115 | } | ||
1116 | |||
1117 | macro_rules! U32Inner1 { | ||
1118 | () => { U32Inner2!() }; | ||
1119 | } | ||
1120 | |||
1121 | macro_rules! U32 { | ||
1122 | () => { U32Inner1!() }; | ||
1123 | } | ||
1124 | |||
1125 | trait Foo { | ||
1126 | type Ty; | ||
1127 | } | ||
1128 | |||
1129 | impl<T> Foo for T { | ||
1130 | type Ty = U32!(); | ||
1131 | } | ||
1132 | |||
1133 | type TayTo = U32!(); | ||
1134 | |||
1135 | fn testy() { | ||
1136 | let a: <() as Foo>::Ty; | ||
1137 | let b: TayTo; | ||
1138 | } | ||
1139 | "#, | ||
1140 | expect![[r#" | ||
1141 | 259..308 '{ ...yTo; }': () | ||
1142 | 269..270 'a': u32 | ||
1143 | 297..298 'b': u32 | ||
1144 | "#]], | ||
1145 | ); | ||
1146 | } | ||
1147 | |||
1148 | #[test] | ||
1149 | fn macros_in_type_alias_position_generics() { | ||
1150 | check_infer( | ||
1151 | r#" | ||
1152 | struct Foo<A, B>(A, B); | ||
1153 | |||
1154 | macro_rules! U32 { | ||
1155 | () => { u32 }; | ||
1156 | } | ||
1157 | |||
1158 | macro_rules! Bar { | ||
1159 | () => { Foo<U32!(), U32!()> }; | ||
1160 | } | ||
1161 | |||
1162 | trait Moo { | ||
1163 | type Ty; | ||
1164 | } | ||
1165 | |||
1166 | impl<T> Moo for T { | ||
1167 | type Ty = Bar!(); | ||
1168 | } | ||
1169 | |||
1170 | type TayTo = Bar!(); | ||
1171 | |||
1172 | fn main() { | ||
1173 | let a: <() as Moo>::Ty; | ||
1174 | let b: TayTo; | ||
1175 | } | ||
1176 | "#, | ||
1177 | expect![[r#" | ||
1178 | 228..277 '{ ...yTo; }': () | ||
1179 | 238..239 'a': Foo<u32, u32> | ||
1180 | 266..267 'b': Foo<u32, u32> | ||
1181 | "#]], | ||
1182 | ); | ||
1183 | } | ||
1184 | |||
1185 | #[test] | ||
1186 | fn macros_in_type_position() { | ||
1187 | check_infer( | ||
1188 | r#" | ||
1189 | struct Foo<A, B>(A, B); | ||
1190 | |||
1191 | macro_rules! U32 { | ||
1192 | () => { u32 }; | ||
1193 | } | ||
1194 | |||
1195 | macro_rules! Bar { | ||
1196 | () => { Foo<U32!(), U32!()> }; | ||
1197 | } | ||
1198 | |||
1199 | fn main() { | ||
1200 | let a: Bar!(); | ||
1201 | } | ||
1202 | "#, | ||
1203 | expect![[r#" | ||
1204 | 133..155 '{ ...!(); }': () | ||
1205 | 143..144 'a': Foo<u32, u32> | ||
1206 | "#]], | ||
1207 | ); | ||
1208 | } | ||
1209 | |||
1210 | #[test] | ||
1211 | fn macros_in_type_generics() { | ||
1212 | check_infer( | ||
1213 | r#" | ||
1214 | struct Foo<A, B>(A, B); | ||
1215 | |||
1216 | macro_rules! U32 { | ||
1217 | () => { u32 }; | ||
1218 | } | ||
1219 | |||
1220 | macro_rules! Bar { | ||
1221 | () => { Foo<U32!(), U32!()> }; | ||
1222 | } | ||
1223 | |||
1224 | trait Moo { | ||
1225 | type Ty; | ||
1226 | } | ||
1227 | |||
1228 | impl<T> Moo for T { | ||
1229 | type Ty = Foo<Bar!(), Bar!()>; | ||
1230 | } | ||
1231 | |||
1232 | type TayTo = Foo<Bar!(), U32!()>; | ||
1233 | |||
1234 | fn main() { | ||
1235 | let a: <() as Moo>::Ty; | ||
1236 | let b: TayTo; | ||
1237 | } | ||
1238 | "#, | ||
1239 | expect![[r#" | ||
1240 | 254..303 '{ ...yTo; }': () | ||
1241 | 264..265 'a': Foo<Foo<u32, u32>, Foo<u32, u32>> | ||
1242 | 292..293 'b': Foo<Foo<u32, u32>, u32> | ||
1243 | "#]], | ||
1244 | ); | ||
1245 | } | ||
1246 | |||
1247 | #[test] | ||
1248 | fn infinitely_recursive_macro_type() { | ||
1249 | check_infer( | ||
1250 | r#" | ||
1251 | struct Bar<T, X>(T, X); | ||
1252 | |||
1253 | macro_rules! Foo { | ||
1254 | () => { Foo!() } | ||
1255 | } | ||
1256 | |||
1257 | macro_rules! U32 { | ||
1258 | () => { u32 } | ||
1259 | } | ||
1260 | |||
1261 | type A = Foo!(); | ||
1262 | type B = Bar<Foo!(), U32!()>; | ||
1263 | |||
1264 | fn main() { | ||
1265 | let a: A; | ||
1266 | let b: B; | ||
1267 | } | ||
1268 | "#, | ||
1269 | expect![[r#" | ||
1270 | 166..197 '{ ...: B; }': () | ||
1271 | 176..177 'a': {unknown} | ||
1272 | 190..191 'b': Bar<{unknown}, u32> | ||
1273 | "#]], | ||
1274 | ); | ||
1275 | } | ||
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs index 61f18b0d2..a4c132bc5 100644 --- a/crates/hir_ty/src/tests/method_resolution.rs +++ b/crates/hir_ty/src/tests/method_resolution.rs | |||
@@ -1292,3 +1292,60 @@ mod b { | |||
1292 | "#]], | 1292 | "#]], |
1293 | ) | 1293 | ) |
1294 | } | 1294 | } |
1295 | |||
1296 | #[test] | ||
1297 | fn trait_impl_in_unnamed_const() { | ||
1298 | check_types( | ||
1299 | r#" | ||
1300 | struct S; | ||
1301 | |||
1302 | trait Tr { | ||
1303 | fn method(&self) -> u16; | ||
1304 | } | ||
1305 | |||
1306 | const _: () = { | ||
1307 | impl Tr for S {} | ||
1308 | }; | ||
1309 | |||
1310 | fn f() { | ||
1311 | S.method(); | ||
1312 | //^^^^^^^^^^ u16 | ||
1313 | } | ||
1314 | "#, | ||
1315 | ); | ||
1316 | } | ||
1317 | |||
1318 | #[test] | ||
1319 | fn inherent_impl_in_unnamed_const() { | ||
1320 | check_types( | ||
1321 | r#" | ||
1322 | struct S; | ||
1323 | |||
1324 | const _: () = { | ||
1325 | impl S { | ||
1326 | fn method(&self) -> u16 { 0 } | ||
1327 | |||
1328 | pub(super) fn super_method(&self) -> u16 { 0 } | ||
1329 | |||
1330 | pub(crate) fn crate_method(&self) -> u16 { 0 } | ||
1331 | |||
1332 | pub fn pub_method(&self) -> u16 { 0 } | ||
1333 | } | ||
1334 | }; | ||
1335 | |||
1336 | fn f() { | ||
1337 | S.method(); | ||
1338 | //^^^^^^^^^^ u16 | ||
1339 | |||
1340 | S.super_method(); | ||
1341 | //^^^^^^^^^^^^^^^^ u16 | ||
1342 | |||
1343 | S.crate_method(); | ||
1344 | //^^^^^^^^^^^^^^^^ u16 | ||
1345 | |||
1346 | S.pub_method(); | ||
1347 | //^^^^^^^^^^^^^^ u16 | ||
1348 | } | ||
1349 | "#, | ||
1350 | ); | ||
1351 | } | ||
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs index 85a28e76b..f514b3efe 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}; | 3 | use super::{check_infer, check_infer_with_mismatches, check_types}; |
4 | 4 | ||
5 | #[test] | 5 | #[test] |
6 | fn infer_pattern() { | 6 | fn infer_pattern() { |
@@ -825,3 +825,29 @@ fn foo(foo: Foo) { | |||
825 | "#]], | 825 | "#]], |
826 | ); | 826 | ); |
827 | } | 827 | } |
828 | |||
829 | #[test] | ||
830 | fn macro_pat() { | ||
831 | check_types( | ||
832 | r#" | ||
833 | macro_rules! pat { | ||
834 | ($name:ident) => { Enum::Variant1($name) } | ||
835 | } | ||
836 | |||
837 | enum Enum { | ||
838 | Variant1(u8), | ||
839 | Variant2, | ||
840 | } | ||
841 | |||
842 | fn f(e: Enum) { | ||
843 | match e { | ||
844 | pat!(bind) => { | ||
845 | bind; | ||
846 | //^^^^ u8 | ||
847 | } | ||
848 | Enum::Variant2 => {} | ||
849 | } | ||
850 | } | ||
851 | "#, | ||
852 | ) | ||
853 | } | ||
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs index b69f86050..9cd9f473d 100644 --- a/crates/hir_ty/src/tests/regression.rs +++ b/crates/hir_ty/src/tests/regression.rs | |||
@@ -974,3 +974,41 @@ fn param_overrides_fn() { | |||
974 | "#, | 974 | "#, |
975 | ) | 975 | ) |
976 | } | 976 | } |
977 | |||
978 | #[test] | ||
979 | fn lifetime_from_chalk_during_deref() { | ||
980 | check_types( | ||
981 | r#" | ||
982 | #[lang = "deref"] | ||
983 | pub trait Deref { | ||
984 | type Target; | ||
985 | } | ||
986 | |||
987 | struct Box<T: ?Sized> {} | ||
988 | impl<T> Deref for Box<T> { | ||
989 | type Target = T; | ||
990 | |||
991 | fn deref(&self) -> &Self::Target { | ||
992 | loop {} | ||
993 | } | ||
994 | } | ||
995 | |||
996 | trait Iterator { | ||
997 | type Item; | ||
998 | } | ||
999 | |||
1000 | pub struct Iter<'a, T: 'a> { | ||
1001 | inner: Box<dyn IterTrait<'a, T, Item = &'a T> + 'a>, | ||
1002 | } | ||
1003 | |||
1004 | trait IterTrait<'a, T: 'a>: Iterator<Item = &'a T> { | ||
1005 | fn clone_box(&self); | ||
1006 | } | ||
1007 | |||
1008 | fn clone_iter<T>(s: Iter<T>) { | ||
1009 | s.inner.clone_box(); | ||
1010 | //^^^^^^^^^^^^^^^^^^^ () | ||
1011 | } | ||
1012 | "#, | ||
1013 | ) | ||
1014 | } | ||
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs index 361cd6302..5948d0bc2 100644 --- a/crates/hir_ty/src/tests/simple.rs +++ b/crates/hir_ty/src/tests/simple.rs | |||
@@ -1765,6 +1765,24 @@ fn main() { | |||
1765 | } | 1765 | } |
1766 | 1766 | ||
1767 | #[test] | 1767 | #[test] |
1768 | fn shadowing_primitive_with_inner_items() { | ||
1769 | check_types( | ||
1770 | r#" | ||
1771 | struct i32; | ||
1772 | struct Foo; | ||
1773 | |||
1774 | impl i32 { fn foo(&self) -> Foo { Foo } } | ||
1775 | |||
1776 | fn main() { | ||
1777 | fn inner() {} | ||
1778 | let x: i32 = i32; | ||
1779 | x.foo(); | ||
1780 | //^ Foo | ||
1781 | }"#, | ||
1782 | ); | ||
1783 | } | ||
1784 | |||
1785 | #[test] | ||
1768 | fn not_shadowing_primitive_by_module() { | 1786 | fn not_shadowing_primitive_by_module() { |
1769 | check_types( | 1787 | check_types( |
1770 | r#" | 1788 | r#" |
@@ -2564,3 +2582,36 @@ fn f() { | |||
2564 | "#, | 2582 | "#, |
2565 | ) | 2583 | ) |
2566 | } | 2584 | } |
2585 | |||
2586 | #[test] | ||
2587 | fn infer_type_alias_variant() { | ||
2588 | check_infer( | ||
2589 | r#" | ||
2590 | type Qux = Foo; | ||
2591 | enum Foo { | ||
2592 | Bar(i32), | ||
2593 | Baz { baz: f32 } | ||
2594 | } | ||
2595 | |||
2596 | fn f() { | ||
2597 | match Foo::Bar(3) { | ||
2598 | Qux::Bar(bar) => (), | ||
2599 | Qux::Baz { baz } => (), | ||
2600 | } | ||
2601 | } | ||
2602 | "#, | ||
2603 | expect![[r#" | ||
2604 | 72..166 '{ ... } }': () | ||
2605 | 78..164 'match ... }': () | ||
2606 | 84..92 'Foo::Bar': Bar(i32) -> Foo | ||
2607 | 84..95 'Foo::Bar(3)': Foo | ||
2608 | 93..94 '3': i32 | ||
2609 | 106..119 'Qux::Bar(bar)': Foo | ||
2610 | 115..118 'bar': i32 | ||
2611 | 123..125 '()': () | ||
2612 | 135..151 'Qux::B... baz }': Foo | ||
2613 | 146..149 'baz': f32 | ||
2614 | 155..157 '()': () | ||
2615 | "#]], | ||
2616 | ) | ||
2617 | } | ||
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index 65b71fdfa..ffc7c8ef4 100644 --- a/crates/hir_ty/src/tests/traits.rs +++ b/crates/hir_ty/src/tests/traits.rs | |||
@@ -263,15 +263,14 @@ mod ops { | |||
263 | fn infer_from_bound_1() { | 263 | fn infer_from_bound_1() { |
264 | check_infer( | 264 | check_infer( |
265 | r#" | 265 | r#" |
266 | trait Trait<T> {} | 266 | trait Trait<T> {} |
267 | struct S<T>(T); | 267 | struct S<T>(T); |
268 | impl<U> Trait<U> for S<U> {} | 268 | impl<U> Trait<U> for S<U> {} |
269 | fn foo<T: Trait<u32>>(t: T) {} | 269 | fn foo<T: Trait<u32>>(t: T) {} |
270 | fn test() { | 270 | fn test() { |
271 | let s = S(unknown); | 271 | let s = S(unknown); |
272 | foo(s); | 272 | foo(s); |
273 | } | 273 | }"#, |
274 | "#, | ||
275 | expect![[r#" | 274 | expect![[r#" |
276 | 85..86 't': T | 275 | 85..86 't': T |
277 | 91..93 '{}': () | 276 | 91..93 '{}': () |
@@ -291,15 +290,14 @@ fn infer_from_bound_1() { | |||
291 | fn infer_from_bound_2() { | 290 | fn infer_from_bound_2() { |
292 | check_infer( | 291 | check_infer( |
293 | r#" | 292 | r#" |
294 | trait Trait<T> {} | 293 | trait Trait<T> {} |
295 | struct S<T>(T); | 294 | struct S<T>(T); |
296 | impl<U> Trait<U> for S<U> {} | 295 | impl<U> Trait<U> for S<U> {} |
297 | fn foo<U, T: Trait<U>>(t: T) -> U {} | 296 | fn foo<U, T: Trait<U>>(t: T) -> U {} |
298 | fn test() { | 297 | fn test() { |
299 | let s = S(unknown); | 298 | let s = S(unknown); |
300 | let x: u32 = foo(s); | 299 | let x: u32 = foo(s); |
301 | } | 300 | }"#, |
302 | "#, | ||
303 | expect![[r#" | 301 | expect![[r#" |
304 | 86..87 't': T | 302 | 86..87 't': T |
305 | 97..99 '{}': () | 303 | 97..99 '{}': () |
@@ -321,13 +319,12 @@ fn trait_default_method_self_bound_implements_trait() { | |||
321 | cov_mark::check!(trait_self_implements_self); | 319 | cov_mark::check!(trait_self_implements_self); |
322 | check_infer( | 320 | check_infer( |
323 | r#" | 321 | r#" |
324 | trait Trait { | 322 | trait Trait { |
325 | fn foo(&self) -> i64; | 323 | fn foo(&self) -> i64; |
326 | fn bar(&self) -> { | 324 | fn bar(&self) -> { |
327 | let x = self.foo(); | 325 | let x = self.foo(); |
328 | } | 326 | } |
329 | } | 327 | }"#, |
330 | "#, | ||
331 | expect![[r#" | 328 | expect![[r#" |
332 | 26..30 'self': &Self | 329 | 26..30 'self': &Self |
333 | 52..56 'self': &Self | 330 | 52..56 'self': &Self |
@@ -343,15 +340,14 @@ fn trait_default_method_self_bound_implements_trait() { | |||
343 | fn trait_default_method_self_bound_implements_super_trait() { | 340 | fn trait_default_method_self_bound_implements_super_trait() { |
344 | check_infer( | 341 | check_infer( |
345 | r#" | 342 | r#" |
346 | trait SuperTrait { | 343 | trait SuperTrait { |
347 | fn foo(&self) -> i64; | 344 | fn foo(&self) -> i64; |
348 | } | 345 | } |
349 | trait Trait: SuperTrait { | 346 | trait Trait: SuperTrait { |
350 | fn bar(&self) -> { | 347 | fn bar(&self) -> { |
351 | let x = self.foo(); | 348 | let x = self.foo(); |
352 | } | 349 | } |
353 | } | 350 | }"#, |
354 | "#, | ||
355 | expect![[r#" | 351 | expect![[r#" |
356 | 31..35 'self': &Self | 352 | 31..35 'self': &Self |
357 | 85..89 'self': &Self | 353 | 85..89 'self': &Self |
@@ -367,18 +363,17 @@ fn trait_default_method_self_bound_implements_super_trait() { | |||
367 | fn infer_project_associated_type() { | 363 | fn infer_project_associated_type() { |
368 | check_infer( | 364 | check_infer( |
369 | r#" | 365 | r#" |
370 | trait Iterable { | 366 | trait Iterable { |
371 | type Item; | 367 | type Item; |
372 | } | 368 | } |
373 | struct S; | 369 | struct S; |
374 | impl Iterable for S { type Item = u32; } | 370 | impl Iterable for S { type Item = u32; } |
375 | fn test<T: Iterable>() { | 371 | fn test<T: Iterable>() { |
376 | let x: <S as Iterable>::Item = 1; | 372 | let x: <S as Iterable>::Item = 1; |
377 | let y: <T as Iterable>::Item = no_matter; | 373 | let y: <T as Iterable>::Item = no_matter; |
378 | let z: T::Item = no_matter; | 374 | let z: T::Item = no_matter; |
379 | let a: <T>::Item = no_matter; | 375 | let a: <T>::Item = no_matter; |
380 | } | 376 | }"#, |
381 | "#, | ||
382 | expect![[r#" | 377 | expect![[r#" |
383 | 108..261 '{ ...ter; }': () | 378 | 108..261 '{ ...ter; }': () |
384 | 118..119 'x': u32 | 379 | 118..119 'x': u32 |
@@ -397,20 +392,19 @@ fn infer_project_associated_type() { | |||
397 | fn infer_return_associated_type() { | 392 | fn infer_return_associated_type() { |
398 | check_infer( | 393 | check_infer( |
399 | r#" | 394 | r#" |
400 | trait Iterable { | 395 | trait Iterable { |
401 | type Item; | 396 | type Item; |
402 | } | 397 | } |
403 | struct S; | 398 | struct S; |
404 | impl Iterable for S { type Item = u32; } | 399 | impl Iterable for S { type Item = u32; } |
405 | fn foo1<T: Iterable>(t: T) -> T::Item {} | 400 | fn foo1<T: Iterable>(t: T) -> T::Item {} |
406 | fn foo2<T: Iterable>(t: T) -> <T as Iterable>::Item {} | 401 | fn foo2<T: Iterable>(t: T) -> <T as Iterable>::Item {} |
407 | fn foo3<T: Iterable>(t: T) -> <T>::Item {} | 402 | fn foo3<T: Iterable>(t: T) -> <T>::Item {} |
408 | fn test() { | 403 | fn test() { |
409 | let x = foo1(S); | 404 | let x = foo1(S); |
410 | let y = foo2(S); | 405 | let y = foo2(S); |
411 | let z = foo3(S); | 406 | let z = foo3(S); |
412 | } | 407 | }"#, |
413 | "#, | ||
414 | expect![[r#" | 408 | expect![[r#" |
415 | 106..107 't': T | 409 | 106..107 't': T |
416 | 123..125 '{}': () | 410 | 123..125 '{}': () |
@@ -439,13 +433,12 @@ fn infer_return_associated_type() { | |||
439 | fn infer_associated_type_bound() { | 433 | fn infer_associated_type_bound() { |
440 | check_infer( | 434 | check_infer( |
441 | r#" | 435 | r#" |
442 | trait Iterable { | 436 | trait Iterable { |
443 | type Item; | 437 | type Item; |
444 | } | 438 | } |
445 | fn test<T: Iterable<Item=u32>>() { | 439 | fn test<T: Iterable<Item=u32>>() { |
446 | let y: T::Item = unknown; | 440 | let y: T::Item = unknown; |
447 | } | 441 | }"#, |
448 | "#, | ||
449 | expect![[r#" | 442 | expect![[r#" |
450 | 67..100 '{ ...own; }': () | 443 | 67..100 '{ ...own; }': () |
451 | 77..78 'y': u32 | 444 | 77..78 'y': u32 |
@@ -458,9 +451,8 @@ fn infer_associated_type_bound() { | |||
458 | fn infer_const_body() { | 451 | fn infer_const_body() { |
459 | check_infer( | 452 | check_infer( |
460 | r#" | 453 | r#" |
461 | const A: u32 = 1 + 1; | 454 | const A: u32 = 1 + 1; |
462 | static B: u64 = { let x = 1; x }; | 455 | static B: u64 = { let x = 1; x };"#, |
463 | "#, | ||
464 | expect![[r#" | 456 | expect![[r#" |
465 | 15..16 '1': u32 | 457 | 15..16 '1': u32 |
466 | 15..20 '1 + 1': u32 | 458 | 15..20 '1 + 1': u32 |
@@ -477,13 +469,12 @@ fn infer_const_body() { | |||
477 | fn tuple_struct_fields() { | 469 | fn tuple_struct_fields() { |
478 | check_infer( | 470 | check_infer( |
479 | r#" | 471 | r#" |
480 | struct S(i32, u64); | 472 | struct S(i32, u64); |
481 | fn test() -> u64 { | 473 | fn test() -> u64 { |
482 | let a = S(4, 6); | 474 | let a = S(4, 6); |
483 | let b = a.0; | 475 | let b = a.0; |
484 | a.1 | 476 | a.1 |
485 | } | 477 | }"#, |
486 | "#, | ||
487 | expect![[r#" | 478 | expect![[r#" |
488 | 37..86 '{ ... a.1 }': u64 | 479 | 37..86 '{ ... a.1 }': u64 |
489 | 47..48 'a': S | 480 | 47..48 'a': S |
@@ -504,13 +495,12 @@ fn tuple_struct_fields() { | |||
504 | fn tuple_struct_with_fn() { | 495 | fn tuple_struct_with_fn() { |
505 | check_infer( | 496 | check_infer( |
506 | r#" | 497 | r#" |
507 | struct S(fn(u32) -> u64); | 498 | struct S(fn(u32) -> u64); |
508 | fn test() -> u64 { | 499 | fn test() -> u64 { |
509 | let a = S(|i| 2*i); | 500 | let a = S(|i| 2*i); |
510 | let b = a.0(4); | 501 | let b = a.0(4); |
511 | a.0(2) | 502 | a.0(2) |
512 | } | 503 | }"#, |
513 | "#, | ||
514 | expect![[r#" | 504 | expect![[r#" |
515 | 43..101 '{ ...0(2) }': u64 | 505 | 43..101 '{ ...0(2) }': u64 |
516 | 53..54 'a': S | 506 | 53..54 'a': S |
@@ -949,27 +939,26 @@ fn test<T: ApplyL>(t: T) { | |||
949 | fn argument_impl_trait() { | 939 | fn argument_impl_trait() { |
950 | check_infer_with_mismatches( | 940 | check_infer_with_mismatches( |
951 | r#" | 941 | r#" |
952 | trait Trait<T> { | 942 | trait Trait<T> { |
953 | fn foo(&self) -> T; | 943 | fn foo(&self) -> T; |
954 | fn foo2(&self) -> i64; | 944 | fn foo2(&self) -> i64; |
955 | } | 945 | } |
956 | fn bar(x: impl Trait<u16>) {} | 946 | fn bar(x: impl Trait<u16>) {} |
957 | struct S<T>(T); | 947 | struct S<T>(T); |
958 | impl<T> Trait<T> for S<T> {} | 948 | impl<T> Trait<T> for S<T> {} |
959 | 949 | ||
960 | fn test(x: impl Trait<u64>, y: &impl Trait<u32>) { | 950 | fn test(x: impl Trait<u64>, y: &impl Trait<u32>) { |
961 | x; | 951 | x; |
962 | y; | 952 | y; |
963 | let z = S(1); | 953 | let z = S(1); |
964 | bar(z); | 954 | bar(z); |
965 | x.foo(); | 955 | x.foo(); |
966 | y.foo(); | 956 | y.foo(); |
967 | z.foo(); | 957 | z.foo(); |
968 | x.foo2(); | 958 | x.foo2(); |
969 | y.foo2(); | 959 | y.foo2(); |
970 | z.foo2(); | 960 | z.foo2(); |
971 | } | 961 | }"#, |
972 | "#, | ||
973 | expect![[r#" | 962 | expect![[r#" |
974 | 29..33 'self': &Self | 963 | 29..33 'self': &Self |
975 | 54..58 'self': &Self | 964 | 54..58 'self': &Self |
@@ -1007,30 +996,29 @@ fn argument_impl_trait() { | |||
1007 | fn argument_impl_trait_type_args_1() { | 996 | fn argument_impl_trait_type_args_1() { |
1008 | check_infer_with_mismatches( | 997 | check_infer_with_mismatches( |
1009 | r#" | 998 | r#" |
1010 | trait Trait {} | 999 | trait Trait {} |
1011 | trait Foo { | 1000 | trait Foo { |
1012 | // this function has an implicit Self param, an explicit type param, | 1001 | // this function has an implicit Self param, an explicit type param, |
1013 | // and an implicit impl Trait param! | 1002 | // and an implicit impl Trait param! |
1014 | fn bar<T>(x: impl Trait) -> T { loop {} } | 1003 | fn bar<T>(x: impl Trait) -> T { loop {} } |
1015 | } | 1004 | } |
1016 | fn foo<T>(x: impl Trait) -> T { loop {} } | 1005 | fn foo<T>(x: impl Trait) -> T { loop {} } |
1017 | struct S; | 1006 | struct S; |
1018 | impl Trait for S {} | 1007 | impl Trait for S {} |
1019 | struct F; | 1008 | struct F; |
1020 | impl Foo for F {} | 1009 | impl Foo for F {} |
1021 | 1010 | ||
1022 | fn test() { | 1011 | fn test() { |
1023 | Foo::bar(S); | 1012 | Foo::bar(S); |
1024 | <F as Foo>::bar(S); | 1013 | <F as Foo>::bar(S); |
1025 | F::bar(S); | 1014 | F::bar(S); |
1026 | Foo::bar::<u32>(S); | 1015 | Foo::bar::<u32>(S); |
1027 | <F as Foo>::bar::<u32>(S); | 1016 | <F as Foo>::bar::<u32>(S); |
1028 | 1017 | ||
1029 | foo(S); | 1018 | foo(S); |
1030 | foo::<u32>(S); | 1019 | foo::<u32>(S); |
1031 | foo::<u32, i32>(S); // we should ignore the extraneous i32 | 1020 | foo::<u32, i32>(S); // we should ignore the extraneous i32 |
1032 | } | 1021 | }"#, |
1033 | "#, | ||
1034 | expect![[r#" | 1022 | expect![[r#" |
1035 | 155..156 'x': impl Trait | 1023 | 155..156 'x': impl Trait |
1036 | 175..186 '{ loop {} }': T | 1024 | 175..186 '{ loop {} }': T |
@@ -1073,21 +1061,20 @@ fn argument_impl_trait_type_args_1() { | |||
1073 | fn argument_impl_trait_type_args_2() { | 1061 | fn argument_impl_trait_type_args_2() { |
1074 | check_infer_with_mismatches( | 1062 | check_infer_with_mismatches( |
1075 | r#" | 1063 | r#" |
1076 | trait Trait {} | 1064 | trait Trait {} |
1077 | struct S; | 1065 | struct S; |
1078 | impl Trait for S {} | 1066 | impl Trait for S {} |
1079 | struct F<T>; | 1067 | struct F<T>; |
1080 | impl<T> F<T> { | 1068 | impl<T> F<T> { |
1081 | fn foo<U>(self, x: impl Trait) -> (T, U) { loop {} } | 1069 | fn foo<U>(self, x: impl Trait) -> (T, U) { loop {} } |
1082 | } | 1070 | } |
1083 | 1071 | ||
1084 | fn test() { | 1072 | fn test() { |
1085 | F.foo(S); | 1073 | F.foo(S); |
1086 | F::<u32>.foo(S); | 1074 | F::<u32>.foo(S); |
1087 | F::<u32>.foo::<i32>(S); | 1075 | F::<u32>.foo::<i32>(S); |
1088 | F::<u32>.foo::<i32, u32>(S); // extraneous argument should be ignored | 1076 | F::<u32>.foo::<i32, u32>(S); // extraneous argument should be ignored |
1089 | } | 1077 | }"#, |
1090 | "#, | ||
1091 | expect![[r#" | 1078 | expect![[r#" |
1092 | 87..91 'self': F<T> | 1079 | 87..91 'self': F<T> |
1093 | 93..94 'x': impl Trait | 1080 | 93..94 'x': impl Trait |
@@ -1115,15 +1102,14 @@ fn argument_impl_trait_type_args_2() { | |||
1115 | fn argument_impl_trait_to_fn_pointer() { | 1102 | fn argument_impl_trait_to_fn_pointer() { |
1116 | check_infer_with_mismatches( | 1103 | check_infer_with_mismatches( |
1117 | r#" | 1104 | r#" |
1118 | trait Trait {} | 1105 | trait Trait {} |
1119 | fn foo(x: impl Trait) { loop {} } | 1106 | fn foo(x: impl Trait) { loop {} } |
1120 | struct S; | 1107 | struct S; |
1121 | impl Trait for S {} | 1108 | impl Trait for S {} |
1122 | 1109 | ||
1123 | fn test() { | 1110 | fn test() { |
1124 | let f: fn(S) -> () = foo; | 1111 | let f: fn(S) -> () = foo; |
1125 | } | 1112 | }"#, |
1126 | "#, | ||
1127 | expect![[r#" | 1113 | expect![[r#" |
1128 | 22..23 'x': impl Trait | 1114 | 22..23 'x': impl Trait |
1129 | 37..48 '{ loop {} }': () | 1115 | 37..48 '{ loop {} }': () |
@@ -1140,24 +1126,23 @@ fn argument_impl_trait_to_fn_pointer() { | |||
1140 | fn impl_trait() { | 1126 | fn impl_trait() { |
1141 | check_infer( | 1127 | check_infer( |
1142 | r#" | 1128 | r#" |
1143 | trait Trait<T> { | 1129 | trait Trait<T> { |
1144 | fn foo(&self) -> T; | 1130 | fn foo(&self) -> T; |
1145 | fn foo2(&self) -> i64; | 1131 | fn foo2(&self) -> i64; |
1146 | } | 1132 | } |
1147 | fn bar() -> impl Trait<u64> {} | 1133 | fn bar() -> impl Trait<u64> {} |
1148 | 1134 | ||
1149 | fn test(x: impl Trait<u64>, y: &impl Trait<u64>) { | 1135 | fn test(x: impl Trait<u64>, y: &impl Trait<u64>) { |
1150 | x; | 1136 | x; |
1151 | y; | 1137 | y; |
1152 | let z = bar(); | 1138 | let z = bar(); |
1153 | x.foo(); | 1139 | x.foo(); |
1154 | y.foo(); | 1140 | y.foo(); |
1155 | z.foo(); | 1141 | z.foo(); |
1156 | x.foo2(); | 1142 | x.foo2(); |
1157 | y.foo2(); | 1143 | y.foo2(); |
1158 | z.foo2(); | 1144 | z.foo2(); |
1159 | } | 1145 | }"#, |
1160 | "#, | ||
1161 | expect![[r#" | 1146 | expect![[r#" |
1162 | 29..33 'self': &Self | 1147 | 29..33 'self': &Self |
1163 | 54..58 'self': &Self | 1148 | 54..58 'self': &Self |
@@ -1191,16 +1176,15 @@ fn simple_return_pos_impl_trait() { | |||
1191 | cov_mark::check!(lower_rpit); | 1176 | cov_mark::check!(lower_rpit); |
1192 | check_infer( | 1177 | check_infer( |
1193 | r#" | 1178 | r#" |
1194 | trait Trait<T> { | 1179 | trait Trait<T> { |
1195 | fn foo(&self) -> T; | 1180 | fn foo(&self) -> T; |
1196 | } | 1181 | } |
1197 | fn bar() -> impl Trait<u64> { loop {} } | 1182 | fn bar() -> impl Trait<u64> { loop {} } |
1198 | 1183 | ||
1199 | fn test() { | 1184 | fn test() { |
1200 | let a = bar(); | 1185 | let a = bar(); |
1201 | a.foo(); | 1186 | a.foo(); |
1202 | } | 1187 | }"#, |
1203 | "#, | ||
1204 | expect![[r#" | 1188 | expect![[r#" |
1205 | 29..33 'self': &Self | 1189 | 29..33 'self': &Self |
1206 | 71..82 '{ loop {} }': ! | 1190 | 71..82 '{ loop {} }': ! |
@@ -1220,25 +1204,24 @@ fn simple_return_pos_impl_trait() { | |||
1220 | fn more_return_pos_impl_trait() { | 1204 | fn more_return_pos_impl_trait() { |
1221 | check_infer( | 1205 | check_infer( |
1222 | r#" | 1206 | r#" |
1223 | trait Iterator { | 1207 | trait Iterator { |
1224 | type Item; | 1208 | type Item; |
1225 | fn next(&mut self) -> Self::Item; | 1209 | fn next(&mut self) -> Self::Item; |
1226 | } | 1210 | } |
1227 | trait Trait<T> { | 1211 | trait Trait<T> { |
1228 | fn foo(&self) -> T; | 1212 | fn foo(&self) -> T; |
1229 | } | 1213 | } |
1230 | fn bar() -> (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>) { loop {} } | 1214 | fn bar() -> (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>) { loop {} } |
1231 | fn baz<T>(t: T) -> (impl Iterator<Item = impl Trait<T>>, impl Trait<T>) { loop {} } | 1215 | fn baz<T>(t: T) -> (impl Iterator<Item = impl Trait<T>>, impl Trait<T>) { loop {} } |
1232 | 1216 | ||
1233 | fn test() { | 1217 | fn test() { |
1234 | let (a, b) = bar(); | 1218 | let (a, b) = bar(); |
1235 | a.next().foo(); | 1219 | a.next().foo(); |
1236 | b.foo(); | 1220 | b.foo(); |
1237 | let (c, d) = baz(1u128); | 1221 | let (c, d) = baz(1u128); |
1238 | c.next().foo(); | 1222 | c.next().foo(); |
1239 | d.foo(); | 1223 | d.foo(); |
1240 | } | 1224 | }"#, |
1241 | "#, | ||
1242 | expect![[r#" | 1225 | expect![[r#" |
1243 | 49..53 'self': &mut Self | 1226 | 49..53 'self': &mut Self |
1244 | 101..105 'self': &Self | 1227 | 101..105 'self': &Self |
@@ -1279,24 +1262,23 @@ fn more_return_pos_impl_trait() { | |||
1279 | fn dyn_trait() { | 1262 | fn dyn_trait() { |
1280 | check_infer( | 1263 | check_infer( |
1281 | r#" | 1264 | r#" |
1282 | trait Trait<T> { | 1265 | trait Trait<T> { |
1283 | fn foo(&self) -> T; | 1266 | fn foo(&self) -> T; |
1284 | fn foo2(&self) -> i64; | 1267 | fn foo2(&self) -> i64; |
1285 | } | 1268 | } |
1286 | fn bar() -> dyn Trait<u64> {} | 1269 | fn bar() -> dyn Trait<u64> {} |
1287 | 1270 | ||
1288 | fn test(x: dyn Trait<u64>, y: &dyn Trait<u64>) { | 1271 | fn test(x: dyn Trait<u64>, y: &dyn Trait<u64>) { |
1289 | x; | 1272 | x; |
1290 | y; | 1273 | y; |
1291 | let z = bar(); | 1274 | let z = bar(); |
1292 | x.foo(); | 1275 | x.foo(); |
1293 | y.foo(); | 1276 | y.foo(); |
1294 | z.foo(); | 1277 | z.foo(); |
1295 | x.foo2(); | 1278 | x.foo2(); |
1296 | y.foo2(); | 1279 | y.foo2(); |
1297 | z.foo2(); | 1280 | z.foo2(); |
1298 | } | 1281 | }"#, |
1299 | "#, | ||
1300 | expect![[r#" | 1282 | expect![[r#" |
1301 | 29..33 'self': &Self | 1283 | 29..33 'self': &Self |
1302 | 54..58 'self': &Self | 1284 | 54..58 'self': &Self |
@@ -1329,22 +1311,21 @@ fn dyn_trait() { | |||
1329 | fn dyn_trait_in_impl() { | 1311 | fn dyn_trait_in_impl() { |
1330 | check_infer( | 1312 | check_infer( |
1331 | r#" | 1313 | r#" |
1332 | trait Trait<T, U> { | 1314 | trait Trait<T, U> { |
1333 | fn foo(&self) -> (T, U); | 1315 | fn foo(&self) -> (T, U); |
1334 | } | 1316 | } |
1335 | struct S<T, U> {} | 1317 | struct S<T, U> {} |
1336 | impl<T, U> S<T, U> { | 1318 | impl<T, U> S<T, U> { |
1337 | fn bar(&self) -> &dyn Trait<T, U> { loop {} } | 1319 | fn bar(&self) -> &dyn Trait<T, U> { loop {} } |
1338 | } | 1320 | } |
1339 | trait Trait2<T, U> { | 1321 | trait Trait2<T, U> { |
1340 | fn baz(&self) -> (T, U); | 1322 | fn baz(&self) -> (T, U); |
1341 | } | 1323 | } |
1342 | impl<T, U> Trait2<T, U> for dyn Trait<T, U> { } | 1324 | impl<T, U> Trait2<T, U> for dyn Trait<T, U> { } |
1343 | 1325 | ||
1344 | fn test(s: S<u32, i32>) { | 1326 | fn test(s: S<u32, i32>) { |
1345 | s.bar().baz(); | 1327 | s.bar().baz(); |
1346 | } | 1328 | }"#, |
1347 | "#, | ||
1348 | expect![[r#" | 1329 | expect![[r#" |
1349 | 32..36 'self': &Self | 1330 | 32..36 'self': &Self |
1350 | 102..106 'self': &S<T, U> | 1331 | 102..106 'self': &S<T, U> |
@@ -1365,20 +1346,19 @@ fn dyn_trait_in_impl() { | |||
1365 | fn dyn_trait_bare() { | 1346 | fn dyn_trait_bare() { |
1366 | check_infer( | 1347 | check_infer( |
1367 | r#" | 1348 | r#" |
1368 | trait Trait { | 1349 | trait Trait { |
1369 | fn foo(&self) -> u64; | 1350 | fn foo(&self) -> u64; |
1370 | } | 1351 | } |
1371 | fn bar() -> Trait {} | 1352 | fn bar() -> Trait {} |
1372 | 1353 | ||
1373 | fn test(x: Trait, y: &Trait) -> u64 { | 1354 | fn test(x: Trait, y: &Trait) -> u64 { |
1374 | x; | 1355 | x; |
1375 | y; | 1356 | y; |
1376 | let z = bar(); | 1357 | let z = bar(); |
1377 | x.foo(); | 1358 | x.foo(); |
1378 | y.foo(); | 1359 | y.foo(); |
1379 | z.foo(); | 1360 | z.foo(); |
1380 | } | 1361 | }"#, |
1381 | "#, | ||
1382 | expect![[r#" | 1362 | expect![[r#" |
1383 | 26..30 'self': &Self | 1363 | 26..30 'self': &Self |
1384 | 60..62 '{}': () | 1364 | 60..62 '{}': () |
@@ -1404,17 +1384,24 @@ fn dyn_trait_bare() { | |||
1404 | fn weird_bounds() { | 1384 | fn weird_bounds() { |
1405 | check_infer( | 1385 | check_infer( |
1406 | r#" | 1386 | r#" |
1407 | trait Trait {} | 1387 | trait Trait {} |
1408 | fn test(a: impl Trait + 'lifetime, b: impl 'lifetime, c: impl (Trait), d: impl ('lifetime), e: impl ?Sized, f: impl Trait + ?Sized) {} | 1388 | fn test( |
1409 | "#, | 1389 | a: impl Trait + 'lifetime, |
1390 | b: impl 'lifetime, | ||
1391 | c: impl (Trait), | ||
1392 | d: impl ('lifetime), | ||
1393 | e: impl ?Sized, | ||
1394 | f: impl Trait + ?Sized | ||
1395 | ) {} | ||
1396 | "#, | ||
1410 | expect![[r#" | 1397 | expect![[r#" |
1411 | 23..24 'a': impl Trait | 1398 | 28..29 'a': impl Trait |
1412 | 50..51 'b': impl | 1399 | 59..60 'b': impl |
1413 | 69..70 'c': impl Trait | 1400 | 82..83 'c': impl Trait |
1414 | 86..87 'd': impl | 1401 | 103..104 'd': impl |
1415 | 107..108 'e': impl | 1402 | 128..129 'e': impl |
1416 | 123..124 'f': impl Trait | 1403 | 148..149 'f': impl Trait |
1417 | 147..149 '{}': () | 1404 | 173..175 '{}': () |
1418 | "#]], | 1405 | "#]], |
1419 | ); | 1406 | ); |
1420 | } | 1407 | } |
@@ -1439,27 +1426,26 @@ fn test(x: (impl Trait + UnknownTrait)) { | |||
1439 | fn assoc_type_bindings() { | 1426 | fn assoc_type_bindings() { |
1440 | check_infer( | 1427 | check_infer( |
1441 | r#" | 1428 | r#" |
1442 | trait Trait { | 1429 | trait Trait { |
1443 | type Type; | 1430 | type Type; |
1444 | } | 1431 | } |
1445 | 1432 | ||
1446 | fn get<T: Trait>(t: T) -> <T as Trait>::Type {} | 1433 | fn get<T: Trait>(t: T) -> <T as Trait>::Type {} |
1447 | fn get2<U, T: Trait<Type = U>>(t: T) -> U {} | 1434 | fn get2<U, T: Trait<Type = U>>(t: T) -> U {} |
1448 | fn set<T: Trait<Type = u64>>(t: T) -> T {t} | 1435 | fn set<T: Trait<Type = u64>>(t: T) -> T {t} |
1449 | 1436 | ||
1450 | struct S<T>; | 1437 | struct S<T>; |
1451 | impl<T> Trait for S<T> { type Type = T; } | 1438 | impl<T> Trait for S<T> { type Type = T; } |
1452 | 1439 | ||
1453 | fn test<T: Trait<Type = u32>>(x: T, y: impl Trait<Type = i64>) { | 1440 | fn test<T: Trait<Type = u32>>(x: T, y: impl Trait<Type = i64>) { |
1454 | get(x); | 1441 | get(x); |
1455 | get2(x); | 1442 | get2(x); |
1456 | get(y); | 1443 | get(y); |
1457 | get2(y); | 1444 | get2(y); |
1458 | get(set(S)); | 1445 | get(set(S)); |
1459 | get2(set(S)); | 1446 | get2(set(S)); |
1460 | get2(S::<str>); | 1447 | get2(S::<str>); |
1461 | } | 1448 | }"#, |
1462 | "#, | ||
1463 | expect![[r#" | 1449 | expect![[r#" |
1464 | 49..50 't': T | 1450 | 49..50 't': T |
1465 | 77..79 '{}': () | 1451 | 77..79 '{}': () |
@@ -1546,18 +1532,17 @@ mod iter { | |||
1546 | fn projection_eq_within_chalk() { | 1532 | fn projection_eq_within_chalk() { |
1547 | check_infer( | 1533 | check_infer( |
1548 | r#" | 1534 | r#" |
1549 | trait Trait1 { | 1535 | trait Trait1 { |
1550 | type Type; | 1536 | type Type; |
1551 | } | 1537 | } |
1552 | trait Trait2<T> { | 1538 | trait Trait2<T> { |
1553 | fn foo(self) -> T; | 1539 | fn foo(self) -> T; |
1554 | } | 1540 | } |
1555 | impl<T, U> Trait2<T> for U where U: Trait1<Type = T> {} | 1541 | impl<T, U> Trait2<T> for U where U: Trait1<Type = T> {} |
1556 | 1542 | ||
1557 | fn test<T: Trait1<Type = u32>>(x: T) { | 1543 | fn test<T: Trait1<Type = u32>>(x: T) { |
1558 | x.foo(); | 1544 | x.foo(); |
1559 | } | 1545 | }"#, |
1560 | "#, | ||
1561 | expect![[r#" | 1546 | expect![[r#" |
1562 | 61..65 'self': Self | 1547 | 61..65 'self': Self |
1563 | 163..164 'x': T | 1548 | 163..164 'x': T |
@@ -1589,19 +1574,18 @@ fn test<T: foo::Trait>(x: T) { | |||
1589 | fn super_trait_method_resolution() { | 1574 | fn super_trait_method_resolution() { |
1590 | check_infer( | 1575 | check_infer( |
1591 | r#" | 1576 | r#" |
1592 | mod foo { | 1577 | mod foo { |
1593 | trait SuperTrait { | 1578 | trait SuperTrait { |
1594 | fn foo(&self) -> u32 {} | 1579 | fn foo(&self) -> u32 {} |
1595 | } | 1580 | } |
1596 | } | 1581 | } |
1597 | trait Trait1: foo::SuperTrait {} | 1582 | trait Trait1: foo::SuperTrait {} |
1598 | trait Trait2 where Self: foo::SuperTrait {} | 1583 | trait Trait2 where Self: foo::SuperTrait {} |
1599 | 1584 | ||
1600 | fn test<T: Trait1, U: Trait2>(x: T, y: U) { | 1585 | fn test<T: Trait1, U: Trait2>(x: T, y: U) { |
1601 | x.foo(); | 1586 | x.foo(); |
1602 | y.foo(); | 1587 | y.foo(); |
1603 | } | 1588 | }"#, |
1604 | "#, | ||
1605 | expect![[r#" | 1589 | expect![[r#" |
1606 | 49..53 'self': &Self | 1590 | 49..53 'self': &Self |
1607 | 62..64 '{}': () | 1591 | 62..64 '{}': () |
@@ -1620,17 +1604,16 @@ fn super_trait_method_resolution() { | |||
1620 | fn super_trait_impl_trait_method_resolution() { | 1604 | fn super_trait_impl_trait_method_resolution() { |
1621 | check_infer( | 1605 | check_infer( |
1622 | r#" | 1606 | r#" |
1623 | mod foo { | 1607 | mod foo { |
1624 | trait SuperTrait { | 1608 | trait SuperTrait { |
1625 | fn foo(&self) -> u32 {} | 1609 | fn foo(&self) -> u32 {} |
1626 | } | 1610 | } |
1627 | } | 1611 | } |
1628 | trait Trait1: foo::SuperTrait {} | 1612 | trait Trait1: foo::SuperTrait {} |
1629 | 1613 | ||
1630 | fn test(x: &impl Trait1) { | 1614 | fn test(x: &impl Trait1) { |
1631 | x.foo(); | 1615 | x.foo(); |
1632 | } | 1616 | }"#, |
1633 | "#, | ||
1634 | expect![[r#" | 1617 | expect![[r#" |
1635 | 49..53 'self': &Self | 1618 | 49..53 'self': &Self |
1636 | 62..64 '{}': () | 1619 | 62..64 '{}': () |
@@ -1667,20 +1650,19 @@ fn super_trait_cycle() { | |||
1667 | fn super_trait_assoc_type_bounds() { | 1650 | fn super_trait_assoc_type_bounds() { |
1668 | check_infer( | 1651 | check_infer( |
1669 | r#" | 1652 | r#" |
1670 | trait SuperTrait { type Type; } | 1653 | trait SuperTrait { type Type; } |
1671 | trait Trait where Self: SuperTrait {} | 1654 | trait Trait where Self: SuperTrait {} |
1672 | 1655 | ||
1673 | fn get2<U, T: Trait<Type = U>>(t: T) -> U {} | 1656 | fn get2<U, T: Trait<Type = U>>(t: T) -> U {} |
1674 | fn set<T: Trait<Type = u64>>(t: T) -> T {t} | 1657 | fn set<T: Trait<Type = u64>>(t: T) -> T {t} |
1675 | 1658 | ||
1676 | struct S<T>; | 1659 | struct S<T>; |
1677 | impl<T> SuperTrait for S<T> { type Type = T; } | 1660 | impl<T> SuperTrait for S<T> { type Type = T; } |
1678 | impl<T> Trait for S<T> {} | 1661 | impl<T> Trait for S<T> {} |
1679 | 1662 | ||
1680 | fn test() { | 1663 | fn test() { |
1681 | get2(set(S)); | 1664 | get2(set(S)); |
1682 | } | 1665 | }"#, |
1683 | "#, | ||
1684 | expect![[r#" | 1666 | expect![[r#" |
1685 | 102..103 't': T | 1667 | 102..103 't': T |
1686 | 113..115 '{}': () | 1668 | 113..115 '{}': () |
@@ -1701,16 +1683,15 @@ fn super_trait_assoc_type_bounds() { | |||
1701 | fn fn_trait() { | 1683 | fn fn_trait() { |
1702 | check_infer_with_mismatches( | 1684 | check_infer_with_mismatches( |
1703 | r#" | 1685 | r#" |
1704 | trait FnOnce<Args> { | 1686 | trait FnOnce<Args> { |
1705 | type Output; | 1687 | type Output; |
1706 | 1688 | ||
1707 | fn call_once(self, args: Args) -> <Self as FnOnce<Args>>::Output; | 1689 | fn call_once(self, args: Args) -> <Self as FnOnce<Args>>::Output; |
1708 | } | 1690 | } |
1709 | 1691 | ||
1710 | fn test<F: FnOnce(u32, u64) -> u128>(f: F) { | 1692 | fn test<F: FnOnce(u32, u64) -> u128>(f: F) { |
1711 | f.call_once((1, 2)); | 1693 | f.call_once((1, 2)); |
1712 | } | 1694 | }"#, |
1713 | "#, | ||
1714 | expect![[r#" | 1695 | expect![[r#" |
1715 | 56..60 'self': Self | 1696 | 56..60 'self': Self |
1716 | 62..66 'args': Args | 1697 | 62..66 'args': Args |
@@ -1729,37 +1710,36 @@ fn fn_trait() { | |||
1729 | fn fn_ptr_and_item() { | 1710 | fn fn_ptr_and_item() { |
1730 | check_infer_with_mismatches( | 1711 | check_infer_with_mismatches( |
1731 | r#" | 1712 | r#" |
1732 | #[lang="fn_once"] | 1713 | #[lang="fn_once"] |
1733 | trait FnOnce<Args> { | 1714 | trait FnOnce<Args> { |
1734 | type Output; | 1715 | type Output; |
1735 | 1716 | ||
1736 | fn call_once(self, args: Args) -> Self::Output; | 1717 | fn call_once(self, args: Args) -> Self::Output; |
1737 | } | 1718 | } |
1738 | 1719 | ||
1739 | trait Foo<T> { | 1720 | trait Foo<T> { |
1740 | fn foo(&self) -> T; | 1721 | fn foo(&self) -> T; |
1741 | } | 1722 | } |
1742 | 1723 | ||
1743 | struct Bar<T>(T); | 1724 | struct Bar<T>(T); |
1744 | 1725 | ||
1745 | impl<A1, R, F: FnOnce(A1) -> R> Foo<(A1, R)> for Bar<F> { | 1726 | impl<A1, R, F: FnOnce(A1) -> R> Foo<(A1, R)> for Bar<F> { |
1746 | fn foo(&self) -> (A1, R) { loop {} } | 1727 | fn foo(&self) -> (A1, R) { loop {} } |
1747 | } | 1728 | } |
1748 | 1729 | ||
1749 | enum Opt<T> { None, Some(T) } | 1730 | enum Opt<T> { None, Some(T) } |
1750 | impl<T> Opt<T> { | 1731 | impl<T> Opt<T> { |
1751 | fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Opt<U> { loop {} } | 1732 | fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Opt<U> { loop {} } |
1752 | } | 1733 | } |
1753 | 1734 | ||
1754 | fn test() { | 1735 | fn test() { |
1755 | let bar: Bar<fn(u8) -> u32>; | 1736 | let bar: Bar<fn(u8) -> u32>; |
1756 | bar.foo(); | 1737 | bar.foo(); |
1757 | 1738 | ||
1758 | let opt: Opt<u8>; | 1739 | let opt: Opt<u8>; |
1759 | let f: fn(u8) -> u32; | 1740 | let f: fn(u8) -> u32; |
1760 | opt.map(f); | 1741 | opt.map(f); |
1761 | } | 1742 | }"#, |
1762 | "#, | ||
1763 | expect![[r#" | 1743 | expect![[r#" |
1764 | 74..78 'self': Self | 1744 | 74..78 'self': Self |
1765 | 80..84 'args': Args | 1745 | 80..84 'args': Args |
@@ -1790,46 +1770,45 @@ fn fn_ptr_and_item() { | |||
1790 | fn fn_trait_deref_with_ty_default() { | 1770 | fn fn_trait_deref_with_ty_default() { |
1791 | check_infer( | 1771 | check_infer( |
1792 | r#" | 1772 | r#" |
1793 | #[lang = "deref"] | 1773 | #[lang = "deref"] |
1794 | trait Deref { | 1774 | trait Deref { |
1795 | type Target; | 1775 | type Target; |
1796 | 1776 | ||
1797 | fn deref(&self) -> &Self::Target; | 1777 | fn deref(&self) -> &Self::Target; |
1798 | } | 1778 | } |
1799 | 1779 | ||
1800 | #[lang="fn_once"] | 1780 | #[lang="fn_once"] |
1801 | trait FnOnce<Args> { | 1781 | trait FnOnce<Args> { |
1802 | type Output; | 1782 | type Output; |
1803 | 1783 | ||
1804 | fn call_once(self, args: Args) -> Self::Output; | 1784 | fn call_once(self, args: Args) -> Self::Output; |
1805 | } | 1785 | } |
1806 | 1786 | ||
1807 | struct Foo; | 1787 | struct Foo; |
1808 | 1788 | ||
1809 | impl Foo { | 1789 | impl Foo { |
1810 | fn foo(&self) -> usize {} | 1790 | fn foo(&self) -> usize {} |
1811 | } | 1791 | } |
1812 | 1792 | ||
1813 | struct Lazy<T, F = fn() -> T>(F); | 1793 | struct Lazy<T, F = fn() -> T>(F); |
1814 | 1794 | ||
1815 | impl<T, F> Lazy<T, F> { | 1795 | impl<T, F> Lazy<T, F> { |
1816 | pub fn new(f: F) -> Lazy<T, F> {} | 1796 | pub fn new(f: F) -> Lazy<T, F> {} |
1817 | } | 1797 | } |
1818 | 1798 | ||
1819 | impl<T, F: FnOnce() -> T> Deref for Lazy<T, F> { | 1799 | impl<T, F: FnOnce() -> T> Deref for Lazy<T, F> { |
1820 | type Target = T; | 1800 | type Target = T; |
1821 | } | 1801 | } |
1822 | 1802 | ||
1823 | fn test() { | 1803 | fn test() { |
1824 | let lazy1: Lazy<Foo, _> = Lazy::new(|| Foo); | 1804 | let lazy1: Lazy<Foo, _> = Lazy::new(|| Foo); |
1825 | let r1 = lazy1.foo(); | 1805 | let r1 = lazy1.foo(); |
1826 | 1806 | ||
1827 | fn make_foo_fn() -> Foo {} | 1807 | fn make_foo_fn() -> Foo {} |
1828 | let make_foo_fn_ptr: fn() -> Foo = make_foo_fn; | 1808 | let make_foo_fn_ptr: fn() -> Foo = make_foo_fn; |
1829 | let lazy2: Lazy<Foo, _> = Lazy::new(make_foo_fn_ptr); | 1809 | let lazy2: Lazy<Foo, _> = Lazy::new(make_foo_fn_ptr); |
1830 | let r2 = lazy2.foo(); | 1810 | let r2 = lazy2.foo(); |
1831 | } | 1811 | }"#, |
1832 | "#, | ||
1833 | expect![[r#" | 1812 | expect![[r#" |
1834 | 64..68 'self': &Self | 1813 | 64..68 'self': &Self |
1835 | 165..169 'self': Self | 1814 | 165..169 'self': Self |
@@ -1865,23 +1844,22 @@ fn fn_trait_deref_with_ty_default() { | |||
1865 | fn closure_1() { | 1844 | fn closure_1() { |
1866 | check_infer_with_mismatches( | 1845 | check_infer_with_mismatches( |
1867 | r#" | 1846 | r#" |
1868 | #[lang = "fn_once"] | 1847 | #[lang = "fn_once"] |
1869 | trait FnOnce<Args> { | 1848 | trait FnOnce<Args> { |
1870 | type Output; | 1849 | type Output; |
1871 | } | 1850 | } |
1872 | 1851 | ||
1873 | enum Option<T> { Some(T), None } | 1852 | enum Option<T> { Some(T), None } |
1874 | impl<T> Option<T> { | 1853 | impl<T> Option<T> { |
1875 | fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> { loop {} } | 1854 | fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> { loop {} } |
1876 | } | 1855 | } |
1877 | 1856 | ||
1878 | fn test() { | 1857 | fn test() { |
1879 | let x = Option::Some(1u32); | 1858 | let x = Option::Some(1u32); |
1880 | x.map(|v| v + 1); | 1859 | x.map(|v| v + 1); |
1881 | x.map(|_v| 1u64); | 1860 | x.map(|_v| 1u64); |
1882 | let y: Option<i64> = x.map(|_v| 1); | 1861 | let y: Option<i64> = x.map(|_v| 1); |
1883 | } | 1862 | }"#, |
1884 | "#, | ||
1885 | expect![[r#" | 1863 | expect![[r#" |
1886 | 147..151 'self': Option<T> | 1864 | 147..151 'self': Option<T> |
1887 | 153..154 'f': F | 1865 | 153..154 'f': F |
@@ -1919,38 +1897,63 @@ fn closure_1() { | |||
1919 | fn closure_2() { | 1897 | fn closure_2() { |
1920 | check_infer_with_mismatches( | 1898 | check_infer_with_mismatches( |
1921 | r#" | 1899 | r#" |
1922 | trait FnOnce<Args> { | 1900 | #[lang = "add"] |
1923 | type Output; | 1901 | pub trait Add<Rhs = Self> { |
1924 | } | 1902 | type Output; |
1903 | fn add(self, rhs: Rhs) -> Self::Output; | ||
1904 | } | ||
1925 | 1905 | ||
1926 | fn test<F: FnOnce(u32) -> u64>(f: F) { | 1906 | trait FnOnce<Args> { |
1927 | f(1); | 1907 | type Output; |
1928 | let g = |v| v + 1; | 1908 | } |
1929 | g(1u64); | 1909 | |
1930 | let h = |v| 1u128 + v; | 1910 | impl Add for u64 { |
1931 | } | 1911 | type Output = Self; |
1932 | "#, | 1912 | fn add(self, rhs: u64) -> Self::Output {0} |
1913 | } | ||
1914 | |||
1915 | impl Add for u128 { | ||
1916 | type Output = Self; | ||
1917 | fn add(self, rhs: u128) -> Self::Output {0} | ||
1918 | } | ||
1919 | |||
1920 | fn test<F: FnOnce(u32) -> u64>(f: F) { | ||
1921 | f(1); | ||
1922 | let g = |v| v + 1; | ||
1923 | g(1u64); | ||
1924 | let h = |v| 1u128 + v; | ||
1925 | }"#, | ||
1933 | expect![[r#" | 1926 | expect![[r#" |
1934 | 72..73 'f': F | 1927 | 72..76 'self': Self |
1935 | 78..154 '{ ...+ v; }': () | 1928 | 78..81 'rhs': Rhs |
1936 | 84..85 'f': F | 1929 | 203..207 'self': u64 |
1937 | 84..88 'f(1)': {unknown} | 1930 | 209..212 'rhs': u64 |
1938 | 86..87 '1': i32 | 1931 | 235..238 '{0}': u64 |
1939 | 98..99 'g': |u64| -> i32 | 1932 | 236..237 '0': u64 |
1940 | 102..111 '|v| v + 1': |u64| -> i32 | 1933 | 297..301 'self': u128 |
1941 | 103..104 'v': u64 | 1934 | 303..306 'rhs': u128 |
1942 | 106..107 'v': u64 | 1935 | 330..333 '{0}': u128 |
1943 | 106..111 'v + 1': i32 | 1936 | 331..332 '0': u128 |
1944 | 110..111 '1': i32 | 1937 | 368..369 'f': F |
1945 | 117..118 'g': |u64| -> i32 | 1938 | 374..450 '{ ...+ v; }': () |
1946 | 117..124 'g(1u64)': i32 | 1939 | 380..381 'f': F |
1947 | 119..123 '1u64': u64 | 1940 | 380..384 'f(1)': {unknown} |
1948 | 134..135 'h': |u128| -> u128 | 1941 | 382..383 '1': i32 |
1949 | 138..151 '|v| 1u128 + v': |u128| -> u128 | 1942 | 394..395 'g': |u64| -> u64 |
1950 | 139..140 'v': u128 | 1943 | 398..407 '|v| v + 1': |u64| -> u64 |
1951 | 142..147 '1u128': u128 | 1944 | 399..400 'v': u64 |
1952 | 142..151 '1u128 + v': u128 | 1945 | 402..403 'v': u64 |
1953 | 150..151 'v': u128 | 1946 | 402..407 'v + 1': u64 |
1947 | 406..407 '1': u64 | ||
1948 | 413..414 'g': |u64| -> u64 | ||
1949 | 413..420 'g(1u64)': u64 | ||
1950 | 415..419 '1u64': u64 | ||
1951 | 430..431 'h': |u128| -> u128 | ||
1952 | 434..447 '|v| 1u128 + v': |u128| -> u128 | ||
1953 | 435..436 'v': u128 | ||
1954 | 438..443 '1u128': u128 | ||
1955 | 438..447 '1u128 + v': u128 | ||
1956 | 446..447 'v': u128 | ||
1954 | "#]], | 1957 | "#]], |
1955 | ); | 1958 | ); |
1956 | } | 1959 | } |
@@ -1959,29 +1962,28 @@ fn closure_2() { | |||
1959 | fn closure_as_argument_inference_order() { | 1962 | fn closure_as_argument_inference_order() { |
1960 | check_infer_with_mismatches( | 1963 | check_infer_with_mismatches( |
1961 | r#" | 1964 | r#" |
1962 | #[lang = "fn_once"] | 1965 | #[lang = "fn_once"] |
1963 | trait FnOnce<Args> { | 1966 | trait FnOnce<Args> { |
1964 | type Output; | 1967 | type Output; |
1965 | } | 1968 | } |
1966 | 1969 | ||
1967 | fn foo1<T, U, F: FnOnce(T) -> U>(x: T, f: F) -> U { loop {} } | 1970 | fn foo1<T, U, F: FnOnce(T) -> U>(x: T, f: F) -> U { loop {} } |
1968 | fn foo2<T, U, F: FnOnce(T) -> U>(f: F, x: T) -> U { loop {} } | 1971 | fn foo2<T, U, F: FnOnce(T) -> U>(f: F, x: T) -> U { loop {} } |
1969 | 1972 | ||
1970 | struct S; | 1973 | struct S; |
1971 | impl S { | 1974 | impl S { |
1972 | fn method(self) -> u64; | 1975 | fn method(self) -> u64; |
1973 | 1976 | ||
1974 | fn foo1<T, U, F: FnOnce(T) -> U>(self, x: T, f: F) -> U { loop {} } | 1977 | fn foo1<T, U, F: FnOnce(T) -> U>(self, x: T, f: F) -> U { loop {} } |
1975 | fn foo2<T, U, F: FnOnce(T) -> U>(self, f: F, x: T) -> U { loop {} } | 1978 | fn foo2<T, U, F: FnOnce(T) -> U>(self, f: F, x: T) -> U { loop {} } |
1976 | } | 1979 | } |
1977 | 1980 | ||
1978 | fn test() { | 1981 | fn test() { |
1979 | let x1 = foo1(S, |s| s.method()); | 1982 | let x1 = foo1(S, |s| s.method()); |
1980 | let x2 = foo2(|s| s.method(), S); | 1983 | let x2 = foo2(|s| s.method(), S); |
1981 | let x3 = S.foo1(S, |s| s.method()); | 1984 | let x3 = S.foo1(S, |s| s.method()); |
1982 | let x4 = S.foo2(|s| s.method(), S); | 1985 | let x4 = S.foo2(|s| s.method(), S); |
1983 | } | 1986 | }"#, |
1984 | "#, | ||
1985 | expect![[r#" | 1987 | expect![[r#" |
1986 | 94..95 'x': T | 1988 | 94..95 'x': T |
1987 | 100..101 'f': F | 1989 | 100..101 'f': F |
@@ -2110,27 +2112,26 @@ fn test<T, U>() where T::Item: Trait2, T: Trait<U::Item>, U: Trait<()> { | |||
2110 | fn unselected_projection_on_impl_self() { | 2112 | fn unselected_projection_on_impl_self() { |
2111 | check_infer( | 2113 | check_infer( |
2112 | r#" | 2114 | r#" |
2113 | //- /main.rs | 2115 | //- /main.rs |
2114 | trait Trait { | 2116 | trait Trait { |
2115 | type Item; | 2117 | type Item; |
2116 | 2118 | ||
2117 | fn f(&self, x: Self::Item); | 2119 | fn f(&self, x: Self::Item); |
2118 | } | 2120 | } |
2119 | 2121 | ||
2120 | struct S; | 2122 | struct S; |
2121 | 2123 | ||
2122 | impl Trait for S { | 2124 | impl Trait for S { |
2123 | type Item = u32; | 2125 | type Item = u32; |
2124 | fn f(&self, x: Self::Item) { let y = x; } | 2126 | fn f(&self, x: Self::Item) { let y = x; } |
2125 | } | 2127 | } |
2126 | 2128 | ||
2127 | struct S2; | 2129 | struct S2; |
2128 | 2130 | ||
2129 | impl Trait for S2 { | 2131 | impl Trait for S2 { |
2130 | type Item = i32; | 2132 | type Item = i32; |
2131 | fn f(&self, x: <Self>::Item) { let y = x; } | 2133 | fn f(&self, x: <Self>::Item) { let y = x; } |
2132 | } | 2134 | }"#, |
2133 | "#, | ||
2134 | expect![[r#" | 2135 | expect![[r#" |
2135 | 40..44 'self': &Self | 2136 | 40..44 'self': &Self |
2136 | 46..47 'x': Trait::Item<Self> | 2137 | 46..47 'x': Trait::Item<Self> |
@@ -2366,58 +2367,57 @@ fn test<I: Iterator<Item: Iterator<Item = u32>>>() { | |||
2366 | fn proc_macro_server_types() { | 2367 | fn proc_macro_server_types() { |
2367 | check_infer( | 2368 | check_infer( |
2368 | r#" | 2369 | r#" |
2369 | macro_rules! with_api { | 2370 | macro_rules! with_api { |
2370 | ($S:ident, $self:ident, $m:ident) => { | 2371 | ($S:ident, $self:ident, $m:ident) => { |
2371 | $m! { | 2372 | $m! { |
2372 | TokenStream { | 2373 | TokenStream { |
2373 | fn new() -> $S::TokenStream; | 2374 | fn new() -> $S::TokenStream; |
2374 | }, | 2375 | }, |
2375 | Group { | 2376 | Group { |
2376 | }, | 2377 | }, |
2377 | } | ||
2378 | }; | ||
2379 | } | 2378 | } |
2380 | macro_rules! associated_item { | 2379 | }; |
2381 | (type TokenStream) => | 2380 | } |
2382 | (type TokenStream: 'static;); | 2381 | macro_rules! associated_item { |
2383 | (type Group) => | 2382 | (type TokenStream) => |
2384 | (type Group: 'static;); | 2383 | (type TokenStream: 'static;); |
2385 | ($($item:tt)*) => ($($item)*;) | 2384 | (type Group) => |
2386 | } | 2385 | (type Group: 'static;); |
2387 | macro_rules! declare_server_traits { | 2386 | ($($item:tt)*) => ($($item)*;) |
2388 | ($($name:ident { | 2387 | } |
2389 | $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* | 2388 | macro_rules! declare_server_traits { |
2390 | }),* $(,)?) => { | 2389 | ($($name:ident { |
2391 | pub trait Types { | 2390 | $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* |
2392 | $(associated_item!(type $name);)* | 2391 | }),* $(,)?) => { |
2393 | } | 2392 | pub trait Types { |
2394 | 2393 | $(associated_item!(type $name);)* | |
2395 | $(pub trait $name: Types { | ||
2396 | $(associated_item!(fn $method($($arg: $arg_ty),*) $(-> $ret_ty)?);)* | ||
2397 | })* | ||
2398 | |||
2399 | pub trait Server: Types $(+ $name)* {} | ||
2400 | impl<S: Types $(+ $name)*> Server for S {} | ||
2401 | } | ||
2402 | } | 2394 | } |
2403 | 2395 | ||
2404 | with_api!(Self, self_, declare_server_traits); | 2396 | $(pub trait $name: Types { |
2405 | struct G {} | 2397 | $(associated_item!(fn $method($($arg: $arg_ty),*) $(-> $ret_ty)?);)* |
2406 | struct T {} | 2398 | })* |
2407 | struct Rustc; | ||
2408 | impl Types for Rustc { | ||
2409 | type TokenStream = T; | ||
2410 | type Group = G; | ||
2411 | } | ||
2412 | 2399 | ||
2413 | fn make<T>() -> T { loop {} } | 2400 | pub trait Server: Types $(+ $name)* {} |
2414 | impl TokenStream for Rustc { | 2401 | impl<S: Types $(+ $name)*> Server for S {} |
2415 | fn new() -> Self::TokenStream { | 2402 | } |
2416 | let group: Self::Group = make(); | 2403 | } |
2417 | make() | 2404 | |
2418 | } | 2405 | with_api!(Self, self_, declare_server_traits); |
2419 | } | 2406 | struct G {} |
2420 | "#, | 2407 | struct T {} |
2408 | struct Rustc; | ||
2409 | impl Types for Rustc { | ||
2410 | type TokenStream = T; | ||
2411 | type Group = G; | ||
2412 | } | ||
2413 | |||
2414 | fn make<T>() -> T { loop {} } | ||
2415 | impl TokenStream for Rustc { | ||
2416 | fn new() -> Self::TokenStream { | ||
2417 | let group: Self::Group = make(); | ||
2418 | make() | ||
2419 | } | ||
2420 | }"#, | ||
2421 | expect![[r#" | 2421 | expect![[r#" |
2422 | 1061..1072 '{ loop {} }': T | 2422 | 1061..1072 '{ loop {} }': T |
2423 | 1063..1070 'loop {}': ! | 2423 | 1063..1070 'loop {}': ! |
@@ -2436,23 +2436,22 @@ fn proc_macro_server_types() { | |||
2436 | fn unify_impl_trait() { | 2436 | fn unify_impl_trait() { |
2437 | check_infer_with_mismatches( | 2437 | check_infer_with_mismatches( |
2438 | r#" | 2438 | r#" |
2439 | trait Trait<T> {} | 2439 | trait Trait<T> {} |
2440 | 2440 | ||
2441 | fn foo(x: impl Trait<u32>) { loop {} } | 2441 | fn foo(x: impl Trait<u32>) { loop {} } |
2442 | fn bar<T>(x: impl Trait<T>) -> T { loop {} } | 2442 | fn bar<T>(x: impl Trait<T>) -> T { loop {} } |
2443 | 2443 | ||
2444 | struct S<T>(T); | 2444 | struct S<T>(T); |
2445 | impl<T> Trait<T> for S<T> {} | 2445 | impl<T> Trait<T> for S<T> {} |
2446 | 2446 | ||
2447 | fn default<T>() -> T { loop {} } | 2447 | fn default<T>() -> T { loop {} } |
2448 | 2448 | ||
2449 | fn test() -> impl Trait<i32> { | 2449 | fn test() -> impl Trait<i32> { |
2450 | let s1 = S(default()); | 2450 | let s1 = S(default()); |
2451 | foo(s1); | 2451 | foo(s1); |
2452 | let x: i32 = bar(S(default())); | 2452 | let x: i32 = bar(S(default())); |
2453 | S(default()) | 2453 | S(default()) |
2454 | } | 2454 | }"#, |
2455 | "#, | ||
2456 | expect![[r#" | 2455 | expect![[r#" |
2457 | 26..27 'x': impl Trait<u32> | 2456 | 26..27 'x': impl Trait<u32> |
2458 | 46..57 '{ loop {} }': () | 2457 | 46..57 '{ loop {} }': () |
@@ -2493,30 +2492,29 @@ fn unify_impl_trait() { | |||
2493 | fn assoc_types_from_bounds() { | 2492 | fn assoc_types_from_bounds() { |
2494 | check_infer( | 2493 | check_infer( |
2495 | r#" | 2494 | r#" |
2496 | //- /main.rs | 2495 | //- /main.rs |
2497 | #[lang = "fn_once"] | 2496 | #[lang = "fn_once"] |
2498 | trait FnOnce<Args> { | 2497 | trait FnOnce<Args> { |
2499 | type Output; | 2498 | type Output; |
2500 | } | 2499 | } |
2501 | 2500 | ||
2502 | trait T { | 2501 | trait T { |
2503 | type O; | 2502 | type O; |
2504 | } | 2503 | } |
2505 | 2504 | ||
2506 | impl T for () { | 2505 | impl T for () { |
2507 | type O = (); | 2506 | type O = (); |
2508 | } | 2507 | } |
2509 | 2508 | ||
2510 | fn f<X, F>(_v: F) | 2509 | fn f<X, F>(_v: F) |
2511 | where | 2510 | where |
2512 | X: T, | 2511 | X: T, |
2513 | F: FnOnce(&X::O), | 2512 | F: FnOnce(&X::O), |
2514 | { } | 2513 | { } |
2515 | 2514 | ||
2516 | fn main() { | 2515 | fn main() { |
2517 | f::<(), _>(|z| { z; }); | 2516 | f::<(), _>(|z| { z; }); |
2518 | } | 2517 | }"#, |
2519 | "#, | ||
2520 | expect![[r#" | 2518 | expect![[r#" |
2521 | 133..135 '_v': F | 2519 | 133..135 '_v': F |
2522 | 178..181 '{ }': () | 2520 | 178..181 '{ }': () |
@@ -2602,76 +2600,75 @@ fn test() { | |||
2602 | fn iterator_chain() { | 2600 | fn iterator_chain() { |
2603 | check_infer_with_mismatches( | 2601 | check_infer_with_mismatches( |
2604 | r#" | 2602 | r#" |
2605 | //- /main.rs | 2603 | //- /main.rs |
2606 | #[lang = "fn_once"] | 2604 | #[lang = "fn_once"] |
2607 | trait FnOnce<Args> { | 2605 | trait FnOnce<Args> { |
2608 | type Output; | 2606 | type Output; |
2609 | } | 2607 | } |
2610 | #[lang = "fn_mut"] | 2608 | #[lang = "fn_mut"] |
2611 | trait FnMut<Args>: FnOnce<Args> { } | 2609 | trait FnMut<Args>: FnOnce<Args> { } |
2612 | 2610 | ||
2613 | enum Option<T> { Some(T), None } | 2611 | enum Option<T> { Some(T), None } |
2614 | use Option::*; | 2612 | use Option::*; |
2615 | 2613 | ||
2616 | pub trait Iterator { | 2614 | pub trait Iterator { |
2617 | type Item; | 2615 | type Item; |
2618 | 2616 | ||
2619 | fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F> | 2617 | fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F> |
2620 | where | 2618 | where |
2621 | F: FnMut(Self::Item) -> Option<B>, | 2619 | F: FnMut(Self::Item) -> Option<B>, |
2622 | { loop {} } | 2620 | { loop {} } |
2623 | 2621 | ||
2624 | fn for_each<F>(self, f: F) | 2622 | fn for_each<F>(self, f: F) |
2625 | where | 2623 | where |
2626 | F: FnMut(Self::Item), | 2624 | F: FnMut(Self::Item), |
2627 | { loop {} } | 2625 | { loop {} } |
2628 | } | 2626 | } |
2629 | 2627 | ||
2630 | pub trait IntoIterator { | 2628 | pub trait IntoIterator { |
2631 | type Item; | 2629 | type Item; |
2632 | type IntoIter: Iterator<Item = Self::Item>; | 2630 | type IntoIter: Iterator<Item = Self::Item>; |
2633 | fn into_iter(self) -> Self::IntoIter; | 2631 | fn into_iter(self) -> Self::IntoIter; |
2634 | } | 2632 | } |
2635 | 2633 | ||
2636 | pub struct FilterMap<I, F> { } | 2634 | pub struct FilterMap<I, F> { } |
2637 | impl<B, I: Iterator, F> Iterator for FilterMap<I, F> | 2635 | impl<B, I: Iterator, F> Iterator for FilterMap<I, F> |
2638 | where | 2636 | where |
2639 | F: FnMut(I::Item) -> Option<B>, | 2637 | F: FnMut(I::Item) -> Option<B>, |
2640 | { | 2638 | { |
2641 | type Item = B; | 2639 | type Item = B; |
2642 | } | 2640 | } |
2643 | 2641 | ||
2644 | #[stable(feature = "rust1", since = "1.0.0")] | 2642 | #[stable(feature = "rust1", since = "1.0.0")] |
2645 | impl<I: Iterator> IntoIterator for I { | 2643 | impl<I: Iterator> IntoIterator for I { |
2646 | type Item = I::Item; | 2644 | type Item = I::Item; |
2647 | type IntoIter = I; | 2645 | type IntoIter = I; |
2648 | 2646 | ||
2649 | fn into_iter(self) -> I { | 2647 | fn into_iter(self) -> I { |
2650 | self | 2648 | self |
2651 | } | 2649 | } |
2652 | } | 2650 | } |
2653 | 2651 | ||
2654 | struct Vec<T> {} | 2652 | struct Vec<T> {} |
2655 | impl<T> Vec<T> { | 2653 | impl<T> Vec<T> { |
2656 | fn new() -> Self { loop {} } | 2654 | fn new() -> Self { loop {} } |
2657 | } | 2655 | } |
2658 | 2656 | ||
2659 | impl<T> IntoIterator for Vec<T> { | 2657 | impl<T> IntoIterator for Vec<T> { |
2660 | type Item = T; | 2658 | type Item = T; |
2661 | type IntoIter = IntoIter<T>; | 2659 | type IntoIter = IntoIter<T>; |
2662 | } | 2660 | } |
2663 | 2661 | ||
2664 | pub struct IntoIter<T> { } | 2662 | pub struct IntoIter<T> { } |
2665 | impl<T> Iterator for IntoIter<T> { | 2663 | impl<T> Iterator for IntoIter<T> { |
2666 | type Item = T; | 2664 | type Item = T; |
2667 | } | 2665 | } |
2668 | 2666 | ||
2669 | fn main() { | 2667 | fn main() { |
2670 | Vec::<i32>::new().into_iter() | 2668 | Vec::<i32>::new().into_iter() |
2671 | .filter_map(|x| if x > 0 { Some(x as u32) } else { None }) | 2669 | .filter_map(|x| if x > 0 { Some(x as u32) } else { None }) |
2672 | .for_each(|y| { y; }); | 2670 | .for_each(|y| { y; }); |
2673 | } | 2671 | }"#, |
2674 | "#, | ||
2675 | expect![[r#" | 2672 | expect![[r#" |
2676 | 226..230 'self': Self | 2673 | 226..230 'self': Self |
2677 | 232..233 'f': F | 2674 | 232..233 'f': F |
@@ -2753,14 +2750,13 @@ fn main() { | |||
2753 | fn trait_object_no_coercion() { | 2750 | fn trait_object_no_coercion() { |
2754 | check_infer_with_mismatches( | 2751 | check_infer_with_mismatches( |
2755 | r#" | 2752 | r#" |
2756 | trait Foo {} | 2753 | trait Foo {} |
2757 | 2754 | ||
2758 | fn foo(x: &dyn Foo) {} | 2755 | fn foo(x: &dyn Foo) {} |
2759 | 2756 | ||
2760 | fn test(x: &dyn Foo) { | 2757 | fn test(x: &dyn Foo) { |
2761 | foo(x); | 2758 | foo(x); |
2762 | } | 2759 | }"#, |
2763 | "#, | ||
2764 | expect![[r#" | 2760 | expect![[r#" |
2765 | 21..22 'x': &dyn Foo | 2761 | 21..22 'x': &dyn Foo |
2766 | 34..36 '{}': () | 2762 | 34..36 '{}': () |
@@ -2777,23 +2773,22 @@ fn trait_object_no_coercion() { | |||
2777 | fn builtin_copy() { | 2773 | fn builtin_copy() { |
2778 | check_infer_with_mismatches( | 2774 | check_infer_with_mismatches( |
2779 | r#" | 2775 | r#" |
2780 | #[lang = "copy"] | 2776 | #[lang = "copy"] |
2781 | trait Copy {} | 2777 | trait Copy {} |
2782 | 2778 | ||
2783 | struct IsCopy; | 2779 | struct IsCopy; |
2784 | impl Copy for IsCopy {} | 2780 | impl Copy for IsCopy {} |
2785 | struct NotCopy; | 2781 | struct NotCopy; |
2786 | 2782 | ||
2787 | trait Test { fn test(&self) -> bool; } | 2783 | trait Test { fn test(&self) -> bool; } |
2788 | impl<T: Copy> Test for T {} | 2784 | impl<T: Copy> Test for T {} |
2789 | 2785 | ||
2790 | fn test() { | 2786 | fn test() { |
2791 | IsCopy.test(); | 2787 | IsCopy.test(); |
2792 | NotCopy.test(); | 2788 | NotCopy.test(); |
2793 | (IsCopy, IsCopy).test(); | 2789 | (IsCopy, IsCopy).test(); |
2794 | (IsCopy, NotCopy).test(); | 2790 | (IsCopy, NotCopy).test(); |
2795 | } | 2791 | }"#, |
2796 | "#, | ||
2797 | expect![[r#" | 2792 | expect![[r#" |
2798 | 110..114 'self': &Self | 2793 | 110..114 'self': &Self |
2799 | 166..267 '{ ...t(); }': () | 2794 | 166..267 '{ ...t(); }': () |
@@ -2817,24 +2812,23 @@ fn builtin_copy() { | |||
2817 | fn builtin_fn_def_copy() { | 2812 | fn builtin_fn_def_copy() { |
2818 | check_infer_with_mismatches( | 2813 | check_infer_with_mismatches( |
2819 | r#" | 2814 | r#" |
2820 | #[lang = "copy"] | 2815 | #[lang = "copy"] |
2821 | trait Copy {} | 2816 | trait Copy {} |
2822 | 2817 | ||
2823 | fn foo() {} | 2818 | fn foo() {} |
2824 | fn bar<T: Copy>(T) -> T {} | 2819 | fn bar<T: Copy>(T) -> T {} |
2825 | struct Struct(usize); | 2820 | struct Struct(usize); |
2826 | enum Enum { Variant(usize) } | 2821 | enum Enum { Variant(usize) } |
2827 | 2822 | ||
2828 | trait Test { fn test(&self) -> bool; } | 2823 | trait Test { fn test(&self) -> bool; } |
2829 | impl<T: Copy> Test for T {} | 2824 | impl<T: Copy> Test for T {} |
2830 | 2825 | ||
2831 | fn test() { | 2826 | fn test() { |
2832 | foo.test(); | 2827 | foo.test(); |
2833 | bar.test(); | 2828 | bar.test(); |
2834 | Struct.test(); | 2829 | Struct.test(); |
2835 | Enum::Variant.test(); | 2830 | Enum::Variant.test(); |
2836 | } | 2831 | }"#, |
2837 | "#, | ||
2838 | expect![[r#" | 2832 | expect![[r#" |
2839 | 41..43 '{}': () | 2833 | 41..43 '{}': () |
2840 | 60..61 'T': {unknown} | 2834 | 60..61 'T': {unknown} |
@@ -2858,18 +2852,17 @@ fn builtin_fn_def_copy() { | |||
2858 | fn builtin_fn_ptr_copy() { | 2852 | fn builtin_fn_ptr_copy() { |
2859 | check_infer_with_mismatches( | 2853 | check_infer_with_mismatches( |
2860 | r#" | 2854 | r#" |
2861 | #[lang = "copy"] | 2855 | #[lang = "copy"] |
2862 | trait Copy {} | 2856 | trait Copy {} |
2863 | 2857 | ||
2864 | trait Test { fn test(&self) -> bool; } | 2858 | trait Test { fn test(&self) -> bool; } |
2865 | impl<T: Copy> Test for T {} | 2859 | impl<T: Copy> Test for T {} |
2866 | 2860 | ||
2867 | fn test(f1: fn(), f2: fn(usize) -> u8, f3: fn(u8, u8) -> &u8) { | 2861 | fn test(f1: fn(), f2: fn(usize) -> u8, f3: fn(u8, u8) -> &u8) { |
2868 | f1.test(); | 2862 | f1.test(); |
2869 | f2.test(); | 2863 | f2.test(); |
2870 | f3.test(); | 2864 | f3.test(); |
2871 | } | 2865 | }"#, |
2872 | "#, | ||
2873 | expect![[r#" | 2866 | expect![[r#" |
2874 | 54..58 'self': &Self | 2867 | 54..58 'self': &Self |
2875 | 108..110 'f1': fn() | 2868 | 108..110 'f1': fn() |
@@ -2890,19 +2883,18 @@ fn builtin_fn_ptr_copy() { | |||
2890 | fn builtin_sized() { | 2883 | fn builtin_sized() { |
2891 | check_infer_with_mismatches( | 2884 | check_infer_with_mismatches( |
2892 | r#" | 2885 | r#" |
2893 | #[lang = "sized"] | 2886 | #[lang = "sized"] |
2894 | trait Sized {} | 2887 | trait Sized {} |
2895 | 2888 | ||
2896 | trait Test { fn test(&self) -> bool; } | 2889 | trait Test { fn test(&self) -> bool; } |
2897 | impl<T: Sized> Test for T {} | 2890 | impl<T: Sized> Test for T {} |
2898 | 2891 | ||
2899 | fn test() { | 2892 | fn test() { |
2900 | 1u8.test(); | 2893 | 1u8.test(); |
2901 | (*"foo").test(); // not Sized | 2894 | (*"foo").test(); // not Sized |
2902 | (1u8, 1u8).test(); | 2895 | (1u8, 1u8).test(); |
2903 | (1u8, *"foo").test(); // not Sized | 2896 | (1u8, *"foo").test(); // not Sized |
2904 | } | 2897 | }"#, |
2905 | "#, | ||
2906 | expect![[r#" | 2898 | expect![[r#" |
2907 | 56..60 'self': &Self | 2899 | 56..60 'self': &Self |
2908 | 113..228 '{ ...ized }': () | 2900 | 113..228 '{ ...ized }': () |
@@ -2972,19 +2964,18 @@ impl<A: Step> iter::Iterator for ops::Range<A> { | |||
2972 | fn infer_closure_arg() { | 2964 | fn infer_closure_arg() { |
2973 | check_infer( | 2965 | check_infer( |
2974 | r#" | 2966 | r#" |
2975 | //- /lib.rs | 2967 | //- /lib.rs |
2976 | 2968 | ||
2977 | enum Option<T> { | 2969 | enum Option<T> { |
2978 | None, | 2970 | None, |
2979 | Some(T) | 2971 | Some(T) |
2980 | } | 2972 | } |
2981 | 2973 | ||
2982 | fn foo() { | 2974 | fn foo() { |
2983 | let s = Option::None; | 2975 | let s = Option::None; |
2984 | let f = |x: Option<i32>| {}; | 2976 | let f = |x: Option<i32>| {}; |
2985 | (&f)(s) | 2977 | (&f)(s) |
2986 | } | 2978 | }"#, |
2987 | "#, | ||
2988 | expect![[r#" | 2979 | expect![[r#" |
2989 | 52..126 '{ ...)(s) }': () | 2980 | 52..126 '{ ...)(s) }': () |
2990 | 62..63 's': Option<i32> | 2981 | 62..63 's': Option<i32> |
@@ -3053,46 +3044,45 @@ fn infer_box_fn_arg() { | |||
3053 | // The type mismatch is a bug | 3044 | // The type mismatch is a bug |
3054 | check_infer_with_mismatches( | 3045 | check_infer_with_mismatches( |
3055 | r#" | 3046 | r#" |
3056 | //- /lib.rs deps:std | 3047 | //- /lib.rs deps:std |
3057 | 3048 | ||
3058 | #[lang = "fn_once"] | 3049 | #[lang = "fn_once"] |
3059 | pub trait FnOnce<Args> { | 3050 | pub trait FnOnce<Args> { |
3060 | type Output; | 3051 | type Output; |
3061 | 3052 | ||
3062 | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; | 3053 | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; |
3063 | } | 3054 | } |
3064 | 3055 | ||
3065 | #[lang = "deref"] | 3056 | #[lang = "deref"] |
3066 | pub trait Deref { | 3057 | pub trait Deref { |
3067 | type Target: ?Sized; | 3058 | type Target: ?Sized; |
3068 | 3059 | ||
3069 | fn deref(&self) -> &Self::Target; | 3060 | fn deref(&self) -> &Self::Target; |
3070 | } | 3061 | } |
3071 | 3062 | ||
3072 | #[lang = "owned_box"] | 3063 | #[lang = "owned_box"] |
3073 | pub struct Box<T: ?Sized> { | 3064 | pub struct Box<T: ?Sized> { |
3074 | inner: *mut T, | 3065 | inner: *mut T, |
3075 | } | 3066 | } |
3076 | 3067 | ||
3077 | impl<T: ?Sized> Deref for Box<T> { | 3068 | impl<T: ?Sized> Deref for Box<T> { |
3078 | type Target = T; | 3069 | type Target = T; |
3079 | 3070 | ||
3080 | fn deref(&self) -> &T { | 3071 | fn deref(&self) -> &T { |
3081 | &self.inner | 3072 | &self.inner |
3082 | } | 3073 | } |
3083 | } | 3074 | } |
3084 | 3075 | ||
3085 | enum Option<T> { | 3076 | enum Option<T> { |
3086 | None, | 3077 | None, |
3087 | Some(T) | 3078 | Some(T) |
3088 | } | 3079 | } |
3089 | 3080 | ||
3090 | fn foo() { | 3081 | fn foo() { |
3091 | let s = Option::None; | 3082 | let s = Option::None; |
3092 | let f: Box<dyn FnOnce(&Option<i32>)> = box (|ps| {}); | 3083 | let f: Box<dyn FnOnce(&Option<i32>)> = box (|ps| {}); |
3093 | f(&s); | 3084 | f(&s); |
3094 | } | 3085 | }"#, |
3095 | "#, | ||
3096 | expect![[r#" | 3086 | expect![[r#" |
3097 | 100..104 'self': Self | 3087 | 100..104 'self': Self |
3098 | 106..110 'args': Args | 3088 | 106..110 'args': Args |
@@ -3258,8 +3248,7 @@ fn f() { | |||
3258 | ().method(); | 3248 | ().method(); |
3259 | //^^^^^^^^^^^ u8 | 3249 | //^^^^^^^^^^^ u8 |
3260 | } | 3250 | } |
3261 | } | 3251 | }"#, |
3262 | "#, | ||
3263 | expect![[r#" | 3252 | expect![[r#" |
3264 | 46..50 'self': &Self | 3253 | 46..50 'self': &Self |
3265 | 58..63 '{ 0 }': u8 | 3254 | 58..63 '{ 0 }': u8 |
@@ -3313,8 +3302,7 @@ fn f() { | |||
3313 | fn inner() -> S { | 3302 | fn inner() -> S { |
3314 | let s = inner(); | 3303 | let s = inner(); |
3315 | } | 3304 | } |
3316 | } | 3305 | }"#, |
3317 | "#, | ||
3318 | expect![[r#" | 3306 | expect![[r#" |
3319 | 17..73 '{ ... } }': () | 3307 | 17..73 '{ ... } }': () |
3320 | 39..71 '{ ... }': () | 3308 | 39..71 '{ ... }': () |
@@ -3349,8 +3337,7 @@ fn test() { | |||
3349 | let x = A; | 3337 | let x = A; |
3350 | let y = A; | 3338 | let y = A; |
3351 | let r = x.do_op(y); | 3339 | let r = x.do_op(y); |
3352 | } | 3340 | }"#, |
3353 | "#, | ||
3354 | expect![[r#" | 3341 | expect![[r#" |
3355 | 63..67 'self': Self | 3342 | 63..67 'self': Self |
3356 | 69..72 'rhs': RHS | 3343 | 69..72 'rhs': RHS |
@@ -3399,9 +3386,7 @@ impl foo::Bar for F { | |||
3399 | fn foo() { | 3386 | fn foo() { |
3400 | use foo::Bar; | 3387 | use foo::Bar; |
3401 | let x = <F as Bar>::boo(); | 3388 | let x = <F as Bar>::boo(); |
3402 | } | 3389 | }"#, |
3403 | |||
3404 | "#, | ||
3405 | expect![[r#" | 3390 | expect![[r#" |
3406 | 132..163 '{ ... }': Bar::Output<Self> | 3391 | 132..163 '{ ... }': Bar::Output<Self> |
3407 | 146..153 'loop {}': ! | 3392 | 146..153 'loop {}': ! |
@@ -3413,3 +3398,79 @@ fn foo() { | |||
3413 | "#]], | 3398 | "#]], |
3414 | ); | 3399 | ); |
3415 | } | 3400 | } |
3401 | |||
3402 | #[test] | ||
3403 | fn renamed_extern_crate_in_block() { | ||
3404 | check_types( | ||
3405 | r#" | ||
3406 | //- /lib.rs crate:lib deps:serde | ||
3407 | use serde::Deserialize; | ||
3408 | |||
3409 | struct Foo {} | ||
3410 | |||
3411 | const _ : () = { | ||
3412 | extern crate serde as _serde; | ||
3413 | impl _serde::Deserialize for Foo { | ||
3414 | fn deserialize() -> u8 { 0 } | ||
3415 | } | ||
3416 | }; | ||
3417 | |||
3418 | fn foo() { | ||
3419 | Foo::deserialize(); | ||
3420 | //^^^^^^^^^^^^^^^^^^ u8 | ||
3421 | } | ||
3422 | |||
3423 | //- /serde.rs crate:serde | ||
3424 | |||
3425 | pub trait Deserialize { | ||
3426 | fn deserialize() -> u8; | ||
3427 | }"#, | ||
3428 | ); | ||
3429 | } | ||
3430 | |||
3431 | #[test] | ||
3432 | fn bin_op_adt_with_rhs_primitive() { | ||
3433 | check_infer_with_mismatches( | ||
3434 | r#" | ||
3435 | #[lang = "add"] | ||
3436 | pub trait Add<Rhs = Self> { | ||
3437 | type Output; | ||
3438 | fn add(self, rhs: Rhs) -> Self::Output; | ||
3439 | } | ||
3440 | |||
3441 | struct Wrapper(u32); | ||
3442 | impl Add<u32> for Wrapper { | ||
3443 | type Output = Self; | ||
3444 | fn add(self, rhs: u32) -> Wrapper { | ||
3445 | Wrapper(rhs) | ||
3446 | } | ||
3447 | } | ||
3448 | fn main(){ | ||
3449 | let wrapped = Wrapper(10); | ||
3450 | let num: u32 = 2; | ||
3451 | let res = wrapped + num; | ||
3452 | |||
3453 | }"#, | ||
3454 | expect![[r#" | ||
3455 | 72..76 'self': Self | ||
3456 | 78..81 'rhs': Rhs | ||
3457 | 192..196 'self': Wrapper | ||
3458 | 198..201 'rhs': u32 | ||
3459 | 219..247 '{ ... }': Wrapper | ||
3460 | 229..236 'Wrapper': Wrapper(u32) -> Wrapper | ||
3461 | 229..241 'Wrapper(rhs)': Wrapper | ||
3462 | 237..240 'rhs': u32 | ||
3463 | 259..345 '{ ...um; }': () | ||
3464 | 269..276 'wrapped': Wrapper | ||
3465 | 279..286 'Wrapper': Wrapper(u32) -> Wrapper | ||
3466 | 279..290 'Wrapper(10)': Wrapper | ||
3467 | 287..289 '10': u32 | ||
3468 | 300..303 'num': u32 | ||
3469 | 311..312 '2': u32 | ||
3470 | 322..325 'res': Wrapper | ||
3471 | 328..335 'wrapped': Wrapper | ||
3472 | 328..341 'wrapped + num': Wrapper | ||
3473 | 338..341 'num': u32 | ||
3474 | "#]], | ||
3475 | ) | ||
3476 | } | ||
diff --git a/crates/hir_ty/src/traits/chalk/tls.rs b/crates/hir_ty/src/tls.rs index 8892a63a9..87c671a42 100644 --- a/crates/hir_ty/src/traits/chalk/tls.rs +++ b/crates/hir_ty/src/tls.rs | |||
@@ -4,8 +4,10 @@ use std::fmt; | |||
4 | use chalk_ir::{AliasTy, GenericArg, Goal, Goals, Lifetime, ProgramClauseImplication}; | 4 | use chalk_ir::{AliasTy, GenericArg, Goal, Goals, Lifetime, ProgramClauseImplication}; |
5 | use itertools::Itertools; | 5 | use itertools::Itertools; |
6 | 6 | ||
7 | use super::{from_chalk, Interner}; | 7 | use crate::{ |
8 | use crate::{db::HirDatabase, from_assoc_type_id, CallableDefId}; | 8 | chalk_db, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, mapping::from_chalk, |
9 | CallableDefId, Interner, | ||
10 | }; | ||
9 | use hir_def::{AdtId, AssocContainerId, Lookup, TypeAliasId}; | 11 | use hir_def::{AdtId, AssocContainerId, Lookup, TypeAliasId}; |
10 | 12 | ||
11 | pub(crate) use unsafe_tls::{set_current_program, with_current_program}; | 13 | pub(crate) use unsafe_tls::{set_current_program, with_current_program}; |
@@ -15,7 +17,7 @@ pub(crate) struct DebugContext<'a>(&'a dyn HirDatabase); | |||
15 | impl DebugContext<'_> { | 17 | impl DebugContext<'_> { |
16 | pub(crate) fn debug_struct_id( | 18 | pub(crate) fn debug_struct_id( |
17 | &self, | 19 | &self, |
18 | id: super::AdtId, | 20 | id: chalk_db::AdtId, |
19 | f: &mut fmt::Formatter<'_>, | 21 | f: &mut fmt::Formatter<'_>, |
20 | ) -> Result<(), fmt::Error> { | 22 | ) -> Result<(), fmt::Error> { |
21 | let name = match id.0 { | 23 | let name = match id.0 { |
@@ -28,17 +30,17 @@ impl DebugContext<'_> { | |||
28 | 30 | ||
29 | pub(crate) fn debug_trait_id( | 31 | pub(crate) fn debug_trait_id( |
30 | &self, | 32 | &self, |
31 | id: super::TraitId, | 33 | id: chalk_db::TraitId, |
32 | fmt: &mut fmt::Formatter<'_>, | 34 | fmt: &mut fmt::Formatter<'_>, |
33 | ) -> Result<(), fmt::Error> { | 35 | ) -> Result<(), fmt::Error> { |
34 | let trait_: hir_def::TraitId = from_chalk(self.0, id); | 36 | let trait_: hir_def::TraitId = from_chalk_trait_id(id); |
35 | let trait_data = self.0.trait_data(trait_); | 37 | let trait_data = self.0.trait_data(trait_); |
36 | write!(fmt, "{}", trait_data.name) | 38 | write!(fmt, "{}", trait_data.name) |
37 | } | 39 | } |
38 | 40 | ||
39 | pub(crate) fn debug_assoc_type_id( | 41 | pub(crate) fn debug_assoc_type_id( |
40 | &self, | 42 | &self, |
41 | id: super::AssocTypeId, | 43 | id: chalk_db::AssocTypeId, |
42 | fmt: &mut fmt::Formatter<'_>, | 44 | fmt: &mut fmt::Formatter<'_>, |
43 | ) -> Result<(), fmt::Error> { | 45 | ) -> Result<(), fmt::Error> { |
44 | let type_alias: TypeAliasId = from_assoc_type_id(id); | 46 | let type_alias: TypeAliasId = from_assoc_type_id(id); |
diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs index e5e8cff33..9936d0803 100644 --- a/crates/hir_ty/src/traits.rs +++ b/crates/hir_ty/src/traits.rs | |||
@@ -1,28 +1,26 @@ | |||
1 | //! Trait solving using Chalk. | 1 | //! Trait solving using Chalk. |
2 | |||
2 | use std::env::var; | 3 | use std::env::var; |
3 | 4 | ||
4 | use base_db::CrateId; | ||
5 | use chalk_ir::cast::Cast; | 5 | use chalk_ir::cast::Cast; |
6 | use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver}; | 6 | use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver}; |
7 | |||
8 | use base_db::CrateId; | ||
7 | use hir_def::{lang_item::LangItemTarget, TraitId}; | 9 | use hir_def::{lang_item::LangItemTarget, TraitId}; |
8 | use stdx::panic_context; | 10 | use stdx::panic_context; |
9 | 11 | ||
10 | use crate::{ | 12 | use crate::{ |
11 | db::HirDatabase, AliasTy, Canonical, DebruijnIndex, HirDisplay, Substitution, Ty, TyKind, | 13 | db::HirDatabase, AliasEq, AliasTy, Canonical, DomainGoal, Guidance, HirDisplay, InEnvironment, |
12 | TypeWalk, WhereClause, | 14 | Interner, Solution, TraitRefExt, Ty, TyKind, WhereClause, |
13 | }; | 15 | }; |
14 | 16 | ||
15 | use self::chalk::{from_chalk, Interner, ToChalk}; | ||
16 | |||
17 | pub(crate) mod chalk; | ||
18 | |||
19 | /// This controls how much 'time' we give the Chalk solver before giving up. | 17 | /// This controls how much 'time' we give the Chalk solver before giving up. |
20 | const CHALK_SOLVER_FUEL: i32 = 100; | 18 | const CHALK_SOLVER_FUEL: i32 = 100; |
21 | 19 | ||
22 | #[derive(Debug, Copy, Clone)] | 20 | #[derive(Debug, Copy, Clone)] |
23 | struct ChalkContext<'a> { | 21 | pub(crate) struct ChalkContext<'a> { |
24 | db: &'a dyn HirDatabase, | 22 | pub(crate) db: &'a dyn HirDatabase, |
25 | krate: CrateId, | 23 | pub(crate) krate: CrateId, |
26 | } | 24 | } |
27 | 25 | ||
28 | fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> { | 26 | fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> { |
@@ -70,55 +68,6 @@ impl Default for TraitEnvironment { | |||
70 | } | 68 | } |
71 | } | 69 | } |
72 | 70 | ||
73 | /// Something (usually a goal), along with an environment. | ||
74 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
75 | pub struct InEnvironment<T> { | ||
76 | pub environment: chalk_ir::Environment<Interner>, | ||
77 | pub goal: T, | ||
78 | } | ||
79 | |||
80 | impl<T> InEnvironment<T> { | ||
81 | pub fn new(environment: chalk_ir::Environment<Interner>, value: T) -> InEnvironment<T> { | ||
82 | InEnvironment { environment, goal: value } | ||
83 | } | ||
84 | } | ||
85 | |||
86 | /// Something that needs to be proven (by Chalk) during type checking, e.g. that | ||
87 | /// a certain type implements a certain trait. Proving the Obligation might | ||
88 | /// result in additional information about inference variables. | ||
89 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
90 | pub enum DomainGoal { | ||
91 | Holds(WhereClause), | ||
92 | } | ||
93 | |||
94 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
95 | pub struct AliasEq { | ||
96 | pub alias: AliasTy, | ||
97 | pub ty: Ty, | ||
98 | } | ||
99 | |||
100 | impl TypeWalk for AliasEq { | ||
101 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
102 | self.ty.walk(f); | ||
103 | match &self.alias { | ||
104 | AliasTy::Projection(projection_ty) => projection_ty.walk(f), | ||
105 | AliasTy::Opaque(opaque) => opaque.walk(f), | ||
106 | } | ||
107 | } | ||
108 | |||
109 | fn walk_mut_binders( | ||
110 | &mut self, | ||
111 | f: &mut impl FnMut(&mut Ty, DebruijnIndex), | ||
112 | binders: DebruijnIndex, | ||
113 | ) { | ||
114 | self.ty.walk_mut_binders(f, binders); | ||
115 | match &mut self.alias { | ||
116 | AliasTy::Projection(projection_ty) => projection_ty.walk_mut_binders(f, binders), | ||
117 | AliasTy::Opaque(opaque) => opaque.walk_mut_binders(f, binders), | ||
118 | } | ||
119 | } | ||
120 | } | ||
121 | |||
122 | /// Solve a trait goal using Chalk. | 71 | /// Solve a trait goal using Chalk. |
123 | pub(crate) fn trait_solve_query( | 72 | pub(crate) fn trait_solve_query( |
124 | db: &dyn HirDatabase, | 73 | db: &dyn HirDatabase, |
@@ -130,6 +79,7 @@ pub(crate) fn trait_solve_query( | |||
130 | db.trait_data(it.hir_trait_id()).name.to_string() | 79 | db.trait_data(it.hir_trait_id()).name.to_string() |
131 | } | 80 | } |
132 | DomainGoal::Holds(WhereClause::AliasEq(_)) => "alias_eq".to_string(), | 81 | DomainGoal::Holds(WhereClause::AliasEq(_)) => "alias_eq".to_string(), |
82 | _ => "??".to_string(), | ||
133 | }); | 83 | }); |
134 | log::info!("trait_solve_query({})", goal.value.goal.display(db)); | 84 | log::info!("trait_solve_query({})", goal.value.goal.display(db)); |
135 | 85 | ||
@@ -138,19 +88,18 @@ pub(crate) fn trait_solve_query( | |||
138 | .. | 88 | .. |
139 | })) = &goal.value.goal | 89 | })) = &goal.value.goal |
140 | { | 90 | { |
141 | if let TyKind::BoundVar(_) = projection_ty.self_type_parameter().kind(&Interner) { | 91 | if let TyKind::BoundVar(_) = projection_ty.self_type_parameter(&Interner).kind(&Interner) { |
142 | // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible | 92 | // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible |
143 | return Some(Solution::Ambig(Guidance::Unknown)); | 93 | return Some(Solution::Ambig(Guidance::Unknown)); |
144 | } | 94 | } |
145 | } | 95 | } |
146 | 96 | ||
147 | let canonical = goal.to_chalk(db).cast(&Interner); | 97 | let canonical = goal.cast(&Interner); |
148 | 98 | ||
149 | // We currently don't deal with universes (I think / hope they're not yet | 99 | // We currently don't deal with universes (I think / hope they're not yet |
150 | // relevant for our use cases?) | 100 | // relevant for our use cases?) |
151 | let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 }; | 101 | let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 }; |
152 | let solution = solve(db, krate, &u_canonical); | 102 | solve(db, krate, &u_canonical) |
153 | solution.map(|solution| solution_from_chalk(db, solution)) | ||
154 | } | 103 | } |
155 | 104 | ||
156 | fn solve( | 105 | fn solve( |
@@ -197,7 +146,7 @@ fn solve( | |||
197 | // don't set the TLS for Chalk unless Chalk debugging is active, to make | 146 | // don't set the TLS for Chalk unless Chalk debugging is active, to make |
198 | // extra sure we only use it for debugging | 147 | // extra sure we only use it for debugging |
199 | let solution = | 148 | let solution = |
200 | if is_chalk_debug() { chalk::tls::set_current_program(db, solve) } else { solve() }; | 149 | if is_chalk_debug() { crate::tls::set_current_program(db, solve) } else { solve() }; |
201 | 150 | ||
202 | solution | 151 | solution |
203 | } | 152 | } |
@@ -218,69 +167,6 @@ fn is_chalk_print() -> bool { | |||
218 | std::env::var("CHALK_PRINT").is_ok() | 167 | std::env::var("CHALK_PRINT").is_ok() |
219 | } | 168 | } |
220 | 169 | ||
221 | fn solution_from_chalk( | ||
222 | db: &dyn HirDatabase, | ||
223 | solution: chalk_solve::Solution<Interner>, | ||
224 | ) -> Solution { | ||
225 | let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution<Interner>>| { | ||
226 | let result = from_chalk(db, subst); | ||
227 | SolutionVariables(result) | ||
228 | }; | ||
229 | match solution { | ||
230 | chalk_solve::Solution::Unique(constr_subst) => { | ||
231 | let subst = chalk_ir::Canonical { | ||
232 | value: constr_subst.value.subst, | ||
233 | binders: constr_subst.binders, | ||
234 | }; | ||
235 | Solution::Unique(convert_subst(subst)) | ||
236 | } | ||
237 | chalk_solve::Solution::Ambig(chalk_solve::Guidance::Definite(subst)) => { | ||
238 | Solution::Ambig(Guidance::Definite(convert_subst(subst))) | ||
239 | } | ||
240 | chalk_solve::Solution::Ambig(chalk_solve::Guidance::Suggested(subst)) => { | ||
241 | Solution::Ambig(Guidance::Suggested(convert_subst(subst))) | ||
242 | } | ||
243 | chalk_solve::Solution::Ambig(chalk_solve::Guidance::Unknown) => { | ||
244 | Solution::Ambig(Guidance::Unknown) | ||
245 | } | ||
246 | } | ||
247 | } | ||
248 | |||
249 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
250 | pub struct SolutionVariables(pub Canonical<Substitution>); | ||
251 | |||
252 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
253 | /// A (possible) solution for a proposed goal. | ||
254 | pub enum Solution { | ||
255 | /// The goal indeed holds, and there is a unique value for all existential | ||
256 | /// variables. | ||
257 | Unique(SolutionVariables), | ||
258 | |||
259 | /// The goal may be provable in multiple ways, but regardless we may have some guidance | ||
260 | /// for type inference. In this case, we don't return any lifetime | ||
261 | /// constraints, since we have not "committed" to any particular solution | ||
262 | /// yet. | ||
263 | Ambig(Guidance), | ||
264 | } | ||
265 | |||
266 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
267 | /// When a goal holds ambiguously (e.g., because there are multiple possible | ||
268 | /// solutions), we issue a set of *guidance* back to type inference. | ||
269 | pub enum Guidance { | ||
270 | /// The existential variables *must* have the given values if the goal is | ||
271 | /// ever to hold, but that alone isn't enough to guarantee the goal will | ||
272 | /// actually hold. | ||
273 | Definite(SolutionVariables), | ||
274 | |||
275 | /// There are multiple plausible values for the existentials, but the ones | ||
276 | /// here are suggested as the preferred choice heuristically. These should | ||
277 | /// be used for inference fallback only. | ||
278 | Suggested(SolutionVariables), | ||
279 | |||
280 | /// There's no useful information to feed back to type inference | ||
281 | Unknown, | ||
282 | } | ||
283 | |||
284 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | 170 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
285 | pub enum FnTrait { | 171 | pub enum FnTrait { |
286 | FnOnce, | 172 | FnOnce, |
diff --git a/crates/hir_ty/src/traits/chalk/mapping.rs b/crates/hir_ty/src/traits/chalk/mapping.rs deleted file mode 100644 index 452b357e8..000000000 --- a/crates/hir_ty/src/traits/chalk/mapping.rs +++ /dev/null | |||
@@ -1,575 +0,0 @@ | |||
1 | //! This module contains the implementations of the `ToChalk` trait, which | ||
2 | //! handles conversion between our data types and their corresponding types in | ||
3 | //! Chalk (in both directions); plus some helper functions for more specialized | ||
4 | //! conversions. | ||
5 | |||
6 | use chalk_ir::{cast::Cast, fold::shift::Shift, interner::HasInterner, LifetimeData}; | ||
7 | use chalk_solve::rust_ir; | ||
8 | |||
9 | use base_db::salsa::InternKey; | ||
10 | use hir_def::{GenericDefId, TypeAliasId}; | ||
11 | |||
12 | use crate::{ | ||
13 | db::HirDatabase, | ||
14 | primitive::UintTy, | ||
15 | traits::{Canonical, DomainGoal}, | ||
16 | AliasTy, CallableDefId, FnPointer, GenericArg, InEnvironment, OpaqueTy, ProjectionTy, | ||
17 | QuantifiedWhereClause, Scalar, Substitution, TraitRef, Ty, TypeWalk, WhereClause, | ||
18 | }; | ||
19 | |||
20 | use super::interner::*; | ||
21 | use super::*; | ||
22 | |||
23 | impl ToChalk for Ty { | ||
24 | type Chalk = chalk_ir::Ty<Interner>; | ||
25 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Ty<Interner> { | ||
26 | match self.into_inner() { | ||
27 | TyKind::Ref(m, ty) => ref_to_chalk(db, m, ty), | ||
28 | TyKind::Array(ty) => array_to_chalk(db, ty), | ||
29 | TyKind::Function(FnPointer { sig, substs, .. }) => { | ||
30 | let substitution = chalk_ir::FnSubst(substs.to_chalk(db).shifted_in(&Interner)); | ||
31 | chalk_ir::TyKind::Function(chalk_ir::FnPointer { | ||
32 | num_binders: 0, | ||
33 | sig, | ||
34 | substitution, | ||
35 | }) | ||
36 | .intern(&Interner) | ||
37 | } | ||
38 | TyKind::AssociatedType(assoc_type_id, substs) => { | ||
39 | let substitution = substs.to_chalk(db); | ||
40 | chalk_ir::TyKind::AssociatedType(assoc_type_id, substitution).intern(&Interner) | ||
41 | } | ||
42 | |||
43 | TyKind::OpaqueType(id, substs) => { | ||
44 | let substitution = substs.to_chalk(db); | ||
45 | chalk_ir::TyKind::OpaqueType(id, substitution).intern(&Interner) | ||
46 | } | ||
47 | |||
48 | TyKind::ForeignType(id) => chalk_ir::TyKind::Foreign(id).intern(&Interner), | ||
49 | |||
50 | TyKind::Scalar(scalar) => chalk_ir::TyKind::Scalar(scalar).intern(&Interner), | ||
51 | |||
52 | TyKind::Tuple(cardinality, substs) => { | ||
53 | let substitution = substs.to_chalk(db); | ||
54 | chalk_ir::TyKind::Tuple(cardinality, substitution).intern(&Interner) | ||
55 | } | ||
56 | TyKind::Raw(mutability, ty) => { | ||
57 | let ty = ty.to_chalk(db); | ||
58 | chalk_ir::TyKind::Raw(mutability, ty).intern(&Interner) | ||
59 | } | ||
60 | TyKind::Slice(ty) => chalk_ir::TyKind::Slice(ty.to_chalk(db)).intern(&Interner), | ||
61 | TyKind::Str => chalk_ir::TyKind::Str.intern(&Interner), | ||
62 | TyKind::FnDef(id, substs) => { | ||
63 | let substitution = substs.to_chalk(db); | ||
64 | chalk_ir::TyKind::FnDef(id, substitution).intern(&Interner) | ||
65 | } | ||
66 | TyKind::Never => chalk_ir::TyKind::Never.intern(&Interner), | ||
67 | |||
68 | TyKind::Closure(closure_id, substs) => { | ||
69 | let substitution = substs.to_chalk(db); | ||
70 | chalk_ir::TyKind::Closure(closure_id, substitution).intern(&Interner) | ||
71 | } | ||
72 | |||
73 | TyKind::Adt(adt_id, substs) => { | ||
74 | let substitution = substs.to_chalk(db); | ||
75 | chalk_ir::TyKind::Adt(adt_id, substitution).intern(&Interner) | ||
76 | } | ||
77 | TyKind::Alias(AliasTy::Projection(proj_ty)) => { | ||
78 | let associated_ty_id = proj_ty.associated_ty_id; | ||
79 | let substitution = proj_ty.substitution.to_chalk(db); | ||
80 | chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { | ||
81 | associated_ty_id, | ||
82 | substitution, | ||
83 | }) | ||
84 | .cast(&Interner) | ||
85 | .intern(&Interner) | ||
86 | } | ||
87 | TyKind::Alias(AliasTy::Opaque(opaque_ty)) => { | ||
88 | let opaque_ty_id = opaque_ty.opaque_ty_id; | ||
89 | let substitution = opaque_ty.substitution.to_chalk(db); | ||
90 | chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { opaque_ty_id, substitution }) | ||
91 | .cast(&Interner) | ||
92 | .intern(&Interner) | ||
93 | } | ||
94 | TyKind::Placeholder(idx) => idx.to_ty::<Interner>(&Interner), | ||
95 | TyKind::BoundVar(idx) => chalk_ir::TyKind::BoundVar(idx).intern(&Interner), | ||
96 | TyKind::InferenceVar(..) => panic!("uncanonicalized infer ty"), | ||
97 | TyKind::Dyn(dyn_ty) => { | ||
98 | let where_clauses = chalk_ir::QuantifiedWhereClauses::from_iter( | ||
99 | &Interner, | ||
100 | dyn_ty.bounds.value.interned().iter().cloned().map(|p| p.to_chalk(db)), | ||
101 | ); | ||
102 | let bounded_ty = chalk_ir::DynTy { | ||
103 | bounds: make_binders(where_clauses, 1), | ||
104 | lifetime: LifetimeData::Static.intern(&Interner), | ||
105 | }; | ||
106 | chalk_ir::TyKind::Dyn(bounded_ty).intern(&Interner) | ||
107 | } | ||
108 | TyKind::Unknown => chalk_ir::TyKind::Error.intern(&Interner), | ||
109 | } | ||
110 | } | ||
111 | fn from_chalk(db: &dyn HirDatabase, chalk: chalk_ir::Ty<Interner>) -> Self { | ||
112 | match chalk.data(&Interner).kind.clone() { | ||
113 | chalk_ir::TyKind::Error => TyKind::Unknown, | ||
114 | chalk_ir::TyKind::Array(ty, _size) => TyKind::Array(from_chalk(db, ty)), | ||
115 | chalk_ir::TyKind::Placeholder(idx) => TyKind::Placeholder(idx), | ||
116 | chalk_ir::TyKind::Alias(chalk_ir::AliasTy::Projection(proj)) => { | ||
117 | let associated_ty = proj.associated_ty_id; | ||
118 | let parameters = from_chalk(db, proj.substitution); | ||
119 | TyKind::Alias(AliasTy::Projection(ProjectionTy { | ||
120 | associated_ty_id: associated_ty, | ||
121 | substitution: parameters, | ||
122 | })) | ||
123 | } | ||
124 | chalk_ir::TyKind::Alias(chalk_ir::AliasTy::Opaque(opaque_ty)) => { | ||
125 | let opaque_ty_id = opaque_ty.opaque_ty_id; | ||
126 | let parameters = from_chalk(db, opaque_ty.substitution); | ||
127 | TyKind::Alias(AliasTy::Opaque(OpaqueTy { opaque_ty_id, substitution: parameters })) | ||
128 | } | ||
129 | chalk_ir::TyKind::Function(chalk_ir::FnPointer { | ||
130 | num_binders, | ||
131 | sig, | ||
132 | substitution, | ||
133 | .. | ||
134 | }) => { | ||
135 | assert_eq!(num_binders, 0); | ||
136 | let substs: Substitution = from_chalk( | ||
137 | db, | ||
138 | substitution.0.shifted_out(&Interner).expect("fn ptr should have no binders"), | ||
139 | ); | ||
140 | TyKind::Function(FnPointer { num_args: (substs.len(&Interner) - 1), sig, substs }) | ||
141 | } | ||
142 | chalk_ir::TyKind::BoundVar(idx) => TyKind::BoundVar(idx), | ||
143 | chalk_ir::TyKind::InferenceVar(_iv, _kind) => TyKind::Unknown, | ||
144 | chalk_ir::TyKind::Dyn(where_clauses) => { | ||
145 | assert_eq!(where_clauses.bounds.binders.len(&Interner), 1); | ||
146 | let bounds = where_clauses | ||
147 | .bounds | ||
148 | .skip_binders() | ||
149 | .iter(&Interner) | ||
150 | .map(|c| from_chalk(db, c.clone())); | ||
151 | TyKind::Dyn(crate::DynTy { | ||
152 | bounds: crate::Binders::new( | ||
153 | 1, | ||
154 | crate::QuantifiedWhereClauses::from_iter(&Interner, bounds), | ||
155 | ), | ||
156 | }) | ||
157 | } | ||
158 | |||
159 | chalk_ir::TyKind::Adt(adt_id, subst) => TyKind::Adt(adt_id, from_chalk(db, subst)), | ||
160 | chalk_ir::TyKind::AssociatedType(type_id, subst) => { | ||
161 | TyKind::AssociatedType(type_id, from_chalk(db, subst)) | ||
162 | } | ||
163 | |||
164 | chalk_ir::TyKind::OpaqueType(opaque_type_id, subst) => { | ||
165 | TyKind::OpaqueType(opaque_type_id, from_chalk(db, subst)) | ||
166 | } | ||
167 | |||
168 | chalk_ir::TyKind::Scalar(scalar) => TyKind::Scalar(scalar), | ||
169 | chalk_ir::TyKind::Tuple(cardinality, subst) => { | ||
170 | TyKind::Tuple(cardinality, from_chalk(db, subst)) | ||
171 | } | ||
172 | chalk_ir::TyKind::Raw(mutability, ty) => TyKind::Raw(mutability, from_chalk(db, ty)), | ||
173 | chalk_ir::TyKind::Slice(ty) => TyKind::Slice(from_chalk(db, ty)), | ||
174 | chalk_ir::TyKind::Ref(mutability, _lifetime, ty) => { | ||
175 | TyKind::Ref(mutability, from_chalk(db, ty)) | ||
176 | } | ||
177 | chalk_ir::TyKind::Str => TyKind::Str, | ||
178 | chalk_ir::TyKind::Never => TyKind::Never, | ||
179 | |||
180 | chalk_ir::TyKind::FnDef(fn_def_id, subst) => { | ||
181 | TyKind::FnDef(fn_def_id, from_chalk(db, subst)) | ||
182 | } | ||
183 | |||
184 | chalk_ir::TyKind::Closure(id, subst) => TyKind::Closure(id, from_chalk(db, subst)), | ||
185 | |||
186 | chalk_ir::TyKind::Foreign(foreign_def_id) => TyKind::ForeignType(foreign_def_id), | ||
187 | chalk_ir::TyKind::Generator(_, _) => unimplemented!(), // FIXME | ||
188 | chalk_ir::TyKind::GeneratorWitness(_, _) => unimplemented!(), // FIXME | ||
189 | } | ||
190 | .intern(&Interner) | ||
191 | } | ||
192 | } | ||
193 | |||
194 | /// We currently don't model lifetimes, but Chalk does. So, we have to insert a | ||
195 | /// fake lifetime here, because Chalks built-in logic may expect it to be there. | ||
196 | fn ref_to_chalk( | ||
197 | db: &dyn HirDatabase, | ||
198 | mutability: chalk_ir::Mutability, | ||
199 | ty: Ty, | ||
200 | ) -> chalk_ir::Ty<Interner> { | ||
201 | let arg = ty.to_chalk(db); | ||
202 | let lifetime = LifetimeData::Static.intern(&Interner); | ||
203 | chalk_ir::TyKind::Ref(mutability, lifetime, arg).intern(&Interner) | ||
204 | } | ||
205 | |||
206 | /// We currently don't model constants, but Chalk does. So, we have to insert a | ||
207 | /// fake constant here, because Chalks built-in logic may expect it to be there. | ||
208 | fn array_to_chalk(db: &dyn HirDatabase, ty: Ty) -> chalk_ir::Ty<Interner> { | ||
209 | let arg = ty.to_chalk(db); | ||
210 | let usize_ty = chalk_ir::TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner); | ||
211 | let const_ = chalk_ir::ConstData { | ||
212 | ty: usize_ty, | ||
213 | value: chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { interned: () }), | ||
214 | } | ||
215 | .intern(&Interner); | ||
216 | chalk_ir::TyKind::Array(arg, const_).intern(&Interner) | ||
217 | } | ||
218 | |||
219 | impl ToChalk for GenericArg { | ||
220 | type Chalk = chalk_ir::GenericArg<Interner>; | ||
221 | |||
222 | fn to_chalk(self, db: &dyn HirDatabase) -> Self::Chalk { | ||
223 | match self.interned { | ||
224 | crate::GenericArgData::Ty(ty) => ty.to_chalk(db).cast(&Interner), | ||
225 | } | ||
226 | } | ||
227 | |||
228 | fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self { | ||
229 | match chalk.interned() { | ||
230 | chalk_ir::GenericArgData::Ty(ty) => Ty::from_chalk(db, ty.clone()).cast(&Interner), | ||
231 | chalk_ir::GenericArgData::Lifetime(_) => unimplemented!(), | ||
232 | chalk_ir::GenericArgData::Const(_) => unimplemented!(), | ||
233 | } | ||
234 | } | ||
235 | } | ||
236 | |||
237 | impl ToChalk for Substitution { | ||
238 | type Chalk = chalk_ir::Substitution<Interner>; | ||
239 | |||
240 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Substitution<Interner> { | ||
241 | chalk_ir::Substitution::from_iter( | ||
242 | &Interner, | ||
243 | self.iter(&Interner).map(|ty| ty.clone().to_chalk(db)), | ||
244 | ) | ||
245 | } | ||
246 | |||
247 | fn from_chalk( | ||
248 | db: &dyn HirDatabase, | ||
249 | parameters: chalk_ir::Substitution<Interner>, | ||
250 | ) -> Substitution { | ||
251 | let tys = parameters.iter(&Interner).map(|p| from_chalk(db, p.clone())).collect(); | ||
252 | Substitution(tys) | ||
253 | } | ||
254 | } | ||
255 | |||
256 | impl ToChalk for TraitRef { | ||
257 | type Chalk = chalk_ir::TraitRef<Interner>; | ||
258 | |||
259 | fn to_chalk(self: TraitRef, db: &dyn HirDatabase) -> chalk_ir::TraitRef<Interner> { | ||
260 | let trait_id = self.trait_id; | ||
261 | let substitution = self.substitution.to_chalk(db); | ||
262 | chalk_ir::TraitRef { trait_id, substitution } | ||
263 | } | ||
264 | |||
265 | fn from_chalk(db: &dyn HirDatabase, trait_ref: chalk_ir::TraitRef<Interner>) -> Self { | ||
266 | let trait_id = trait_ref.trait_id; | ||
267 | let substs = from_chalk(db, trait_ref.substitution); | ||
268 | TraitRef { trait_id, substitution: substs } | ||
269 | } | ||
270 | } | ||
271 | |||
272 | impl ToChalk for hir_def::TraitId { | ||
273 | type Chalk = TraitId; | ||
274 | |||
275 | fn to_chalk(self, _db: &dyn HirDatabase) -> TraitId { | ||
276 | chalk_ir::TraitId(self.as_intern_id()) | ||
277 | } | ||
278 | |||
279 | fn from_chalk(_db: &dyn HirDatabase, trait_id: TraitId) -> hir_def::TraitId { | ||
280 | InternKey::from_intern_id(trait_id.0) | ||
281 | } | ||
282 | } | ||
283 | |||
284 | impl ToChalk for hir_def::ImplId { | ||
285 | type Chalk = ImplId; | ||
286 | |||
287 | fn to_chalk(self, _db: &dyn HirDatabase) -> ImplId { | ||
288 | chalk_ir::ImplId(self.as_intern_id()) | ||
289 | } | ||
290 | |||
291 | fn from_chalk(_db: &dyn HirDatabase, impl_id: ImplId) -> hir_def::ImplId { | ||
292 | InternKey::from_intern_id(impl_id.0) | ||
293 | } | ||
294 | } | ||
295 | |||
296 | impl ToChalk for CallableDefId { | ||
297 | type Chalk = FnDefId; | ||
298 | |||
299 | fn to_chalk(self, db: &dyn HirDatabase) -> FnDefId { | ||
300 | db.intern_callable_def(self).into() | ||
301 | } | ||
302 | |||
303 | fn from_chalk(db: &dyn HirDatabase, fn_def_id: FnDefId) -> CallableDefId { | ||
304 | db.lookup_intern_callable_def(fn_def_id.into()) | ||
305 | } | ||
306 | } | ||
307 | |||
308 | pub(crate) struct TypeAliasAsValue(pub(crate) TypeAliasId); | ||
309 | |||
310 | impl ToChalk for TypeAliasAsValue { | ||
311 | type Chalk = AssociatedTyValueId; | ||
312 | |||
313 | fn to_chalk(self, _db: &dyn HirDatabase) -> AssociatedTyValueId { | ||
314 | rust_ir::AssociatedTyValueId(self.0.as_intern_id()) | ||
315 | } | ||
316 | |||
317 | fn from_chalk( | ||
318 | _db: &dyn HirDatabase, | ||
319 | assoc_ty_value_id: AssociatedTyValueId, | ||
320 | ) -> TypeAliasAsValue { | ||
321 | TypeAliasAsValue(TypeAliasId::from_intern_id(assoc_ty_value_id.0)) | ||
322 | } | ||
323 | } | ||
324 | |||
325 | impl ToChalk for WhereClause { | ||
326 | type Chalk = chalk_ir::WhereClause<Interner>; | ||
327 | |||
328 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::WhereClause<Interner> { | ||
329 | match self { | ||
330 | WhereClause::Implemented(trait_ref) => { | ||
331 | chalk_ir::WhereClause::Implemented(trait_ref.to_chalk(db)) | ||
332 | } | ||
333 | WhereClause::AliasEq(alias_eq) => chalk_ir::WhereClause::AliasEq(alias_eq.to_chalk(db)), | ||
334 | } | ||
335 | } | ||
336 | |||
337 | fn from_chalk( | ||
338 | db: &dyn HirDatabase, | ||
339 | where_clause: chalk_ir::WhereClause<Interner>, | ||
340 | ) -> WhereClause { | ||
341 | match where_clause { | ||
342 | chalk_ir::WhereClause::Implemented(tr) => WhereClause::Implemented(from_chalk(db, tr)), | ||
343 | chalk_ir::WhereClause::AliasEq(alias_eq) => { | ||
344 | WhereClause::AliasEq(from_chalk(db, alias_eq)) | ||
345 | } | ||
346 | |||
347 | chalk_ir::WhereClause::LifetimeOutlives(_) => { | ||
348 | // we shouldn't get these from Chalk | ||
349 | panic!("encountered LifetimeOutlives from Chalk") | ||
350 | } | ||
351 | |||
352 | chalk_ir::WhereClause::TypeOutlives(_) => { | ||
353 | // we shouldn't get these from Chalk | ||
354 | panic!("encountered TypeOutlives from Chalk") | ||
355 | } | ||
356 | } | ||
357 | } | ||
358 | } | ||
359 | |||
360 | impl ToChalk for ProjectionTy { | ||
361 | type Chalk = chalk_ir::ProjectionTy<Interner>; | ||
362 | |||
363 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::ProjectionTy<Interner> { | ||
364 | chalk_ir::ProjectionTy { | ||
365 | associated_ty_id: self.associated_ty_id, | ||
366 | substitution: self.substitution.to_chalk(db), | ||
367 | } | ||
368 | } | ||
369 | |||
370 | fn from_chalk( | ||
371 | db: &dyn HirDatabase, | ||
372 | projection_ty: chalk_ir::ProjectionTy<Interner>, | ||
373 | ) -> ProjectionTy { | ||
374 | ProjectionTy { | ||
375 | associated_ty_id: projection_ty.associated_ty_id, | ||
376 | substitution: from_chalk(db, projection_ty.substitution), | ||
377 | } | ||
378 | } | ||
379 | } | ||
380 | impl ToChalk for OpaqueTy { | ||
381 | type Chalk = chalk_ir::OpaqueTy<Interner>; | ||
382 | |||
383 | fn to_chalk(self, db: &dyn HirDatabase) -> Self::Chalk { | ||
384 | chalk_ir::OpaqueTy { | ||
385 | opaque_ty_id: self.opaque_ty_id, | ||
386 | substitution: self.substitution.to_chalk(db), | ||
387 | } | ||
388 | } | ||
389 | |||
390 | fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self { | ||
391 | OpaqueTy { | ||
392 | opaque_ty_id: chalk.opaque_ty_id, | ||
393 | substitution: from_chalk(db, chalk.substitution), | ||
394 | } | ||
395 | } | ||
396 | } | ||
397 | |||
398 | impl ToChalk for AliasTy { | ||
399 | type Chalk = chalk_ir::AliasTy<Interner>; | ||
400 | |||
401 | fn to_chalk(self, db: &dyn HirDatabase) -> Self::Chalk { | ||
402 | match self { | ||
403 | AliasTy::Projection(projection_ty) => { | ||
404 | chalk_ir::AliasTy::Projection(projection_ty.to_chalk(db)) | ||
405 | } | ||
406 | AliasTy::Opaque(opaque_ty) => chalk_ir::AliasTy::Opaque(opaque_ty.to_chalk(db)), | ||
407 | } | ||
408 | } | ||
409 | |||
410 | fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self { | ||
411 | match chalk { | ||
412 | chalk_ir::AliasTy::Projection(projection_ty) => { | ||
413 | AliasTy::Projection(from_chalk(db, projection_ty)) | ||
414 | } | ||
415 | chalk_ir::AliasTy::Opaque(opaque_ty) => AliasTy::Opaque(from_chalk(db, opaque_ty)), | ||
416 | } | ||
417 | } | ||
418 | } | ||
419 | |||
420 | impl ToChalk for AliasEq { | ||
421 | type Chalk = chalk_ir::AliasEq<Interner>; | ||
422 | |||
423 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasEq<Interner> { | ||
424 | chalk_ir::AliasEq { alias: self.alias.to_chalk(db), ty: self.ty.to_chalk(db) } | ||
425 | } | ||
426 | |||
427 | fn from_chalk(db: &dyn HirDatabase, alias_eq: chalk_ir::AliasEq<Interner>) -> Self { | ||
428 | AliasEq { alias: from_chalk(db, alias_eq.alias), ty: from_chalk(db, alias_eq.ty) } | ||
429 | } | ||
430 | } | ||
431 | |||
432 | impl ToChalk for DomainGoal { | ||
433 | type Chalk = chalk_ir::DomainGoal<Interner>; | ||
434 | |||
435 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::DomainGoal<Interner> { | ||
436 | match self { | ||
437 | DomainGoal::Holds(WhereClause::Implemented(tr)) => tr.to_chalk(db).cast(&Interner), | ||
438 | DomainGoal::Holds(WhereClause::AliasEq(alias_eq)) => { | ||
439 | alias_eq.to_chalk(db).cast(&Interner) | ||
440 | } | ||
441 | } | ||
442 | } | ||
443 | |||
444 | fn from_chalk(_db: &dyn HirDatabase, _goal: chalk_ir::DomainGoal<Interner>) -> Self { | ||
445 | unimplemented!() | ||
446 | } | ||
447 | } | ||
448 | |||
449 | impl<T> ToChalk for Canonical<T> | ||
450 | where | ||
451 | T: ToChalk, | ||
452 | T::Chalk: HasInterner<Interner = Interner>, | ||
453 | { | ||
454 | type Chalk = chalk_ir::Canonical<T::Chalk>; | ||
455 | |||
456 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical<T::Chalk> { | ||
457 | let value = self.value.to_chalk(db); | ||
458 | chalk_ir::Canonical { value, binders: self.binders } | ||
459 | } | ||
460 | |||
461 | fn from_chalk(db: &dyn HirDatabase, canonical: chalk_ir::Canonical<T::Chalk>) -> Canonical<T> { | ||
462 | Canonical { binders: canonical.binders, value: from_chalk(db, canonical.value) } | ||
463 | } | ||
464 | } | ||
465 | |||
466 | impl<T: ToChalk> ToChalk for InEnvironment<T> | ||
467 | where | ||
468 | T::Chalk: chalk_ir::interner::HasInterner<Interner = Interner>, | ||
469 | { | ||
470 | type Chalk = chalk_ir::InEnvironment<T::Chalk>; | ||
471 | |||
472 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::InEnvironment<T::Chalk> { | ||
473 | chalk_ir::InEnvironment { environment: self.environment, goal: self.goal.to_chalk(db) } | ||
474 | } | ||
475 | |||
476 | fn from_chalk( | ||
477 | _db: &dyn HirDatabase, | ||
478 | _in_env: chalk_ir::InEnvironment<T::Chalk>, | ||
479 | ) -> InEnvironment<T> { | ||
480 | unimplemented!() | ||
481 | } | ||
482 | } | ||
483 | |||
484 | impl<T: ToChalk> ToChalk for crate::Binders<T> | ||
485 | where | ||
486 | T::Chalk: chalk_ir::interner::HasInterner<Interner = Interner>, | ||
487 | { | ||
488 | type Chalk = chalk_ir::Binders<T::Chalk>; | ||
489 | |||
490 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Binders<T::Chalk> { | ||
491 | chalk_ir::Binders::new( | ||
492 | chalk_ir::VariableKinds::from_iter( | ||
493 | &Interner, | ||
494 | std::iter::repeat(chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)) | ||
495 | .take(self.num_binders), | ||
496 | ), | ||
497 | self.value.to_chalk(db), | ||
498 | ) | ||
499 | } | ||
500 | |||
501 | fn from_chalk(db: &dyn HirDatabase, binders: chalk_ir::Binders<T::Chalk>) -> crate::Binders<T> { | ||
502 | let (v, b) = binders.into_value_and_skipped_binders(); | ||
503 | crate::Binders::new(b.len(&Interner), from_chalk(db, v)) | ||
504 | } | ||
505 | } | ||
506 | |||
507 | pub(super) fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T> | ||
508 | where | ||
509 | T: HasInterner<Interner = Interner>, | ||
510 | { | ||
511 | chalk_ir::Binders::new( | ||
512 | chalk_ir::VariableKinds::from_iter( | ||
513 | &Interner, | ||
514 | std::iter::repeat(chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)) | ||
515 | .take(num_vars), | ||
516 | ), | ||
517 | value, | ||
518 | ) | ||
519 | } | ||
520 | |||
521 | pub(super) fn convert_where_clauses( | ||
522 | db: &dyn HirDatabase, | ||
523 | def: GenericDefId, | ||
524 | substs: &Substitution, | ||
525 | ) -> Vec<chalk_ir::QuantifiedWhereClause<Interner>> { | ||
526 | let generic_predicates = db.generic_predicates(def); | ||
527 | let mut result = Vec::with_capacity(generic_predicates.len()); | ||
528 | for pred in generic_predicates.iter() { | ||
529 | result.push(pred.clone().subst(substs).to_chalk(db)); | ||
530 | } | ||
531 | result | ||
532 | } | ||
533 | |||
534 | pub(super) fn generic_predicate_to_inline_bound( | ||
535 | db: &dyn HirDatabase, | ||
536 | pred: &QuantifiedWhereClause, | ||
537 | self_ty: &Ty, | ||
538 | ) -> Option<chalk_ir::Binders<rust_ir::InlineBound<Interner>>> { | ||
539 | // An InlineBound is like a GenericPredicate, except the self type is left out. | ||
540 | // We don't have a special type for this, but Chalk does. | ||
541 | let self_ty_shifted_in = self_ty.clone().shift_bound_vars(DebruijnIndex::ONE); | ||
542 | match &pred.value { | ||
543 | WhereClause::Implemented(trait_ref) => { | ||
544 | if trait_ref.self_type_parameter() != &self_ty_shifted_in { | ||
545 | // we can only convert predicates back to type bounds if they | ||
546 | // have the expected self type | ||
547 | return None; | ||
548 | } | ||
549 | let args_no_self = trait_ref.substitution.interned(&Interner)[1..] | ||
550 | .iter() | ||
551 | .map(|ty| ty.clone().to_chalk(db).cast(&Interner)) | ||
552 | .collect(); | ||
553 | let trait_bound = rust_ir::TraitBound { trait_id: trait_ref.trait_id, args_no_self }; | ||
554 | Some(make_binders(rust_ir::InlineBound::TraitBound(trait_bound), pred.num_binders)) | ||
555 | } | ||
556 | WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => { | ||
557 | if projection_ty.self_type_parameter() != &self_ty_shifted_in { | ||
558 | return None; | ||
559 | } | ||
560 | let trait_ = projection_ty.trait_(db); | ||
561 | let args_no_self = projection_ty.substitution.interned(&Interner)[1..] | ||
562 | .iter() | ||
563 | .map(|ty| ty.clone().to_chalk(db).cast(&Interner)) | ||
564 | .collect(); | ||
565 | let alias_eq_bound = rust_ir::AliasEqBound { | ||
566 | value: ty.clone().to_chalk(db), | ||
567 | trait_bound: rust_ir::TraitBound { trait_id: trait_.to_chalk(db), args_no_self }, | ||
568 | associated_ty_id: projection_ty.associated_ty_id, | ||
569 | parameters: Vec::new(), // FIXME we don't support generic associated types yet | ||
570 | }; | ||
571 | Some(make_binders(rust_ir::InlineBound::AliasEqBound(alias_eq_bound), pred.num_binders)) | ||
572 | } | ||
573 | _ => None, | ||
574 | } | ||
575 | } | ||
diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs index b23e91b1b..2f04ee57a 100644 --- a/crates/hir_ty/src/utils.rs +++ b/crates/hir_ty/src/utils.rs | |||
@@ -1,22 +1,21 @@ | |||
1 | //! Helper functions for working with def, which don't need to be a separate | 1 | //! Helper functions for working with def, which don't need to be a separate |
2 | //! query, but can't be computed directly from `*Data` (ie, which need a `db`). | 2 | //! query, but can't be computed directly from `*Data` (ie, which need a `db`). |
3 | use std::sync::Arc; | ||
4 | 3 | ||
5 | use chalk_ir::{BoundVar, DebruijnIndex}; | 4 | use chalk_ir::{fold::Shift, BoundVar, DebruijnIndex}; |
6 | use hir_def::{ | 5 | use hir_def::{ |
7 | adt::VariantData, | ||
8 | db::DefDatabase, | 6 | db::DefDatabase, |
9 | generics::{ | 7 | generics::{ |
10 | GenericParams, TypeParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, | 8 | GenericParams, TypeParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, |
11 | }, | 9 | }, |
10 | intern::Interned, | ||
12 | path::Path, | 11 | path::Path, |
13 | resolver::{HasResolver, TypeNs}, | 12 | resolver::{HasResolver, TypeNs}, |
14 | type_ref::TypeRef, | 13 | type_ref::TypeRef, |
15 | AssocContainerId, GenericDefId, Lookup, TraitId, TypeAliasId, TypeParamId, VariantId, | 14 | AssocContainerId, GenericDefId, Lookup, TraitId, TypeAliasId, TypeParamId, |
16 | }; | 15 | }; |
17 | use hir_expand::name::{name, Name}; | 16 | use hir_expand::name::{name, Name}; |
18 | 17 | ||
19 | use crate::{db::HirDatabase, Interner, Substitution, TraitRef, TyKind, TypeWalk, WhereClause}; | 18 | use crate::{db::HirDatabase, Interner, Substitution, TraitRef, TraitRefExt, TyKind, WhereClause}; |
20 | 19 | ||
21 | fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { | 20 | fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { |
22 | let resolver = trait_.resolver(db); | 21 | let resolver = trait_.resolver(db); |
@@ -32,11 +31,10 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { | |||
32 | .filter_map(|pred| match pred { | 31 | .filter_map(|pred| match pred { |
33 | WherePredicate::ForLifetime { target, bound, .. } | 32 | WherePredicate::ForLifetime { target, bound, .. } |
34 | | WherePredicate::TypeBound { target, bound } => match target { | 33 | | WherePredicate::TypeBound { target, bound } => match target { |
35 | WherePredicateTypeTarget::TypeRef(TypeRef::Path(p)) | 34 | WherePredicateTypeTarget::TypeRef(type_ref) => match &**type_ref { |
36 | if p == &Path::from(name![Self]) => | 35 | TypeRef::Path(p) if p == &Path::from(name![Self]) => bound.as_path(), |
37 | { | 36 | _ => None, |
38 | bound.as_path() | 37 | }, |
39 | } | ||
40 | WherePredicateTypeTarget::TypeParam(local_id) if Some(*local_id) == trait_self => { | 38 | WherePredicateTypeTarget::TypeParam(local_id) if Some(*local_id) == trait_self => { |
41 | bound.as_path() | 39 | bound.as_path() |
42 | } | 40 | } |
@@ -66,19 +64,21 @@ fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef) -> Vec<Tr | |||
66 | .filter_map(|pred| { | 64 | .filter_map(|pred| { |
67 | pred.as_ref().filter_map(|pred| match pred.skip_binders() { | 65 | pred.as_ref().filter_map(|pred| match pred.skip_binders() { |
68 | // FIXME: how to correctly handle higher-ranked bounds here? | 66 | // FIXME: how to correctly handle higher-ranked bounds here? |
69 | WhereClause::Implemented(tr) => { | 67 | WhereClause::Implemented(tr) => Some( |
70 | Some(tr.clone().shift_bound_vars_out(DebruijnIndex::ONE)) | 68 | tr.clone() |
71 | } | 69 | .shifted_out_to(&Interner, DebruijnIndex::ONE) |
70 | .expect("FIXME unexpected higher-ranked trait bound"), | ||
71 | ), | ||
72 | _ => None, | 72 | _ => None, |
73 | }) | 73 | }) |
74 | }) | 74 | }) |
75 | .map(|pred| pred.subst(&trait_ref.substitution)) | 75 | .map(|pred| pred.substitute(&Interner, &trait_ref.substitution)) |
76 | .collect() | 76 | .collect() |
77 | } | 77 | } |
78 | 78 | ||
79 | /// Returns an iterator over the whole super trait hierarchy (including the | 79 | /// Returns an iterator over the whole super trait hierarchy (including the |
80 | /// trait itself). | 80 | /// trait itself). |
81 | pub(super) fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { | 81 | pub fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { |
82 | // we need to take care a bit here to avoid infinite loops in case of cycles | 82 | // we need to take care a bit here to avoid infinite loops in case of cycles |
83 | // (i.e. if we have `trait A: B; trait B: A;`) | 83 | // (i.e. if we have `trait A: B; trait B: A;`) |
84 | let mut result = vec![trait_]; | 84 | let mut result = vec![trait_]; |
@@ -103,6 +103,8 @@ pub(super) fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<Tra | |||
103 | /// we have `Self: Trait<u32, i32>` and `Trait<T, U>: OtherTrait<U>` we'll get | 103 | /// we have `Self: Trait<u32, i32>` and `Trait<T, U>: OtherTrait<U>` we'll get |
104 | /// `Self: OtherTrait<i32>`. | 104 | /// `Self: OtherTrait<i32>`. |
105 | pub(super) fn all_super_trait_refs(db: &dyn HirDatabase, trait_ref: TraitRef) -> Vec<TraitRef> { | 105 | pub(super) fn all_super_trait_refs(db: &dyn HirDatabase, trait_ref: TraitRef) -> Vec<TraitRef> { |
106 | // FIXME: replace by Chalk's `super_traits`, maybe make this a query | ||
107 | |||
106 | // we need to take care a bit here to avoid infinite loops in case of cycles | 108 | // we need to take care a bit here to avoid infinite loops in case of cycles |
107 | // (i.e. if we have `trait A: B; trait B: A;`) | 109 | // (i.e. if we have `trait A: B; trait B: A;`) |
108 | let mut result = vec![trait_ref]; | 110 | let mut result = vec![trait_ref]; |
@@ -132,25 +134,6 @@ pub(super) fn associated_type_by_name_including_super_traits( | |||
132 | }) | 134 | }) |
133 | } | 135 | } |
134 | 136 | ||
135 | pub(super) fn variant_data(db: &dyn DefDatabase, var: VariantId) -> Arc<VariantData> { | ||
136 | match var { | ||
137 | VariantId::StructId(it) => db.struct_data(it).variant_data.clone(), | ||
138 | VariantId::UnionId(it) => db.union_data(it).variant_data.clone(), | ||
139 | VariantId::EnumVariantId(it) => { | ||
140 | db.enum_data(it.parent).variants[it.local_id].variant_data.clone() | ||
141 | } | ||
142 | } | ||
143 | } | ||
144 | |||
145 | /// Helper for mutating `Arc<[T]>` (i.e. `Arc::make_mut` for Arc slices). | ||
146 | /// The underlying values are cloned if there are other strong references. | ||
147 | pub(crate) fn make_mut_slice<T: Clone>(a: &mut Arc<[T]>) -> &mut [T] { | ||
148 | if Arc::get_mut(a).is_none() { | ||
149 | *a = a.iter().cloned().collect(); | ||
150 | } | ||
151 | Arc::get_mut(a).unwrap() | ||
152 | } | ||
153 | |||
154 | pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics { | 137 | pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics { |
155 | let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def))); | 138 | let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def))); |
156 | Generics { def, params: db.generic_params(def), parent_generics } | 139 | Generics { def, params: db.generic_params(def), parent_generics } |
@@ -159,7 +142,7 @@ pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics { | |||
159 | #[derive(Debug)] | 142 | #[derive(Debug)] |
160 | pub(crate) struct Generics { | 143 | pub(crate) struct Generics { |
161 | def: GenericDefId, | 144 | def: GenericDefId, |
162 | pub(crate) params: Arc<GenericParams>, | 145 | pub(crate) params: Interned<GenericParams>, |
163 | parent_generics: Option<Box<Generics>>, | 146 | parent_generics: Option<Box<Generics>>, |
164 | } | 147 | } |
165 | 148 | ||
diff --git a/crates/hir_ty/src/walk.rs b/crates/hir_ty/src/walk.rs new file mode 100644 index 000000000..6ef1d5336 --- /dev/null +++ b/crates/hir_ty/src/walk.rs | |||
@@ -0,0 +1,150 @@ | |||
1 | //! The `TypeWalk` trait (probably to be replaced by Chalk's `Fold` and | ||
2 | //! `Visit`). | ||
3 | |||
4 | use chalk_ir::interner::HasInterner; | ||
5 | |||
6 | use crate::{ | ||
7 | AliasEq, AliasTy, Binders, CallableSig, FnSubst, GenericArg, GenericArgData, Interner, | ||
8 | OpaqueTy, ProjectionTy, Substitution, TraitRef, Ty, TyKind, WhereClause, | ||
9 | }; | ||
10 | |||
11 | /// This allows walking structures that contain types to do something with those | ||
12 | /// types, similar to Chalk's `Fold` trait. | ||
13 | pub trait TypeWalk { | ||
14 | fn walk(&self, f: &mut impl FnMut(&Ty)); | ||
15 | } | ||
16 | |||
17 | impl TypeWalk for Ty { | ||
18 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
19 | match self.kind(&Interner) { | ||
20 | TyKind::Alias(AliasTy::Projection(p_ty)) => { | ||
21 | for t in p_ty.substitution.iter(&Interner) { | ||
22 | t.walk(f); | ||
23 | } | ||
24 | } | ||
25 | TyKind::Alias(AliasTy::Opaque(o_ty)) => { | ||
26 | for t in o_ty.substitution.iter(&Interner) { | ||
27 | t.walk(f); | ||
28 | } | ||
29 | } | ||
30 | TyKind::Dyn(dyn_ty) => { | ||
31 | for p in dyn_ty.bounds.skip_binders().interned().iter() { | ||
32 | p.walk(f); | ||
33 | } | ||
34 | } | ||
35 | TyKind::Slice(ty) | ||
36 | | TyKind::Array(ty, _) | ||
37 | | TyKind::Ref(_, _, ty) | ||
38 | | TyKind::Raw(_, ty) => { | ||
39 | ty.walk(f); | ||
40 | } | ||
41 | TyKind::Function(fn_pointer) => { | ||
42 | fn_pointer.substitution.0.walk(f); | ||
43 | } | ||
44 | TyKind::Adt(_, substs) | ||
45 | | TyKind::FnDef(_, substs) | ||
46 | | TyKind::Tuple(_, substs) | ||
47 | | TyKind::OpaqueType(_, substs) | ||
48 | | TyKind::AssociatedType(_, substs) | ||
49 | | TyKind::Closure(.., substs) => { | ||
50 | substs.walk(f); | ||
51 | } | ||
52 | _ => {} | ||
53 | } | ||
54 | f(self); | ||
55 | } | ||
56 | } | ||
57 | |||
58 | impl<T: TypeWalk> TypeWalk for Vec<T> { | ||
59 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
60 | for t in self { | ||
61 | t.walk(f); | ||
62 | } | ||
63 | } | ||
64 | } | ||
65 | |||
66 | impl TypeWalk for OpaqueTy { | ||
67 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
68 | self.substitution.walk(f); | ||
69 | } | ||
70 | } | ||
71 | |||
72 | impl TypeWalk for ProjectionTy { | ||
73 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
74 | self.substitution.walk(f); | ||
75 | } | ||
76 | } | ||
77 | |||
78 | impl TypeWalk for AliasTy { | ||
79 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
80 | match self { | ||
81 | AliasTy::Projection(it) => it.walk(f), | ||
82 | AliasTy::Opaque(it) => it.walk(f), | ||
83 | } | ||
84 | } | ||
85 | } | ||
86 | |||
87 | impl TypeWalk for GenericArg { | ||
88 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
89 | match &self.interned() { | ||
90 | GenericArgData::Ty(ty) => { | ||
91 | ty.walk(f); | ||
92 | } | ||
93 | _ => {} | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | |||
98 | impl TypeWalk for Substitution { | ||
99 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
100 | for t in self.iter(&Interner) { | ||
101 | t.walk(f); | ||
102 | } | ||
103 | } | ||
104 | } | ||
105 | |||
106 | impl<T: TypeWalk + HasInterner<Interner = Interner>> TypeWalk for Binders<T> { | ||
107 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
108 | self.skip_binders().walk(f); | ||
109 | } | ||
110 | } | ||
111 | |||
112 | impl TypeWalk for TraitRef { | ||
113 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
114 | self.substitution.walk(f); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | impl TypeWalk for WhereClause { | ||
119 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
120 | match self { | ||
121 | WhereClause::Implemented(trait_ref) => trait_ref.walk(f), | ||
122 | WhereClause::AliasEq(alias_eq) => alias_eq.walk(f), | ||
123 | _ => {} | ||
124 | } | ||
125 | } | ||
126 | } | ||
127 | |||
128 | impl TypeWalk for CallableSig { | ||
129 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
130 | for t in self.params_and_return.iter() { | ||
131 | t.walk(f); | ||
132 | } | ||
133 | } | ||
134 | } | ||
135 | |||
136 | impl TypeWalk for AliasEq { | ||
137 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
138 | self.ty.walk(f); | ||
139 | match &self.alias { | ||
140 | AliasTy::Projection(projection_ty) => projection_ty.walk(f), | ||
141 | AliasTy::Opaque(opaque) => opaque.walk(f), | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | |||
146 | impl TypeWalk for FnSubst<Interner> { | ||
147 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
148 | self.0.walk(f) | ||
149 | } | ||
150 | } | ||
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index dd42116a7..1c911a8b2 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -20,12 +20,12 @@ use itertools::Itertools; | |||
20 | use rustc_hash::FxHashSet; | 20 | use rustc_hash::FxHashSet; |
21 | use syntax::{ | 21 | use syntax::{ |
22 | ast::{self, AstNode}, | 22 | ast::{self, AstNode}, |
23 | SyntaxNode, SyntaxNodePtr, TextRange, | 23 | SyntaxNode, SyntaxNodePtr, TextRange, TextSize, |
24 | }; | 24 | }; |
25 | use text_edit::TextEdit; | 25 | use text_edit::TextEdit; |
26 | use unlinked_file::UnlinkedFile; | 26 | use unlinked_file::UnlinkedFile; |
27 | 27 | ||
28 | use crate::{FileId, Label, SourceChange}; | 28 | use crate::{Assist, AssistId, AssistKind, FileId, Label, SourceChange}; |
29 | 29 | ||
30 | use self::fixes::DiagnosticWithFix; | 30 | use self::fixes::DiagnosticWithFix; |
31 | 31 | ||
@@ -35,7 +35,7 @@ pub struct Diagnostic { | |||
35 | pub message: String, | 35 | pub message: String, |
36 | pub range: TextRange, | 36 | pub range: TextRange, |
37 | pub severity: Severity, | 37 | pub severity: Severity, |
38 | pub fix: Option<Fix>, | 38 | pub fix: Option<Assist>, |
39 | pub unused: bool, | 39 | pub unused: bool, |
40 | pub code: Option<DiagnosticCode>, | 40 | pub code: Option<DiagnosticCode>, |
41 | } | 41 | } |
@@ -56,7 +56,7 @@ impl Diagnostic { | |||
56 | } | 56 | } |
57 | } | 57 | } |
58 | 58 | ||
59 | fn with_fix(self, fix: Option<Fix>) -> Self { | 59 | fn with_fix(self, fix: Option<Assist>) -> Self { |
60 | Self { fix, ..self } | 60 | Self { fix, ..self } |
61 | } | 61 | } |
62 | 62 | ||
@@ -69,21 +69,6 @@ impl Diagnostic { | |||
69 | } | 69 | } |
70 | } | 70 | } |
71 | 71 | ||
72 | #[derive(Debug)] | ||
73 | pub struct Fix { | ||
74 | pub label: Label, | ||
75 | pub source_change: SourceChange, | ||
76 | /// Allows to trigger the fix only when the caret is in the range given | ||
77 | pub fix_trigger_range: TextRange, | ||
78 | } | ||
79 | |||
80 | impl Fix { | ||
81 | fn new(label: &str, source_change: SourceChange, fix_trigger_range: TextRange) -> Self { | ||
82 | let label = Label::new(label); | ||
83 | Self { label, source_change, fix_trigger_range } | ||
84 | } | ||
85 | } | ||
86 | |||
87 | #[derive(Debug, Copy, Clone)] | 72 | #[derive(Debug, Copy, Clone)] |
88 | pub enum Severity { | 73 | pub enum Severity { |
89 | Error, | 74 | Error, |
@@ -99,6 +84,7 @@ pub struct DiagnosticsConfig { | |||
99 | pub(crate) fn diagnostics( | 84 | pub(crate) fn diagnostics( |
100 | db: &RootDatabase, | 85 | db: &RootDatabase, |
101 | config: &DiagnosticsConfig, | 86 | config: &DiagnosticsConfig, |
87 | resolve: bool, | ||
102 | file_id: FileId, | 88 | file_id: FileId, |
103 | ) -> Vec<Diagnostic> { | 89 | ) -> Vec<Diagnostic> { |
104 | let _p = profile::span("diagnostics"); | 90 | let _p = profile::span("diagnostics"); |
@@ -122,25 +108,25 @@ pub(crate) fn diagnostics( | |||
122 | let res = RefCell::new(res); | 108 | let res = RefCell::new(res); |
123 | let sink_builder = DiagnosticSinkBuilder::new() | 109 | let sink_builder = DiagnosticSinkBuilder::new() |
124 | .on::<hir::diagnostics::UnresolvedModule, _>(|d| { | 110 | .on::<hir::diagnostics::UnresolvedModule, _>(|d| { |
125 | res.borrow_mut().push(diagnostic_with_fix(d, &sema)); | 111 | res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve)); |
126 | }) | 112 | }) |
127 | .on::<hir::diagnostics::MissingFields, _>(|d| { | 113 | .on::<hir::diagnostics::MissingFields, _>(|d| { |
128 | res.borrow_mut().push(diagnostic_with_fix(d, &sema)); | 114 | res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve)); |
129 | }) | 115 | }) |
130 | .on::<hir::diagnostics::MissingOkOrSomeInTailExpr, _>(|d| { | 116 | .on::<hir::diagnostics::MissingOkOrSomeInTailExpr, _>(|d| { |
131 | res.borrow_mut().push(diagnostic_with_fix(d, &sema)); | 117 | res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve)); |
132 | }) | 118 | }) |
133 | .on::<hir::diagnostics::NoSuchField, _>(|d| { | 119 | .on::<hir::diagnostics::NoSuchField, _>(|d| { |
134 | res.borrow_mut().push(diagnostic_with_fix(d, &sema)); | 120 | res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve)); |
135 | }) | 121 | }) |
136 | .on::<hir::diagnostics::RemoveThisSemicolon, _>(|d| { | 122 | .on::<hir::diagnostics::RemoveThisSemicolon, _>(|d| { |
137 | res.borrow_mut().push(diagnostic_with_fix(d, &sema)); | 123 | res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve)); |
138 | }) | 124 | }) |
139 | .on::<hir::diagnostics::IncorrectCase, _>(|d| { | 125 | .on::<hir::diagnostics::IncorrectCase, _>(|d| { |
140 | res.borrow_mut().push(warning_with_fix(d, &sema)); | 126 | res.borrow_mut().push(warning_with_fix(d, &sema, resolve)); |
141 | }) | 127 | }) |
142 | .on::<hir::diagnostics::ReplaceFilterMapNextWithFindMap, _>(|d| { | 128 | .on::<hir::diagnostics::ReplaceFilterMapNextWithFindMap, _>(|d| { |
143 | res.borrow_mut().push(warning_with_fix(d, &sema)); | 129 | res.borrow_mut().push(warning_with_fix(d, &sema, resolve)); |
144 | }) | 130 | }) |
145 | .on::<hir::diagnostics::InactiveCode, _>(|d| { | 131 | .on::<hir::diagnostics::InactiveCode, _>(|d| { |
146 | // If there's inactive code somewhere in a macro, don't propagate to the call-site. | 132 | // If there's inactive code somewhere in a macro, don't propagate to the call-site. |
@@ -159,14 +145,16 @@ pub(crate) fn diagnostics( | |||
159 | ); | 145 | ); |
160 | }) | 146 | }) |
161 | .on::<UnlinkedFile, _>(|d| { | 147 | .on::<UnlinkedFile, _>(|d| { |
148 | // Limit diagnostic to the first few characters in the file. This matches how VS Code | ||
149 | // renders it with the full span, but on other editors, and is less invasive. | ||
150 | let range = sema.diagnostics_display_range(d.display_source()).range; | ||
151 | let range = range.intersect(TextRange::up_to(TextSize::of("..."))).unwrap_or(range); | ||
152 | |||
162 | // Override severity and mark as unused. | 153 | // Override severity and mark as unused. |
163 | res.borrow_mut().push( | 154 | res.borrow_mut().push( |
164 | Diagnostic::hint( | 155 | Diagnostic::hint(range, d.message()) |
165 | sema.diagnostics_display_range(d.display_source()).range, | 156 | .with_fix(d.fix(&sema, resolve)) |
166 | d.message(), | 157 | .with_code(Some(d.code())), |
167 | ) | ||
168 | .with_fix(d.fix(&sema)) | ||
169 | .with_code(Some(d.code())), | ||
170 | ); | 158 | ); |
171 | }) | 159 | }) |
172 | .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| { | 160 | .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| { |
@@ -221,15 +209,23 @@ pub(crate) fn diagnostics( | |||
221 | res.into_inner() | 209 | res.into_inner() |
222 | } | 210 | } |
223 | 211 | ||
224 | fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { | 212 | fn diagnostic_with_fix<D: DiagnosticWithFix>( |
213 | d: &D, | ||
214 | sema: &Semantics<RootDatabase>, | ||
215 | resolve: bool, | ||
216 | ) -> Diagnostic { | ||
225 | Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message()) | 217 | Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message()) |
226 | .with_fix(d.fix(&sema)) | 218 | .with_fix(d.fix(&sema, resolve)) |
227 | .with_code(Some(d.code())) | 219 | .with_code(Some(d.code())) |
228 | } | 220 | } |
229 | 221 | ||
230 | fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { | 222 | fn warning_with_fix<D: DiagnosticWithFix>( |
223 | d: &D, | ||
224 | sema: &Semantics<RootDatabase>, | ||
225 | resolve: bool, | ||
226 | ) -> Diagnostic { | ||
231 | Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message()) | 227 | Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message()) |
232 | .with_fix(d.fix(&sema)) | 228 | .with_fix(d.fix(&sema, resolve)) |
233 | .with_code(Some(d.code())) | 229 | .with_code(Some(d.code())) |
234 | } | 230 | } |
235 | 231 | ||
@@ -259,7 +255,8 @@ fn check_unnecessary_braces_in_use_statement( | |||
259 | 255 | ||
260 | acc.push( | 256 | acc.push( |
261 | Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string()) | 257 | Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string()) |
262 | .with_fix(Some(Fix::new( | 258 | .with_fix(Some(fix( |
259 | "remove_braces", | ||
263 | "Remove unnecessary braces", | 260 | "Remove unnecessary braces", |
264 | SourceChange::from_text_edit(file_id, edit), | 261 | SourceChange::from_text_edit(file_id, edit), |
265 | use_range, | 262 | use_range, |
@@ -282,6 +279,23 @@ fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement( | |||
282 | None | 279 | None |
283 | } | 280 | } |
284 | 281 | ||
282 | fn fix(id: &'static str, label: &str, source_change: SourceChange, target: TextRange) -> Assist { | ||
283 | let mut res = unresolved_fix(id, label, target); | ||
284 | res.source_change = Some(source_change); | ||
285 | res | ||
286 | } | ||
287 | |||
288 | fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist { | ||
289 | assert!(!id.contains(' ')); | ||
290 | Assist { | ||
291 | id: AssistId(id, AssistKind::QuickFix), | ||
292 | label: Label::new(label), | ||
293 | group: None, | ||
294 | target, | ||
295 | source_change: None, | ||
296 | } | ||
297 | } | ||
298 | |||
285 | #[cfg(test)] | 299 | #[cfg(test)] |
286 | mod tests { | 300 | mod tests { |
287 | use expect_test::{expect, Expect}; | 301 | use expect_test::{expect, Expect}; |
@@ -300,16 +314,17 @@ mod tests { | |||
300 | 314 | ||
301 | let (analysis, file_position) = fixture::position(ra_fixture_before); | 315 | let (analysis, file_position) = fixture::position(ra_fixture_before); |
302 | let diagnostic = analysis | 316 | let diagnostic = analysis |
303 | .diagnostics(&DiagnosticsConfig::default(), file_position.file_id) | 317 | .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id) |
304 | .unwrap() | 318 | .unwrap() |
305 | .pop() | 319 | .pop() |
306 | .unwrap(); | 320 | .unwrap(); |
307 | let fix = diagnostic.fix.unwrap(); | 321 | let fix = diagnostic.fix.unwrap(); |
308 | let actual = { | 322 | let actual = { |
309 | let file_id = *fix.source_change.source_file_edits.keys().next().unwrap(); | 323 | let source_change = fix.source_change.unwrap(); |
324 | let file_id = *source_change.source_file_edits.keys().next().unwrap(); | ||
310 | let mut actual = analysis.file_text(file_id).unwrap().to_string(); | 325 | let mut actual = analysis.file_text(file_id).unwrap().to_string(); |
311 | 326 | ||
312 | for edit in fix.source_change.source_file_edits.values() { | 327 | for edit in source_change.source_file_edits.values() { |
313 | edit.apply(&mut actual); | 328 | edit.apply(&mut actual); |
314 | } | 329 | } |
315 | actual | 330 | actual |
@@ -317,9 +332,9 @@ mod tests { | |||
317 | 332 | ||
318 | assert_eq_text!(&after, &actual); | 333 | assert_eq_text!(&after, &actual); |
319 | assert!( | 334 | assert!( |
320 | fix.fix_trigger_range.contains_inclusive(file_position.offset), | 335 | fix.target.contains_inclusive(file_position.offset), |
321 | "diagnostic fix range {:?} does not touch cursor position {:?}", | 336 | "diagnostic fix range {:?} does not touch cursor position {:?}", |
322 | fix.fix_trigger_range, | 337 | fix.target, |
323 | file_position.offset | 338 | file_position.offset |
324 | ); | 339 | ); |
325 | } | 340 | } |
@@ -328,7 +343,7 @@ mod tests { | |||
328 | fn check_no_fix(ra_fixture: &str) { | 343 | fn check_no_fix(ra_fixture: &str) { |
329 | let (analysis, file_position) = fixture::position(ra_fixture); | 344 | let (analysis, file_position) = fixture::position(ra_fixture); |
330 | let diagnostic = analysis | 345 | let diagnostic = analysis |
331 | .diagnostics(&DiagnosticsConfig::default(), file_position.file_id) | 346 | .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id) |
332 | .unwrap() | 347 | .unwrap() |
333 | .pop() | 348 | .pop() |
334 | .unwrap(); | 349 | .unwrap(); |
@@ -342,7 +357,7 @@ mod tests { | |||
342 | let diagnostics = files | 357 | let diagnostics = files |
343 | .into_iter() | 358 | .into_iter() |
344 | .flat_map(|file_id| { | 359 | .flat_map(|file_id| { |
345 | analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap() | 360 | analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap() |
346 | }) | 361 | }) |
347 | .collect::<Vec<_>>(); | 362 | .collect::<Vec<_>>(); |
348 | assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics); | 363 | assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics); |
@@ -350,7 +365,8 @@ mod tests { | |||
350 | 365 | ||
351 | fn check_expect(ra_fixture: &str, expect: Expect) { | 366 | fn check_expect(ra_fixture: &str, expect: Expect) { |
352 | let (analysis, file_id) = fixture::file(ra_fixture); | 367 | let (analysis, file_id) = fixture::file(ra_fixture); |
353 | let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap(); | 368 | let diagnostics = |
369 | analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap(); | ||
354 | expect.assert_debug_eq(&diagnostics) | 370 | expect.assert_debug_eq(&diagnostics) |
355 | } | 371 | } |
356 | 372 | ||
@@ -663,24 +679,31 @@ fn test_fn() { | |||
663 | range: 0..8, | 679 | range: 0..8, |
664 | severity: Error, | 680 | severity: Error, |
665 | fix: Some( | 681 | fix: Some( |
666 | Fix { | 682 | Assist { |
683 | id: AssistId( | ||
684 | "create_module", | ||
685 | QuickFix, | ||
686 | ), | ||
667 | label: "Create module", | 687 | label: "Create module", |
668 | source_change: SourceChange { | 688 | group: None, |
669 | source_file_edits: {}, | 689 | target: 0..8, |
670 | file_system_edits: [ | 690 | source_change: Some( |
671 | CreateFile { | 691 | SourceChange { |
672 | dst: AnchoredPathBuf { | 692 | source_file_edits: {}, |
673 | anchor: FileId( | 693 | file_system_edits: [ |
674 | 0, | 694 | CreateFile { |
675 | ), | 695 | dst: AnchoredPathBuf { |
676 | path: "foo.rs", | 696 | anchor: FileId( |
697 | 0, | ||
698 | ), | ||
699 | path: "foo.rs", | ||
700 | }, | ||
701 | initial_contents: "", | ||
677 | }, | 702 | }, |
678 | initial_contents: "", | 703 | ], |
679 | }, | 704 | is_snippet: false, |
680 | ], | 705 | }, |
681 | is_snippet: false, | 706 | ), |
682 | }, | ||
683 | fix_trigger_range: 0..8, | ||
684 | }, | 707 | }, |
685 | ), | 708 | ), |
686 | unused: false, | 709 | unused: false, |
@@ -702,7 +725,7 @@ fn test_fn() { | |||
702 | expect![[r#" | 725 | expect![[r#" |
703 | [ | 726 | [ |
704 | Diagnostic { | 727 | Diagnostic { |
705 | message: "unresolved macro call", | 728 | message: "unresolved macro `foo::bar!`", |
706 | range: 5..8, | 729 | range: 5..8, |
707 | severity: Error, | 730 | severity: Error, |
708 | fix: None, | 731 | fix: None, |
@@ -888,10 +911,11 @@ struct Foo { | |||
888 | 911 | ||
889 | let (analysis, file_id) = fixture::file(r#"mod foo;"#); | 912 | let (analysis, file_id) = fixture::file(r#"mod foo;"#); |
890 | 913 | ||
891 | let diagnostics = analysis.diagnostics(&config, file_id).unwrap(); | 914 | let diagnostics = analysis.diagnostics(&config, true, file_id).unwrap(); |
892 | assert!(diagnostics.is_empty()); | 915 | assert!(diagnostics.is_empty()); |
893 | 916 | ||
894 | let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap(); | 917 | let diagnostics = |
918 | analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap(); | ||
895 | assert!(!diagnostics.is_empty()); | 919 | assert!(!diagnostics.is_empty()); |
896 | } | 920 | } |
897 | 921 | ||
@@ -997,8 +1021,9 @@ impl TestStruct { | |||
997 | let expected = r#"fn foo() {}"#; | 1021 | let expected = r#"fn foo() {}"#; |
998 | 1022 | ||
999 | let (analysis, file_position) = fixture::position(input); | 1023 | let (analysis, file_position) = fixture::position(input); |
1000 | let diagnostics = | 1024 | let diagnostics = analysis |
1001 | analysis.diagnostics(&DiagnosticsConfig::default(), file_position.file_id).unwrap(); | 1025 | .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id) |
1026 | .unwrap(); | ||
1002 | assert_eq!(diagnostics.len(), 1); | 1027 | assert_eq!(diagnostics.len(), 1); |
1003 | 1028 | ||
1004 | check_fix(input, expected); | 1029 | check_fix(input, expected); |
diff --git a/crates/ide/src/diagnostics/field_shorthand.rs b/crates/ide/src/diagnostics/field_shorthand.rs index 5c89e2170..2b1787f9b 100644 --- a/crates/ide/src/diagnostics/field_shorthand.rs +++ b/crates/ide/src/diagnostics/field_shorthand.rs | |||
@@ -5,7 +5,7 @@ use ide_db::{base_db::FileId, source_change::SourceChange}; | |||
5 | use syntax::{ast, match_ast, AstNode, SyntaxNode}; | 5 | use syntax::{ast, match_ast, AstNode, SyntaxNode}; |
6 | use text_edit::TextEdit; | 6 | use text_edit::TextEdit; |
7 | 7 | ||
8 | use crate::{Diagnostic, Fix}; | 8 | use crate::{diagnostics::fix, Diagnostic}; |
9 | 9 | ||
10 | pub(super) fn check(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) { | 10 | pub(super) fn check(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) { |
11 | match_ast! { | 11 | match_ast! { |
@@ -47,7 +47,8 @@ fn check_expr_field_shorthand( | |||
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()).with_fix( | 49 | Diagnostic::hint(field_range, "Shorthand struct initialization".to_string()).with_fix( |
50 | Some(Fix::new( | 50 | Some(fix( |
51 | "use_expr_field_shorthand", | ||
51 | "Use struct shorthand initialization", | 52 | "Use struct shorthand initialization", |
52 | SourceChange::from_text_edit(file_id, edit), | 53 | SourceChange::from_text_edit(file_id, edit), |
53 | field_range, | 54 | field_range, |
@@ -86,7 +87,8 @@ fn check_pat_field_shorthand( | |||
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_fix( | 89 | acc.push(Diagnostic::hint(field_range, "Shorthand struct pattern".to_string()).with_fix( |
89 | Some(Fix::new( | 90 | Some(fix( |
91 | "use_pat_field_shorthand", | ||
90 | "Use struct field shorthand", | 92 | "Use struct field shorthand", |
91 | SourceChange::from_text_edit(file_id, edit), | 93 | SourceChange::from_text_edit(file_id, edit), |
92 | field_range, | 94 | field_range, |
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs index 5fb3e2d91..7be8b3459 100644 --- a/crates/ide/src/diagnostics/fixes.rs +++ b/crates/ide/src/diagnostics/fixes.rs | |||
@@ -20,20 +20,30 @@ use syntax::{ | |||
20 | }; | 20 | }; |
21 | use text_edit::TextEdit; | 21 | use text_edit::TextEdit; |
22 | 22 | ||
23 | use crate::{diagnostics::Fix, references::rename::rename_with_semantics, FilePosition}; | 23 | use crate::{ |
24 | diagnostics::{fix, unresolved_fix}, | ||
25 | references::rename::rename_with_semantics, | ||
26 | Assist, FilePosition, | ||
27 | }; | ||
24 | 28 | ||
25 | /// A [Diagnostic] that potentially has a fix available. | 29 | /// A [Diagnostic] that potentially has a fix available. |
26 | /// | 30 | /// |
27 | /// [Diagnostic]: hir::diagnostics::Diagnostic | 31 | /// [Diagnostic]: hir::diagnostics::Diagnostic |
28 | pub(crate) trait DiagnosticWithFix: Diagnostic { | 32 | pub(crate) trait DiagnosticWithFix: Diagnostic { |
29 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix>; | 33 | /// `resolve` determines if the diagnostic should fill in the `edit` field |
34 | /// of the assist. | ||
35 | /// | ||
36 | /// If `resolve` is false, the edit will be computed later, on demand, and | ||
37 | /// can be omitted. | ||
38 | fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist>; | ||
30 | } | 39 | } |
31 | 40 | ||
32 | impl DiagnosticWithFix for UnresolvedModule { | 41 | impl DiagnosticWithFix for UnresolvedModule { |
33 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { | 42 | fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { |
34 | let root = sema.db.parse_or_expand(self.file)?; | 43 | let root = sema.db.parse_or_expand(self.file)?; |
35 | let unresolved_module = self.decl.to_node(&root); | 44 | let unresolved_module = self.decl.to_node(&root); |
36 | Some(Fix::new( | 45 | Some(fix( |
46 | "create_module", | ||
37 | "Create module", | 47 | "Create module", |
38 | FileSystemEdit::CreateFile { | 48 | FileSystemEdit::CreateFile { |
39 | dst: AnchoredPathBuf { | 49 | dst: AnchoredPathBuf { |
@@ -49,7 +59,7 @@ impl DiagnosticWithFix for UnresolvedModule { | |||
49 | } | 59 | } |
50 | 60 | ||
51 | impl DiagnosticWithFix for NoSuchField { | 61 | impl DiagnosticWithFix for NoSuchField { |
52 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { | 62 | fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { |
53 | let root = sema.db.parse_or_expand(self.file)?; | 63 | let root = sema.db.parse_or_expand(self.file)?; |
54 | missing_record_expr_field_fix( | 64 | missing_record_expr_field_fix( |
55 | &sema, | 65 | &sema, |
@@ -60,7 +70,7 @@ impl DiagnosticWithFix for NoSuchField { | |||
60 | } | 70 | } |
61 | 71 | ||
62 | impl DiagnosticWithFix for MissingFields { | 72 | impl DiagnosticWithFix for MissingFields { |
63 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { | 73 | fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { |
64 | // Note that although we could add a diagnostics to | 74 | // Note that although we could add a diagnostics to |
65 | // fill the missing tuple field, e.g : | 75 | // fill the missing tuple field, e.g : |
66 | // `struct A(usize);` | 76 | // `struct A(usize);` |
@@ -86,7 +96,8 @@ impl DiagnosticWithFix for MissingFields { | |||
86 | .into_text_edit(&mut builder); | 96 | .into_text_edit(&mut builder); |
87 | builder.finish() | 97 | builder.finish() |
88 | }; | 98 | }; |
89 | Some(Fix::new( | 99 | Some(fix( |
100 | "fill_missing_fields", | ||
90 | "Fill struct fields", | 101 | "Fill struct fields", |
91 | SourceChange::from_text_edit(self.file.original_file(sema.db), edit), | 102 | SourceChange::from_text_edit(self.file.original_file(sema.db), edit), |
92 | sema.original_range(&field_list_parent.syntax()).range, | 103 | sema.original_range(&field_list_parent.syntax()).range, |
@@ -95,7 +106,7 @@ impl DiagnosticWithFix for MissingFields { | |||
95 | } | 106 | } |
96 | 107 | ||
97 | impl DiagnosticWithFix for MissingOkOrSomeInTailExpr { | 108 | impl DiagnosticWithFix for MissingOkOrSomeInTailExpr { |
98 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { | 109 | fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { |
99 | let root = sema.db.parse_or_expand(self.file)?; | 110 | let root = sema.db.parse_or_expand(self.file)?; |
100 | let tail_expr = self.expr.to_node(&root); | 111 | let tail_expr = self.expr.to_node(&root); |
101 | let tail_expr_range = tail_expr.syntax().text_range(); | 112 | let tail_expr_range = tail_expr.syntax().text_range(); |
@@ -103,12 +114,12 @@ impl DiagnosticWithFix for MissingOkOrSomeInTailExpr { | |||
103 | let edit = TextEdit::replace(tail_expr_range, replacement); | 114 | let edit = TextEdit::replace(tail_expr_range, replacement); |
104 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); | 115 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); |
105 | let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" }; | 116 | let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" }; |
106 | Some(Fix::new(name, source_change, tail_expr_range)) | 117 | Some(fix("wrap_tail_expr", name, source_change, tail_expr_range)) |
107 | } | 118 | } |
108 | } | 119 | } |
109 | 120 | ||
110 | impl DiagnosticWithFix for RemoveThisSemicolon { | 121 | impl DiagnosticWithFix for RemoveThisSemicolon { |
111 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { | 122 | fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { |
112 | let root = sema.db.parse_or_expand(self.file)?; | 123 | let root = sema.db.parse_or_expand(self.file)?; |
113 | 124 | ||
114 | let semicolon = self | 125 | let semicolon = self |
@@ -123,12 +134,12 @@ impl DiagnosticWithFix for RemoveThisSemicolon { | |||
123 | let edit = TextEdit::delete(semicolon); | 134 | let edit = TextEdit::delete(semicolon); |
124 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); | 135 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); |
125 | 136 | ||
126 | Some(Fix::new("Remove this semicolon", source_change, semicolon)) | 137 | Some(fix("remove_semicolon", "Remove this semicolon", source_change, semicolon)) |
127 | } | 138 | } |
128 | } | 139 | } |
129 | 140 | ||
130 | impl DiagnosticWithFix for IncorrectCase { | 141 | impl DiagnosticWithFix for IncorrectCase { |
131 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { | 142 | fn fix(&self, sema: &Semantics<RootDatabase>, resolve: bool) -> Option<Assist> { |
132 | let root = sema.db.parse_or_expand(self.file)?; | 143 | let root = sema.db.parse_or_expand(self.file)?; |
133 | let name_node = self.ident.to_node(&root); | 144 | let name_node = self.ident.to_node(&root); |
134 | 145 | ||
@@ -136,16 +147,19 @@ impl DiagnosticWithFix for IncorrectCase { | |||
136 | let frange = name_node.original_file_range(sema.db); | 147 | let frange = name_node.original_file_range(sema.db); |
137 | let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; | 148 | let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; |
138 | 149 | ||
139 | let rename_changes = | ||
140 | rename_with_semantics(sema, file_position, &self.suggested_text).ok()?; | ||
141 | |||
142 | let label = format!("Rename to {}", self.suggested_text); | 150 | let label = format!("Rename to {}", self.suggested_text); |
143 | Some(Fix::new(&label, rename_changes, frange.range)) | 151 | let mut res = unresolved_fix("change_case", &label, frange.range); |
152 | if resolve { | ||
153 | let source_change = rename_with_semantics(sema, file_position, &self.suggested_text); | ||
154 | res.source_change = Some(source_change.ok().unwrap_or_default()); | ||
155 | } | ||
156 | |||
157 | Some(res) | ||
144 | } | 158 | } |
145 | } | 159 | } |
146 | 160 | ||
147 | impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap { | 161 | impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap { |
148 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { | 162 | fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { |
149 | let root = sema.db.parse_or_expand(self.file)?; | 163 | let root = sema.db.parse_or_expand(self.file)?; |
150 | let next_expr = self.next_expr.to_node(&root); | 164 | let next_expr = self.next_expr.to_node(&root); |
151 | let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?; | 165 | let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?; |
@@ -163,7 +177,8 @@ impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap { | |||
163 | 177 | ||
164 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); | 178 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); |
165 | 179 | ||
166 | Some(Fix::new( | 180 | Some(fix( |
181 | "replace_with_find_map", | ||
167 | "Replace filter_map(..).next() with find_map()", | 182 | "Replace filter_map(..).next() with find_map()", |
168 | source_change, | 183 | source_change, |
169 | trigger_range, | 184 | trigger_range, |
@@ -175,7 +190,7 @@ fn missing_record_expr_field_fix( | |||
175 | sema: &Semantics<RootDatabase>, | 190 | sema: &Semantics<RootDatabase>, |
176 | usage_file_id: FileId, | 191 | usage_file_id: FileId, |
177 | record_expr_field: &ast::RecordExprField, | 192 | record_expr_field: &ast::RecordExprField, |
178 | ) -> Option<Fix> { | 193 | ) -> Option<Assist> { |
179 | let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?; | 194 | let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?; |
180 | let def_id = sema.resolve_variant(record_lit)?; | 195 | let def_id = sema.resolve_variant(record_lit)?; |
181 | let module; | 196 | let module; |
@@ -233,7 +248,12 @@ fn missing_record_expr_field_fix( | |||
233 | def_file_id, | 248 | def_file_id, |
234 | TextEdit::insert(last_field_syntax.text_range().end(), new_field), | 249 | TextEdit::insert(last_field_syntax.text_range().end(), new_field), |
235 | ); | 250 | ); |
236 | return Some(Fix::new("Create field", source_change, record_expr_field.syntax().text_range())); | 251 | return Some(fix( |
252 | "create_field", | ||
253 | "Create field", | ||
254 | source_change, | ||
255 | record_expr_field.syntax().text_range(), | ||
256 | )); | ||
237 | 257 | ||
238 | fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> { | 258 | fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> { |
239 | match field_def_list { | 259 | match field_def_list { |
diff --git a/crates/ide/src/diagnostics/unlinked_file.rs b/crates/ide/src/diagnostics/unlinked_file.rs index e174fb767..7d39f4fbe 100644 --- a/crates/ide/src/diagnostics/unlinked_file.rs +++ b/crates/ide/src/diagnostics/unlinked_file.rs | |||
@@ -16,9 +16,10 @@ use syntax::{ | |||
16 | }; | 16 | }; |
17 | use text_edit::TextEdit; | 17 | use text_edit::TextEdit; |
18 | 18 | ||
19 | use crate::Fix; | 19 | use crate::{ |
20 | 20 | diagnostics::{fix, fixes::DiagnosticWithFix}, | |
21 | use super::fixes::DiagnosticWithFix; | 21 | Assist, |
22 | }; | ||
22 | 23 | ||
23 | // Diagnostic: unlinked-file | 24 | // Diagnostic: unlinked-file |
24 | // | 25 | // |
@@ -49,7 +50,7 @@ impl Diagnostic for UnlinkedFile { | |||
49 | } | 50 | } |
50 | 51 | ||
51 | impl DiagnosticWithFix for UnlinkedFile { | 52 | impl DiagnosticWithFix for UnlinkedFile { |
52 | fn fix(&self, sema: &hir::Semantics<RootDatabase>) -> Option<Fix> { | 53 | fn fix(&self, sema: &hir::Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { |
53 | // If there's an existing module that could add a `mod` item to include the unlinked file, | 54 | // If there's an existing module that could add a `mod` item to include the unlinked file, |
54 | // suggest that as a fix. | 55 | // suggest that as a fix. |
55 | 56 | ||
@@ -100,7 +101,7 @@ fn make_fix( | |||
100 | parent_file_id: FileId, | 101 | parent_file_id: FileId, |
101 | new_mod_name: &str, | 102 | new_mod_name: &str, |
102 | added_file_id: FileId, | 103 | added_file_id: FileId, |
103 | ) -> Option<Fix> { | 104 | ) -> Option<Assist> { |
104 | fn is_outline_mod(item: &ast::Item) -> bool { | 105 | fn is_outline_mod(item: &ast::Item) -> bool { |
105 | matches!(item, ast::Item::Module(m) if m.item_list().is_none()) | 106 | matches!(item, ast::Item::Module(m) if m.item_list().is_none()) |
106 | } | 107 | } |
@@ -152,7 +153,8 @@ fn make_fix( | |||
152 | 153 | ||
153 | let edit = builder.finish(); | 154 | let edit = builder.finish(); |
154 | let trigger_range = db.parse(added_file_id).tree().syntax().text_range(); | 155 | let trigger_range = db.parse(added_file_id).tree().syntax().text_range(); |
155 | Some(Fix::new( | 156 | Some(fix( |
157 | "add_mod_declaration", | ||
156 | &format!("Insert `{}`", mod_decl), | 158 | &format!("Insert `{}`", mod_decl), |
157 | SourceChange::from_text_edit(parent_file_id, edit), | 159 | SourceChange::from_text_edit(parent_file_id, edit), |
158 | trigger_range, | 160 | trigger_range, |
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 67e2e5a1c..cb5a8e19a 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs | |||
@@ -1,6 +1,9 @@ | |||
1 | //! Extracts, resolves and rewrites links and intra-doc links in markdown documentation. | 1 | //! Extracts, resolves and rewrites links and intra-doc links in markdown documentation. |
2 | 2 | ||
3 | use std::{convert::TryFrom, iter::once, ops::Range}; | 3 | use std::{ |
4 | convert::{TryFrom, TryInto}, | ||
5 | iter::once, | ||
6 | }; | ||
4 | 7 | ||
5 | use itertools::Itertools; | 8 | use itertools::Itertools; |
6 | use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag}; | 9 | use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag}; |
@@ -16,8 +19,7 @@ use ide_db::{ | |||
16 | RootDatabase, | 19 | RootDatabase, |
17 | }; | 20 | }; |
18 | use syntax::{ | 21 | use syntax::{ |
19 | ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxNode, SyntaxToken, TextRange, TextSize, | 22 | ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode, SyntaxToken, TextRange, TokenAtOffset, T, |
20 | TokenAtOffset, T, | ||
21 | }; | 23 | }; |
22 | 24 | ||
23 | use crate::{FilePosition, Semantics}; | 25 | use crate::{FilePosition, Semantics}; |
@@ -26,12 +28,7 @@ pub(crate) type DocumentationLink = String; | |||
26 | 28 | ||
27 | /// Rewrite documentation links in markdown to point to an online host (e.g. docs.rs) | 29 | /// Rewrite documentation links in markdown to point to an online host (e.g. docs.rs) |
28 | pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition) -> String { | 30 | pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition) -> String { |
29 | let mut cb = |link: BrokenLink| { | 31 | let mut cb = broken_link_clone_cb; |
30 | Some(( | ||
31 | /*url*/ link.reference.to_owned().into(), | ||
32 | /*title*/ link.reference.to_owned().into(), | ||
33 | )) | ||
34 | }; | ||
35 | let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb)); | 32 | let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb)); |
36 | 33 | ||
37 | let doc = map_links(doc, |target, title: &str| { | 34 | let doc = map_links(doc, |target, title: &str| { |
@@ -111,86 +108,39 @@ pub(crate) fn external_docs( | |||
111 | let node = token.parent()?; | 108 | let node = token.parent()?; |
112 | let definition = match_ast! { | 109 | let definition = match_ast! { |
113 | match node { | 110 | match node { |
114 | ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)), | 111 | ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db))?, |
115 | ast::Name(name) => NameClass::classify(&sema, &name).map(|d| d.referenced_or_defined(sema.db)), | 112 | ast::Name(name) => NameClass::classify(&sema, &name).map(|d| d.referenced_or_defined(sema.db))?, |
116 | _ => None, | 113 | _ => return None, |
117 | } | 114 | } |
118 | }; | 115 | }; |
119 | 116 | ||
120 | get_doc_link(db, definition?) | 117 | get_doc_link(db, definition) |
121 | } | 118 | } |
122 | 119 | ||
123 | /// Extracts all links from a given markdown text. | 120 | /// Extracts all links from a given markdown text. |
124 | pub(crate) fn extract_definitions_from_markdown( | 121 | pub(crate) fn extract_definitions_from_markdown( |
125 | markdown: &str, | 122 | markdown: &str, |
126 | ) -> Vec<(Range<usize>, String, Option<hir::Namespace>)> { | 123 | ) -> Vec<(TextRange, String, Option<hir::Namespace>)> { |
127 | let mut res = vec![]; | 124 | Parser::new_with_broken_link_callback( |
128 | let mut cb = |link: BrokenLink| { | 125 | markdown, |
129 | // These allocations are actually unnecessary but the lifetimes on BrokenLinkCallback are wrong | 126 | Options::empty(), |
130 | // this is fixed in the repo but not on the crates.io release yet | 127 | Some(&mut broken_link_clone_cb), |
131 | Some(( | 128 | ) |
132 | /*url*/ link.reference.to_owned().into(), | 129 | .into_offset_iter() |
133 | /*title*/ link.reference.to_owned().into(), | 130 | .filter_map(|(event, range)| { |
134 | )) | ||
135 | }; | ||
136 | let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb)); | ||
137 | for (event, range) in doc.into_offset_iter() { | ||
138 | if let Event::Start(Tag::Link(_, target, title)) = event { | 131 | if let Event::Start(Tag::Link(_, target, title)) = event { |
139 | let link = if target.is_empty() { title } else { target }; | 132 | let link = if target.is_empty() { title } else { target }; |
140 | let (link, ns) = parse_intra_doc_link(&link); | 133 | let (link, ns) = parse_intra_doc_link(&link); |
141 | res.push((range, link.to_string(), ns)); | 134 | Some(( |
142 | } | 135 | TextRange::new(range.start.try_into().ok()?, range.end.try_into().ok()?), |
143 | } | 136 | link.to_string(), |
144 | res | 137 | ns, |
145 | } | 138 | )) |
146 | 139 | } else { | |
147 | /// Extracts a link from a comment at the given position returning the spanning range, link and | 140 | None |
148 | /// optionally it's namespace. | ||
149 | pub(crate) fn extract_positioned_link_from_comment( | ||
150 | position: TextSize, | ||
151 | comment: &ast::Comment, | ||
152 | ) -> Option<(TextRange, String, Option<hir::Namespace>)> { | ||
153 | let doc_comment = comment.doc_comment()?; | ||
154 | let comment_start = | ||
155 | comment.syntax().text_range().start() + TextSize::from(comment.prefix().len() as u32); | ||
156 | let def_links = extract_definitions_from_markdown(doc_comment); | ||
157 | let (range, def_link, ns) = | ||
158 | def_links.into_iter().find_map(|(Range { start, end }, def_link, ns)| { | ||
159 | let range = TextRange::at( | ||
160 | comment_start + TextSize::from(start as u32), | ||
161 | TextSize::from((end - start) as u32), | ||
162 | ); | ||
163 | range.contains(position).then(|| (range, def_link, ns)) | ||
164 | })?; | ||
165 | Some((range, def_link, ns)) | ||
166 | } | ||
167 | |||
168 | /// Turns a syntax node into it's [`Definition`] if it can hold docs. | ||
169 | pub(crate) fn doc_owner_to_def( | ||
170 | sema: &Semantics<RootDatabase>, | ||
171 | item: &SyntaxNode, | ||
172 | ) -> Option<Definition> { | ||
173 | let res: hir::ModuleDef = match_ast! { | ||
174 | match item { | ||
175 | ast::SourceFile(_it) => sema.scope(item).module()?.into(), | ||
176 | ast::Fn(it) => sema.to_def(&it)?.into(), | ||
177 | ast::Struct(it) => sema.to_def(&it)?.into(), | ||
178 | ast::Enum(it) => sema.to_def(&it)?.into(), | ||
179 | ast::Union(it) => sema.to_def(&it)?.into(), | ||
180 | ast::Trait(it) => sema.to_def(&it)?.into(), | ||
181 | ast::Const(it) => sema.to_def(&it)?.into(), | ||
182 | ast::Static(it) => sema.to_def(&it)?.into(), | ||
183 | ast::TypeAlias(it) => sema.to_def(&it)?.into(), | ||
184 | ast::Variant(it) => sema.to_def(&it)?.into(), | ||
185 | ast::Trait(it) => sema.to_def(&it)?.into(), | ||
186 | ast::Impl(it) => return sema.to_def(&it).map(Definition::SelfType), | ||
187 | ast::Macro(it) => return sema.to_def(&it).map(Definition::Macro), | ||
188 | ast::TupleField(it) => return sema.to_def(&it).map(Definition::Field), | ||
189 | ast::RecordField(it) => return sema.to_def(&it).map(Definition::Field), | ||
190 | _ => return None, | ||
191 | } | 141 | } |
192 | }; | 142 | }) |
193 | Some(Definition::ModuleDef(res)) | 143 | .collect() |
194 | } | 144 | } |
195 | 145 | ||
196 | pub(crate) fn resolve_doc_path_for_def( | 146 | pub(crate) fn resolve_doc_path_for_def( |
@@ -220,6 +170,42 @@ pub(crate) fn resolve_doc_path_for_def( | |||
220 | } | 170 | } |
221 | } | 171 | } |
222 | 172 | ||
173 | pub(crate) fn doc_attributes( | ||
174 | sema: &Semantics<RootDatabase>, | ||
175 | node: &SyntaxNode, | ||
176 | ) -> Option<(hir::AttrsWithOwner, Definition)> { | ||
177 | match_ast! { | ||
178 | match node { | ||
179 | ast::SourceFile(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))), | ||
180 | ast::Module(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))), | ||
181 | ast::Fn(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Function(def)))), | ||
182 | ast::Struct(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(def))))), | ||
183 | ast::Union(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Union(def))))), | ||
184 | ast::Enum(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(def))))), | ||
185 | ast::Variant(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Variant(def)))), | ||
186 | ast::Trait(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Trait(def)))), | ||
187 | ast::Static(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Static(def)))), | ||
188 | ast::Const(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Const(def)))), | ||
189 | ast::TypeAlias(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::TypeAlias(def)))), | ||
190 | ast::Impl(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::SelfType(def))), | ||
191 | ast::RecordField(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))), | ||
194 | // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))), | ||
195 | _ => return None | ||
196 | } | ||
197 | } | ||
198 | } | ||
199 | |||
200 | fn broken_link_clone_cb<'a, 'b>(link: BrokenLink<'a>) -> Option<(CowStr<'b>, CowStr<'b>)> { | ||
201 | // These allocations are actually unnecessary but the lifetimes on BrokenLinkCallback are wrong | ||
202 | // this is fixed in the repo but not on the crates.io release yet | ||
203 | Some(( | ||
204 | /*url*/ link.reference.to_owned().into(), | ||
205 | /*title*/ link.reference.to_owned().into(), | ||
206 | )) | ||
207 | } | ||
208 | |||
223 | // FIXME: | 209 | // FIXME: |
224 | // BUG: For Option::Some | 210 | // BUG: For Option::Some |
225 | // Returns https://doc.rust-lang.org/nightly/core/prelude/v1/enum.Option.html#variant.Some | 211 | // Returns https://doc.rust-lang.org/nightly/core/prelude/v1/enum.Option.html#variant.Some |
@@ -228,20 +214,20 @@ pub(crate) fn resolve_doc_path_for_def( | |||
228 | // This should cease to be a problem if RFC2988 (Stable Rustdoc URLs) is implemented | 214 | // This should cease to be a problem if RFC2988 (Stable Rustdoc URLs) is implemented |
229 | // https://github.com/rust-lang/rfcs/pull/2988 | 215 | // https://github.com/rust-lang/rfcs/pull/2988 |
230 | fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> { | 216 | fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> { |
231 | // Get the outermost definition for the moduledef. This is used to resolve the public path to the type, | 217 | // Get the outermost definition for the module def. This is used to resolve the public path to the type, |
232 | // then we can join the method, field, etc onto it if required. | 218 | // then we can join the method, field, etc onto it if required. |
233 | let target_def: ModuleDef = match definition { | 219 | let target_def: ModuleDef = match definition { |
234 | Definition::ModuleDef(moddef) => match moddef { | 220 | Definition::ModuleDef(def) => match def { |
235 | ModuleDef::Function(f) => f | 221 | ModuleDef::Function(f) => f |
236 | .as_assoc_item(db) | 222 | .as_assoc_item(db) |
237 | .and_then(|assoc| match assoc.container(db) { | 223 | .and_then(|assoc| match assoc.container(db) { |
238 | AssocItemContainer::Trait(t) => Some(t.into()), | 224 | AssocItemContainer::Trait(t) => Some(t.into()), |
239 | AssocItemContainer::Impl(impld) => { | 225 | AssocItemContainer::Impl(impl_) => { |
240 | impld.self_ty(db).as_adt().map(|adt| adt.into()) | 226 | impl_.self_ty(db).as_adt().map(|adt| adt.into()) |
241 | } | 227 | } |
242 | }) | 228 | }) |
243 | .unwrap_or_else(|| f.clone().into()), | 229 | .unwrap_or_else(|| def), |
244 | moddef => moddef, | 230 | def => def, |
245 | }, | 231 | }, |
246 | Definition::Field(f) => f.parent_def(db).into(), | 232 | Definition::Field(f) => f.parent_def(db).into(), |
247 | // FIXME: Handle macros | 233 | // FIXME: Handle macros |
@@ -250,17 +236,28 @@ fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> { | |||
250 | 236 | ||
251 | let ns = ItemInNs::from(target_def); | 237 | let ns = ItemInNs::from(target_def); |
252 | 238 | ||
253 | let module = definition.module(db)?; | 239 | let krate = match definition { |
254 | let krate = module.krate(); | 240 | // Definition::module gives back the parent module, we don't want that as it fails for root modules |
241 | Definition::ModuleDef(ModuleDef::Module(module)) => module.krate(), | ||
242 | _ => definition.module(db)?.krate(), | ||
243 | }; | ||
255 | let import_map = db.import_map(krate.into()); | 244 | let import_map = db.import_map(krate.into()); |
256 | let base = once(krate.display_name(db)?.to_string()) | 245 | |
257 | .chain(import_map.path_of(ns)?.segments.iter().map(|name| name.to_string())) | 246 | let mut base = krate.display_name(db)?.to_string(); |
258 | .join("/") | 247 | let is_root_module = matches!( |
259 | + "/"; | 248 | definition, |
249 | Definition::ModuleDef(ModuleDef::Module(module)) if krate.root_module(db) == module | ||
250 | ); | ||
251 | if !is_root_module { | ||
252 | base = once(base) | ||
253 | .chain(import_map.path_of(ns)?.segments.iter().map(|name| name.to_string())) | ||
254 | .join("/"); | ||
255 | } | ||
256 | base += "/"; | ||
260 | 257 | ||
261 | let filename = get_symbol_filename(db, &target_def); | 258 | let filename = get_symbol_filename(db, &target_def); |
262 | let fragment = match definition { | 259 | let fragment = match definition { |
263 | Definition::ModuleDef(moddef) => match moddef { | 260 | Definition::ModuleDef(def) => match def { |
264 | ModuleDef::Function(f) => { | 261 | ModuleDef::Function(f) => { |
265 | get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::Function(f))) | 262 | get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::Function(f))) |
266 | } | 263 | } |
@@ -547,6 +544,19 @@ mod tests { | |||
547 | } | 544 | } |
548 | 545 | ||
549 | #[test] | 546 | #[test] |
547 | fn test_doc_url_crate() { | ||
548 | check( | ||
549 | r#" | ||
550 | //- /main.rs crate:main deps:test | ||
551 | use test$0::Foo; | ||
552 | //- /lib.rs crate:test | ||
553 | pub struct Foo; | ||
554 | "#, | ||
555 | expect![[r#"https://docs.rs/test/*/test/index.html"#]], | ||
556 | ); | ||
557 | } | ||
558 | |||
559 | #[test] | ||
550 | fn test_doc_url_struct() { | 560 | fn test_doc_url_struct() { |
551 | check( | 561 | check( |
552 | r#" | 562 | r#" |
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index 9eeabbeda..eebae5ebe 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs | |||
@@ -1,9 +1,9 @@ | |||
1 | use std::iter; | ||
2 | |||
1 | use hir::Semantics; | 3 | use hir::Semantics; |
2 | use ide_db::RootDatabase; | 4 | use ide_db::RootDatabase; |
3 | use syntax::{ | 5 | use syntax::{ |
4 | algo::{find_node_at_offset, SyntaxRewriter}, | 6 | algo::find_node_at_offset, ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*, |
5 | ast, AstNode, NodeOrToken, SyntaxKind, | ||
6 | SyntaxKind::*, | ||
7 | SyntaxNode, WalkEvent, T, | 7 | SyntaxNode, WalkEvent, T, |
8 | }; | 8 | }; |
9 | 9 | ||
@@ -44,26 +44,23 @@ fn expand_macro_recur( | |||
44 | sema: &Semantics<RootDatabase>, | 44 | sema: &Semantics<RootDatabase>, |
45 | macro_call: &ast::MacroCall, | 45 | macro_call: &ast::MacroCall, |
46 | ) -> Option<SyntaxNode> { | 46 | ) -> Option<SyntaxNode> { |
47 | let mut expanded = sema.expand(macro_call)?; | 47 | let expanded = sema.expand(macro_call)?.clone_for_update(); |
48 | 48 | ||
49 | let children = expanded.descendants().filter_map(ast::MacroCall::cast); | 49 | let children = expanded.descendants().filter_map(ast::MacroCall::cast); |
50 | let mut rewriter = SyntaxRewriter::default(); | 50 | let mut replacements = Vec::new(); |
51 | 51 | ||
52 | for child in children.into_iter() { | 52 | for child in children { |
53 | if let Some(new_node) = expand_macro_recur(sema, &child) { | 53 | if let Some(new_node) = expand_macro_recur(sema, &child) { |
54 | // Replace the whole node if it is root | 54 | // check if the whole original syntax is replaced |
55 | // `replace_descendants` will not replace the parent node | ||
56 | // but `SyntaxNode::descendants include itself | ||
57 | if expanded == *child.syntax() { | 55 | if expanded == *child.syntax() { |
58 | expanded = new_node; | 56 | return Some(new_node); |
59 | } else { | ||
60 | rewriter.replace(child.syntax(), &new_node) | ||
61 | } | 57 | } |
58 | replacements.push((child, new_node)); | ||
62 | } | 59 | } |
63 | } | 60 | } |
64 | 61 | ||
65 | let res = rewriter.rewrite(&expanded); | 62 | replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new)); |
66 | Some(res) | 63 | Some(expanded) |
67 | } | 64 | } |
68 | 65 | ||
69 | // FIXME: It would also be cool to share logic here and in the mbe tests, | 66 | // FIXME: It would also be cool to share logic here and in the mbe tests, |
@@ -91,24 +88,42 @@ fn insert_whitespaces(syn: SyntaxNode) -> String { | |||
91 | let is_last = | 88 | let is_last = |
92 | |f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) }; | 89 | |f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) }; |
93 | 90 | ||
94 | res += &match token.kind() { | 91 | match token.kind() { |
95 | k if is_text(k) && is_next(|it| !it.is_punct(), true) => token.text().to_string() + " ", | 92 | k if is_text(k) && is_next(|it| !it.is_punct(), true) => { |
93 | res.push_str(token.text()); | ||
94 | res.push(' '); | ||
95 | } | ||
96 | L_CURLY if is_next(|it| it != R_CURLY, true) => { | 96 | L_CURLY if is_next(|it| it != R_CURLY, true) => { |
97 | indent += 1; | 97 | indent += 1; |
98 | let leading_space = if is_last(is_text, false) { " " } else { "" }; | 98 | if is_last(is_text, false) { |
99 | format!("{}{{\n{}", leading_space, " ".repeat(indent)) | 99 | res.push(' '); |
100 | } | ||
101 | res.push_str("{\n"); | ||
102 | res.extend(iter::repeat(" ").take(2 * indent)); | ||
100 | } | 103 | } |
101 | R_CURLY if is_last(|it| it != L_CURLY, true) => { | 104 | R_CURLY if is_last(|it| it != L_CURLY, true) => { |
102 | indent = indent.saturating_sub(1); | 105 | indent = indent.saturating_sub(1); |
103 | format!("\n{}}}", " ".repeat(indent)) | 106 | res.push('\n'); |
107 | res.extend(iter::repeat(" ").take(2 * indent)); | ||
108 | res.push_str("}"); | ||
104 | } | 109 | } |
105 | R_CURLY => format!("}}\n{}", " ".repeat(indent)), | 110 | R_CURLY => { |
106 | T![;] => format!(";\n{}", " ".repeat(indent)), | 111 | res.push_str("}\n"); |
107 | T![->] => " -> ".to_string(), | 112 | res.extend(iter::repeat(" ").take(2 * indent)); |
108 | T![=] => " = ".to_string(), | 113 | } |
109 | T![=>] => " => ".to_string(), | 114 | LIFETIME_IDENT if is_next(|it| it == IDENT, true) => { |
110 | _ => token.text().to_string(), | 115 | res.push_str(token.text()); |
111 | }; | 116 | res.push(' '); |
117 | } | ||
118 | T![;] => { | ||
119 | res.push_str(";\n"); | ||
120 | res.extend(iter::repeat(" ").take(2 * indent)); | ||
121 | } | ||
122 | T![->] => res.push_str(" -> "), | ||
123 | T![=] => res.push_str(" = "), | ||
124 | T![=>] => res.push_str(" => "), | ||
125 | _ => res.push_str(token.text()), | ||
126 | } | ||
112 | 127 | ||
113 | last = Some(token.kind()); | 128 | last = Some(token.kind()); |
114 | } | 129 | } |
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs index 153726ce8..2b9ed123c 100644 --- a/crates/ide/src/folding_ranges.rs +++ b/crates/ide/src/folding_ranges.rs | |||
@@ -19,6 +19,7 @@ pub enum FoldKind { | |||
19 | Region, | 19 | Region, |
20 | Consts, | 20 | Consts, |
21 | Statics, | 21 | Statics, |
22 | Array, | ||
22 | } | 23 | } |
23 | 24 | ||
24 | #[derive(Debug)] | 25 | #[derive(Debug)] |
@@ -119,6 +120,7 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> { | |||
119 | match kind { | 120 | match kind { |
120 | COMMENT => Some(FoldKind::Comment), | 121 | COMMENT => Some(FoldKind::Comment), |
121 | ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList), | 122 | ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList), |
123 | ARRAY_EXPR => Some(FoldKind::Array), | ||
122 | ASSOC_ITEM_LIST | 124 | ASSOC_ITEM_LIST |
123 | | RECORD_FIELD_LIST | 125 | | RECORD_FIELD_LIST |
124 | | RECORD_PAT_FIELD_LIST | 126 | | RECORD_PAT_FIELD_LIST |
@@ -269,6 +271,7 @@ mod tests { | |||
269 | FoldKind::Region => "region", | 271 | FoldKind::Region => "region", |
270 | FoldKind::Consts => "consts", | 272 | FoldKind::Consts => "consts", |
271 | FoldKind::Statics => "statics", | 273 | FoldKind::Statics => "statics", |
274 | FoldKind::Array => "array", | ||
272 | }; | 275 | }; |
273 | assert_eq!(kind, &attr.unwrap()); | 276 | assert_eq!(kind, &attr.unwrap()); |
274 | } | 277 | } |
@@ -465,6 +468,20 @@ fn foo<fold arglist>( | |||
465 | } | 468 | } |
466 | 469 | ||
467 | #[test] | 470 | #[test] |
471 | fn fold_multiline_array() { | ||
472 | check( | ||
473 | r#" | ||
474 | const FOO: [usize; 4] = <fold array>[ | ||
475 | 1, | ||
476 | 2, | ||
477 | 3, | ||
478 | 4, | ||
479 | ]</fold>; | ||
480 | "#, | ||
481 | ) | ||
482 | } | ||
483 | |||
484 | #[test] | ||
468 | fn fold_region() { | 485 | fn fold_region() { |
469 | check( | 486 | check( |
470 | r#" | 487 | r#" |
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 8574d1e3f..a04333e63 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use either::Either; | 1 | use either::Either; |
2 | use hir::Semantics; | 2 | use hir::{InFile, Semantics}; |
3 | use ide_db::{ | 3 | use ide_db::{ |
4 | defs::{NameClass, NameRefClass}, | 4 | defs::{NameClass, NameRefClass}, |
5 | RootDatabase, | 5 | RootDatabase, |
@@ -8,7 +8,7 @@ use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, Toke | |||
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | display::TryToNav, | 10 | display::TryToNav, |
11 | doc_links::{doc_owner_to_def, extract_positioned_link_from_comment, resolve_doc_path_for_def}, | 11 | doc_links::{doc_attributes, extract_definitions_from_markdown, resolve_doc_path_for_def}, |
12 | FilePosition, NavigationTarget, RangeInfo, | 12 | FilePosition, NavigationTarget, RangeInfo, |
13 | }; | 13 | }; |
14 | 14 | ||
@@ -32,9 +32,16 @@ pub(crate) fn goto_definition( | |||
32 | let original_token = pick_best(file.token_at_offset(position.offset))?; | 32 | let original_token = pick_best(file.token_at_offset(position.offset))?; |
33 | let token = sema.descend_into_macros(original_token.clone()); | 33 | let token = sema.descend_into_macros(original_token.clone()); |
34 | let parent = token.parent()?; | 34 | let parent = token.parent()?; |
35 | if let Some(comment) = ast::Comment::cast(token) { | 35 | if let Some(_) = ast::Comment::cast(token) { |
36 | let (_, link, ns) = extract_positioned_link_from_comment(position.offset, &comment)?; | 36 | let (attributes, def) = doc_attributes(&sema, &parent)?; |
37 | let def = doc_owner_to_def(&sema, &parent)?; | 37 | |
38 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; | ||
39 | let (_, link, ns) = | ||
40 | extract_definitions_from_markdown(docs.as_str()).into_iter().find(|(range, ..)| { | ||
41 | doc_mapping.map(range.clone()).map_or(false, |InFile { file_id, value: range }| { | ||
42 | file_id == position.file_id.into() && range.contains(position.offset) | ||
43 | }) | ||
44 | })?; | ||
38 | let nav = resolve_doc_path_for_def(db, def, &link, ns)?.try_to_nav(db)?; | 45 | let nav = resolve_doc_path_for_def(db, def, &link, ns)?.try_to_nav(db)?; |
39 | return Some(RangeInfo::new(original_token.text_range(), vec![nav])); | 46 | return Some(RangeInfo::new(original_token.text_range(), vec![nav])); |
40 | } | 47 | } |
@@ -103,6 +110,13 @@ mod tests { | |||
103 | assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() }); | 110 | assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() }); |
104 | } | 111 | } |
105 | 112 | ||
113 | fn check_unresolved(ra_fixture: &str) { | ||
114 | let (analysis, position) = fixture::position(ra_fixture); | ||
115 | let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info; | ||
116 | |||
117 | assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {:?}", navs) | ||
118 | } | ||
119 | |||
106 | #[test] | 120 | #[test] |
107 | fn goto_def_for_extern_crate() { | 121 | fn goto_def_for_extern_crate() { |
108 | check( | 122 | check( |
@@ -920,17 +934,12 @@ fn f() -> impl Iterator<Item$0 = u8> {} | |||
920 | } | 934 | } |
921 | 935 | ||
922 | #[test] | 936 | #[test] |
923 | #[should_panic = "unresolved reference"] | ||
924 | fn unknown_assoc_ty() { | 937 | fn unknown_assoc_ty() { |
925 | check( | 938 | check_unresolved( |
926 | r#" | 939 | r#" |
927 | trait Iterator { | 940 | trait Iterator { type Item; } |
928 | type Item; | ||
929 | //^^^^ | ||
930 | } | ||
931 | |||
932 | fn f() -> impl Iterator<Invalid$0 = u8> {} | 941 | fn f() -> impl Iterator<Invalid$0 = u8> {} |
933 | "#, | 942 | "#, |
934 | ) | 943 | ) |
935 | } | 944 | } |
936 | 945 | ||
@@ -1160,4 +1169,51 @@ fn fn_macro() {} | |||
1160 | "#, | 1169 | "#, |
1161 | ) | 1170 | ) |
1162 | } | 1171 | } |
1172 | |||
1173 | #[test] | ||
1174 | fn goto_intra_doc_links() { | ||
1175 | check( | ||
1176 | r#" | ||
1177 | |||
1178 | pub mod theitem { | ||
1179 | /// This is the item. Cool! | ||
1180 | pub struct TheItem; | ||
1181 | //^^^^^^^ | ||
1182 | } | ||
1183 | |||
1184 | /// Gives you a [`TheItem$0`]. | ||
1185 | /// | ||
1186 | /// [`TheItem`]: theitem::TheItem | ||
1187 | pub fn gimme() -> theitem::TheItem { | ||
1188 | theitem::TheItem | ||
1189 | } | ||
1190 | "#, | ||
1191 | ); | ||
1192 | } | ||
1193 | |||
1194 | #[test] | ||
1195 | fn goto_ident_from_pat_macro() { | ||
1196 | check( | ||
1197 | r#" | ||
1198 | macro_rules! pat { | ||
1199 | ($name:ident) => { Enum::Variant1($name) } | ||
1200 | } | ||
1201 | |||
1202 | enum Enum { | ||
1203 | Variant1(u8), | ||
1204 | Variant2, | ||
1205 | } | ||
1206 | |||
1207 | fn f(e: Enum) { | ||
1208 | match e { | ||
1209 | pat!(bind) => { | ||
1210 | //^^^^ | ||
1211 | bind$0 | ||
1212 | } | ||
1213 | Enum::Variant2 => {} | ||
1214 | } | ||
1215 | } | ||
1216 | "#, | ||
1217 | ); | ||
1218 | } | ||
1163 | } | 1219 | } |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 614433417..9de653739 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use either::Either; | 1 | use either::Either; |
2 | use hir::{ | 2 | use hir::{ |
3 | AsAssocItem, AssocItemContainer, GenericParam, HasAttrs, HasSource, HirDisplay, Module, | 3 | AsAssocItem, AssocItemContainer, GenericParam, HasAttrs, HasSource, HirDisplay, InFile, Module, |
4 | ModuleDef, Semantics, | 4 | ModuleDef, Semantics, |
5 | }; | 5 | }; |
6 | use ide_db::{ | 6 | use ide_db::{ |
@@ -16,8 +16,8 @@ use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, Toke | |||
16 | use crate::{ | 16 | use crate::{ |
17 | display::{macro_label, TryToNav}, | 17 | display::{macro_label, TryToNav}, |
18 | doc_links::{ | 18 | doc_links::{ |
19 | doc_owner_to_def, extract_positioned_link_from_comment, remove_links, | 19 | doc_attributes, extract_definitions_from_markdown, remove_links, resolve_doc_path_for_def, |
20 | resolve_doc_path_for_def, rewrite_links, | 20 | rewrite_links, |
21 | }, | 21 | }, |
22 | markdown_remove::remove_markdown, | 22 | markdown_remove::remove_markdown, |
23 | markup::Markup, | 23 | markup::Markup, |
@@ -116,11 +116,19 @@ pub(crate) fn hover( | |||
116 | ), | 116 | ), |
117 | 117 | ||
118 | _ => ast::Comment::cast(token.clone()) | 118 | _ => ast::Comment::cast(token.clone()) |
119 | .and_then(|comment| { | 119 | .and_then(|_| { |
120 | let (attributes, def) = doc_attributes(&sema, &node)?; | ||
121 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; | ||
120 | let (idl_range, link, ns) = | 122 | let (idl_range, link, ns) = |
121 | extract_positioned_link_from_comment(position.offset, &comment)?; | 123 | extract_definitions_from_markdown(docs.as_str()).into_iter().find_map(|(range, link, ns)| { |
124 | let InFile { file_id, value: range } = doc_mapping.map(range.clone())?; | ||
125 | if file_id == position.file_id.into() && range.contains(position.offset) { | ||
126 | Some((range, link, ns)) | ||
127 | } else { | ||
128 | None | ||
129 | } | ||
130 | })?; | ||
122 | range = Some(idl_range); | 131 | range = Some(idl_range); |
123 | let def = doc_owner_to_def(&sema, &node)?; | ||
124 | resolve_doc_path_for_def(db, def, &link, ns) | 132 | resolve_doc_path_for_def(db, def, &link, ns) |
125 | }) | 133 | }) |
126 | .map(Definition::ModuleDef), | 134 | .map(Definition::ModuleDef), |
@@ -3814,23 +3822,33 @@ fn main() { | |||
3814 | fn hover_intra_doc_links() { | 3822 | fn hover_intra_doc_links() { |
3815 | check( | 3823 | check( |
3816 | r#" | 3824 | r#" |
3817 | /// This is the [`foo`](foo$0) function. | 3825 | |
3818 | fn foo() {} | 3826 | pub mod theitem { |
3827 | /// This is the item. Cool! | ||
3828 | pub struct TheItem; | ||
3829 | } | ||
3830 | |||
3831 | /// Gives you a [`TheItem$0`]. | ||
3832 | /// | ||
3833 | /// [`TheItem`]: theitem::TheItem | ||
3834 | pub fn gimme() -> theitem::TheItem { | ||
3835 | theitem::TheItem | ||
3836 | } | ||
3819 | "#, | 3837 | "#, |
3820 | expect![[r#" | 3838 | expect![[r#" |
3821 | *[`foo`](foo)* | 3839 | *[`TheItem`]* |
3822 | 3840 | ||
3823 | ```rust | 3841 | ```rust |
3824 | test | 3842 | test::theitem |
3825 | ``` | 3843 | ``` |
3826 | 3844 | ||
3827 | ```rust | 3845 | ```rust |
3828 | fn foo() | 3846 | pub struct TheItem |
3829 | ``` | 3847 | ``` |
3830 | 3848 | ||
3831 | --- | 3849 | --- |
3832 | 3850 | ||
3833 | This is the [`foo`](https://docs.rs/test/*/test/fn.foo.html) function. | 3851 | This is the item. Cool! |
3834 | "#]], | 3852 | "#]], |
3835 | ); | 3853 | ); |
3836 | } | 3854 | } |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 3f73c0632..99e45633e 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -69,7 +69,7 @@ use crate::display::ToNav; | |||
69 | pub use crate::{ | 69 | pub use crate::{ |
70 | annotations::{Annotation, AnnotationConfig, AnnotationKind}, | 70 | annotations::{Annotation, AnnotationConfig, AnnotationKind}, |
71 | call_hierarchy::CallItem, | 71 | call_hierarchy::CallItem, |
72 | diagnostics::{Diagnostic, DiagnosticsConfig, Fix, Severity}, | 72 | diagnostics::{Diagnostic, DiagnosticsConfig, Severity}, |
73 | display::navigation_target::NavigationTarget, | 73 | display::navigation_target::NavigationTarget, |
74 | expand_macro::ExpandedMacro, | 74 | expand_macro::ExpandedMacro, |
75 | file_structure::{StructureNode, StructureNodeKind}, | 75 | file_structure::{StructureNode, StructureNodeKind}, |
@@ -82,7 +82,7 @@ pub use crate::{ | |||
82 | references::{rename::RenameError, ReferenceSearchResult}, | 82 | references::{rename::RenameError, ReferenceSearchResult}, |
83 | runnables::{Runnable, RunnableKind, TestId}, | 83 | runnables::{Runnable, RunnableKind, TestId}, |
84 | syntax_highlighting::{ | 84 | syntax_highlighting::{ |
85 | tags::{Highlight, HlMod, HlMods, HlPunct, HlTag}, | 85 | tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag}, |
86 | HlRange, | 86 | HlRange, |
87 | }, | 87 | }, |
88 | }; | 88 | }; |
@@ -244,6 +244,12 @@ impl Analysis { | |||
244 | self.with_db(|db| db.parse(file_id).tree()) | 244 | self.with_db(|db| db.parse(file_id).tree()) |
245 | } | 245 | } |
246 | 246 | ||
247 | /// Returns true if this file belongs to an immutable library. | ||
248 | pub fn is_library_file(&self, file_id: FileId) -> Cancelable<bool> { | ||
249 | use ide_db::base_db::SourceDatabaseExt; | ||
250 | self.with_db(|db| db.source_root(db.file_source_root(file_id)).is_library) | ||
251 | } | ||
252 | |||
247 | /// Gets the file's `LineIndex`: data structure to convert between absolute | 253 | /// Gets the file's `LineIndex`: data structure to convert between absolute |
248 | /// offsets and line/column representation. | 254 | /// offsets and line/column representation. |
249 | pub fn file_line_index(&self, file_id: FileId) -> Cancelable<Arc<LineIndex>> { | 255 | pub fn file_line_index(&self, file_id: FileId) -> Cancelable<Arc<LineIndex>> { |
@@ -526,9 +532,39 @@ impl Analysis { | |||
526 | pub fn diagnostics( | 532 | pub fn diagnostics( |
527 | &self, | 533 | &self, |
528 | config: &DiagnosticsConfig, | 534 | config: &DiagnosticsConfig, |
535 | resolve: bool, | ||
529 | file_id: FileId, | 536 | file_id: FileId, |
530 | ) -> Cancelable<Vec<Diagnostic>> { | 537 | ) -> Cancelable<Vec<Diagnostic>> { |
531 | self.with_db(|db| diagnostics::diagnostics(db, config, file_id)) | 538 | self.with_db(|db| diagnostics::diagnostics(db, config, resolve, file_id)) |
539 | } | ||
540 | |||
541 | /// Convenience function to return assists + quick fixes for diagnostics | ||
542 | pub fn assists_with_fixes( | ||
543 | &self, | ||
544 | assist_config: &AssistConfig, | ||
545 | diagnostics_config: &DiagnosticsConfig, | ||
546 | resolve: bool, | ||
547 | frange: FileRange, | ||
548 | ) -> Cancelable<Vec<Assist>> { | ||
549 | let include_fixes = match &assist_config.allowed { | ||
550 | Some(it) => it.iter().any(|&it| it == AssistKind::None || it == AssistKind::QuickFix), | ||
551 | None => true, | ||
552 | }; | ||
553 | |||
554 | self.with_db(|db| { | ||
555 | let mut res = Assist::get(db, assist_config, resolve, frange); | ||
556 | ssr::add_ssr_assist(db, &mut res, resolve, frange); | ||
557 | |||
558 | if include_fixes { | ||
559 | res.extend( | ||
560 | diagnostics::diagnostics(db, diagnostics_config, resolve, frange.file_id) | ||
561 | .into_iter() | ||
562 | .filter_map(|it| it.fix) | ||
563 | .filter(|it| it.target.intersect(frange.range).is_some()), | ||
564 | ); | ||
565 | } | ||
566 | res | ||
567 | }) | ||
532 | } | 568 | } |
533 | 569 | ||
534 | /// Returns the edit required to rename reference at the position to the new | 570 | /// Returns the edit required to rename reference at the position to the new |
diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs index 8d37f4f92..246f10a0a 100644 --- a/crates/ide/src/move_item.rs +++ b/crates/ide/src/move_item.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use std::iter::once; | 1 | use std::{iter::once, mem}; |
2 | 2 | ||
3 | use hir::Semantics; | 3 | use hir::Semantics; |
4 | use ide_db::{base_db::FileRange, RootDatabase}; | 4 | use ide_db::{base_db::FileRange, RootDatabase}; |
@@ -102,7 +102,7 @@ fn move_in_direction( | |||
102 | ast::GenericArgList(it) => swap_sibling_in_list(node, it.generic_args(), range, direction), | 102 | ast::GenericArgList(it) => swap_sibling_in_list(node, it.generic_args(), range, direction), |
103 | ast::VariantList(it) => swap_sibling_in_list(node, it.variants(), range, direction), | 103 | ast::VariantList(it) => swap_sibling_in_list(node, it.variants(), range, direction), |
104 | ast::TypeBoundList(it) => swap_sibling_in_list(node, it.bounds(), range, direction), | 104 | ast::TypeBoundList(it) => swap_sibling_in_list(node, it.bounds(), range, direction), |
105 | _ => Some(replace_nodes(node, &match direction { | 105 | _ => Some(replace_nodes(range, node, &match direction { |
106 | Direction::Up => node.prev_sibling(), | 106 | Direction::Up => node.prev_sibling(), |
107 | Direction::Down => node.next_sibling(), | 107 | Direction::Down => node.next_sibling(), |
108 | }?)) | 108 | }?)) |
@@ -125,7 +125,7 @@ fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>( | |||
125 | .next(); | 125 | .next(); |
126 | 126 | ||
127 | if let Some((l, r)) = list_lookup { | 127 | if let Some((l, r)) = list_lookup { |
128 | Some(replace_nodes(l.syntax(), r.syntax())) | 128 | Some(replace_nodes(range, l.syntax(), r.syntax())) |
129 | } else { | 129 | } else { |
130 | // Cursor is beyond any movable list item (for example, on curly brace in enum). | 130 | // Cursor is beyond any movable list item (for example, on curly brace in enum). |
131 | // It's not necessary, that parent of list is movable (arg list's parent is not, for example), | 131 | // It's not necessary, that parent of list is movable (arg list's parent is not, for example), |
@@ -134,11 +134,38 @@ fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>( | |||
134 | } | 134 | } |
135 | } | 135 | } |
136 | 136 | ||
137 | fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit { | 137 | fn replace_nodes<'a>( |
138 | range: TextRange, | ||
139 | mut first: &'a SyntaxNode, | ||
140 | mut second: &'a SyntaxNode, | ||
141 | ) -> TextEdit { | ||
142 | let cursor_offset = if range.is_empty() { | ||
143 | // FIXME: `applySnippetTextEdits` does not support non-empty selection ranges | ||
144 | if first.text_range().contains_range(range) { | ||
145 | Some(range.start() - first.text_range().start()) | ||
146 | } else if second.text_range().contains_range(range) { | ||
147 | mem::swap(&mut first, &mut second); | ||
148 | Some(range.start() - first.text_range().start()) | ||
149 | } else { | ||
150 | None | ||
151 | } | ||
152 | } else { | ||
153 | None | ||
154 | }; | ||
155 | |||
156 | let first_with_cursor = match cursor_offset { | ||
157 | Some(offset) => { | ||
158 | let mut item_text = first.text().to_string(); | ||
159 | item_text.insert_str(offset.into(), "$0"); | ||
160 | item_text | ||
161 | } | ||
162 | None => first.text().to_string(), | ||
163 | }; | ||
164 | |||
138 | let mut edit = TextEditBuilder::default(); | 165 | let mut edit = TextEditBuilder::default(); |
139 | 166 | ||
140 | algo::diff(first, second).into_text_edit(&mut edit); | 167 | algo::diff(first, second).into_text_edit(&mut edit); |
141 | algo::diff(second, first).into_text_edit(&mut edit); | 168 | edit.replace(second.text_range(), first_with_cursor); |
142 | 169 | ||
143 | edit.finish() | 170 | edit.finish() |
144 | } | 171 | } |
@@ -188,7 +215,7 @@ fn main() { | |||
188 | expect![[r#" | 215 | expect![[r#" |
189 | fn main() { | 216 | fn main() { |
190 | match true { | 217 | match true { |
191 | false => { | 218 | false =>$0 { |
192 | println!("Test"); | 219 | println!("Test"); |
193 | }, | 220 | }, |
194 | true => { | 221 | true => { |
@@ -222,7 +249,7 @@ fn main() { | |||
222 | false => { | 249 | false => { |
223 | println!("Test"); | 250 | println!("Test"); |
224 | }, | 251 | }, |
225 | true => { | 252 | true =>$0 { |
226 | println!("Hello, world"); | 253 | println!("Hello, world"); |
227 | } | 254 | } |
228 | }; | 255 | }; |
@@ -274,7 +301,7 @@ fn main() { | |||
274 | "#, | 301 | "#, |
275 | expect![[r#" | 302 | expect![[r#" |
276 | fn main() { | 303 | fn main() { |
277 | let test2 = 456; | 304 | let test2$0 = 456; |
278 | let test = 123; | 305 | let test = 123; |
279 | } | 306 | } |
280 | "#]], | 307 | "#]], |
@@ -293,7 +320,7 @@ fn main() { | |||
293 | "#, | 320 | "#, |
294 | expect![[r#" | 321 | expect![[r#" |
295 | fn main() { | 322 | fn main() { |
296 | println!("All I want to say is..."); | 323 | println!("All I want to say is...");$0 |
297 | println!("Hello, world"); | 324 | println!("Hello, world"); |
298 | } | 325 | } |
299 | "#]], | 326 | "#]], |
@@ -313,7 +340,7 @@ fn main() { | |||
313 | fn main() { | 340 | fn main() { |
314 | if true { | 341 | if true { |
315 | println!("Test"); | 342 | println!("Test"); |
316 | } | 343 | }$0 |
317 | 344 | ||
318 | println!("Hello, world"); | 345 | println!("Hello, world"); |
319 | } | 346 | } |
@@ -334,7 +361,7 @@ fn main() { | |||
334 | fn main() { | 361 | fn main() { |
335 | for i in 0..10 { | 362 | for i in 0..10 { |
336 | println!("Test"); | 363 | println!("Test"); |
337 | } | 364 | }$0 |
338 | 365 | ||
339 | println!("Hello, world"); | 366 | println!("Hello, world"); |
340 | } | 367 | } |
@@ -355,7 +382,7 @@ fn main() { | |||
355 | fn main() { | 382 | fn main() { |
356 | loop { | 383 | loop { |
357 | println!("Test"); | 384 | println!("Test"); |
358 | } | 385 | }$0 |
359 | 386 | ||
360 | println!("Hello, world"); | 387 | println!("Hello, world"); |
361 | } | 388 | } |
@@ -376,7 +403,7 @@ fn main() { | |||
376 | fn main() { | 403 | fn main() { |
377 | while true { | 404 | while true { |
378 | println!("Test"); | 405 | println!("Test"); |
379 | } | 406 | }$0 |
380 | 407 | ||
381 | println!("Hello, world"); | 408 | println!("Hello, world"); |
382 | } | 409 | } |
@@ -393,7 +420,7 @@ fn main() { | |||
393 | "#, | 420 | "#, |
394 | expect![[r#" | 421 | expect![[r#" |
395 | fn main() { | 422 | fn main() { |
396 | return 123; | 423 | return 123;$0 |
397 | 424 | ||
398 | println!("Hello, world"); | 425 | println!("Hello, world"); |
399 | } | 426 | } |
@@ -430,7 +457,7 @@ fn main() {} | |||
430 | fn foo() {}$0$0 | 457 | fn foo() {}$0$0 |
431 | "#, | 458 | "#, |
432 | expect![[r#" | 459 | expect![[r#" |
433 | fn foo() {} | 460 | fn foo() {}$0 |
434 | 461 | ||
435 | fn main() {} | 462 | fn main() {} |
436 | "#]], | 463 | "#]], |
@@ -451,7 +478,7 @@ impl Wow for Yay $0$0{} | |||
451 | expect![[r#" | 478 | expect![[r#" |
452 | struct Yay; | 479 | struct Yay; |
453 | 480 | ||
454 | impl Wow for Yay {} | 481 | impl Wow for Yay $0{} |
455 | 482 | ||
456 | trait Wow {} | 483 | trait Wow {} |
457 | "#]], | 484 | "#]], |
@@ -467,7 +494,7 @@ use std::vec::Vec; | |||
467 | use std::collections::HashMap$0$0; | 494 | use std::collections::HashMap$0$0; |
468 | "#, | 495 | "#, |
469 | expect![[r#" | 496 | expect![[r#" |
470 | use std::collections::HashMap; | 497 | use std::collections::HashMap$0; |
471 | use std::vec::Vec; | 498 | use std::vec::Vec; |
472 | "#]], | 499 | "#]], |
473 | Direction::Up, | 500 | Direction::Up, |
@@ -502,7 +529,7 @@ fn main() { | |||
502 | } | 529 | } |
503 | 530 | ||
504 | #[test] | 531 | #[test] |
505 | fn test_moves_param_up() { | 532 | fn test_moves_param() { |
506 | check( | 533 | check( |
507 | r#" | 534 | r#" |
508 | fn test(one: i32, two$0$0: u32) {} | 535 | fn test(one: i32, two$0$0: u32) {} |
@@ -512,7 +539,7 @@ fn main() { | |||
512 | } | 539 | } |
513 | "#, | 540 | "#, |
514 | expect![[r#" | 541 | expect![[r#" |
515 | fn test(two: u32, one: i32) {} | 542 | fn test(two$0: u32, one: i32) {} |
516 | 543 | ||
517 | fn main() { | 544 | fn main() { |
518 | test(123, 456); | 545 | test(123, 456); |
@@ -520,6 +547,15 @@ fn main() { | |||
520 | "#]], | 547 | "#]], |
521 | Direction::Up, | 548 | Direction::Up, |
522 | ); | 549 | ); |
550 | check( | ||
551 | r#" | ||
552 | fn f($0$0arg: u8, arg2: u16) {} | ||
553 | "#, | ||
554 | expect![[r#" | ||
555 | fn f(arg2: u16, $0arg: u8) {} | ||
556 | "#]], | ||
557 | Direction::Down, | ||
558 | ); | ||
523 | } | 559 | } |
524 | 560 | ||
525 | #[test] | 561 | #[test] |
@@ -536,7 +572,7 @@ fn main() { | |||
536 | fn test(one: i32, two: u32) {} | 572 | fn test(one: i32, two: u32) {} |
537 | 573 | ||
538 | fn main() { | 574 | fn main() { |
539 | test(456, 123); | 575 | test(456$0, 123); |
540 | } | 576 | } |
541 | "#]], | 577 | "#]], |
542 | Direction::Up, | 578 | Direction::Up, |
@@ -557,7 +593,7 @@ fn main() { | |||
557 | fn test(one: i32, two: u32) {} | 593 | fn test(one: i32, two: u32) {} |
558 | 594 | ||
559 | fn main() { | 595 | fn main() { |
560 | test(456, 123); | 596 | test(456, 123$0); |
561 | } | 597 | } |
562 | "#]], | 598 | "#]], |
563 | Direction::Down, | 599 | Direction::Down, |
@@ -594,7 +630,7 @@ struct Test<A, B$0$0>(A, B); | |||
594 | fn main() {} | 630 | fn main() {} |
595 | "#, | 631 | "#, |
596 | expect![[r#" | 632 | expect![[r#" |
597 | struct Test<B, A>(A, B); | 633 | struct Test<B$0, A>(A, B); |
598 | 634 | ||
599 | fn main() {} | 635 | fn main() {} |
600 | "#]], | 636 | "#]], |
@@ -616,7 +652,7 @@ fn main() { | |||
616 | struct Test<A, B>(A, B); | 652 | struct Test<A, B>(A, B); |
617 | 653 | ||
618 | fn main() { | 654 | fn main() { |
619 | let t = Test::<&str, i32>(123, "yay"); | 655 | let t = Test::<&str$0, i32>(123, "yay"); |
620 | } | 656 | } |
621 | "#]], | 657 | "#]], |
622 | Direction::Up, | 658 | Direction::Up, |
@@ -636,7 +672,7 @@ fn main() {} | |||
636 | "#, | 672 | "#, |
637 | expect![[r#" | 673 | expect![[r#" |
638 | enum Hello { | 674 | enum Hello { |
639 | Two, | 675 | Two$0, |
640 | One | 676 | One |
641 | } | 677 | } |
642 | 678 | ||
@@ -663,7 +699,7 @@ trait One {} | |||
663 | 699 | ||
664 | trait Two {} | 700 | trait Two {} |
665 | 701 | ||
666 | fn test<T: Two + One>(t: T) {} | 702 | fn test<T: Two$0 + One>(t: T) {} |
667 | 703 | ||
668 | fn main() {} | 704 | fn main() {} |
669 | "#]], | 705 | "#]], |
@@ -709,7 +745,7 @@ trait Yay { | |||
709 | impl Yay for Test { | 745 | impl Yay for Test { |
710 | type One = i32; | 746 | type One = i32; |
711 | 747 | ||
712 | fn inner() { | 748 | fn inner() {$0 |
713 | println!("Mmmm"); | 749 | println!("Mmmm"); |
714 | } | 750 | } |
715 | 751 | ||
@@ -736,7 +772,7 @@ fn test() { | |||
736 | "#, | 772 | "#, |
737 | expect![[r#" | 773 | expect![[r#" |
738 | fn test() { | 774 | fn test() { |
739 | mod hi { | 775 | mod hi {$0 |
740 | fn inner() {} | 776 | fn inner() {} |
741 | } | 777 | } |
742 | 778 | ||
@@ -764,7 +800,7 @@ fn main() {} | |||
764 | expect![[r#" | 800 | expect![[r#" |
765 | fn main() {} | 801 | fn main() {} |
766 | 802 | ||
767 | #[derive(Debug)] | 803 | $0#[derive(Debug)] |
768 | enum FooBar { | 804 | enum FooBar { |
769 | Foo, | 805 | Foo, |
770 | Bar, | 806 | Bar, |
@@ -784,7 +820,7 @@ fn main() {} | |||
784 | expect![[r#" | 820 | expect![[r#" |
785 | fn main() {} | 821 | fn main() {} |
786 | 822 | ||
787 | enum FooBar { | 823 | $0enum FooBar { |
788 | Foo, | 824 | Foo, |
789 | Bar, | 825 | Bar, |
790 | } | 826 | } |
@@ -804,7 +840,7 @@ fn main() {} | |||
804 | expect![[r#" | 840 | expect![[r#" |
805 | struct Test; | 841 | struct Test; |
806 | 842 | ||
807 | impl SomeTrait for Test {} | 843 | $0impl SomeTrait for Test {} |
808 | 844 | ||
809 | trait SomeTrait {} | 845 | trait SomeTrait {} |
810 | 846 | ||
@@ -831,7 +867,7 @@ fn main() {} | |||
831 | enum FooBar { | 867 | enum FooBar { |
832 | Foo, | 868 | Foo, |
833 | Bar, | 869 | Bar, |
834 | } | 870 | }$0 |
835 | "#]], | 871 | "#]], |
836 | Direction::Down, | 872 | Direction::Down, |
837 | ); | 873 | ); |
@@ -848,7 +884,7 @@ fn main() {} | |||
848 | expect![[r#" | 884 | expect![[r#" |
849 | struct Test; | 885 | struct Test; |
850 | 886 | ||
851 | impl SomeTrait for Test {} | 887 | impl SomeTrait for Test {}$0 |
852 | 888 | ||
853 | trait SomeTrait {} | 889 | trait SomeTrait {} |
854 | 890 | ||
diff --git a/crates/ide/src/prime_caches.rs b/crates/ide/src/prime_caches.rs index ea0acfaa0..03597f507 100644 --- a/crates/ide/src/prime_caches.rs +++ b/crates/ide/src/prime_caches.rs | |||
@@ -27,6 +27,7 @@ pub(crate) fn prime_caches(db: &RootDatabase, cb: &(dyn Fn(PrimeCachesProgress) | |||
27 | let topo = &graph.crates_in_topological_order(); | 27 | let topo = &graph.crates_in_topological_order(); |
28 | 28 | ||
29 | cb(PrimeCachesProgress::Started); | 29 | cb(PrimeCachesProgress::Started); |
30 | let _d = stdx::defer(|| cb(PrimeCachesProgress::Finished)); | ||
30 | 31 | ||
31 | // FIXME: This would be easy to parallelize, since it's in the ideal ordering for that. | 32 | // FIXME: This would be easy to parallelize, since it's in the ideal ordering for that. |
32 | // Unfortunately rayon prevents panics from propagation out of a `scope`, which breaks | 33 | // Unfortunately rayon prevents panics from propagation out of a `scope`, which breaks |
@@ -41,6 +42,4 @@ pub(crate) fn prime_caches(db: &RootDatabase, cb: &(dyn Fn(PrimeCachesProgress) | |||
41 | }); | 42 | }); |
42 | db.crate_def_map(*krate); | 43 | db.crate_def_map(*krate); |
43 | } | 44 | } |
44 | |||
45 | cb(PrimeCachesProgress::Finished); | ||
46 | } | 45 | } |
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index 5ccb84714..18552459b 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs | |||
@@ -12,7 +12,10 @@ use syntax::{ | |||
12 | SyntaxNode, SyntaxToken, T, | 12 | SyntaxNode, SyntaxToken, T, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | use crate::{syntax_highlighting::tags::HlPunct, Highlight, HlMod, HlTag}; | 15 | use crate::{ |
16 | syntax_highlighting::tags::{HlOperator, HlPunct}, | ||
17 | Highlight, HlMod, HlTag, | ||
18 | }; | ||
16 | 19 | ||
17 | pub(super) fn element( | 20 | pub(super) fn element( |
18 | sema: &Semantics<RootDatabase>, | 21 | sema: &Semantics<RootDatabase>, |
@@ -132,7 +135,7 @@ pub(super) fn element( | |||
132 | INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(), | 135 | INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(), |
133 | BYTE => HlTag::ByteLiteral.into(), | 136 | BYTE => HlTag::ByteLiteral.into(), |
134 | CHAR => HlTag::CharLiteral.into(), | 137 | CHAR => HlTag::CharLiteral.into(), |
135 | QUESTION => Highlight::new(HlTag::Operator) | HlMod::ControlFlow, | 138 | QUESTION => Highlight::new(HlTag::Operator(HlOperator::Other)) | HlMod::ControlFlow, |
136 | LIFETIME => { | 139 | LIFETIME => { |
137 | let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap(); | 140 | let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap(); |
138 | 141 | ||
@@ -146,8 +149,11 @@ pub(super) fn element( | |||
146 | } | 149 | } |
147 | } | 150 | } |
148 | p if p.is_punct() => match p { | 151 | p if p.is_punct() => match p { |
152 | T![&] if element.parent().and_then(ast::BinExpr::cast).is_some() => { | ||
153 | HlTag::Operator(HlOperator::Bitwise).into() | ||
154 | } | ||
149 | T![&] => { | 155 | T![&] => { |
150 | let h = HlTag::Operator.into(); | 156 | let h = HlTag::Operator(HlOperator::Other).into(); |
151 | let is_unsafe = element | 157 | let is_unsafe = element |
152 | .parent() | 158 | .parent() |
153 | .and_then(ast::RefExpr::cast) | 159 | .and_then(ast::RefExpr::cast) |
@@ -159,13 +165,18 @@ pub(super) fn element( | |||
159 | h | 165 | h |
160 | } | 166 | } |
161 | } | 167 | } |
162 | T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => HlTag::Operator.into(), | 168 | T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => { |
169 | HlTag::Operator(HlOperator::Other).into() | ||
170 | } | ||
163 | T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => { | 171 | T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => { |
164 | HlTag::Symbol(SymbolKind::Macro).into() | 172 | HlTag::Symbol(SymbolKind::Macro).into() |
165 | } | 173 | } |
166 | T![!] if element.parent().and_then(ast::NeverType::cast).is_some() => { | 174 | T![!] if element.parent().and_then(ast::NeverType::cast).is_some() => { |
167 | HlTag::BuiltinType.into() | 175 | HlTag::BuiltinType.into() |
168 | } | 176 | } |
177 | T![!] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { | ||
178 | HlTag::Operator(HlOperator::Logical).into() | ||
179 | } | ||
169 | T![*] if element.parent().and_then(ast::PtrType::cast).is_some() => { | 180 | T![*] if element.parent().and_then(ast::PtrType::cast).is_some() => { |
170 | HlTag::Keyword.into() | 181 | HlTag::Keyword.into() |
171 | } | 182 | } |
@@ -175,9 +186,9 @@ pub(super) fn element( | |||
175 | let expr = prefix_expr.expr()?; | 186 | let expr = prefix_expr.expr()?; |
176 | let ty = sema.type_of_expr(&expr)?; | 187 | let ty = sema.type_of_expr(&expr)?; |
177 | if ty.is_raw_ptr() { | 188 | if ty.is_raw_ptr() { |
178 | HlTag::Operator | HlMod::Unsafe | 189 | HlTag::Operator(HlOperator::Other) | HlMod::Unsafe |
179 | } else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() { | 190 | } else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() { |
180 | HlTag::Operator.into() | 191 | HlTag::Operator(HlOperator::Other).into() |
181 | } else { | 192 | } else { |
182 | HlTag::Punctuation(HlPunct::Other).into() | 193 | HlTag::Punctuation(HlPunct::Other).into() |
183 | } | 194 | } |
@@ -188,19 +199,43 @@ pub(super) fn element( | |||
188 | let expr = prefix_expr.expr()?; | 199 | let expr = prefix_expr.expr()?; |
189 | match expr { | 200 | match expr { |
190 | ast::Expr::Literal(_) => HlTag::NumericLiteral, | 201 | ast::Expr::Literal(_) => HlTag::NumericLiteral, |
191 | _ => HlTag::Operator, | 202 | _ => HlTag::Operator(HlOperator::Other), |
192 | } | 203 | } |
193 | .into() | 204 | .into() |
194 | } | 205 | } |
195 | _ if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { | 206 | _ if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { |
196 | HlTag::Operator.into() | 207 | HlTag::Operator(HlOperator::Other).into() |
208 | } | ||
209 | T![+] | T![-] | T![*] | T![/] | T![+=] | T![-=] | T![*=] | T![/=] | ||
210 | if element.parent().and_then(ast::BinExpr::cast).is_some() => | ||
211 | { | ||
212 | HlTag::Operator(HlOperator::Arithmetic).into() | ||
213 | } | ||
214 | T![|] | T![&] | T![!] | T![^] | T![|=] | T![&=] | T![^=] | ||
215 | if element.parent().and_then(ast::BinExpr::cast).is_some() => | ||
216 | { | ||
217 | HlTag::Operator(HlOperator::Bitwise).into() | ||
218 | } | ||
219 | T![&&] | T![||] if element.parent().and_then(ast::BinExpr::cast).is_some() => { | ||
220 | HlTag::Operator(HlOperator::Logical).into() | ||
221 | } | ||
222 | T![>] | T![<] | T![==] | T![>=] | T![<=] | T![!=] | ||
223 | if element.parent().and_then(ast::BinExpr::cast).is_some() => | ||
224 | { | ||
225 | HlTag::Operator(HlOperator::Comparison).into() | ||
226 | } | ||
227 | _ if element.parent().and_then(ast::BinExpr::cast).is_some() => { | ||
228 | HlTag::Operator(HlOperator::Other).into() | ||
197 | } | 229 | } |
198 | _ if element.parent().and_then(ast::BinExpr::cast).is_some() => HlTag::Operator.into(), | ||
199 | _ if element.parent().and_then(ast::RangeExpr::cast).is_some() => { | 230 | _ if element.parent().and_then(ast::RangeExpr::cast).is_some() => { |
200 | HlTag::Operator.into() | 231 | HlTag::Operator(HlOperator::Other).into() |
232 | } | ||
233 | _ if element.parent().and_then(ast::RangePat::cast).is_some() => { | ||
234 | HlTag::Operator(HlOperator::Other).into() | ||
235 | } | ||
236 | _ if element.parent().and_then(ast::RestPat::cast).is_some() => { | ||
237 | HlTag::Operator(HlOperator::Other).into() | ||
201 | } | 238 | } |
202 | _ if element.parent().and_then(ast::RangePat::cast).is_some() => HlTag::Operator.into(), | ||
203 | _ if element.parent().and_then(ast::RestPat::cast).is_some() => HlTag::Operator.into(), | ||
204 | _ if element.parent().and_then(ast::Attr::cast).is_some() => HlTag::Attribute.into(), | 239 | _ if element.parent().and_then(ast::Attr::cast).is_some() => HlTag::Attribute.into(), |
205 | kind => HlTag::Punctuation(match kind { | 240 | kind => HlTag::Punctuation(match kind { |
206 | T!['['] | T![']'] => HlPunct::Bracket, | 241 | T!['['] | T![']'] => HlPunct::Bracket, |
@@ -323,8 +358,18 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { | |||
323 | hir::ModuleDef::Trait(_) => HlTag::Symbol(SymbolKind::Trait), | 358 | hir::ModuleDef::Trait(_) => HlTag::Symbol(SymbolKind::Trait), |
324 | hir::ModuleDef::TypeAlias(type_) => { | 359 | hir::ModuleDef::TypeAlias(type_) => { |
325 | let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias)); | 360 | let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias)); |
326 | if type_.as_assoc_item(db).is_some() { | 361 | if let Some(item) = type_.as_assoc_item(db) { |
327 | h |= HlMod::Associated | 362 | h |= HlMod::Associated; |
363 | match item.container(db) { | ||
364 | AssocItemContainer::Impl(i) => { | ||
365 | if i.trait_(db).is_some() { | ||
366 | h |= HlMod::Trait; | ||
367 | } | ||
368 | } | ||
369 | AssocItemContainer::Trait(_t) => { | ||
370 | h |= HlMod::Trait; | ||
371 | } | ||
372 | } | ||
328 | } | 373 | } |
329 | return h; | 374 | return h; |
330 | } | 375 | } |
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index b62d43256..bc221d599 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs | |||
@@ -1,17 +1,17 @@ | |||
1 | //! "Recursive" Syntax highlighting for code in doctests and fixtures. | 1 | //! "Recursive" Syntax highlighting for code in doctests and fixtures. |
2 | 2 | ||
3 | use std::{mem, ops::Range}; | 3 | use std::mem; |
4 | 4 | ||
5 | use either::Either; | 5 | use either::Either; |
6 | use hir::{HasAttrs, InFile, Semantics}; | 6 | use hir::{InFile, Semantics}; |
7 | use ide_db::{call_info::ActiveParameter, defs::Definition, SymbolKind}; | 7 | use ide_db::{call_info::ActiveParameter, helpers::rust_doc::is_rust_fence, SymbolKind}; |
8 | use syntax::{ | 8 | use syntax::{ |
9 | ast::{self, AstNode}, | 9 | ast::{self, AstNode}, |
10 | match_ast, AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize, | 10 | AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
14 | doc_links::{extract_definitions_from_markdown, resolve_doc_path_for_def}, | 14 | doc_links::{doc_attributes, extract_definitions_from_markdown, resolve_doc_path_for_def}, |
15 | Analysis, HlMod, HlRange, HlTag, RootDatabase, | 15 | Analysis, HlMod, HlRange, HlTag, RootDatabase, |
16 | }; | 16 | }; |
17 | 17 | ||
@@ -78,44 +78,6 @@ pub(super) fn ra_fixture( | |||
78 | } | 78 | } |
79 | 79 | ||
80 | const RUSTDOC_FENCE: &'static str = "```"; | 80 | const RUSTDOC_FENCE: &'static str = "```"; |
81 | const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[ | ||
82 | "", | ||
83 | "rust", | ||
84 | "should_panic", | ||
85 | "ignore", | ||
86 | "no_run", | ||
87 | "compile_fail", | ||
88 | "edition2015", | ||
89 | "edition2018", | ||
90 | "edition2021", | ||
91 | ]; | ||
92 | |||
93 | fn doc_attributes<'node>( | ||
94 | sema: &Semantics<RootDatabase>, | ||
95 | node: &'node SyntaxNode, | ||
96 | ) -> Option<(hir::AttrsWithOwner, Definition)> { | ||
97 | match_ast! { | ||
98 | match node { | ||
99 | ast::SourceFile(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))), | ||
100 | ast::Module(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))), | ||
101 | ast::Fn(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Function(def)))), | ||
102 | ast::Struct(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(def))))), | ||
103 | ast::Union(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Union(def))))), | ||
104 | ast::Enum(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(def))))), | ||
105 | ast::Variant(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Variant(def)))), | ||
106 | ast::Trait(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Trait(def)))), | ||
107 | ast::Static(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Static(def)))), | ||
108 | ast::Const(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Const(def)))), | ||
109 | ast::TypeAlias(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::TypeAlias(def)))), | ||
110 | ast::Impl(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::SelfType(def))), | ||
111 | ast::RecordField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))), | ||
112 | ast::TupleField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))), | ||
113 | ast::Macro(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Macro(def))), | ||
114 | // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))), | ||
115 | _ => return None | ||
116 | } | ||
117 | } | ||
118 | } | ||
119 | 81 | ||
120 | /// Injection of syntax highlighting of doctests. | 82 | /// Injection of syntax highlighting of doctests. |
121 | pub(super) fn doc_comment( | 83 | pub(super) fn doc_comment( |
@@ -139,8 +101,28 @@ pub(super) fn doc_comment( | |||
139 | // Replace the original, line-spanning comment ranges by new, only comment-prefix | 101 | // Replace the original, line-spanning comment ranges by new, only comment-prefix |
140 | // spanning comment ranges. | 102 | // spanning comment ranges. |
141 | let mut new_comments = Vec::new(); | 103 | let mut new_comments = Vec::new(); |
142 | let mut intra_doc_links = Vec::new(); | ||
143 | let mut string; | 104 | let mut string; |
105 | |||
106 | if let Some((docs, doc_mapping)) = attributes.docs_with_rangemap(sema.db) { | ||
107 | extract_definitions_from_markdown(docs.as_str()) | ||
108 | .into_iter() | ||
109 | .filter_map(|(range, link, ns)| { | ||
110 | let def = resolve_doc_path_for_def(sema.db, def, &link, ns)?; | ||
111 | let InFile { file_id, value: range } = doc_mapping.map(range)?; | ||
112 | (file_id == node.file_id).then(|| (range, def)) | ||
113 | }) | ||
114 | .for_each(|(range, def)| { | ||
115 | hl.add(HlRange { | ||
116 | range, | ||
117 | highlight: module_def_to_hl_tag(def) | ||
118 | | HlMod::Documentation | ||
119 | | HlMod::Injected | ||
120 | | HlMod::IntraDocLink, | ||
121 | binding_hash: None, | ||
122 | }) | ||
123 | }); | ||
124 | } | ||
125 | |||
144 | for attr in attributes.by_key("doc").attrs() { | 126 | for attr in attributes.by_key("doc").attrs() { |
145 | 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); |
146 | if file_id != node.file_id { | 128 | if file_id != node.file_id { |
@@ -181,30 +163,11 @@ pub(super) fn doc_comment( | |||
181 | is_codeblock = !is_codeblock; | 163 | is_codeblock = !is_codeblock; |
182 | // Check whether code is rust by inspecting fence guards | 164 | // Check whether code is rust by inspecting fence guards |
183 | let guards = &line[idx + RUSTDOC_FENCE.len()..]; | 165 | let guards = &line[idx + RUSTDOC_FENCE.len()..]; |
184 | let is_rust = | 166 | let is_rust = is_rust_fence(guards); |
185 | guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim())); | ||
186 | is_doctest = is_codeblock && is_rust; | 167 | is_doctest = is_codeblock && is_rust; |
187 | continue; | 168 | continue; |
188 | } | 169 | } |
189 | None if !is_doctest => { | 170 | None if !is_doctest => continue, |
190 | intra_doc_links.extend( | ||
191 | extract_definitions_from_markdown(line) | ||
192 | .into_iter() | ||
193 | .filter_map(|(range, link, ns)| { | ||
194 | Some(range).zip(resolve_doc_path_for_def(sema.db, def, &link, ns)) | ||
195 | }) | ||
196 | .map(|(Range { start, end }, def)| { | ||
197 | ( | ||
198 | def, | ||
199 | TextRange::at( | ||
200 | prev_range_start + TextSize::from(start as u32), | ||
201 | TextSize::from((end - start) as u32), | ||
202 | ), | ||
203 | ) | ||
204 | }), | ||
205 | ); | ||
206 | continue; | ||
207 | } | ||
208 | None => (), | 171 | None => (), |
209 | } | 172 | } |
210 | 173 | ||
@@ -223,17 +186,6 @@ pub(super) fn doc_comment( | |||
223 | } | 186 | } |
224 | } | 187 | } |
225 | 188 | ||
226 | for (def, range) in intra_doc_links { | ||
227 | hl.add(HlRange { | ||
228 | range, | ||
229 | highlight: module_def_to_hl_tag(def) | ||
230 | | HlMod::Documentation | ||
231 | | HlMod::Injected | ||
232 | | HlMod::IntraDocLink, | ||
233 | binding_hash: None, | ||
234 | }); | ||
235 | } | ||
236 | |||
237 | if new_comments.is_empty() { | 189 | if new_comments.is_empty() { |
238 | return; // no need to run an analysis on an empty file | 190 | return; // no need to run an analysis on an empty file |
239 | } | 191 | } |
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index 1cec991aa..e58392d67 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/crates/ide/src/syntax_highlighting/tags.rs | |||
@@ -28,7 +28,7 @@ pub enum HlTag { | |||
28 | FormatSpecifier, | 28 | FormatSpecifier, |
29 | Keyword, | 29 | Keyword, |
30 | NumericLiteral, | 30 | NumericLiteral, |
31 | Operator, | 31 | Operator(HlOperator), |
32 | Punctuation(HlPunct), | 32 | Punctuation(HlPunct), |
33 | StringLiteral, | 33 | StringLiteral, |
34 | UnresolvedReference, | 34 | UnresolvedReference, |
@@ -87,6 +87,20 @@ pub enum HlPunct { | |||
87 | Other, | 87 | Other, |
88 | } | 88 | } |
89 | 89 | ||
90 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||
91 | pub enum HlOperator { | ||
92 | /// |, &, !, ^, |=, &=, ^= | ||
93 | Bitwise, | ||
94 | /// +, -, *, /, +=, -=, *=, /= | ||
95 | Arithmetic, | ||
96 | /// &&, ||, ! | ||
97 | Logical, | ||
98 | /// >, <, ==, >=, <=, != | ||
99 | Comparison, | ||
100 | /// | ||
101 | Other, | ||
102 | } | ||
103 | |||
90 | impl HlTag { | 104 | impl HlTag { |
91 | fn as_str(self) -> &'static str { | 105 | fn as_str(self) -> &'static str { |
92 | match self { | 106 | match self { |
@@ -133,7 +147,13 @@ impl HlTag { | |||
133 | HlPunct::Other => "punctuation", | 147 | HlPunct::Other => "punctuation", |
134 | }, | 148 | }, |
135 | HlTag::NumericLiteral => "numeric_literal", | 149 | HlTag::NumericLiteral => "numeric_literal", |
136 | HlTag::Operator => "operator", | 150 | HlTag::Operator(op) => match op { |
151 | HlOperator::Bitwise => "bitwise", | ||
152 | HlOperator::Arithmetic => "arithmetic", | ||
153 | HlOperator::Logical => "logical", | ||
154 | HlOperator::Comparison => "comparison", | ||
155 | HlOperator::Other => "operator", | ||
156 | }, | ||
137 | HlTag::StringLiteral => "string_literal", | 157 | HlTag::StringLiteral => "string_literal", |
138 | HlTag::UnresolvedReference => "unresolved_reference", | 158 | HlTag::UnresolvedReference => "unresolved_reference", |
139 | HlTag::None => "none", | 159 | HlTag::None => "none", |
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 045162eb8..6ee6d85fb 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html | |||
@@ -76,7 +76,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
76 | <span class="comment documentation">/// </span><span class="comment injected">// calls bar on foo</span> | 76 | <span class="comment documentation">/// </span><span class="comment injected">// calls bar on foo</span> |
77 | <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="parenthesis injected">(</span><span class="none injected">foo</span><span class="operator injected">.</span><span class="none injected">bar</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> | 77 | <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="parenthesis injected">(</span><span class="none injected">foo</span><span class="operator injected">.</span><span class="none injected">bar</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> |
78 | <span class="comment documentation">///</span> | 78 | <span class="comment documentation">///</span> |
79 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">bar</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="variable injected">foo</span><span class="operator injected">.</span><span class="field injected">bar</span><span class="none injected"> </span><span class="operator injected">||</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="semicolon injected">;</span> | 79 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">bar</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="variable injected">foo</span><span class="operator injected">.</span><span class="field injected">bar</span><span class="none injected"> </span><span class="logical injected">||</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="semicolon injected">;</span> |
80 | <span class="comment documentation">///</span> | 80 | <span class="comment documentation">///</span> |
81 | <span class="comment documentation">/// </span><span class="comment injected">/* multi-line</span> | 81 | <span class="comment documentation">/// </span><span class="comment injected">/* multi-line</span> |
82 | <span class="comment documentation">/// </span><span class="comment injected"> comment */</span> | 82 | <span class="comment documentation">/// </span><span class="comment injected"> comment */</span> |
@@ -100,10 +100,18 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
100 | <span class="brace">}</span> | 100 | <span class="brace">}</span> |
101 | 101 | ||
102 | <span class="comment documentation">/// </span><span class="struct documentation intra_doc_link injected">[`Foo`](Foo)</span><span class="comment documentation"> is a struct</span> | 102 | <span class="comment documentation">/// </span><span class="struct documentation intra_doc_link injected">[`Foo`](Foo)</span><span class="comment documentation"> is a struct</span> |
103 | <span class="comment documentation">/// </span><span class="function documentation intra_doc_link injected">[`all_the_links`](all_the_links)</span><span class="comment documentation"> is this function</span> | 103 | <span class="comment documentation">/// This function is > </span><span class="function documentation intra_doc_link injected">[`all_the_links`](all_the_links)</span><span class="comment documentation"> <</span> |
104 | <span class="comment documentation">/// [`noop`](noop) is a macro below</span> | 104 | <span class="comment documentation">/// [`noop`](noop) is a macro below</span> |
105 | <span class="comment documentation">/// </span><span class="struct documentation intra_doc_link injected">[`Item`]</span><span class="comment documentation"> is a struct in the module </span><span class="module documentation intra_doc_link injected">[`module`]</span> | ||
106 | <span class="comment documentation">///</span> | ||
107 | <span class="comment documentation">/// [`Item`]: module::Item</span> | ||
108 | <span class="comment documentation">/// [mix_and_match]: ThisShouldntResolve</span> | ||
105 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">all_the_links</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> | 109 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">all_the_links</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> |
106 | 110 | ||
111 | <span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">module</span> <span class="brace">{</span> | ||
112 | <span class="keyword">pub</span> <span class="keyword">struct</span> <span class="struct declaration">Item</span><span class="semicolon">;</span> | ||
113 | <span class="brace">}</span> | ||
114 | |||
107 | <span class="comment documentation">/// ```</span> | 115 | <span class="comment documentation">/// ```</span> |
108 | <span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> | 116 | <span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> |
109 | <span class="comment documentation">/// ```</span> | 117 | <span class="comment documentation">/// ```</span> |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index 973173254..c43bcb691 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html | |||
@@ -213,7 +213,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
213 | <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="semicolon">;</span> | 213 | <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="semicolon">;</span> |
214 | <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="semicolon">;</span> | 214 | <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="semicolon">;</span> |
215 | 215 | ||
216 | <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="operator">!</span><span class="bool_literal">true</span><span class="semicolon">;</span> | 216 | <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="logical">!</span><span class="bool_literal">true</span><span class="semicolon">;</span> |
217 | 217 | ||
218 | <span class="label declaration">'foo</span><span class="colon">:</span> <span class="keyword control">loop</span> <span class="brace">{</span> | 218 | <span class="label declaration">'foo</span><span class="colon">:</span> <span class="keyword control">loop</span> <span class="brace">{</span> |
219 | <span class="keyword control">break</span> <span class="label">'foo</span><span class="semicolon">;</span> | 219 | <span class="keyword control">break</span> <span class="label">'foo</span><span class="semicolon">;</span> |
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 369ae0972..17cc6334b 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -1,6 +1,8 @@ | |||
1 | use std::time::Instant; | ||
2 | |||
1 | use expect_test::{expect_file, ExpectFile}; | 3 | use expect_test::{expect_file, ExpectFile}; |
2 | use ide_db::SymbolKind; | 4 | use ide_db::SymbolKind; |
3 | use test_utils::{bench, bench_fixture, skip_slow_tests}; | 5 | use test_utils::{bench, bench_fixture, skip_slow_tests, AssertLinear}; |
4 | 6 | ||
5 | use crate::{fixture, FileRange, HlTag, TextRange}; | 7 | use crate::{fixture, FileRange, HlTag, TextRange}; |
6 | 8 | ||
@@ -258,6 +260,36 @@ fn benchmark_syntax_highlighting_long_struct() { | |||
258 | } | 260 | } |
259 | 261 | ||
260 | #[test] | 262 | #[test] |
263 | fn syntax_highlighting_not_quadratic() { | ||
264 | if skip_slow_tests() { | ||
265 | return; | ||
266 | } | ||
267 | |||
268 | let mut al = AssertLinear::default(); | ||
269 | while al.next_round() { | ||
270 | for i in 6..=10 { | ||
271 | let n = 1 << i; | ||
272 | |||
273 | let fixture = bench_fixture::big_struct_n(n); | ||
274 | let (analysis, file_id) = fixture::file(&fixture); | ||
275 | |||
276 | let time = Instant::now(); | ||
277 | |||
278 | let hash = analysis | ||
279 | .highlight(file_id) | ||
280 | .unwrap() | ||
281 | .iter() | ||
282 | .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Struct)) | ||
283 | .count(); | ||
284 | assert!(hash > n as usize); | ||
285 | |||
286 | let elapsed = time.elapsed(); | ||
287 | al.sample(n as f64, elapsed.as_millis() as f64); | ||
288 | } | ||
289 | } | ||
290 | } | ||
291 | |||
292 | #[test] | ||
261 | fn benchmark_syntax_highlighting_parser() { | 293 | fn benchmark_syntax_highlighting_parser() { |
262 | if skip_slow_tests() { | 294 | if skip_slow_tests() { |
263 | return; | 295 | return; |
@@ -275,7 +307,7 @@ fn benchmark_syntax_highlighting_parser() { | |||
275 | .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function)) | 307 | .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function)) |
276 | .count() | 308 | .count() |
277 | }; | 309 | }; |
278 | assert_eq!(hash, 1629); | 310 | assert_eq!(hash, 1632); |
279 | } | 311 | } |
280 | 312 | ||
281 | #[test] | 313 | #[test] |
@@ -544,10 +576,18 @@ impl Foo { | |||
544 | } | 576 | } |
545 | 577 | ||
546 | /// [`Foo`](Foo) is a struct | 578 | /// [`Foo`](Foo) is a struct |
547 | /// [`all_the_links`](all_the_links) is this function | 579 | /// This function is > [`all_the_links`](all_the_links) < |
548 | /// [`noop`](noop) is a macro below | 580 | /// [`noop`](noop) is a macro below |
581 | /// [`Item`] is a struct in the module [`module`] | ||
582 | /// | ||
583 | /// [`Item`]: module::Item | ||
584 | /// [mix_and_match]: ThisShouldntResolve | ||
549 | pub fn all_the_links() {} | 585 | pub fn all_the_links() {} |
550 | 586 | ||
587 | pub mod module { | ||
588 | pub struct Item; | ||
589 | } | ||
590 | |||
551 | /// ``` | 591 | /// ``` |
552 | /// noop!(1); | 592 | /// noop!(1); |
553 | /// ``` | 593 | /// ``` |
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs index 11408d445..82c732390 100644 --- a/crates/ide/src/typing.rs +++ b/crates/ide/src/typing.rs | |||
@@ -22,18 +22,19 @@ use ide_db::{ | |||
22 | use syntax::{ | 22 | use syntax::{ |
23 | algo::find_node_at_offset, | 23 | algo::find_node_at_offset, |
24 | ast::{self, edit::IndentLevel, AstToken}, | 24 | ast::{self, edit::IndentLevel, AstToken}, |
25 | AstNode, SourceFile, | 25 | AstNode, Parse, SourceFile, |
26 | SyntaxKind::{FIELD_EXPR, METHOD_CALL_EXPR}, | 26 | SyntaxKind::{FIELD_EXPR, METHOD_CALL_EXPR}, |
27 | TextRange, TextSize, | 27 | TextRange, TextSize, |
28 | }; | 28 | }; |
29 | 29 | ||
30 | use text_edit::TextEdit; | 30 | use text_edit::{Indel, TextEdit}; |
31 | 31 | ||
32 | use crate::SourceChange; | 32 | use crate::SourceChange; |
33 | 33 | ||
34 | pub(crate) use on_enter::on_enter; | 34 | pub(crate) use on_enter::on_enter; |
35 | 35 | ||
36 | pub(crate) const TRIGGER_CHARS: &str = ".=>"; | 36 | // Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`. |
37 | pub(crate) const TRIGGER_CHARS: &str = ".=>{"; | ||
37 | 38 | ||
38 | // Feature: On Typing Assists | 39 | // Feature: On Typing Assists |
39 | // | 40 | // |
@@ -41,6 +42,7 @@ pub(crate) const TRIGGER_CHARS: &str = ".=>"; | |||
41 | // | 42 | // |
42 | // - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression | 43 | // - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression |
43 | // - typing `.` in a chain method call auto-indents | 44 | // - typing `.` in a chain method call auto-indents |
45 | // - typing `{` in front of an expression inserts a closing `}` after the expression | ||
44 | // | 46 | // |
45 | // VS Code:: | 47 | // VS Code:: |
46 | // | 48 | // |
@@ -57,28 +59,79 @@ pub(crate) fn on_char_typed( | |||
57 | position: FilePosition, | 59 | position: FilePosition, |
58 | char_typed: char, | 60 | char_typed: char, |
59 | ) -> Option<SourceChange> { | 61 | ) -> Option<SourceChange> { |
60 | assert!(TRIGGER_CHARS.contains(char_typed)); | 62 | if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) { |
61 | let file = &db.parse(position.file_id).tree(); | 63 | return None; |
62 | assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed)); | 64 | } |
65 | let file = &db.parse(position.file_id); | ||
66 | if !stdx::always!(file.tree().syntax().text().char_at(position.offset) == Some(char_typed)) { | ||
67 | return None; | ||
68 | } | ||
63 | let edit = on_char_typed_inner(file, position.offset, char_typed)?; | 69 | let edit = on_char_typed_inner(file, position.offset, char_typed)?; |
64 | Some(SourceChange::from_text_edit(position.file_id, edit)) | 70 | Some(SourceChange::from_text_edit(position.file_id, edit)) |
65 | } | 71 | } |
66 | 72 | ||
67 | fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> { | 73 | fn on_char_typed_inner( |
68 | assert!(TRIGGER_CHARS.contains(char_typed)); | 74 | file: &Parse<SourceFile>, |
75 | offset: TextSize, | ||
76 | char_typed: char, | ||
77 | ) -> Option<TextEdit> { | ||
78 | if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) { | ||
79 | return None; | ||
80 | } | ||
69 | match char_typed { | 81 | match char_typed { |
70 | '.' => on_dot_typed(file, offset), | 82 | '.' => on_dot_typed(&file.tree(), offset), |
71 | '=' => on_eq_typed(file, offset), | 83 | '=' => on_eq_typed(&file.tree(), offset), |
72 | '>' => on_arrow_typed(file, offset), | 84 | '>' => on_arrow_typed(&file.tree(), offset), |
85 | '{' => on_opening_brace_typed(file, offset), | ||
73 | _ => unreachable!(), | 86 | _ => unreachable!(), |
74 | } | 87 | } |
75 | } | 88 | } |
76 | 89 | ||
90 | /// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a | ||
91 | /// block. | ||
92 | fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<TextEdit> { | ||
93 | if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some('{')) { | ||
94 | return None; | ||
95 | } | ||
96 | |||
97 | let brace_token = file.tree().syntax().token_at_offset(offset).right_biased()?; | ||
98 | |||
99 | // Remove the `{` to get a better parse tree, and reparse | ||
100 | let file = file.reparse(&Indel::delete(brace_token.text_range())); | ||
101 | |||
102 | let mut expr: ast::Expr = find_node_at_offset(file.tree().syntax(), offset)?; | ||
103 | if expr.syntax().text_range().start() != offset { | ||
104 | return None; | ||
105 | } | ||
106 | |||
107 | // Enclose the outermost expression starting at `offset` | ||
108 | while let Some(parent) = expr.syntax().parent() { | ||
109 | if parent.text_range().start() != expr.syntax().text_range().start() { | ||
110 | break; | ||
111 | } | ||
112 | |||
113 | match ast::Expr::cast(parent) { | ||
114 | Some(parent) => expr = parent, | ||
115 | None => break, | ||
116 | } | ||
117 | } | ||
118 | |||
119 | // If it's a statement in a block, we don't know how many statements should be included | ||
120 | if ast::ExprStmt::can_cast(expr.syntax().parent()?.kind()) { | ||
121 | return None; | ||
122 | } | ||
123 | |||
124 | // Insert `}` right after the expression. | ||
125 | Some(TextEdit::insert(expr.syntax().text_range().end() + TextSize::of("{"), "}".to_string())) | ||
126 | } | ||
127 | |||
77 | /// Returns an edit which should be applied after `=` was typed. Primarily, | 128 | /// Returns an edit which should be applied after `=` was typed. Primarily, |
78 | /// this works when adding `let =`. | 129 | /// this works when adding `let =`. |
79 | // FIXME: use a snippet completion instead of this hack here. | 130 | // FIXME: use a snippet completion instead of this hack here. |
80 | fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { | 131 | fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { |
81 | assert_eq!(file.syntax().text().char_at(offset), Some('=')); | 132 | if !stdx::always!(file.syntax().text().char_at(offset) == Some('=')) { |
133 | return None; | ||
134 | } | ||
82 | let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; | 135 | let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; |
83 | if let_stmt.semicolon_token().is_some() { | 136 | if let_stmt.semicolon_token().is_some() { |
84 | return None; | 137 | return None; |
@@ -100,7 +153,9 @@ fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { | |||
100 | 153 | ||
101 | /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately. | 154 | /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately. |
102 | fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { | 155 | fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { |
103 | assert_eq!(file.syntax().text().char_at(offset), Some('.')); | 156 | if !stdx::always!(file.syntax().text().char_at(offset) == Some('.')) { |
157 | return None; | ||
158 | } | ||
104 | let whitespace = | 159 | let whitespace = |
105 | file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?; | 160 | file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?; |
106 | 161 | ||
@@ -129,7 +184,9 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { | |||
129 | /// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }` | 184 | /// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }` |
130 | fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { | 185 | fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { |
131 | let file_text = file.syntax().text(); | 186 | let file_text = file.syntax().text(); |
132 | assert_eq!(file_text.char_at(offset), Some('>')); | 187 | if !stdx::always!(file_text.char_at(offset) == Some('>')) { |
188 | return None; | ||
189 | } | ||
133 | let after_arrow = offset + TextSize::of('>'); | 190 | let after_arrow = offset + TextSize::of('>'); |
134 | if file_text.char_at(after_arrow) != Some('{') { | 191 | if file_text.char_at(after_arrow) != Some('{') { |
135 | return None; | 192 | return None; |
@@ -152,7 +209,7 @@ mod tests { | |||
152 | let edit = TextEdit::insert(offset, char_typed.to_string()); | 209 | let edit = TextEdit::insert(offset, char_typed.to_string()); |
153 | edit.apply(&mut before); | 210 | edit.apply(&mut before); |
154 | let parse = SourceFile::parse(&before); | 211 | let parse = SourceFile::parse(&before); |
155 | on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| { | 212 | on_char_typed_inner(&parse, offset, char_typed).map(|it| { |
156 | it.apply(&mut before); | 213 | it.apply(&mut before); |
157 | before.to_string() | 214 | before.to_string() |
158 | }) | 215 | }) |
@@ -165,8 +222,8 @@ mod tests { | |||
165 | assert_eq_text!(ra_fixture_after, &actual); | 222 | assert_eq_text!(ra_fixture_after, &actual); |
166 | } | 223 | } |
167 | 224 | ||
168 | fn type_char_noop(char_typed: char, before: &str) { | 225 | fn type_char_noop(char_typed: char, ra_fixture_before: &str) { |
169 | let file_change = do_type_char(char_typed, before); | 226 | let file_change = do_type_char(char_typed, ra_fixture_before); |
170 | assert!(file_change.is_none()) | 227 | assert!(file_change.is_none()) |
171 | } | 228 | } |
172 | 229 | ||
@@ -183,16 +240,16 @@ mod tests { | |||
183 | // "); | 240 | // "); |
184 | type_char( | 241 | type_char( |
185 | '=', | 242 | '=', |
186 | r" | 243 | r#" |
187 | fn foo() { | 244 | fn foo() { |
188 | let foo $0 1 + 1 | 245 | let foo $0 1 + 1 |
189 | } | 246 | } |
190 | ", | 247 | "#, |
191 | r" | 248 | r#" |
192 | fn foo() { | 249 | fn foo() { |
193 | let foo = 1 + 1; | 250 | let foo = 1 + 1; |
194 | } | 251 | } |
195 | ", | 252 | "#, |
196 | ); | 253 | ); |
197 | // do_check(r" | 254 | // do_check(r" |
198 | // fn foo() { | 255 | // fn foo() { |
@@ -211,27 +268,27 @@ fn foo() { | |||
211 | fn indents_new_chain_call() { | 268 | fn indents_new_chain_call() { |
212 | type_char( | 269 | type_char( |
213 | '.', | 270 | '.', |
214 | r" | 271 | r#" |
215 | fn main() { | 272 | fn main() { |
216 | xs.foo() | 273 | xs.foo() |
217 | $0 | 274 | $0 |
218 | } | 275 | } |
219 | ", | 276 | "#, |
220 | r" | 277 | r#" |
221 | fn main() { | 278 | fn main() { |
222 | xs.foo() | 279 | xs.foo() |
223 | . | 280 | . |
224 | } | 281 | } |
225 | ", | 282 | "#, |
226 | ); | 283 | ); |
227 | type_char_noop( | 284 | type_char_noop( |
228 | '.', | 285 | '.', |
229 | r" | 286 | r#" |
230 | fn main() { | 287 | fn main() { |
231 | xs.foo() | 288 | xs.foo() |
232 | $0 | 289 | $0 |
233 | } | 290 | } |
234 | ", | 291 | "#, |
235 | ) | 292 | ) |
236 | } | 293 | } |
237 | 294 | ||
@@ -240,26 +297,26 @@ fn foo() { | |||
240 | type_char( | 297 | type_char( |
241 | '.', | 298 | '.', |
242 | r" | 299 | r" |
243 | fn main() { | 300 | fn main() { |
244 | xs.foo() | 301 | xs.foo() |
245 | $0; | 302 | $0; |
246 | } | 303 | } |
247 | ", | ||
248 | r" | ||
249 | fn main() { | ||
250 | xs.foo() | ||
251 | .; | ||
252 | } | ||
253 | ", | 304 | ", |
305 | r#" | ||
306 | fn main() { | ||
307 | xs.foo() | ||
308 | .; | ||
309 | } | ||
310 | "#, | ||
254 | ); | 311 | ); |
255 | type_char_noop( | 312 | type_char_noop( |
256 | '.', | 313 | '.', |
257 | r" | 314 | r#" |
258 | fn main() { | 315 | fn main() { |
259 | xs.foo() | 316 | xs.foo() |
260 | $0; | 317 | $0; |
261 | } | 318 | } |
262 | ", | 319 | "#, |
263 | ) | 320 | ) |
264 | } | 321 | } |
265 | 322 | ||
@@ -288,30 +345,30 @@ fn main() { | |||
288 | fn indents_continued_chain_call() { | 345 | fn indents_continued_chain_call() { |
289 | type_char( | 346 | type_char( |
290 | '.', | 347 | '.', |
291 | r" | 348 | r#" |
292 | fn main() { | 349 | fn main() { |
293 | xs.foo() | 350 | xs.foo() |
294 | .first() | 351 | .first() |
295 | $0 | 352 | $0 |
296 | } | 353 | } |
297 | ", | 354 | "#, |
298 | r" | 355 | r#" |
299 | fn main() { | 356 | fn main() { |
300 | xs.foo() | 357 | xs.foo() |
301 | .first() | 358 | .first() |
302 | . | 359 | . |
303 | } | 360 | } |
304 | ", | 361 | "#, |
305 | ); | 362 | ); |
306 | type_char_noop( | 363 | type_char_noop( |
307 | '.', | 364 | '.', |
308 | r" | 365 | r#" |
309 | fn main() { | 366 | fn main() { |
310 | xs.foo() | 367 | xs.foo() |
311 | .first() | 368 | .first() |
312 | $0 | 369 | $0 |
313 | } | 370 | } |
314 | ", | 371 | "#, |
315 | ); | 372 | ); |
316 | } | 373 | } |
317 | 374 | ||
@@ -319,33 +376,33 @@ fn main() { | |||
319 | fn indents_middle_of_chain_call() { | 376 | fn indents_middle_of_chain_call() { |
320 | type_char( | 377 | type_char( |
321 | '.', | 378 | '.', |
322 | r" | 379 | r#" |
323 | fn source_impl() { | 380 | fn source_impl() { |
324 | let var = enum_defvariant_list().unwrap() | 381 | let var = enum_defvariant_list().unwrap() |
325 | $0 | 382 | $0 |
326 | .nth(92) | 383 | .nth(92) |
327 | .unwrap(); | 384 | .unwrap(); |
328 | } | 385 | } |
329 | ", | 386 | "#, |
330 | r" | 387 | r#" |
331 | fn source_impl() { | 388 | fn source_impl() { |
332 | let var = enum_defvariant_list().unwrap() | 389 | let var = enum_defvariant_list().unwrap() |
333 | . | 390 | . |
334 | .nth(92) | 391 | .nth(92) |
335 | .unwrap(); | 392 | .unwrap(); |
336 | } | 393 | } |
337 | ", | 394 | "#, |
338 | ); | 395 | ); |
339 | type_char_noop( | 396 | type_char_noop( |
340 | '.', | 397 | '.', |
341 | r" | 398 | r#" |
342 | fn source_impl() { | 399 | fn source_impl() { |
343 | let var = enum_defvariant_list().unwrap() | 400 | let var = enum_defvariant_list().unwrap() |
344 | $0 | 401 | $0 |
345 | .nth(92) | 402 | .nth(92) |
346 | .unwrap(); | 403 | .unwrap(); |
347 | } | 404 | } |
348 | ", | 405 | "#, |
349 | ); | 406 | ); |
350 | } | 407 | } |
351 | 408 | ||
@@ -353,24 +410,113 @@ fn main() { | |||
353 | fn dont_indent_freestanding_dot() { | 410 | fn dont_indent_freestanding_dot() { |
354 | type_char_noop( | 411 | type_char_noop( |
355 | '.', | 412 | '.', |
356 | r" | 413 | r#" |
357 | fn main() { | 414 | fn main() { |
358 | $0 | 415 | $0 |
359 | } | 416 | } |
360 | ", | 417 | "#, |
361 | ); | 418 | ); |
362 | type_char_noop( | 419 | type_char_noop( |
363 | '.', | 420 | '.', |
364 | r" | 421 | r#" |
365 | fn main() { | 422 | fn main() { |
366 | $0 | 423 | $0 |
367 | } | 424 | } |
368 | ", | 425 | "#, |
369 | ); | 426 | ); |
370 | } | 427 | } |
371 | 428 | ||
372 | #[test] | 429 | #[test] |
373 | fn adds_space_after_return_type() { | 430 | fn adds_space_after_return_type() { |
374 | type_char('>', "fn foo() -$0{ 92 }", "fn foo() -> { 92 }") | 431 | type_char( |
432 | '>', | ||
433 | r#" | ||
434 | fn foo() -$0{ 92 } | ||
435 | "#, | ||
436 | r#" | ||
437 | fn foo() -> { 92 } | ||
438 | "#, | ||
439 | ); | ||
440 | } | ||
441 | |||
442 | #[test] | ||
443 | fn adds_closing_brace() { | ||
444 | type_char( | ||
445 | '{', | ||
446 | r#" | ||
447 | fn f() { match () { _ => $0() } } | ||
448 | "#, | ||
449 | r#" | ||
450 | fn f() { match () { _ => {()} } } | ||
451 | "#, | ||
452 | ); | ||
453 | type_char( | ||
454 | '{', | ||
455 | r#" | ||
456 | fn f() { $0() } | ||
457 | "#, | ||
458 | r#" | ||
459 | fn f() { {()} } | ||
460 | "#, | ||
461 | ); | ||
462 | type_char( | ||
463 | '{', | ||
464 | r#" | ||
465 | fn f() { let x = $0(); } | ||
466 | "#, | ||
467 | r#" | ||
468 | fn f() { let x = {()}; } | ||
469 | "#, | ||
470 | ); | ||
471 | type_char( | ||
472 | '{', | ||
473 | r#" | ||
474 | fn f() { let x = $0a.b(); } | ||
475 | "#, | ||
476 | r#" | ||
477 | fn f() { let x = {a.b()}; } | ||
478 | "#, | ||
479 | ); | ||
480 | type_char( | ||
481 | '{', | ||
482 | r#" | ||
483 | const S: () = $0(); | ||
484 | fn f() {} | ||
485 | "#, | ||
486 | r#" | ||
487 | const S: () = {()}; | ||
488 | fn f() {} | ||
489 | "#, | ||
490 | ); | ||
491 | type_char( | ||
492 | '{', | ||
493 | r#" | ||
494 | const S: () = $0a.b(); | ||
495 | fn f() {} | ||
496 | "#, | ||
497 | r#" | ||
498 | const S: () = {a.b()}; | ||
499 | fn f() {} | ||
500 | "#, | ||
501 | ); | ||
502 | type_char( | ||
503 | '{', | ||
504 | r#" | ||
505 | fn f() { | ||
506 | match x { | ||
507 | 0 => $0(), | ||
508 | 1 => (), | ||
509 | } | ||
510 | } | ||
511 | "#, | ||
512 | r#" | ||
513 | fn f() { | ||
514 | match x { | ||
515 | 0 => {()}, | ||
516 | 1 => (), | ||
517 | } | ||
518 | } | ||
519 | "#, | ||
520 | ); | ||
375 | } | 521 | } |
376 | } | 522 | } |
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs index 9144681bf..7d2db201a 100644 --- a/crates/ide/src/typing/on_enter.rs +++ b/crates/ide/src/typing/on_enter.rs | |||
@@ -4,10 +4,11 @@ | |||
4 | use ide_db::base_db::{FilePosition, SourceDatabase}; | 4 | use ide_db::base_db::{FilePosition, SourceDatabase}; |
5 | use ide_db::RootDatabase; | 5 | use ide_db::RootDatabase; |
6 | use syntax::{ | 6 | use syntax::{ |
7 | ast::{self, AstToken}, | 7 | algo::find_node_at_offset, |
8 | ast::{self, edit::IndentLevel, AstToken}, | ||
8 | AstNode, SmolStr, SourceFile, | 9 | AstNode, SmolStr, SourceFile, |
9 | SyntaxKind::*, | 10 | SyntaxKind::*, |
10 | SyntaxToken, TextRange, TextSize, TokenAtOffset, | 11 | SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, |
11 | }; | 12 | }; |
12 | 13 | ||
13 | use text_edit::TextEdit; | 14 | use text_edit::TextEdit; |
@@ -18,6 +19,8 @@ use text_edit::TextEdit; | |||
18 | // | 19 | // |
19 | // - kbd:[Enter] inside triple-slash comments automatically inserts `///` | 20 | // - kbd:[Enter] inside triple-slash comments automatically inserts `///` |
20 | // - kbd:[Enter] in the middle or after a trailing space in `//` inserts `//` | 21 | // - kbd:[Enter] in the middle or after a trailing space in `//` inserts `//` |
22 | // - kbd:[Enter] inside `//!` doc comments automatically inserts `//!` | ||
23 | // - kbd:[Enter] after `{` indents contents and closing `}` of single-line block | ||
21 | // | 24 | // |
22 | // This action needs to be assigned to shortcut explicitly. | 25 | // This action needs to be assigned to shortcut explicitly. |
23 | // | 26 | // |
@@ -37,25 +40,43 @@ use text_edit::TextEdit; | |||
37 | pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> { | 40 | pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> { |
38 | let parse = db.parse(position.file_id); | 41 | let parse = db.parse(position.file_id); |
39 | let file = parse.tree(); | 42 | let file = parse.tree(); |
40 | let comment = file | 43 | let token = file.syntax().token_at_offset(position.offset).left_biased()?; |
41 | .syntax() | ||
42 | .token_at_offset(position.offset) | ||
43 | .left_biased() | ||
44 | .and_then(ast::Comment::cast)?; | ||
45 | 44 | ||
45 | if let Some(comment) = ast::Comment::cast(token.clone()) { | ||
46 | return on_enter_in_comment(&comment, &file, position.offset); | ||
47 | } | ||
48 | |||
49 | if token.kind() == L_CURLY { | ||
50 | // Typing enter after the `{` of a block expression, where the `}` is on the same line | ||
51 | if let Some(edit) = find_node_at_offset(file.syntax(), position.offset - TextSize::of('{')) | ||
52 | .and_then(|block| on_enter_in_block(block, position)) | ||
53 | { | ||
54 | cov_mark::hit!(indent_block_contents); | ||
55 | return Some(edit); | ||
56 | } | ||
57 | } | ||
58 | |||
59 | None | ||
60 | } | ||
61 | |||
62 | fn on_enter_in_comment( | ||
63 | comment: &ast::Comment, | ||
64 | file: &ast::SourceFile, | ||
65 | offset: TextSize, | ||
66 | ) -> Option<TextEdit> { | ||
46 | if comment.kind().shape.is_block() { | 67 | if comment.kind().shape.is_block() { |
47 | return None; | 68 | return None; |
48 | } | 69 | } |
49 | 70 | ||
50 | let prefix = comment.prefix(); | 71 | let prefix = comment.prefix(); |
51 | let comment_range = comment.syntax().text_range(); | 72 | let comment_range = comment.syntax().text_range(); |
52 | if position.offset < comment_range.start() + TextSize::of(prefix) { | 73 | if offset < comment_range.start() + TextSize::of(prefix) { |
53 | return None; | 74 | return None; |
54 | } | 75 | } |
55 | 76 | ||
56 | let mut remove_trailing_whitespace = false; | 77 | let mut remove_trailing_whitespace = false; |
57 | // Continuing single-line non-doc comments (like this one :) ) is annoying | 78 | // Continuing single-line non-doc comments (like this one :) ) is annoying |
58 | if prefix == "//" && comment_range.end() == position.offset { | 79 | if prefix == "//" && comment_range.end() == offset { |
59 | if comment.text().ends_with(' ') { | 80 | if comment.text().ends_with(' ') { |
60 | cov_mark::hit!(continues_end_of_line_comment_with_space); | 81 | cov_mark::hit!(continues_end_of_line_comment_with_space); |
61 | remove_trailing_whitespace = true; | 82 | remove_trailing_whitespace = true; |
@@ -69,14 +90,42 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text | |||
69 | let delete = if remove_trailing_whitespace { | 90 | let delete = if remove_trailing_whitespace { |
70 | let trimmed_len = comment.text().trim_end().len() as u32; | 91 | let trimmed_len = comment.text().trim_end().len() as u32; |
71 | let trailing_whitespace_len = comment.text().len() as u32 - trimmed_len; | 92 | let trailing_whitespace_len = comment.text().len() as u32 - trimmed_len; |
72 | TextRange::new(position.offset - TextSize::from(trailing_whitespace_len), position.offset) | 93 | TextRange::new(offset - TextSize::from(trailing_whitespace_len), offset) |
73 | } else { | 94 | } else { |
74 | TextRange::empty(position.offset) | 95 | TextRange::empty(offset) |
75 | }; | 96 | }; |
76 | let edit = TextEdit::replace(delete, inserted); | 97 | let edit = TextEdit::replace(delete, inserted); |
77 | Some(edit) | 98 | Some(edit) |
78 | } | 99 | } |
79 | 100 | ||
101 | fn on_enter_in_block(block: ast::BlockExpr, position: FilePosition) -> Option<TextEdit> { | ||
102 | let contents = block_contents(&block)?; | ||
103 | |||
104 | if block.syntax().text().contains_char('\n') { | ||
105 | return None; | ||
106 | } | ||
107 | |||
108 | let indent = IndentLevel::from_node(block.syntax()); | ||
109 | let mut edit = TextEdit::insert(position.offset, format!("\n{}$0", indent + 1)); | ||
110 | edit.union(TextEdit::insert(contents.text_range().end(), format!("\n{}", indent))).ok()?; | ||
111 | Some(edit) | ||
112 | } | ||
113 | |||
114 | fn block_contents(block: &ast::BlockExpr) -> Option<SyntaxNode> { | ||
115 | let mut node = block.tail_expr().map(|e| e.syntax().clone()); | ||
116 | |||
117 | for stmt in block.statements() { | ||
118 | if node.is_some() { | ||
119 | // More than 1 node in the block | ||
120 | return None; | ||
121 | } | ||
122 | |||
123 | node = Some(stmt.syntax().clone()); | ||
124 | } | ||
125 | |||
126 | node | ||
127 | } | ||
128 | |||
80 | fn followed_by_comment(comment: &ast::Comment) -> bool { | 129 | fn followed_by_comment(comment: &ast::Comment) -> bool { |
81 | let ws = match comment.syntax().next_token().and_then(ast::Whitespace::cast) { | 130 | let ws = match comment.syntax().next_token().and_then(ast::Whitespace::cast) { |
82 | Some(it) => it, | 131 | Some(it) => it, |
@@ -187,6 +236,25 @@ fn foo() { | |||
187 | } | 236 | } |
188 | 237 | ||
189 | #[test] | 238 | #[test] |
239 | fn continues_another_doc_comment() { | ||
240 | do_check( | ||
241 | r#" | ||
242 | fn main() { | ||
243 | //! Documentation for$0 on enter | ||
244 | let x = 1 + 1; | ||
245 | } | ||
246 | "#, | ||
247 | r#" | ||
248 | fn main() { | ||
249 | //! Documentation for | ||
250 | //! $0 on enter | ||
251 | let x = 1 + 1; | ||
252 | } | ||
253 | "#, | ||
254 | ); | ||
255 | } | ||
256 | |||
257 | #[test] | ||
190 | fn continues_code_comment_in_the_middle_of_line() { | 258 | fn continues_code_comment_in_the_middle_of_line() { |
191 | do_check( | 259 | do_check( |
192 | r" | 260 | r" |
@@ -276,4 +344,144 @@ fn main() { | |||
276 | ", | 344 | ", |
277 | ); | 345 | ); |
278 | } | 346 | } |
347 | |||
348 | #[test] | ||
349 | fn indents_fn_body_block() { | ||
350 | cov_mark::check!(indent_block_contents); | ||
351 | do_check( | ||
352 | r#" | ||
353 | fn f() {$0()} | ||
354 | "#, | ||
355 | r#" | ||
356 | fn f() { | ||
357 | $0() | ||
358 | } | ||
359 | "#, | ||
360 | ); | ||
361 | } | ||
362 | |||
363 | #[test] | ||
364 | fn indents_block_expr() { | ||
365 | do_check( | ||
366 | r#" | ||
367 | fn f() { | ||
368 | let x = {$0()}; | ||
369 | } | ||
370 | "#, | ||
371 | r#" | ||
372 | fn f() { | ||
373 | let x = { | ||
374 | $0() | ||
375 | }; | ||
376 | } | ||
377 | "#, | ||
378 | ); | ||
379 | } | ||
380 | |||
381 | #[test] | ||
382 | fn indents_match_arm() { | ||
383 | do_check( | ||
384 | r#" | ||
385 | fn f() { | ||
386 | match 6 { | ||
387 | 1 => {$0f()}, | ||
388 | _ => (), | ||
389 | } | ||
390 | } | ||
391 | "#, | ||
392 | r#" | ||
393 | fn f() { | ||
394 | match 6 { | ||
395 | 1 => { | ||
396 | $0f() | ||
397 | }, | ||
398 | _ => (), | ||
399 | } | ||
400 | } | ||
401 | "#, | ||
402 | ); | ||
403 | } | ||
404 | |||
405 | #[test] | ||
406 | fn indents_block_with_statement() { | ||
407 | do_check( | ||
408 | r#" | ||
409 | fn f() {$0a = b} | ||
410 | "#, | ||
411 | r#" | ||
412 | fn f() { | ||
413 | $0a = b | ||
414 | } | ||
415 | "#, | ||
416 | ); | ||
417 | do_check( | ||
418 | r#" | ||
419 | fn f() {$0fn f() {}} | ||
420 | "#, | ||
421 | r#" | ||
422 | fn f() { | ||
423 | $0fn f() {} | ||
424 | } | ||
425 | "#, | ||
426 | ); | ||
427 | } | ||
428 | |||
429 | #[test] | ||
430 | fn indents_nested_blocks() { | ||
431 | do_check( | ||
432 | r#" | ||
433 | fn f() {$0{}} | ||
434 | "#, | ||
435 | r#" | ||
436 | fn f() { | ||
437 | $0{} | ||
438 | } | ||
439 | "#, | ||
440 | ); | ||
441 | } | ||
442 | |||
443 | #[test] | ||
444 | fn does_not_indent_empty_block() { | ||
445 | do_check_noop( | ||
446 | r#" | ||
447 | fn f() {$0} | ||
448 | "#, | ||
449 | ); | ||
450 | do_check_noop( | ||
451 | r#" | ||
452 | fn f() {{$0}} | ||
453 | "#, | ||
454 | ); | ||
455 | } | ||
456 | |||
457 | #[test] | ||
458 | fn does_not_indent_block_with_too_much_content() { | ||
459 | do_check_noop( | ||
460 | r#" | ||
461 | fn f() {$0 a = b; ()} | ||
462 | "#, | ||
463 | ); | ||
464 | do_check_noop( | ||
465 | r#" | ||
466 | fn f() {$0 a = b; a = b; } | ||
467 | "#, | ||
468 | ); | ||
469 | } | ||
470 | |||
471 | #[test] | ||
472 | fn does_not_indent_multiline_block() { | ||
473 | do_check_noop( | ||
474 | r#" | ||
475 | fn f() {$0 | ||
476 | } | ||
477 | "#, | ||
478 | ); | ||
479 | do_check_noop( | ||
480 | r#" | ||
481 | fn f() {$0 | ||
482 | |||
483 | } | ||
484 | "#, | ||
485 | ); | ||
486 | } | ||
279 | } | 487 | } |
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs index 5ccd7f7a2..49aa70f74 100644 --- a/crates/ide_assists/src/handlers/auto_import.rs +++ b/crates/ide_assists/src/handlers/auto_import.rs | |||
@@ -934,4 +934,37 @@ fn main() { | |||
934 | ", | 934 | ", |
935 | ); | 935 | ); |
936 | } | 936 | } |
937 | |||
938 | #[test] | ||
939 | fn inner_items() { | ||
940 | check_assist( | ||
941 | auto_import, | ||
942 | r#" | ||
943 | mod baz { | ||
944 | pub struct Foo {} | ||
945 | } | ||
946 | |||
947 | mod bar { | ||
948 | fn bar() { | ||
949 | Foo$0; | ||
950 | println!("Hallo"); | ||
951 | } | ||
952 | } | ||
953 | "#, | ||
954 | r#" | ||
955 | mod baz { | ||
956 | pub struct Foo {} | ||
957 | } | ||
958 | |||
959 | mod bar { | ||
960 | use crate::baz::Foo; | ||
961 | |||
962 | fn bar() { | ||
963 | Foo; | ||
964 | println!("Hallo"); | ||
965 | } | ||
966 | } | ||
967 | "#, | ||
968 | ); | ||
969 | } | ||
937 | } | 970 | } |
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs index 5fdc8bf38..5f80a40c8 100644 --- a/crates/ide_assists/src/handlers/extract_function.rs +++ b/crates/ide_assists/src/handlers/extract_function.rs | |||
@@ -75,7 +75,8 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
75 | let insert_after = scope_for_fn_insertion(&body, anchor)?; | 75 | let insert_after = scope_for_fn_insertion(&body, anchor)?; |
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 = vars_defined_in_body_and_outlive(ctx, &body); | 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 | let ret_ty = body_return_ty(ctx, &body)?; | 80 | let ret_ty = body_return_ty(ctx, &body)?; |
80 | 81 | ||
81 | // 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 |
@@ -257,7 +258,7 @@ struct Function { | |||
257 | control_flow: ControlFlow, | 258 | control_flow: ControlFlow, |
258 | ret_ty: RetType, | 259 | ret_ty: RetType, |
259 | body: FunctionBody, | 260 | body: FunctionBody, |
260 | vars_defined_in_body_and_outlive: Vec<Local>, | 261 | vars_defined_in_body_and_outlive: Vec<OutlivedLocal>, |
261 | } | 262 | } |
262 | 263 | ||
263 | #[derive(Debug)] | 264 | #[derive(Debug)] |
@@ -296,9 +297,9 @@ impl Function { | |||
296 | RetType::Expr(ty) => FunType::Single(ty.clone()), | 297 | RetType::Expr(ty) => FunType::Single(ty.clone()), |
297 | RetType::Stmt => match self.vars_defined_in_body_and_outlive.as_slice() { | 298 | RetType::Stmt => match self.vars_defined_in_body_and_outlive.as_slice() { |
298 | [] => FunType::Unit, | 299 | [] => FunType::Unit, |
299 | [var] => FunType::Single(var.ty(ctx.db())), | 300 | [var] => FunType::Single(var.local.ty(ctx.db())), |
300 | vars => { | 301 | vars => { |
301 | let types = vars.iter().map(|v| v.ty(ctx.db())).collect(); | 302 | let types = vars.iter().map(|v| v.local.ty(ctx.db())).collect(); |
302 | FunType::Tuple(types) | 303 | FunType::Tuple(types) |
303 | } | 304 | } |
304 | }, | 305 | }, |
@@ -562,6 +563,12 @@ impl HasTokenAtOffset for FunctionBody { | |||
562 | } | 563 | } |
563 | } | 564 | } |
564 | 565 | ||
566 | #[derive(Debug)] | ||
567 | struct OutlivedLocal { | ||
568 | local: Local, | ||
569 | mut_usage_outside_body: bool, | ||
570 | } | ||
571 | |||
565 | /// Try to guess what user wants to extract | 572 | /// Try to guess what user wants to extract |
566 | /// | 573 | /// |
567 | /// We have basically have two cases: | 574 | /// We have basically have two cases: |
@@ -592,7 +599,12 @@ fn extraction_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Fu | |||
592 | // we have selected a few statements in a block | 599 | // we have selected a few statements in a block |
593 | // so covering_element returns the whole block | 600 | // so covering_element returns the whole block |
594 | if node.kind() == BLOCK_EXPR { | 601 | if node.kind() == BLOCK_EXPR { |
595 | let body = FunctionBody::from_range(node.clone(), selection_range); | 602 | // Extract the full statements. |
603 | let statements_range = node | ||
604 | .children() | ||
605 | .filter(|c| selection_range.intersect(c.text_range()).is_some()) | ||
606 | .fold(selection_range, |acc, c| acc.cover(c.text_range())); | ||
607 | let body = FunctionBody::from_range(node.clone(), statements_range); | ||
596 | if body.is_some() { | 608 | if body.is_some() { |
597 | return body; | 609 | return body; |
598 | } | 610 | } |
@@ -603,7 +615,8 @@ fn extraction_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Fu | |||
603 | // so we try to expand covering_element to parent and repeat the previous | 615 | // so we try to expand covering_element to parent and repeat the previous |
604 | if let Some(parent) = node.parent() { | 616 | if let Some(parent) = node.parent() { |
605 | if parent.kind() == BLOCK_EXPR { | 617 | if parent.kind() == BLOCK_EXPR { |
606 | let body = FunctionBody::from_range(parent, selection_range); | 618 | // Extract the full statement. |
619 | let body = FunctionBody::from_range(parent, node.text_range()); | ||
607 | if body.is_some() { | 620 | if body.is_some() { |
608 | return body; | 621 | return body; |
609 | } | 622 | } |
@@ -707,10 +720,10 @@ fn has_exclusive_usages(ctx: &AssistContext, usages: &LocalUsages, body: &Functi | |||
707 | .any(|reference| reference_is_exclusive(reference, body, ctx)) | 720 | .any(|reference| reference_is_exclusive(reference, body, ctx)) |
708 | } | 721 | } |
709 | 722 | ||
710 | /// checks if this reference requires `&mut` access inside body | 723 | /// checks if this reference requires `&mut` access inside node |
711 | fn reference_is_exclusive( | 724 | fn reference_is_exclusive( |
712 | reference: &FileReference, | 725 | reference: &FileReference, |
713 | body: &FunctionBody, | 726 | node: &dyn HasTokenAtOffset, |
714 | ctx: &AssistContext, | 727 | ctx: &AssistContext, |
715 | ) -> bool { | 728 | ) -> bool { |
716 | // we directly modify variable with set: `n = 0`, `n += 1` | 729 | // we directly modify variable with set: `n = 0`, `n += 1` |
@@ -719,7 +732,7 @@ fn reference_is_exclusive( | |||
719 | } | 732 | } |
720 | 733 | ||
721 | // we take `&mut` reference to variable: `&mut v` | 734 | // we take `&mut` reference to variable: `&mut v` |
722 | let path = match path_element_of_reference(body, reference) { | 735 | let path = match path_element_of_reference(node, reference) { |
723 | Some(path) => path, | 736 | Some(path) => path, |
724 | None => return false, | 737 | None => return false, |
725 | }; | 738 | }; |
@@ -729,6 +742,14 @@ fn reference_is_exclusive( | |||
729 | 742 | ||
730 | /// checks if this expr requires `&mut` access, recurses on field access | 743 | /// checks if this expr requires `&mut` access, recurses on field access |
731 | fn expr_require_exclusive_access(ctx: &AssistContext, expr: &ast::Expr) -> Option<bool> { | 744 | fn expr_require_exclusive_access(ctx: &AssistContext, expr: &ast::Expr) -> Option<bool> { |
745 | match expr { | ||
746 | ast::Expr::MacroCall(_) => { | ||
747 | // FIXME: expand macro and check output for mutable usages of the variable? | ||
748 | return None; | ||
749 | } | ||
750 | _ => (), | ||
751 | } | ||
752 | |||
732 | let parent = expr.syntax().parent()?; | 753 | let parent = expr.syntax().parent()?; |
733 | 754 | ||
734 | if let Some(bin_expr) = ast::BinExpr::cast(parent.clone()) { | 755 | if let Some(bin_expr) = ast::BinExpr::cast(parent.clone()) { |
@@ -787,7 +808,7 @@ impl HasTokenAtOffset for SyntaxNode { | |||
787 | } | 808 | } |
788 | } | 809 | } |
789 | 810 | ||
790 | /// find relevant `ast::PathExpr` for reference | 811 | /// find relevant `ast::Expr` for reference |
791 | /// | 812 | /// |
792 | /// # Preconditions | 813 | /// # Preconditions |
793 | /// | 814 | /// |
@@ -804,7 +825,11 @@ fn path_element_of_reference( | |||
804 | stdx::never!(false, "cannot find path parent of variable usage: {:?}", token); | 825 | stdx::never!(false, "cannot find path parent of variable usage: {:?}", token); |
805 | None | 826 | None |
806 | })?; | 827 | })?; |
807 | stdx::always!(matches!(path, ast::Expr::PathExpr(_))); | 828 | stdx::always!( |
829 | matches!(path, ast::Expr::PathExpr(_) | ast::Expr::MacroCall(_)), | ||
830 | "unexpected expression type for variable usage: {:?}", | ||
831 | path | ||
832 | ); | ||
808 | Some(path) | 833 | Some(path) |
809 | } | 834 | } |
810 | 835 | ||
@@ -820,10 +845,16 @@ fn vars_defined_in_body(body: &FunctionBody, ctx: &AssistContext) -> Vec<Local> | |||
820 | } | 845 | } |
821 | 846 | ||
822 | /// list local variables defined inside `body` that should be returned from extracted function | 847 | /// list local variables defined inside `body` that should be returned from extracted function |
823 | fn vars_defined_in_body_and_outlive(ctx: &AssistContext, body: &FunctionBody) -> Vec<Local> { | 848 | fn vars_defined_in_body_and_outlive( |
824 | let mut vars_defined_in_body = vars_defined_in_body(&body, ctx); | 849 | ctx: &AssistContext, |
825 | vars_defined_in_body.retain(|var| var_outlives_body(ctx, body, var)); | 850 | body: &FunctionBody, |
851 | parent: &SyntaxNode, | ||
852 | ) -> Vec<OutlivedLocal> { | ||
853 | let vars_defined_in_body = vars_defined_in_body(&body, ctx); | ||
826 | vars_defined_in_body | 854 | vars_defined_in_body |
855 | .into_iter() | ||
856 | .filter_map(|var| var_outlives_body(ctx, body, var, parent)) | ||
857 | .collect() | ||
827 | } | 858 | } |
828 | 859 | ||
829 | /// checks if the relevant local was defined before(outside of) body | 860 | /// checks if the relevant local was defined before(outside of) body |
@@ -843,11 +874,23 @@ fn either_syntax(value: &Either<ast::IdentPat, ast::SelfParam>) -> &SyntaxNode { | |||
843 | } | 874 | } |
844 | } | 875 | } |
845 | 876 | ||
846 | /// checks if local variable is used after(outside of) body | 877 | /// returns usage details if local variable is used after(outside of) body |
847 | fn var_outlives_body(ctx: &AssistContext, body: &FunctionBody, var: &Local) -> bool { | 878 | fn var_outlives_body( |
848 | let usages = LocalUsages::find(ctx, *var); | 879 | ctx: &AssistContext, |
880 | body: &FunctionBody, | ||
881 | var: Local, | ||
882 | parent: &SyntaxNode, | ||
883 | ) -> Option<OutlivedLocal> { | ||
884 | let usages = LocalUsages::find(ctx, var); | ||
849 | let has_usages = usages.iter().any(|reference| body.preceedes_range(reference.range)); | 885 | let has_usages = usages.iter().any(|reference| body.preceedes_range(reference.range)); |
850 | has_usages | 886 | if !has_usages { |
887 | return None; | ||
888 | } | ||
889 | let has_mut_usages = usages | ||
890 | .iter() | ||
891 | .filter(|reference| body.preceedes_range(reference.range)) | ||
892 | .any(|reference| reference_is_exclusive(reference, parent, ctx)); | ||
893 | Some(OutlivedLocal { local: var, mut_usage_outside_body: has_mut_usages }) | ||
851 | } | 894 | } |
852 | 895 | ||
853 | fn body_return_ty(ctx: &AssistContext, body: &FunctionBody) -> Option<RetType> { | 896 | fn body_return_ty(ctx: &AssistContext, body: &FunctionBody) -> Option<RetType> { |
@@ -927,16 +970,25 @@ fn format_replacement(ctx: &AssistContext, fun: &Function, indent: IndentLevel) | |||
927 | let mut buf = String::new(); | 970 | let mut buf = String::new(); |
928 | match fun.vars_defined_in_body_and_outlive.as_slice() { | 971 | match fun.vars_defined_in_body_and_outlive.as_slice() { |
929 | [] => {} | 972 | [] => {} |
930 | [var] => format_to!(buf, "let {} = ", var.name(ctx.db()).unwrap()), | 973 | [var] => { |
974 | format_to!(buf, "let {}{} = ", mut_modifier(var), var.local.name(ctx.db()).unwrap()) | ||
975 | } | ||
931 | [v0, vs @ ..] => { | 976 | [v0, vs @ ..] => { |
932 | buf.push_str("let ("); | 977 | buf.push_str("let ("); |
933 | format_to!(buf, "{}", v0.name(ctx.db()).unwrap()); | 978 | format_to!(buf, "{}{}", mut_modifier(v0), v0.local.name(ctx.db()).unwrap()); |
934 | for var in vs { | 979 | for var in vs { |
935 | format_to!(buf, ", {}", var.name(ctx.db()).unwrap()); | 980 | format_to!(buf, ", {}{}", mut_modifier(var), var.local.name(ctx.db()).unwrap()); |
936 | } | 981 | } |
937 | buf.push_str(") = "); | 982 | buf.push_str(") = "); |
938 | } | 983 | } |
939 | } | 984 | } |
985 | fn mut_modifier(var: &OutlivedLocal) -> &'static str { | ||
986 | if var.mut_usage_outside_body { | ||
987 | "mut " | ||
988 | } else { | ||
989 | "" | ||
990 | } | ||
991 | } | ||
940 | format_to!(buf, "{}", expr); | 992 | format_to!(buf, "{}", expr); |
941 | if fun.ret_ty.is_unit() | 993 | if fun.ret_ty.is_unit() |
942 | && (!fun.vars_defined_in_body_and_outlive.is_empty() || !expr.is_block_like()) | 994 | && (!fun.vars_defined_in_body_and_outlive.is_empty() || !expr.is_block_like()) |
@@ -1175,9 +1227,19 @@ fn make_body( | |||
1175 | FunctionBody::Expr(expr) => { | 1227 | FunctionBody::Expr(expr) => { |
1176 | let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax()); | 1228 | let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax()); |
1177 | let expr = ast::Expr::cast(expr).unwrap(); | 1229 | let expr = ast::Expr::cast(expr).unwrap(); |
1178 | let expr = expr.dedent(old_indent).indent(IndentLevel(1)); | 1230 | match expr { |
1231 | ast::Expr::BlockExpr(block) => { | ||
1232 | // If the extracted expression is itself a block, there is no need to wrap it inside another block. | ||
1233 | let block = block.dedent(old_indent); | ||
1234 | // Recreate the block for formatting consistency with other extracted functions. | ||
1235 | make::block_expr(block.statements(), block.tail_expr()) | ||
1236 | } | ||
1237 | _ => { | ||
1238 | let expr = expr.dedent(old_indent).indent(IndentLevel(1)); | ||
1179 | 1239 | ||
1180 | make::block_expr(Vec::new(), Some(expr)) | 1240 | make::block_expr(Vec::new(), Some(expr)) |
1241 | } | ||
1242 | } | ||
1181 | } | 1243 | } |
1182 | FunctionBody::Span { parent, text_range } => { | 1244 | FunctionBody::Span { parent, text_range } => { |
1183 | let mut elements: Vec<_> = parent | 1245 | let mut elements: Vec<_> = parent |
@@ -1199,10 +1261,10 @@ fn make_body( | |||
1199 | match fun.vars_defined_in_body_and_outlive.as_slice() { | 1261 | match fun.vars_defined_in_body_and_outlive.as_slice() { |
1200 | [] => {} | 1262 | [] => {} |
1201 | [var] => { | 1263 | [var] => { |
1202 | tail_expr = Some(path_expr_from_local(ctx, *var)); | 1264 | tail_expr = Some(path_expr_from_local(ctx, var.local)); |
1203 | } | 1265 | } |
1204 | vars => { | 1266 | vars => { |
1205 | let exprs = vars.iter().map(|var| path_expr_from_local(ctx, *var)); | 1267 | let exprs = vars.iter().map(|var| path_expr_from_local(ctx, var.local)); |
1206 | let expr = make::expr_tuple(exprs); | 1268 | let expr = make::expr_tuple(exprs); |
1207 | tail_expr = Some(expr); | 1269 | tail_expr = Some(expr); |
1208 | } | 1270 | } |
@@ -1492,7 +1554,7 @@ fn foo() { | |||
1492 | } | 1554 | } |
1493 | 1555 | ||
1494 | fn $0fun_name() -> i32 { | 1556 | fn $0fun_name() -> i32 { |
1495 | { 1 + 1 } | 1557 | 1 + 1 |
1496 | }"#, | 1558 | }"#, |
1497 | ); | 1559 | ); |
1498 | } | 1560 | } |
@@ -1739,6 +1801,60 @@ fn $0fun_name() -> i32 { | |||
1739 | } | 1801 | } |
1740 | 1802 | ||
1741 | #[test] | 1803 | #[test] |
1804 | fn extract_partial_block_single_line() { | ||
1805 | check_assist( | ||
1806 | extract_function, | ||
1807 | r#" | ||
1808 | fn foo() { | ||
1809 | let n = 1; | ||
1810 | let mut v = $0n * n;$0 | ||
1811 | v += 1; | ||
1812 | }"#, | ||
1813 | r#" | ||
1814 | fn foo() { | ||
1815 | let n = 1; | ||
1816 | let mut v = fun_name(n); | ||
1817 | v += 1; | ||
1818 | } | ||
1819 | |||
1820 | fn $0fun_name(n: i32) -> i32 { | ||
1821 | let mut v = n * n; | ||
1822 | v | ||
1823 | }"#, | ||
1824 | ); | ||
1825 | } | ||
1826 | |||
1827 | #[test] | ||
1828 | fn extract_partial_block() { | ||
1829 | check_assist( | ||
1830 | extract_function, | ||
1831 | r#" | ||
1832 | fn foo() { | ||
1833 | let m = 2; | ||
1834 | let n = 1; | ||
1835 | let mut v = m $0* n; | ||
1836 | let mut w = 3;$0 | ||
1837 | v += 1; | ||
1838 | w += 1; | ||
1839 | }"#, | ||
1840 | r#" | ||
1841 | fn foo() { | ||
1842 | let m = 2; | ||
1843 | let n = 1; | ||
1844 | let (mut v, mut w) = fun_name(m, n); | ||
1845 | v += 1; | ||
1846 | w += 1; | ||
1847 | } | ||
1848 | |||
1849 | fn $0fun_name(m: i32, n: i32) -> (i32, i32) { | ||
1850 | let mut v = m * n; | ||
1851 | let mut w = 3; | ||
1852 | (v, w) | ||
1853 | }"#, | ||
1854 | ); | ||
1855 | } | ||
1856 | |||
1857 | #[test] | ||
1742 | fn argument_form_expr() { | 1858 | fn argument_form_expr() { |
1743 | check_assist( | 1859 | check_assist( |
1744 | extract_function, | 1860 | extract_function, |
@@ -2111,6 +2227,30 @@ fn $0fun_name(n: i32) -> i32 { | |||
2111 | } | 2227 | } |
2112 | 2228 | ||
2113 | #[test] | 2229 | #[test] |
2230 | fn variable_defined_inside_and_used_after_mutably_no_ret() { | ||
2231 | check_assist( | ||
2232 | extract_function, | ||
2233 | r" | ||
2234 | fn foo() { | ||
2235 | let n = 1; | ||
2236 | $0let mut k = n * n;$0 | ||
2237 | k += 1; | ||
2238 | }", | ||
2239 | r" | ||
2240 | fn foo() { | ||
2241 | let n = 1; | ||
2242 | let mut k = fun_name(n); | ||
2243 | k += 1; | ||
2244 | } | ||
2245 | |||
2246 | fn $0fun_name(n: i32) -> i32 { | ||
2247 | let mut k = n * n; | ||
2248 | k | ||
2249 | }", | ||
2250 | ); | ||
2251 | } | ||
2252 | |||
2253 | #[test] | ||
2114 | fn two_variables_defined_inside_and_used_after_no_ret() { | 2254 | fn two_variables_defined_inside_and_used_after_no_ret() { |
2115 | check_assist( | 2255 | check_assist( |
2116 | extract_function, | 2256 | extract_function, |
@@ -2137,6 +2277,38 @@ fn $0fun_name(n: i32) -> (i32, i32) { | |||
2137 | } | 2277 | } |
2138 | 2278 | ||
2139 | #[test] | 2279 | #[test] |
2280 | fn multi_variables_defined_inside_and_used_after_mutably_no_ret() { | ||
2281 | check_assist( | ||
2282 | extract_function, | ||
2283 | r" | ||
2284 | fn foo() { | ||
2285 | let n = 1; | ||
2286 | $0let mut k = n * n; | ||
2287 | let mut m = k + 2; | ||
2288 | let mut o = m + 3; | ||
2289 | o += 1;$0 | ||
2290 | k += o; | ||
2291 | m = 1; | ||
2292 | }", | ||
2293 | r" | ||
2294 | fn foo() { | ||
2295 | let n = 1; | ||
2296 | let (mut k, mut m, o) = fun_name(n); | ||
2297 | k += o; | ||
2298 | m = 1; | ||
2299 | } | ||
2300 | |||
2301 | fn $0fun_name(n: i32) -> (i32, i32, i32) { | ||
2302 | let mut k = n * n; | ||
2303 | let mut m = k + 2; | ||
2304 | let mut o = m + 3; | ||
2305 | o += 1; | ||
2306 | (k, m, o) | ||
2307 | }", | ||
2308 | ); | ||
2309 | } | ||
2310 | |||
2311 | #[test] | ||
2140 | fn nontrivial_patterns_define_variables() { | 2312 | fn nontrivial_patterns_define_variables() { |
2141 | check_assist( | 2313 | check_assist( |
2142 | extract_function, | 2314 | extract_function, |
@@ -2364,17 +2536,15 @@ fn foo() { | |||
2364 | } | 2536 | } |
2365 | 2537 | ||
2366 | fn $0fun_name(n: &mut i32) { | 2538 | fn $0fun_name(n: &mut i32) { |
2367 | { | 2539 | *n += *n; |
2368 | *n += *n; | 2540 | bar(*n); |
2369 | bar(*n); | 2541 | bar(*n+1); |
2370 | bar(*n+1); | 2542 | bar(*n**n); |
2371 | bar(*n**n); | 2543 | bar(&*n); |
2372 | bar(&*n); | 2544 | n.inc(); |
2373 | n.inc(); | 2545 | let v = n; |
2374 | let v = n; | 2546 | *v = v.succ(); |
2375 | *v = v.succ(); | 2547 | n.succ(); |
2376 | n.succ(); | ||
2377 | } | ||
2378 | }", | 2548 | }", |
2379 | ); | 2549 | ); |
2380 | } | 2550 | } |
@@ -3372,4 +3542,36 @@ fn foo() -> Result<(), i64> { | |||
3372 | }"##, | 3542 | }"##, |
3373 | ); | 3543 | ); |
3374 | } | 3544 | } |
3545 | |||
3546 | #[test] | ||
3547 | fn param_usage_in_macro() { | ||
3548 | check_assist( | ||
3549 | extract_function, | ||
3550 | r" | ||
3551 | macro_rules! m { | ||
3552 | ($val:expr) => { $val }; | ||
3553 | } | ||
3554 | |||
3555 | fn foo() { | ||
3556 | let n = 1; | ||
3557 | $0let k = n * m!(n);$0 | ||
3558 | let m = k + 1; | ||
3559 | }", | ||
3560 | r" | ||
3561 | macro_rules! m { | ||
3562 | ($val:expr) => { $val }; | ||
3563 | } | ||
3564 | |||
3565 | fn foo() { | ||
3566 | let n = 1; | ||
3567 | let k = fun_name(n); | ||
3568 | let m = k + 1; | ||
3569 | } | ||
3570 | |||
3571 | fn $0fun_name(n: i32) -> i32 { | ||
3572 | let k = n * m!(n); | ||
3573 | k | ||
3574 | }", | ||
3575 | ); | ||
3576 | } | ||
3375 | } | 3577 | } |
diff --git a/crates/ide_assists/src/handlers/extract_variable.rs b/crates/ide_assists/src/handlers/extract_variable.rs index 7a32483dc..136b9a55b 100644 --- a/crates/ide_assists/src/handlers/extract_variable.rs +++ b/crates/ide_assists/src/handlers/extract_variable.rs | |||
@@ -2,7 +2,8 @@ use stdx::format_to; | |||
2 | use syntax::{ | 2 | use syntax::{ |
3 | ast::{self, AstNode}, | 3 | ast::{self, AstNode}, |
4 | SyntaxKind::{ | 4 | SyntaxKind::{ |
5 | BLOCK_EXPR, BREAK_EXPR, CLOSURE_EXPR, COMMENT, LOOP_EXPR, MATCH_ARM, PATH_EXPR, RETURN_EXPR, | 5 | BLOCK_EXPR, BREAK_EXPR, CLOSURE_EXPR, COMMENT, LOOP_EXPR, MATCH_ARM, MATCH_GUARD, |
6 | PATH_EXPR, RETURN_EXPR, | ||
6 | }, | 7 | }, |
7 | SyntaxNode, | 8 | SyntaxNode, |
8 | }; | 9 | }; |
@@ -147,9 +148,18 @@ impl Anchor { | |||
147 | } | 148 | } |
148 | 149 | ||
149 | if let Some(parent) = node.parent() { | 150 | if let Some(parent) = node.parent() { |
150 | if parent.kind() == MATCH_ARM || parent.kind() == CLOSURE_EXPR { | 151 | if parent.kind() == CLOSURE_EXPR { |
152 | cov_mark::hit!(test_extract_var_in_closure_no_block); | ||
151 | return Some(Anchor::WrapInBlock(node)); | 153 | return Some(Anchor::WrapInBlock(node)); |
152 | } | 154 | } |
155 | if parent.kind() == MATCH_ARM { | ||
156 | if node.kind() == MATCH_GUARD { | ||
157 | cov_mark::hit!(test_extract_var_in_match_guard); | ||
158 | } else { | ||
159 | cov_mark::hit!(test_extract_var_in_match_arm_no_block); | ||
160 | return Some(Anchor::WrapInBlock(node)); | ||
161 | } | ||
162 | } | ||
153 | } | 163 | } |
154 | 164 | ||
155 | if let Some(stmt) = ast::Stmt::cast(node.clone()) { | 165 | if let Some(stmt) = ast::Stmt::cast(node.clone()) { |
@@ -280,9 +290,10 @@ fn foo() { | |||
280 | 290 | ||
281 | #[test] | 291 | #[test] |
282 | fn test_extract_var_in_match_arm_no_block() { | 292 | fn test_extract_var_in_match_arm_no_block() { |
293 | cov_mark::check!(test_extract_var_in_match_arm_no_block); | ||
283 | check_assist( | 294 | check_assist( |
284 | extract_variable, | 295 | extract_variable, |
285 | " | 296 | r#" |
286 | fn main() { | 297 | fn main() { |
287 | let x = true; | 298 | let x = true; |
288 | let tuple = match x { | 299 | let tuple = match x { |
@@ -290,8 +301,8 @@ fn main() { | |||
290 | _ => (0, false) | 301 | _ => (0, false) |
291 | }; | 302 | }; |
292 | } | 303 | } |
293 | ", | 304 | "#, |
294 | " | 305 | r#" |
295 | fn main() { | 306 | fn main() { |
296 | let x = true; | 307 | let x = true; |
297 | let tuple = match x { | 308 | let tuple = match x { |
@@ -299,7 +310,7 @@ fn main() { | |||
299 | _ => (0, false) | 310 | _ => (0, false) |
300 | }; | 311 | }; |
301 | } | 312 | } |
302 | ", | 313 | "#, |
303 | ); | 314 | ); |
304 | } | 315 | } |
305 | 316 | ||
@@ -307,7 +318,7 @@ fn main() { | |||
307 | fn test_extract_var_in_match_arm_with_block() { | 318 | fn test_extract_var_in_match_arm_with_block() { |
308 | check_assist( | 319 | check_assist( |
309 | extract_variable, | 320 | extract_variable, |
310 | " | 321 | r#" |
311 | fn main() { | 322 | fn main() { |
312 | let x = true; | 323 | let x = true; |
313 | let tuple = match x { | 324 | let tuple = match x { |
@@ -318,8 +329,8 @@ fn main() { | |||
318 | _ => (0, false) | 329 | _ => (0, false) |
319 | }; | 330 | }; |
320 | } | 331 | } |
321 | ", | 332 | "#, |
322 | " | 333 | r#" |
323 | fn main() { | 334 | fn main() { |
324 | let x = true; | 335 | let x = true; |
325 | let tuple = match x { | 336 | let tuple = match x { |
@@ -331,24 +342,50 @@ fn main() { | |||
331 | _ => (0, false) | 342 | _ => (0, false) |
332 | }; | 343 | }; |
333 | } | 344 | } |
334 | ", | 345 | "#, |
346 | ); | ||
347 | } | ||
348 | |||
349 | #[test] | ||
350 | fn test_extract_var_in_match_guard() { | ||
351 | cov_mark::check!(test_extract_var_in_match_guard); | ||
352 | check_assist( | ||
353 | extract_variable, | ||
354 | r#" | ||
355 | fn main() { | ||
356 | match () { | ||
357 | () if $010 > 0$0 => 1 | ||
358 | _ => 2 | ||
359 | }; | ||
360 | } | ||
361 | "#, | ||
362 | r#" | ||
363 | fn main() { | ||
364 | let $0var_name = 10 > 0; | ||
365 | match () { | ||
366 | () if var_name => 1 | ||
367 | _ => 2 | ||
368 | }; | ||
369 | } | ||
370 | "#, | ||
335 | ); | 371 | ); |
336 | } | 372 | } |
337 | 373 | ||
338 | #[test] | 374 | #[test] |
339 | fn test_extract_var_in_closure_no_block() { | 375 | fn test_extract_var_in_closure_no_block() { |
376 | cov_mark::check!(test_extract_var_in_closure_no_block); | ||
340 | check_assist( | 377 | check_assist( |
341 | extract_variable, | 378 | extract_variable, |
342 | " | 379 | r#" |
343 | fn main() { | 380 | fn main() { |
344 | let lambda = |x: u32| $0x * 2$0; | 381 | let lambda = |x: u32| $0x * 2$0; |
345 | } | 382 | } |
346 | ", | 383 | "#, |
347 | " | 384 | r#" |
348 | fn main() { | 385 | fn main() { |
349 | let lambda = |x: u32| { let $0var_name = x * 2; var_name }; | 386 | let lambda = |x: u32| { let $0var_name = x * 2; var_name }; |
350 | } | 387 | } |
351 | ", | 388 | "#, |
352 | ); | 389 | ); |
353 | } | 390 | } |
354 | 391 | ||
@@ -356,16 +393,16 @@ fn main() { | |||
356 | fn test_extract_var_in_closure_with_block() { | 393 | fn test_extract_var_in_closure_with_block() { |
357 | check_assist( | 394 | check_assist( |
358 | extract_variable, | 395 | extract_variable, |
359 | " | 396 | r#" |
360 | fn main() { | 397 | fn main() { |
361 | let lambda = |x: u32| { $0x * 2$0 }; | 398 | let lambda = |x: u32| { $0x * 2$0 }; |
362 | } | 399 | } |
363 | ", | 400 | "#, |
364 | " | 401 | r#" |
365 | fn main() { | 402 | fn main() { |
366 | let lambda = |x: u32| { let $0var_name = x * 2; var_name }; | 403 | let lambda = |x: u32| { let $0var_name = x * 2; var_name }; |
367 | } | 404 | } |
368 | ", | 405 | "#, |
369 | ); | 406 | ); |
370 | } | 407 | } |
371 | 408 | ||
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs index 878b3a3fa..be927cc1c 100644 --- a/crates/ide_assists/src/handlers/fill_match_arms.rs +++ b/crates/ide_assists/src/handlers/fill_match_arms.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | use std::iter; | 1 | use std::iter; |
2 | 2 | ||
3 | use either::Either; | ||
3 | use hir::{Adt, HasSource, ModuleDef, Semantics}; | 4 | use hir::{Adt, HasSource, ModuleDef, Semantics}; |
4 | use ide_db::helpers::{mod_path_to_ast, FamousDefs}; | 5 | use ide_db::helpers::{mod_path_to_ast, FamousDefs}; |
5 | use ide_db::RootDatabase; | 6 | use ide_db::RootDatabase; |
@@ -7,7 +8,7 @@ use itertools::Itertools; | |||
7 | use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; | 8 | use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; |
8 | 9 | ||
9 | use crate::{ | 10 | use crate::{ |
10 | utils::{does_pat_match_variant, render_snippet, Cursor}, | 11 | utils::{self, render_snippet, Cursor}, |
11 | AssistContext, AssistId, AssistKind, Assists, | 12 | AssistContext, AssistId, AssistKind, Assists, |
12 | }; | 13 | }; |
13 | 14 | ||
@@ -48,6 +49,18 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
48 | } | 49 | } |
49 | } | 50 | } |
50 | 51 | ||
52 | let top_lvl_pats: Vec<_> = arms | ||
53 | .iter() | ||
54 | .filter_map(ast::MatchArm::pat) | ||
55 | .flat_map(|pat| match pat { | ||
56 | // Special case OrPat as separate top-level pats | ||
57 | Pat::OrPat(or_pat) => Either::Left(or_pat.pats()), | ||
58 | _ => Either::Right(iter::once(pat)), | ||
59 | }) | ||
60 | // Exclude top level wildcards so that they are expanded by this assist, retains status quo in #8129. | ||
61 | .filter(|pat| !matches!(pat, Pat::WildcardPat(_))) | ||
62 | .collect(); | ||
63 | |||
51 | let module = ctx.sema.scope(expr.syntax()).module()?; | 64 | let module = ctx.sema.scope(expr.syntax()).module()?; |
52 | 65 | ||
53 | let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { | 66 | let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { |
@@ -56,27 +69,20 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
56 | let mut variants = variants | 69 | let mut variants = variants |
57 | .into_iter() | 70 | .into_iter() |
58 | .filter_map(|variant| build_pat(ctx.db(), module, variant)) | 71 | .filter_map(|variant| build_pat(ctx.db(), module, variant)) |
59 | .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) | 72 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) |
60 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) | 73 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) |
61 | .collect::<Vec<_>>(); | 74 | .collect::<Vec<_>>(); |
62 | if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() { | 75 | if Some(enum_def) |
76 | == FamousDefs(&ctx.sema, Some(module.krate())) | ||
77 | .core_option_Option() | ||
78 | .map(|x| lift_enum(x)) | ||
79 | { | ||
63 | // Match `Some` variant first. | 80 | // Match `Some` variant first. |
64 | cov_mark::hit!(option_order); | 81 | cov_mark::hit!(option_order); |
65 | variants.reverse() | 82 | variants.reverse() |
66 | } | 83 | } |
67 | variants | 84 | variants |
68 | } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) { | 85 | } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) { |
69 | // Partial fill not currently supported for tuple of enums. | ||
70 | if !arms.is_empty() { | ||
71 | return None; | ||
72 | } | ||
73 | |||
74 | // We do not currently support filling match arms for a tuple | ||
75 | // containing a single enum. | ||
76 | if enum_defs.len() < 2 { | ||
77 | return None; | ||
78 | } | ||
79 | |||
80 | // When calculating the match arms for a tuple of enums, we want | 86 | // When calculating the match arms for a tuple of enums, we want |
81 | // to create a match arm for each possible combination of enum | 87 | // to create a match arm for each possible combination of enum |
82 | // values. The `multi_cartesian_product` method transforms | 88 | // values. The `multi_cartesian_product` method transforms |
@@ -91,7 +97,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
91 | variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant)); | 97 | variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant)); |
92 | ast::Pat::from(make::tuple_pat(patterns)) | 98 | ast::Pat::from(make::tuple_pat(patterns)) |
93 | }) | 99 | }) |
94 | .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) | 100 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) |
95 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) | 101 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) |
96 | .collect() | 102 | .collect() |
97 | } else { | 103 | } else { |
@@ -134,61 +140,114 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
134 | ) | 140 | ) |
135 | } | 141 | } |
136 | 142 | ||
137 | fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool { | 143 | fn is_variant_missing(existing_pats: &[Pat], var: &Pat) -> bool { |
138 | existing_arms.iter().filter_map(|arm| arm.pat()).all(|pat| { | 144 | !existing_pats.iter().any(|pat| does_pat_match_variant(pat, var)) |
139 | // Special casee OrPat as separate top-level pats | 145 | } |
140 | let top_level_pats: Vec<Pat> = match pat { | ||
141 | Pat::OrPat(pats) => pats.pats().collect::<Vec<_>>(), | ||
142 | _ => vec![pat], | ||
143 | }; | ||
144 | 146 | ||
145 | !top_level_pats.iter().any(|pat| does_pat_match_variant(pat, var)) | 147 | // Fixme: this is still somewhat limited, use hir_ty::diagnostics::match_check? |
146 | }) | 148 | fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool { |
149 | match (pat, var) { | ||
150 | (Pat::WildcardPat(_), _) => true, | ||
151 | (Pat::TuplePat(tpat), Pat::TuplePat(tvar)) => { | ||
152 | tpat.fields().zip(tvar.fields()).all(|(p, v)| does_pat_match_variant(&p, &v)) | ||
153 | } | ||
154 | _ => utils::does_pat_match_variant(pat, var), | ||
155 | } | ||
156 | } | ||
157 | |||
158 | #[derive(Eq, PartialEq, Clone)] | ||
159 | enum ExtendedEnum { | ||
160 | Bool, | ||
161 | Enum(hir::Enum), | ||
162 | } | ||
163 | |||
164 | #[derive(Eq, PartialEq, Clone)] | ||
165 | enum ExtendedVariant { | ||
166 | True, | ||
167 | False, | ||
168 | Variant(hir::Variant), | ||
169 | } | ||
170 | |||
171 | fn lift_enum(e: hir::Enum) -> ExtendedEnum { | ||
172 | ExtendedEnum::Enum(e) | ||
173 | } | ||
174 | |||
175 | impl ExtendedEnum { | ||
176 | fn variants(&self, db: &RootDatabase) -> Vec<ExtendedVariant> { | ||
177 | match self { | ||
178 | ExtendedEnum::Enum(e) => { | ||
179 | e.variants(db).into_iter().map(|x| ExtendedVariant::Variant(x)).collect::<Vec<_>>() | ||
180 | } | ||
181 | ExtendedEnum::Bool => { | ||
182 | Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False]) | ||
183 | } | ||
184 | } | ||
185 | } | ||
147 | } | 186 | } |
148 | 187 | ||
149 | fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { | 188 | fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> { |
150 | sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { | 189 | sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { |
151 | Some(Adt::Enum(e)) => Some(e), | 190 | Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)), |
152 | _ => None, | 191 | _ => { |
192 | if ty.is_bool() { | ||
193 | Some(ExtendedEnum::Bool) | ||
194 | } else { | ||
195 | None | ||
196 | } | ||
197 | } | ||
153 | }) | 198 | }) |
154 | } | 199 | } |
155 | 200 | ||
156 | fn resolve_tuple_of_enum_def( | 201 | fn resolve_tuple_of_enum_def( |
157 | sema: &Semantics<RootDatabase>, | 202 | sema: &Semantics<RootDatabase>, |
158 | expr: &ast::Expr, | 203 | expr: &ast::Expr, |
159 | ) -> Option<Vec<hir::Enum>> { | 204 | ) -> Option<Vec<ExtendedEnum>> { |
160 | sema.type_of_expr(&expr)? | 205 | sema.type_of_expr(&expr)? |
161 | .tuple_fields(sema.db) | 206 | .tuple_fields(sema.db) |
162 | .iter() | 207 | .iter() |
163 | .map(|ty| { | 208 | .map(|ty| { |
164 | ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() { | 209 | ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() { |
165 | Some(Adt::Enum(e)) => Some(e), | 210 | Some(Adt::Enum(e)) => Some(lift_enum(e)), |
166 | // For now we only handle expansion for a tuple of enums. Here | 211 | // For now we only handle expansion for a tuple of enums. Here |
167 | // we map non-enum items to None and rely on `collect` to | 212 | // we map non-enum items to None and rely on `collect` to |
168 | // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>. | 213 | // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>. |
169 | _ => None, | 214 | _ => { |
215 | if ty.is_bool() { | ||
216 | Some(ExtendedEnum::Bool) | ||
217 | } else { | ||
218 | None | ||
219 | } | ||
220 | } | ||
170 | }) | 221 | }) |
171 | }) | 222 | }) |
172 | .collect() | 223 | .collect() |
173 | } | 224 | } |
174 | 225 | ||
175 | fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::Variant) -> Option<ast::Pat> { | 226 | fn build_pat(db: &RootDatabase, module: hir::Module, var: ExtendedVariant) -> Option<ast::Pat> { |
176 | let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?); | 227 | match var { |
228 | ExtendedVariant::Variant(var) => { | ||
229 | let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?); | ||
230 | |||
231 | // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though | ||
232 | let pat: ast::Pat = match var.source(db)?.value.kind() { | ||
233 | ast::StructKind::Tuple(field_list) => { | ||
234 | let pats = | ||
235 | iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count()); | ||
236 | make::tuple_struct_pat(path, pats).into() | ||
237 | } | ||
238 | ast::StructKind::Record(field_list) => { | ||
239 | let pats = | ||
240 | field_list.fields().map(|f| make::ident_pat(f.name().unwrap()).into()); | ||
241 | make::record_pat(path, pats).into() | ||
242 | } | ||
243 | ast::StructKind::Unit => make::path_pat(path), | ||
244 | }; | ||
177 | 245 | ||
178 | // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though | 246 | Some(pat) |
179 | let pat: ast::Pat = match var.source(db)?.value.kind() { | ||
180 | ast::StructKind::Tuple(field_list) => { | ||
181 | let pats = iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count()); | ||
182 | make::tuple_struct_pat(path, pats).into() | ||
183 | } | ||
184 | ast::StructKind::Record(field_list) => { | ||
185 | let pats = field_list.fields().map(|f| make::ident_pat(f.name().unwrap()).into()); | ||
186 | make::record_pat(path, pats).into() | ||
187 | } | 247 | } |
188 | ast::StructKind::Unit => make::path_pat(path), | 248 | ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))), |
189 | }; | 249 | ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))), |
190 | 250 | } | |
191 | Some(pat) | ||
192 | } | 251 | } |
193 | 252 | ||
194 | #[cfg(test)] | 253 | #[cfg(test)] |
@@ -221,6 +280,21 @@ mod tests { | |||
221 | } | 280 | } |
222 | 281 | ||
223 | #[test] | 282 | #[test] |
283 | fn all_boolean_match_arms_provided() { | ||
284 | check_assist_not_applicable( | ||
285 | fill_match_arms, | ||
286 | r#" | ||
287 | fn foo(a: bool) { | ||
288 | match a$0 { | ||
289 | true => {} | ||
290 | false => {} | ||
291 | } | ||
292 | } | ||
293 | "#, | ||
294 | ) | ||
295 | } | ||
296 | |||
297 | #[test] | ||
224 | fn tuple_of_non_enum() { | 298 | fn tuple_of_non_enum() { |
225 | // for now this case is not handled, although it potentially could be | 299 | // for now this case is not handled, although it potentially could be |
226 | // in the future | 300 | // in the future |
@@ -236,6 +310,113 @@ mod tests { | |||
236 | } | 310 | } |
237 | 311 | ||
238 | #[test] | 312 | #[test] |
313 | fn fill_match_arms_boolean() { | ||
314 | check_assist( | ||
315 | fill_match_arms, | ||
316 | r#" | ||
317 | fn foo(a: bool) { | ||
318 | match a$0 { | ||
319 | } | ||
320 | } | ||
321 | "#, | ||
322 | r#" | ||
323 | fn foo(a: bool) { | ||
324 | match a { | ||
325 | $0true => {} | ||
326 | false => {} | ||
327 | } | ||
328 | } | ||
329 | "#, | ||
330 | ) | ||
331 | } | ||
332 | |||
333 | #[test] | ||
334 | fn partial_fill_boolean() { | ||
335 | check_assist( | ||
336 | fill_match_arms, | ||
337 | r#" | ||
338 | fn foo(a: bool) { | ||
339 | match a$0 { | ||
340 | true => {} | ||
341 | } | ||
342 | } | ||
343 | "#, | ||
344 | r#" | ||
345 | fn foo(a: bool) { | ||
346 | match a { | ||
347 | true => {} | ||
348 | $0false => {} | ||
349 | } | ||
350 | } | ||
351 | "#, | ||
352 | ) | ||
353 | } | ||
354 | |||
355 | #[test] | ||
356 | fn all_boolean_tuple_arms_provided() { | ||
357 | check_assist_not_applicable( | ||
358 | fill_match_arms, | ||
359 | r#" | ||
360 | fn foo(a: bool) { | ||
361 | match (a, a)$0 { | ||
362 | (true, true) => {} | ||
363 | (true, false) => {} | ||
364 | (false, true) => {} | ||
365 | (false, false) => {} | ||
366 | } | ||
367 | } | ||
368 | "#, | ||
369 | ) | ||
370 | } | ||
371 | |||
372 | #[test] | ||
373 | fn fill_boolean_tuple() { | ||
374 | check_assist( | ||
375 | fill_match_arms, | ||
376 | r#" | ||
377 | fn foo(a: bool) { | ||
378 | match (a, a)$0 { | ||
379 | } | ||
380 | } | ||
381 | "#, | ||
382 | r#" | ||
383 | fn foo(a: bool) { | ||
384 | match (a, a) { | ||
385 | $0(true, true) => {} | ||
386 | (true, false) => {} | ||
387 | (false, true) => {} | ||
388 | (false, false) => {} | ||
389 | } | ||
390 | } | ||
391 | "#, | ||
392 | ) | ||
393 | } | ||
394 | |||
395 | #[test] | ||
396 | fn partial_fill_boolean_tuple() { | ||
397 | check_assist( | ||
398 | fill_match_arms, | ||
399 | r#" | ||
400 | fn foo(a: bool) { | ||
401 | match (a, a)$0 { | ||
402 | (false, true) => {} | ||
403 | } | ||
404 | } | ||
405 | "#, | ||
406 | r#" | ||
407 | fn foo(a: bool) { | ||
408 | match (a, a) { | ||
409 | (false, true) => {} | ||
410 | $0(true, true) => {} | ||
411 | (true, false) => {} | ||
412 | (false, false) => {} | ||
413 | } | ||
414 | } | ||
415 | "#, | ||
416 | ) | ||
417 | } | ||
418 | |||
419 | #[test] | ||
239 | fn partial_fill_record_tuple() { | 420 | fn partial_fill_record_tuple() { |
240 | check_assist( | 421 | check_assist( |
241 | fill_match_arms, | 422 | fill_match_arms, |
@@ -473,20 +654,81 @@ fn main() { | |||
473 | 654 | ||
474 | #[test] | 655 | #[test] |
475 | fn fill_match_arms_tuple_of_enum_partial() { | 656 | fn fill_match_arms_tuple_of_enum_partial() { |
476 | check_assist_not_applicable( | 657 | check_assist( |
477 | fill_match_arms, | 658 | fill_match_arms, |
478 | r#" | 659 | r#" |
479 | enum A { One, Two } | 660 | enum A { One, Two } |
480 | enum B { One, Two } | 661 | enum B { One, Two } |
481 | 662 | ||
482 | fn main() { | 663 | fn main() { |
483 | let a = A::One; | 664 | let a = A::One; |
484 | let b = B::One; | 665 | let b = B::One; |
485 | match (a$0, b) { | 666 | match (a$0, b) { |
486 | (A::Two, B::One) => {} | 667 | (A::Two, B::One) => {} |
487 | } | 668 | } |
488 | } | 669 | } |
489 | "#, | 670 | "#, |
671 | r#" | ||
672 | enum A { One, Two } | ||
673 | enum B { One, Two } | ||
674 | |||
675 | fn main() { | ||
676 | let a = A::One; | ||
677 | let b = B::One; | ||
678 | match (a, b) { | ||
679 | (A::Two, B::One) => {} | ||
680 | $0(A::One, B::One) => {} | ||
681 | (A::One, B::Two) => {} | ||
682 | (A::Two, B::Two) => {} | ||
683 | } | ||
684 | } | ||
685 | "#, | ||
686 | ); | ||
687 | } | ||
688 | |||
689 | #[test] | ||
690 | fn fill_match_arms_tuple_of_enum_partial_with_wildcards() { | ||
691 | let ra_fixture = r#" | ||
692 | fn main() { | ||
693 | let a = Some(1); | ||
694 | let b = Some(()); | ||
695 | match (a$0, b) { | ||
696 | (Some(_), _) => {} | ||
697 | (None, Some(_)) => {} | ||
698 | } | ||
699 | } | ||
700 | "#; | ||
701 | check_assist( | ||
702 | fill_match_arms, | ||
703 | &format!("//- /main.rs crate:main deps:core{}{}", ra_fixture, FamousDefs::FIXTURE), | ||
704 | r#" | ||
705 | fn main() { | ||
706 | let a = Some(1); | ||
707 | let b = Some(()); | ||
708 | match (a, b) { | ||
709 | (Some(_), _) => {} | ||
710 | (None, Some(_)) => {} | ||
711 | $0(None, None) => {} | ||
712 | } | ||
713 | } | ||
714 | "#, | ||
715 | ); | ||
716 | } | ||
717 | |||
718 | #[test] | ||
719 | fn fill_match_arms_partial_with_deep_pattern() { | ||
720 | // Fixme: cannot handle deep patterns | ||
721 | let ra_fixture = r#" | ||
722 | fn main() { | ||
723 | match $0Some(true) { | ||
724 | Some(true) => {} | ||
725 | None => {} | ||
726 | } | ||
727 | } | ||
728 | "#; | ||
729 | check_assist_not_applicable( | ||
730 | fill_match_arms, | ||
731 | &format!("//- /main.rs crate:main deps:core{}{}", ra_fixture, FamousDefs::FIXTURE), | ||
490 | ); | 732 | ); |
491 | } | 733 | } |
492 | 734 | ||
@@ -514,10 +756,7 @@ fn main() { | |||
514 | 756 | ||
515 | #[test] | 757 | #[test] |
516 | fn fill_match_arms_single_element_tuple_of_enum() { | 758 | fn fill_match_arms_single_element_tuple_of_enum() { |
517 | // For now we don't hande the case of a single element tuple, but | 759 | check_assist( |
518 | // we could handle this in the future if `make::tuple_pat` allowed | ||
519 | // creating a tuple with a single pattern. | ||
520 | check_assist_not_applicable( | ||
521 | fill_match_arms, | 760 | fill_match_arms, |
522 | r#" | 761 | r#" |
523 | enum A { One, Two } | 762 | enum A { One, Two } |
@@ -528,6 +767,17 @@ fn main() { | |||
528 | } | 767 | } |
529 | } | 768 | } |
530 | "#, | 769 | "#, |
770 | r#" | ||
771 | enum A { One, Two } | ||
772 | |||
773 | fn main() { | ||
774 | let a = A::One; | ||
775 | match (a, ) { | ||
776 | $0(A::One,) => {} | ||
777 | (A::Two,) => {} | ||
778 | } | ||
779 | } | ||
780 | "#, | ||
531 | ); | 781 | ); |
532 | } | 782 | } |
533 | 783 | ||
diff --git a/crates/ide_assists/src/handlers/flip_comma.rs b/crates/ide_assists/src/handlers/flip_comma.rs index 7f5e75d34..99be5e090 100644 --- a/crates/ide_assists/src/handlers/flip_comma.rs +++ b/crates/ide_assists/src/handlers/flip_comma.rs | |||
@@ -66,26 +66,12 @@ mod tests { | |||
66 | } | 66 | } |
67 | 67 | ||
68 | #[test] | 68 | #[test] |
69 | #[should_panic] | ||
70 | fn flip_comma_before_punct() { | 69 | fn flip_comma_before_punct() { |
71 | // See https://github.com/rust-analyzer/rust-analyzer/issues/1619 | 70 | // See https://github.com/rust-analyzer/rust-analyzer/issues/1619 |
72 | // "Flip comma" assist shouldn't be applicable to the last comma in enum or struct | 71 | // "Flip comma" assist shouldn't be applicable to the last comma in enum or struct |
73 | // declaration body. | 72 | // declaration body. |
74 | check_assist_target( | 73 | check_assist_not_applicable(flip_comma, "pub enum Test { A,$0 }"); |
75 | flip_comma, | 74 | check_assist_not_applicable(flip_comma, "pub struct Test { foo: usize,$0 }"); |
76 | "pub enum Test { \ | ||
77 | A,$0 \ | ||
78 | }", | ||
79 | ",", | ||
80 | ); | ||
81 | |||
82 | check_assist_target( | ||
83 | flip_comma, | ||
84 | "pub struct Test { \ | ||
85 | foo: usize,$0 \ | ||
86 | }", | ||
87 | ",", | ||
88 | ); | ||
89 | } | 75 | } |
90 | 76 | ||
91 | #[test] | 77 | #[test] |
diff --git a/crates/ide_assists/src/handlers/generate_deref.rs b/crates/ide_assists/src/handlers/generate_deref.rs new file mode 100644 index 000000000..4998ff7a4 --- /dev/null +++ b/crates/ide_assists/src/handlers/generate_deref.rs | |||
@@ -0,0 +1,227 @@ | |||
1 | use std::fmt::Display; | ||
2 | |||
3 | use ide_db::{helpers::FamousDefs, RootDatabase}; | ||
4 | use syntax::{ | ||
5 | ast::{self, NameOwner}, | ||
6 | AstNode, SyntaxNode, | ||
7 | }; | ||
8 | |||
9 | use crate::{ | ||
10 | assist_context::{AssistBuilder, AssistContext, Assists}, | ||
11 | utils::generate_trait_impl_text, | ||
12 | AssistId, AssistKind, | ||
13 | }; | ||
14 | |||
15 | // Assist: generate_deref | ||
16 | // | ||
17 | // Generate `Deref` impl using the given struct field. | ||
18 | // | ||
19 | // ``` | ||
20 | // struct A; | ||
21 | // struct B { | ||
22 | // $0a: A | ||
23 | // } | ||
24 | // ``` | ||
25 | // -> | ||
26 | // ``` | ||
27 | // struct A; | ||
28 | // struct B { | ||
29 | // a: A | ||
30 | // } | ||
31 | // | ||
32 | // impl std::ops::Deref for B { | ||
33 | // type Target = A; | ||
34 | // | ||
35 | // fn deref(&self) -> &Self::Target { | ||
36 | // &self.a | ||
37 | // } | ||
38 | // } | ||
39 | // ``` | ||
40 | pub(crate) fn generate_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
41 | generate_record_deref(acc, ctx).or_else(|| generate_tuple_deref(acc, ctx)) | ||
42 | } | ||
43 | |||
44 | fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
45 | let strukt = ctx.find_node_at_offset::<ast::Struct>()?; | ||
46 | let field = ctx.find_node_at_offset::<ast::RecordField>()?; | ||
47 | |||
48 | if existing_deref_impl(&ctx.sema, &strukt).is_some() { | ||
49 | cov_mark::hit!(test_add_record_deref_impl_already_exists); | ||
50 | return None; | ||
51 | } | ||
52 | |||
53 | let field_type = field.ty()?; | ||
54 | let field_name = field.name()?; | ||
55 | let target = field.syntax().text_range(); | ||
56 | acc.add( | ||
57 | AssistId("generate_deref", AssistKind::Generate), | ||
58 | format!("Generate `Deref` impl using `{}`", field_name), | ||
59 | target, | ||
60 | |edit| generate_edit(edit, strukt, field_type.syntax(), field_name.syntax()), | ||
61 | ) | ||
62 | } | ||
63 | |||
64 | fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
65 | let strukt = ctx.find_node_at_offset::<ast::Struct>()?; | ||
66 | let field = ctx.find_node_at_offset::<ast::TupleField>()?; | ||
67 | let field_list = ctx.find_node_at_offset::<ast::TupleFieldList>()?; | ||
68 | let field_list_index = | ||
69 | field_list.syntax().children().into_iter().position(|s| &s == field.syntax())?; | ||
70 | |||
71 | if existing_deref_impl(&ctx.sema, &strukt).is_some() { | ||
72 | cov_mark::hit!(test_add_field_deref_impl_already_exists); | ||
73 | return None; | ||
74 | } | ||
75 | |||
76 | let field_type = field.ty()?; | ||
77 | let target = field.syntax().text_range(); | ||
78 | acc.add( | ||
79 | AssistId("generate_deref", AssistKind::Generate), | ||
80 | format!("Generate `Deref` impl using `{}`", field.syntax()), | ||
81 | target, | ||
82 | |edit| generate_edit(edit, strukt, field_type.syntax(), field_list_index), | ||
83 | ) | ||
84 | } | ||
85 | |||
86 | fn generate_edit( | ||
87 | edit: &mut AssistBuilder, | ||
88 | strukt: ast::Struct, | ||
89 | field_type_syntax: &SyntaxNode, | ||
90 | field_name: impl Display, | ||
91 | ) { | ||
92 | let start_offset = strukt.syntax().text_range().end(); | ||
93 | let impl_code = format!( | ||
94 | r#" type Target = {0}; | ||
95 | |||
96 | fn deref(&self) -> &Self::Target {{ | ||
97 | &self.{1} | ||
98 | }}"#, | ||
99 | field_type_syntax, field_name | ||
100 | ); | ||
101 | let strukt_adt = ast::Adt::Struct(strukt); | ||
102 | let deref_impl = generate_trait_impl_text(&strukt_adt, "std::ops::Deref", &impl_code); | ||
103 | edit.insert(start_offset, deref_impl); | ||
104 | } | ||
105 | |||
106 | fn existing_deref_impl( | ||
107 | sema: &'_ hir::Semantics<'_, RootDatabase>, | ||
108 | strukt: &ast::Struct, | ||
109 | ) -> Option<()> { | ||
110 | let strukt = sema.to_def(strukt)?; | ||
111 | let krate = strukt.module(sema.db).krate(); | ||
112 | |||
113 | let deref_trait = FamousDefs(sema, Some(krate)).core_ops_Deref()?; | ||
114 | let strukt_type = strukt.ty(sema.db); | ||
115 | |||
116 | if strukt_type.impls_trait(sema.db, deref_trait, &[]) { | ||
117 | Some(()) | ||
118 | } else { | ||
119 | None | ||
120 | } | ||
121 | } | ||
122 | |||
123 | #[cfg(test)] | ||
124 | mod tests { | ||
125 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
126 | |||
127 | use super::*; | ||
128 | |||
129 | #[test] | ||
130 | fn test_generate_record_deref() { | ||
131 | check_assist( | ||
132 | generate_deref, | ||
133 | r#"struct A { } | ||
134 | struct B { $0a: A }"#, | ||
135 | r#"struct A { } | ||
136 | struct B { a: A } | ||
137 | |||
138 | impl std::ops::Deref for B { | ||
139 | type Target = A; | ||
140 | |||
141 | fn deref(&self) -> &Self::Target { | ||
142 | &self.a | ||
143 | } | ||
144 | }"#, | ||
145 | ); | ||
146 | } | ||
147 | |||
148 | #[test] | ||
149 | fn test_generate_field_deref_idx_0() { | ||
150 | check_assist( | ||
151 | generate_deref, | ||
152 | r#"struct A { } | ||
153 | struct B($0A);"#, | ||
154 | r#"struct A { } | ||
155 | struct B(A); | ||
156 | |||
157 | impl std::ops::Deref for B { | ||
158 | type Target = A; | ||
159 | |||
160 | fn deref(&self) -> &Self::Target { | ||
161 | &self.0 | ||
162 | } | ||
163 | }"#, | ||
164 | ); | ||
165 | } | ||
166 | #[test] | ||
167 | fn test_generate_field_deref_idx_1() { | ||
168 | check_assist( | ||
169 | generate_deref, | ||
170 | r#"struct A { } | ||
171 | struct B(u8, $0A);"#, | ||
172 | r#"struct A { } | ||
173 | struct B(u8, A); | ||
174 | |||
175 | impl std::ops::Deref for B { | ||
176 | type Target = A; | ||
177 | |||
178 | fn deref(&self) -> &Self::Target { | ||
179 | &self.1 | ||
180 | } | ||
181 | }"#, | ||
182 | ); | ||
183 | } | ||
184 | |||
185 | fn check_not_applicable(ra_fixture: &str) { | ||
186 | let fixture = format!( | ||
187 | "//- /main.rs crate:main deps:core,std\n{}\n{}", | ||
188 | ra_fixture, | ||
189 | FamousDefs::FIXTURE | ||
190 | ); | ||
191 | check_assist_not_applicable(generate_deref, &fixture) | ||
192 | } | ||
193 | |||
194 | #[test] | ||
195 | fn test_generate_record_deref_not_applicable_if_already_impl() { | ||
196 | cov_mark::check!(test_add_record_deref_impl_already_exists); | ||
197 | check_not_applicable( | ||
198 | r#"struct A { } | ||
199 | struct B { $0a: A } | ||
200 | |||
201 | impl std::ops::Deref for B { | ||
202 | type Target = A; | ||
203 | |||
204 | fn deref(&self) -> &Self::Target { | ||
205 | &self.a | ||
206 | } | ||
207 | }"#, | ||
208 | ) | ||
209 | } | ||
210 | |||
211 | #[test] | ||
212 | fn test_generate_field_deref_not_applicable_if_already_impl() { | ||
213 | cov_mark::check!(test_add_field_deref_impl_already_exists); | ||
214 | check_not_applicable( | ||
215 | r#"struct A { } | ||
216 | struct B($0A) | ||
217 | |||
218 | impl std::ops::Deref for B { | ||
219 | type Target = A; | ||
220 | |||
221 | fn deref(&self) -> &Self::Target { | ||
222 | &self.0 | ||
223 | } | ||
224 | }"#, | ||
225 | ) | ||
226 | } | ||
227 | } | ||
diff --git a/crates/ide_assists/src/handlers/inline_local_variable.rs b/crates/ide_assists/src/handlers/inline_local_variable.rs index ea1466dc8..f5dafc8cb 100644 --- a/crates/ide_assists/src/handlers/inline_local_variable.rs +++ b/crates/ide_assists/src/handlers/inline_local_variable.rs | |||
@@ -1,7 +1,9 @@ | |||
1 | use ide_db::{defs::Definition, search::FileReference}; | 1 | use either::Either; |
2 | use hir::PathResolution; | ||
3 | use ide_db::{base_db::FileId, defs::Definition, search::FileReference}; | ||
2 | use rustc_hash::FxHashMap; | 4 | use rustc_hash::FxHashMap; |
3 | use syntax::{ | 5 | use syntax::{ |
4 | ast::{self, AstNode, AstToken}, | 6 | ast::{self, AstNode, AstToken, NameOwner}, |
5 | TextRange, | 7 | TextRange, |
6 | }; | 8 | }; |
7 | 9 | ||
@@ -27,44 +29,28 @@ use crate::{ | |||
27 | // } | 29 | // } |
28 | // ``` | 30 | // ``` |
29 | pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 31 | pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
30 | let let_stmt = ctx.find_node_at_offset::<ast::LetStmt>()?; | 32 | let InlineData { let_stmt, delete_let, replace_usages, target } = |
31 | let bind_pat = match let_stmt.pat()? { | 33 | inline_let(ctx).or_else(|| inline_usage(ctx))?; |
32 | ast::Pat::IdentPat(pat) => pat, | ||
33 | _ => return None, | ||
34 | }; | ||
35 | if bind_pat.mut_token().is_some() { | ||
36 | cov_mark::hit!(test_not_inline_mut_variable); | ||
37 | return None; | ||
38 | } | ||
39 | if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) { | ||
40 | cov_mark::hit!(not_applicable_outside_of_bind_pat); | ||
41 | return None; | ||
42 | } | ||
43 | let initializer_expr = let_stmt.initializer()?; | 34 | let initializer_expr = let_stmt.initializer()?; |
44 | 35 | ||
45 | let def = ctx.sema.to_def(&bind_pat)?; | 36 | let delete_range = if delete_let { |
46 | let def = Definition::Local(def); | 37 | if let Some(whitespace) = let_stmt |
47 | let usages = def.usages(&ctx.sema).all(); | 38 | .syntax() |
48 | if usages.is_empty() { | 39 | .next_sibling_or_token() |
49 | cov_mark::hit!(test_not_applicable_if_variable_unused); | 40 | .and_then(|it| ast::Whitespace::cast(it.as_token()?.clone())) |
50 | return None; | 41 | { |
51 | }; | 42 | Some(TextRange::new( |
52 | 43 | let_stmt.syntax().text_range().start(), | |
53 | let delete_range = if let Some(whitespace) = let_stmt | 44 | whitespace.syntax().text_range().end(), |
54 | .syntax() | 45 | )) |
55 | .next_sibling_or_token() | 46 | } else { |
56 | .and_then(|it| ast::Whitespace::cast(it.as_token()?.clone())) | 47 | Some(let_stmt.syntax().text_range()) |
57 | { | 48 | } |
58 | TextRange::new( | ||
59 | let_stmt.syntax().text_range().start(), | ||
60 | whitespace.syntax().text_range().end(), | ||
61 | ) | ||
62 | } else { | 49 | } else { |
63 | let_stmt.syntax().text_range() | 50 | None |
64 | }; | 51 | }; |
65 | 52 | ||
66 | let wrap_in_parens = usages | 53 | let wrap_in_parens = replace_usages |
67 | .references | ||
68 | .iter() | 54 | .iter() |
69 | .map(|(&file_id, refs)| { | 55 | .map(|(&file_id, refs)| { |
70 | refs.iter() | 56 | refs.iter() |
@@ -114,14 +100,20 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
114 | let init_str = initializer_expr.syntax().text().to_string(); | 100 | let init_str = initializer_expr.syntax().text().to_string(); |
115 | let init_in_paren = format!("({})", &init_str); | 101 | let init_in_paren = format!("({})", &init_str); |
116 | 102 | ||
117 | let target = bind_pat.syntax().text_range(); | 103 | let target = match target { |
104 | ast::NameOrNameRef::Name(it) => it.syntax().text_range(), | ||
105 | ast::NameOrNameRef::NameRef(it) => it.syntax().text_range(), | ||
106 | }; | ||
107 | |||
118 | acc.add( | 108 | acc.add( |
119 | AssistId("inline_local_variable", AssistKind::RefactorInline), | 109 | AssistId("inline_local_variable", AssistKind::RefactorInline), |
120 | "Inline variable", | 110 | "Inline variable", |
121 | target, | 111 | target, |
122 | move |builder| { | 112 | move |builder| { |
123 | builder.delete(delete_range); | 113 | if let Some(range) = delete_range { |
124 | for (file_id, references) in usages.references { | 114 | builder.delete(range); |
115 | } | ||
116 | for (file_id, references) in replace_usages { | ||
125 | for (&should_wrap, reference) in wrap_in_parens[&file_id].iter().zip(references) { | 117 | for (&should_wrap, reference) in wrap_in_parens[&file_id].iter().zip(references) { |
126 | let replacement = | 118 | let replacement = |
127 | if should_wrap { init_in_paren.clone() } else { init_str.clone() }; | 119 | if should_wrap { init_in_paren.clone() } else { init_str.clone() }; |
@@ -140,6 +132,81 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
140 | ) | 132 | ) |
141 | } | 133 | } |
142 | 134 | ||
135 | struct InlineData { | ||
136 | let_stmt: ast::LetStmt, | ||
137 | delete_let: bool, | ||
138 | target: ast::NameOrNameRef, | ||
139 | replace_usages: FxHashMap<FileId, Vec<FileReference>>, | ||
140 | } | ||
141 | |||
142 | fn inline_let(ctx: &AssistContext) -> Option<InlineData> { | ||
143 | let let_stmt = ctx.find_node_at_offset::<ast::LetStmt>()?; | ||
144 | let bind_pat = match let_stmt.pat()? { | ||
145 | ast::Pat::IdentPat(pat) => pat, | ||
146 | _ => return None, | ||
147 | }; | ||
148 | if bind_pat.mut_token().is_some() { | ||
149 | cov_mark::hit!(test_not_inline_mut_variable); | ||
150 | return None; | ||
151 | } | ||
152 | if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) { | ||
153 | cov_mark::hit!(not_applicable_outside_of_bind_pat); | ||
154 | return None; | ||
155 | } | ||
156 | |||
157 | let def = ctx.sema.to_def(&bind_pat)?; | ||
158 | let def = Definition::Local(def); | ||
159 | let usages = def.usages(&ctx.sema).all(); | ||
160 | if usages.is_empty() { | ||
161 | cov_mark::hit!(test_not_applicable_if_variable_unused); | ||
162 | return None; | ||
163 | }; | ||
164 | |||
165 | Some(InlineData { | ||
166 | let_stmt, | ||
167 | delete_let: true, | ||
168 | target: ast::NameOrNameRef::Name(bind_pat.name()?), | ||
169 | replace_usages: usages.references, | ||
170 | }) | ||
171 | } | ||
172 | |||
173 | fn inline_usage(ctx: &AssistContext) -> Option<InlineData> { | ||
174 | let path_expr = ctx.find_node_at_offset::<ast::PathExpr>()?; | ||
175 | let path = path_expr.path()?; | ||
176 | let name = match path.as_single_segment()?.kind()? { | ||
177 | ast::PathSegmentKind::Name(name) => name, | ||
178 | _ => return None, | ||
179 | }; | ||
180 | |||
181 | let local = match ctx.sema.resolve_path(&path)? { | ||
182 | PathResolution::Local(local) => local, | ||
183 | _ => return None, | ||
184 | }; | ||
185 | |||
186 | let bind_pat = match local.source(ctx.db()).value { | ||
187 | Either::Left(ident) => ident, | ||
188 | _ => return None, | ||
189 | }; | ||
190 | |||
191 | let let_stmt = ast::LetStmt::cast(bind_pat.syntax().parent()?)?; | ||
192 | |||
193 | let def = Definition::Local(local); | ||
194 | let mut usages = def.usages(&ctx.sema).all(); | ||
195 | |||
196 | let delete_let = usages.references.values().map(|v| v.len()).sum::<usize>() == 1; | ||
197 | |||
198 | for references in usages.references.values_mut() { | ||
199 | references.retain(|reference| reference.name.as_name_ref() == Some(&name)); | ||
200 | } | ||
201 | |||
202 | Some(InlineData { | ||
203 | let_stmt, | ||
204 | delete_let, | ||
205 | target: ast::NameOrNameRef::NameRef(name), | ||
206 | replace_usages: usages.references, | ||
207 | }) | ||
208 | } | ||
209 | |||
143 | #[cfg(test)] | 210 | #[cfg(test)] |
144 | mod tests { | 211 | mod tests { |
145 | use crate::tests::{check_assist, check_assist_not_applicable}; | 212 | use crate::tests::{check_assist, check_assist_not_applicable}; |
@@ -726,4 +793,84 @@ fn main() { | |||
726 | ", | 793 | ", |
727 | ) | 794 | ) |
728 | } | 795 | } |
796 | |||
797 | #[test] | ||
798 | fn works_on_local_usage() { | ||
799 | check_assist( | ||
800 | inline_local_variable, | ||
801 | r#" | ||
802 | fn f() { | ||
803 | let xyz = 0; | ||
804 | xyz$0; | ||
805 | } | ||
806 | "#, | ||
807 | r#" | ||
808 | fn f() { | ||
809 | 0; | ||
810 | } | ||
811 | "#, | ||
812 | ); | ||
813 | } | ||
814 | |||
815 | #[test] | ||
816 | fn does_not_remove_let_when_multiple_usages() { | ||
817 | check_assist( | ||
818 | inline_local_variable, | ||
819 | r#" | ||
820 | fn f() { | ||
821 | let xyz = 0; | ||
822 | xyz$0; | ||
823 | xyz; | ||
824 | } | ||
825 | "#, | ||
826 | r#" | ||
827 | fn f() { | ||
828 | let xyz = 0; | ||
829 | 0; | ||
830 | xyz; | ||
831 | } | ||
832 | "#, | ||
833 | ); | ||
834 | } | ||
835 | |||
836 | #[test] | ||
837 | fn not_applicable_with_non_ident_pattern() { | ||
838 | check_assist_not_applicable( | ||
839 | inline_local_variable, | ||
840 | r#" | ||
841 | fn main() { | ||
842 | let (x, y) = (0, 1); | ||
843 | x$0; | ||
844 | } | ||
845 | "#, | ||
846 | ); | ||
847 | } | ||
848 | |||
849 | #[test] | ||
850 | fn not_applicable_on_local_usage_in_macro() { | ||
851 | check_assist_not_applicable( | ||
852 | inline_local_variable, | ||
853 | r#" | ||
854 | macro_rules! m { | ||
855 | ($i:ident) => { $i } | ||
856 | } | ||
857 | fn f() { | ||
858 | let xyz = 0; | ||
859 | m!(xyz$0); // replacing it would break the macro | ||
860 | } | ||
861 | "#, | ||
862 | ); | ||
863 | check_assist_not_applicable( | ||
864 | inline_local_variable, | ||
865 | r#" | ||
866 | macro_rules! m { | ||
867 | ($i:ident) => { $i } | ||
868 | } | ||
869 | fn f() { | ||
870 | let xyz$0 = 0; | ||
871 | m!(xyz); // replacing it would break the macro | ||
872 | } | ||
873 | "#, | ||
874 | ); | ||
875 | } | ||
729 | } | 876 | } |
diff --git a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs index 02782eb6d..9f4f71d6c 100644 --- a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs +++ b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs | |||
@@ -1,7 +1,8 @@ | |||
1 | use rustc_hash::FxHashSet; | 1 | use rustc_hash::FxHashSet; |
2 | use syntax::{ | 2 | use syntax::{ |
3 | ast::{self, GenericParamsOwner, NameOwner}, | 3 | ast::{self, edit_in_place::GenericParamsOwnerEdit, make, GenericParamsOwner}, |
4 | AstNode, TextRange, TextSize, | 4 | ted::{self, Position}, |
5 | AstNode, TextRange, | ||
5 | }; | 6 | }; |
6 | 7 | ||
7 | use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists}; | 8 | use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists}; |
@@ -37,10 +38,12 @@ static ASSIST_LABEL: &str = "Introduce named lifetime"; | |||
37 | pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 38 | pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
38 | let lifetime = | 39 | let lifetime = |
39 | ctx.find_node_at_offset::<ast::Lifetime>().filter(|lifetime| lifetime.text() == "'_")?; | 40 | ctx.find_node_at_offset::<ast::Lifetime>().filter(|lifetime| lifetime.text() == "'_")?; |
41 | let lifetime_loc = lifetime.lifetime_ident_token()?.text_range(); | ||
42 | |||
40 | if let Some(fn_def) = lifetime.syntax().ancestors().find_map(ast::Fn::cast) { | 43 | if let Some(fn_def) = lifetime.syntax().ancestors().find_map(ast::Fn::cast) { |
41 | generate_fn_def_assist(acc, &fn_def, lifetime.lifetime_ident_token()?.text_range()) | 44 | generate_fn_def_assist(acc, fn_def, lifetime_loc, lifetime) |
42 | } else if let Some(impl_def) = lifetime.syntax().ancestors().find_map(ast::Impl::cast) { | 45 | } else if let Some(impl_def) = lifetime.syntax().ancestors().find_map(ast::Impl::cast) { |
43 | generate_impl_def_assist(acc, &impl_def, lifetime.lifetime_ident_token()?.text_range()) | 46 | generate_impl_def_assist(acc, impl_def, lifetime_loc, lifetime) |
44 | } else { | 47 | } else { |
45 | None | 48 | None |
46 | } | 49 | } |
@@ -49,26 +52,26 @@ pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) - | |||
49 | /// Generate the assist for the fn def case | 52 | /// Generate the assist for the fn def case |
50 | fn generate_fn_def_assist( | 53 | fn generate_fn_def_assist( |
51 | acc: &mut Assists, | 54 | acc: &mut Assists, |
52 | fn_def: &ast::Fn, | 55 | fn_def: ast::Fn, |
53 | lifetime_loc: TextRange, | 56 | lifetime_loc: TextRange, |
57 | lifetime: ast::Lifetime, | ||
54 | ) -> Option<()> { | 58 | ) -> Option<()> { |
55 | let param_list: ast::ParamList = fn_def.param_list()?; | 59 | let param_list: ast::ParamList = fn_def.param_list()?; |
56 | let new_lifetime_param = generate_unique_lifetime_param_name(&fn_def.generic_param_list())?; | 60 | let new_lifetime_param = generate_unique_lifetime_param_name(fn_def.generic_param_list())?; |
57 | let end_of_fn_ident = fn_def.name()?.ident_token()?.text_range().end(); | ||
58 | let self_param = | 61 | let self_param = |
59 | // use the self if it's a reference and has no explicit lifetime | 62 | // use the self if it's a reference and has no explicit lifetime |
60 | param_list.self_param().filter(|p| p.lifetime().is_none() && p.amp_token().is_some()); | 63 | param_list.self_param().filter(|p| p.lifetime().is_none() && p.amp_token().is_some()); |
61 | // compute the location which implicitly has the same lifetime as the anonymous lifetime | 64 | // compute the location which implicitly has the same lifetime as the anonymous lifetime |
62 | let loc_needing_lifetime = if let Some(self_param) = self_param { | 65 | let loc_needing_lifetime = if let Some(self_param) = self_param { |
63 | // if we have a self reference, use that | 66 | // if we have a self reference, use that |
64 | Some(self_param.name()?.syntax().text_range().start()) | 67 | Some(NeedsLifetime::SelfParam(self_param)) |
65 | } else { | 68 | } else { |
66 | // otherwise, if there's a single reference parameter without a named liftime, use that | 69 | // otherwise, if there's a single reference parameter without a named liftime, use that |
67 | let fn_params_without_lifetime: Vec<_> = param_list | 70 | let fn_params_without_lifetime: Vec<_> = param_list |
68 | .params() | 71 | .params() |
69 | .filter_map(|param| match param.ty() { | 72 | .filter_map(|param| match param.ty() { |
70 | Some(ast::Type::RefType(ascribed_type)) if ascribed_type.lifetime().is_none() => { | 73 | Some(ast::Type::RefType(ascribed_type)) if ascribed_type.lifetime().is_none() => { |
71 | Some(ascribed_type.amp_token()?.text_range().end()) | 74 | Some(NeedsLifetime::RefType(ascribed_type)) |
72 | } | 75 | } |
73 | _ => None, | 76 | _ => None, |
74 | }) | 77 | }) |
@@ -81,30 +84,46 @@ fn generate_fn_def_assist( | |||
81 | } | 84 | } |
82 | }; | 85 | }; |
83 | acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { | 86 | acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { |
84 | add_lifetime_param(fn_def, builder, end_of_fn_ident, new_lifetime_param); | 87 | let fn_def = builder.make_ast_mut(fn_def); |
85 | builder.replace(lifetime_loc, format!("'{}", new_lifetime_param)); | 88 | let lifetime = builder.make_ast_mut(lifetime); |
86 | loc_needing_lifetime.map(|loc| builder.insert(loc, format!("'{} ", new_lifetime_param))); | 89 | let loc_needing_lifetime = |
90 | loc_needing_lifetime.and_then(|it| it.make_mut(builder).to_position()); | ||
91 | |||
92 | add_lifetime_param(fn_def.get_or_create_generic_param_list(), new_lifetime_param); | ||
93 | ted::replace( | ||
94 | lifetime.syntax(), | ||
95 | make_ast_lifetime(new_lifetime_param).clone_for_update().syntax(), | ||
96 | ); | ||
97 | loc_needing_lifetime.map(|position| { | ||
98 | ted::insert(position, make_ast_lifetime(new_lifetime_param).clone_for_update().syntax()) | ||
99 | }); | ||
87 | }) | 100 | }) |
88 | } | 101 | } |
89 | 102 | ||
90 | /// Generate the assist for the impl def case | 103 | /// Generate the assist for the impl def case |
91 | fn generate_impl_def_assist( | 104 | fn generate_impl_def_assist( |
92 | acc: &mut Assists, | 105 | acc: &mut Assists, |
93 | impl_def: &ast::Impl, | 106 | impl_def: ast::Impl, |
94 | lifetime_loc: TextRange, | 107 | lifetime_loc: TextRange, |
108 | lifetime: ast::Lifetime, | ||
95 | ) -> Option<()> { | 109 | ) -> Option<()> { |
96 | let new_lifetime_param = generate_unique_lifetime_param_name(&impl_def.generic_param_list())?; | 110 | let new_lifetime_param = generate_unique_lifetime_param_name(impl_def.generic_param_list())?; |
97 | let end_of_impl_kw = impl_def.impl_token()?.text_range().end(); | ||
98 | acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { | 111 | acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { |
99 | add_lifetime_param(impl_def, builder, end_of_impl_kw, new_lifetime_param); | 112 | let impl_def = builder.make_ast_mut(impl_def); |
100 | builder.replace(lifetime_loc, format!("'{}", new_lifetime_param)); | 113 | let lifetime = builder.make_ast_mut(lifetime); |
114 | |||
115 | add_lifetime_param(impl_def.get_or_create_generic_param_list(), new_lifetime_param); | ||
116 | ted::replace( | ||
117 | lifetime.syntax(), | ||
118 | make_ast_lifetime(new_lifetime_param).clone_for_update().syntax(), | ||
119 | ); | ||
101 | }) | 120 | }) |
102 | } | 121 | } |
103 | 122 | ||
104 | /// Given a type parameter list, generate a unique lifetime parameter name | 123 | /// Given a type parameter list, generate a unique lifetime parameter name |
105 | /// which is not in the list | 124 | /// which is not in the list |
106 | fn generate_unique_lifetime_param_name( | 125 | fn generate_unique_lifetime_param_name( |
107 | existing_type_param_list: &Option<ast::GenericParamList>, | 126 | existing_type_param_list: Option<ast::GenericParamList>, |
108 | ) -> Option<char> { | 127 | ) -> Option<char> { |
109 | match existing_type_param_list { | 128 | match existing_type_param_list { |
110 | Some(type_params) => { | 129 | Some(type_params) => { |
@@ -118,25 +137,37 @@ fn generate_unique_lifetime_param_name( | |||
118 | } | 137 | } |
119 | } | 138 | } |
120 | 139 | ||
121 | /// Add the lifetime param to `builder`. If there are type parameters in `type_params_owner`, add it to the end. Otherwise | 140 | fn add_lifetime_param(type_params: ast::GenericParamList, new_lifetime_param: char) { |
122 | /// add new type params brackets with the lifetime parameter at `new_type_params_loc`. | 141 | let generic_param = |
123 | fn add_lifetime_param<TypeParamsOwner: ast::GenericParamsOwner>( | 142 | make::generic_param(format!("'{}", new_lifetime_param), None).clone_for_update(); |
124 | type_params_owner: &TypeParamsOwner, | 143 | type_params.add_generic_param(generic_param); |
125 | builder: &mut AssistBuilder, | 144 | } |
126 | new_type_params_loc: TextSize, | 145 | |
127 | new_lifetime_param: char, | 146 | fn make_ast_lifetime(new_lifetime_param: char) -> ast::Lifetime { |
128 | ) { | 147 | make::generic_param(format!("'{}", new_lifetime_param), None) |
129 | match type_params_owner.generic_param_list() { | 148 | .syntax() |
130 | // add the new lifetime parameter to an existing type param list | 149 | .descendants() |
131 | Some(type_params) => { | 150 | .find_map(ast::Lifetime::cast) |
132 | builder.insert( | 151 | .unwrap() |
133 | (u32::from(type_params.syntax().text_range().end()) - 1).into(), | 152 | } |
134 | format!(", '{}", new_lifetime_param), | 153 | |
135 | ); | 154 | enum NeedsLifetime { |
155 | SelfParam(ast::SelfParam), | ||
156 | RefType(ast::RefType), | ||
157 | } | ||
158 | |||
159 | impl NeedsLifetime { | ||
160 | fn make_mut(self, builder: &mut AssistBuilder) -> Self { | ||
161 | match self { | ||
162 | Self::SelfParam(it) => Self::SelfParam(builder.make_ast_mut(it)), | ||
163 | Self::RefType(it) => Self::RefType(builder.make_ast_mut(it)), | ||
136 | } | 164 | } |
137 | // create a new type param list containing only the new lifetime parameter | 165 | } |
138 | None => { | 166 | |
139 | builder.insert(new_type_params_loc, format!("<'{}>", new_lifetime_param)); | 167 | fn to_position(self) -> Option<Position> { |
168 | match self { | ||
169 | Self::SelfParam(it) => Some(Position::after(it.amp_token()?)), | ||
170 | Self::RefType(it) => Some(Position::after(it.amp_token()?)), | ||
140 | } | 171 | } |
141 | } | 172 | } |
142 | } | 173 | } |
@@ -312,4 +343,13 @@ mod tests { | |||
312 | r#"fn my_fun<'other, 'a>(self, f: &'a Foo, b: &'other Bar) -> X<'a>"#, | 343 | r#"fn my_fun<'other, 'a>(self, f: &'a Foo, b: &'other Bar) -> X<'a>"#, |
313 | ); | 344 | ); |
314 | } | 345 | } |
346 | |||
347 | #[test] | ||
348 | fn test_function_add_lifetime_to_self_ref_mut() { | ||
349 | check_assist( | ||
350 | introduce_named_lifetime, | ||
351 | r#"fn foo(&mut self) -> &'_$0 ()"#, | ||
352 | r#"fn foo<'a>(&'a mut self) -> &'a ()"#, | ||
353 | ); | ||
354 | } | ||
315 | } | 355 | } |
diff --git a/crates/ide_assists/src/handlers/remove_dbg.rs b/crates/ide_assists/src/handlers/remove_dbg.rs index 6114091f2..c8226550f 100644 --- a/crates/ide_assists/src/handlers/remove_dbg.rs +++ b/crates/ide_assists/src/handlers/remove_dbg.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use syntax::{ | 1 | use syntax::{ |
2 | ast::{self, AstNode}, | 2 | ast::{self, AstNode, AstToken}, |
3 | match_ast, SyntaxElement, TextRange, TextSize, T, | 3 | match_ast, SyntaxElement, TextRange, TextSize, T, |
4 | }; | 4 | }; |
5 | 5 | ||
@@ -24,7 +24,39 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
24 | let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?; | 24 | let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?; |
25 | let new_contents = adjusted_macro_contents(¯o_call)?; | 25 | let new_contents = adjusted_macro_contents(¯o_call)?; |
26 | 26 | ||
27 | let macro_text_range = macro_call.syntax().text_range(); | 27 | let parent = macro_call.syntax().parent(); |
28 | |||
29 | let macro_text_range = if let Some(it) = parent.as_ref() { | ||
30 | if new_contents.is_empty() { | ||
31 | match_ast! { | ||
32 | match it { | ||
33 | ast::BlockExpr(_it) => { | ||
34 | macro_call.syntax() | ||
35 | .prev_sibling_or_token() | ||
36 | .and_then(whitespace_start) | ||
37 | .map(|start| TextRange::new(start, macro_call.syntax().text_range().end())) | ||
38 | .unwrap_or(macro_call.syntax().text_range()) | ||
39 | }, | ||
40 | ast::ExprStmt(it) => { | ||
41 | let start = it | ||
42 | .syntax() | ||
43 | .prev_sibling_or_token() | ||
44 | .and_then(whitespace_start) | ||
45 | .unwrap_or(it.syntax().text_range().start()); | ||
46 | let end = it.syntax().text_range().end(); | ||
47 | |||
48 | TextRange::new(start, end) | ||
49 | }, | ||
50 | _ => macro_call.syntax().text_range() | ||
51 | } | ||
52 | } | ||
53 | } else { | ||
54 | macro_call.syntax().text_range() | ||
55 | } | ||
56 | } else { | ||
57 | macro_call.syntax().text_range() | ||
58 | }; | ||
59 | |||
28 | let macro_end = if macro_call.semicolon_token().is_some() { | 60 | let macro_end = if macro_call.semicolon_token().is_some() { |
29 | macro_text_range.end() - TextSize::of(';') | 61 | macro_text_range.end() - TextSize::of(';') |
30 | } else { | 62 | } else { |
@@ -36,11 +68,22 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
36 | "Remove dbg!()", | 68 | "Remove dbg!()", |
37 | macro_text_range, | 69 | macro_text_range, |
38 | |builder| { | 70 | |builder| { |
39 | builder.replace(TextRange::new(macro_text_range.start(), macro_end), new_contents); | 71 | builder.replace( |
72 | TextRange::new(macro_text_range.start(), macro_end), | ||
73 | if new_contents.is_empty() && parent.and_then(ast::LetStmt::cast).is_some() { | ||
74 | ast::make::expr_unit().to_string() | ||
75 | } else { | ||
76 | new_contents | ||
77 | }, | ||
78 | ); | ||
40 | }, | 79 | }, |
41 | ) | 80 | ) |
42 | } | 81 | } |
43 | 82 | ||
83 | fn whitespace_start(it: SyntaxElement) -> Option<TextSize> { | ||
84 | Some(it.into_token().and_then(ast::Whitespace::cast)?.syntax().text_range().start()) | ||
85 | } | ||
86 | |||
44 | fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> { | 87 | fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> { |
45 | let contents = get_valid_macrocall_contents(¯o_call, "dbg")?; | 88 | let contents = get_valid_macrocall_contents(¯o_call, "dbg")?; |
46 | let macro_text_with_brackets = macro_call.token_tree()?.syntax().text(); | 89 | let macro_text_with_brackets = macro_call.token_tree()?.syntax().text(); |
@@ -94,15 +137,11 @@ fn get_valid_macrocall_contents( | |||
94 | let mut contents_between_brackets = children_with_tokens.collect::<Vec<_>>(); | 137 | let mut contents_between_brackets = children_with_tokens.collect::<Vec<_>>(); |
95 | let last_child = contents_between_brackets.pop()?; | 138 | let last_child = contents_between_brackets.pop()?; |
96 | 139 | ||
97 | if contents_between_brackets.is_empty() { | 140 | match (first_child.kind(), last_child.kind()) { |
98 | None | 141 | (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) => { |
99 | } else { | 142 | Some(contents_between_brackets) |
100 | match (first_child.kind(), last_child.kind()) { | ||
101 | (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) => { | ||
102 | Some(contents_between_brackets) | ||
103 | } | ||
104 | _ => None, | ||
105 | } | 143 | } |
144 | _ => None, | ||
106 | } | 145 | } |
107 | } | 146 | } |
108 | 147 | ||
@@ -418,4 +457,48 @@ fn main() { | |||
418 | }"#, | 457 | }"#, |
419 | ); | 458 | ); |
420 | } | 459 | } |
460 | |||
461 | #[test] | ||
462 | fn test_remove_empty_dbg() { | ||
463 | check_assist(remove_dbg, r#"fn foo() { $0dbg!(); }"#, r#"fn foo() { }"#); | ||
464 | check_assist( | ||
465 | remove_dbg, | ||
466 | r#" | ||
467 | fn foo() { | ||
468 | $0dbg!(); | ||
469 | } | ||
470 | "#, | ||
471 | r#" | ||
472 | fn foo() { | ||
473 | } | ||
474 | "#, | ||
475 | ); | ||
476 | check_assist( | ||
477 | remove_dbg, | ||
478 | r#" | ||
479 | fn foo() { | ||
480 | let test = $0dbg!(); | ||
481 | }"#, | ||
482 | r#" | ||
483 | fn foo() { | ||
484 | let test = (); | ||
485 | }"#, | ||
486 | ); | ||
487 | check_assist( | ||
488 | remove_dbg, | ||
489 | r#" | ||
490 | fn foo() { | ||
491 | let t = { | ||
492 | println!("Hello, world"); | ||
493 | $0dbg!() | ||
494 | }; | ||
495 | }"#, | ||
496 | r#" | ||
497 | fn foo() { | ||
498 | let t = { | ||
499 | println!("Hello, world"); | ||
500 | }; | ||
501 | }"#, | ||
502 | ); | ||
503 | } | ||
421 | } | 504 | } |
diff --git a/crates/ide_assists/src/handlers/reorder_fields.rs b/crates/ide_assists/src/handlers/reorder_fields.rs index 383ca6c47..1a95135ca 100644 --- a/crates/ide_assists/src/handlers/reorder_fields.rs +++ b/crates/ide_assists/src/handlers/reorder_fields.rs | |||
@@ -1,6 +1,8 @@ | |||
1 | use either::Either; | ||
2 | use itertools::Itertools; | ||
1 | use rustc_hash::FxHashMap; | 3 | use rustc_hash::FxHashMap; |
2 | 4 | ||
3 | use syntax::{algo, ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode}; | 5 | use syntax::{ast, ted, AstNode}; |
4 | 6 | ||
5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 7 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
6 | 8 | ||
@@ -22,60 +24,70 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; | |||
22 | pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 24 | pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
23 | let record = ctx | 25 | let record = ctx |
24 | .find_node_at_offset::<ast::RecordExpr>() | 26 | .find_node_at_offset::<ast::RecordExpr>() |
25 | .map(|it| it.syntax().clone()) | 27 | .map(Either::Left) |
26 | .or_else(|| ctx.find_node_at_offset::<ast::RecordPat>().map(|it| it.syntax().clone()))?; | 28 | .or_else(|| ctx.find_node_at_offset::<ast::RecordPat>().map(Either::Right))?; |
27 | |||
28 | let path = record.children().find_map(ast::Path::cast)?; | ||
29 | 29 | ||
30 | let path = record.as_ref().either(|it| it.path(), |it| it.path())?; | ||
30 | let ranks = compute_fields_ranks(&path, &ctx)?; | 31 | let ranks = compute_fields_ranks(&path, &ctx)?; |
32 | let get_rank_of_field = | ||
33 | |of: Option<_>| *ranks.get(&of.unwrap_or_default()).unwrap_or(&usize::MAX); | ||
31 | 34 | ||
32 | let fields: Vec<SyntaxNode> = { | 35 | let field_list = match &record { |
33 | let field_kind = match record.kind() { | 36 | Either::Left(it) => Either::Left(it.record_expr_field_list()?), |
34 | RECORD_EXPR => RECORD_EXPR_FIELD, | 37 | Either::Right(it) => Either::Right(it.record_pat_field_list()?), |
35 | RECORD_PAT => RECORD_PAT_FIELD, | ||
36 | _ => { | ||
37 | stdx::never!(); | ||
38 | return None; | ||
39 | } | ||
40 | }; | ||
41 | record.children().flat_map(|n| n.children()).filter(|n| n.kind() == field_kind).collect() | ||
42 | }; | 38 | }; |
43 | 39 | let fields = match field_list { | |
44 | let sorted_fields = { | 40 | Either::Left(it) => Either::Left(( |
45 | let mut fields = fields.clone(); | 41 | it.fields() |
46 | fields.sort_by_key(|node| *ranks.get(&get_field_name(node)).unwrap_or(&usize::max_value())); | 42 | .sorted_unstable_by_key(|field| { |
47 | fields | 43 | get_rank_of_field(field.field_name().map(|it| it.to_string())) |
44 | }) | ||
45 | .collect::<Vec<_>>(), | ||
46 | it, | ||
47 | )), | ||
48 | Either::Right(it) => Either::Right(( | ||
49 | it.fields() | ||
50 | .sorted_unstable_by_key(|field| { | ||
51 | get_rank_of_field(field.field_name().map(|it| it.to_string())) | ||
52 | }) | ||
53 | .collect::<Vec<_>>(), | ||
54 | it, | ||
55 | )), | ||
48 | }; | 56 | }; |
49 | 57 | ||
50 | if sorted_fields == fields { | 58 | let is_sorted = fields.as_ref().either( |
59 | |(sorted, field_list)| field_list.fields().zip(sorted).all(|(a, b)| a == *b), | ||
60 | |(sorted, field_list)| field_list.fields().zip(sorted).all(|(a, b)| a == *b), | ||
61 | ); | ||
62 | if is_sorted { | ||
51 | cov_mark::hit!(reorder_sorted_fields); | 63 | cov_mark::hit!(reorder_sorted_fields); |
52 | return None; | 64 | return None; |
53 | } | 65 | } |
54 | 66 | let target = record.as_ref().either(AstNode::syntax, AstNode::syntax).text_range(); | |
55 | let target = record.text_range(); | ||
56 | acc.add( | 67 | acc.add( |
57 | AssistId("reorder_fields", AssistKind::RefactorRewrite), | 68 | AssistId("reorder_fields", AssistKind::RefactorRewrite), |
58 | "Reorder record fields", | 69 | "Reorder record fields", |
59 | target, | 70 | target, |
60 | |edit| { | 71 | |builder| match fields { |
61 | let mut rewriter = algo::SyntaxRewriter::default(); | 72 | Either::Left((sorted, field_list)) => { |
62 | for (old, new) in fields.iter().zip(&sorted_fields) { | 73 | replace(builder.make_ast_mut(field_list).fields(), sorted) |
63 | rewriter.replace(old, new); | 74 | } |
75 | Either::Right((sorted, field_list)) => { | ||
76 | replace(builder.make_ast_mut(field_list).fields(), sorted) | ||
64 | } | 77 | } |
65 | edit.rewrite(rewriter); | ||
66 | }, | 78 | }, |
67 | ) | 79 | ) |
68 | } | 80 | } |
69 | 81 | ||
70 | fn get_field_name(node: &SyntaxNode) -> String { | 82 | fn replace<T: AstNode + PartialEq>( |
71 | let res = match_ast! { | 83 | fields: impl Iterator<Item = T>, |
72 | match node { | 84 | sorted_fields: impl IntoIterator<Item = T>, |
73 | ast::RecordExprField(field) => field.field_name().map(|it| it.to_string()), | 85 | ) { |
74 | ast::RecordPatField(field) => field.field_name().map(|it| it.to_string()), | 86 | fields.zip(sorted_fields).filter(|(field, sorted)| field != sorted).for_each( |
75 | _ => None, | 87 | |(field, sorted_field)| { |
76 | } | 88 | ted::replace(field.syntax(), sorted_field.syntax().clone_for_update()); |
77 | }; | 89 | }, |
78 | res.unwrap_or_default() | 90 | ); |
79 | } | 91 | } |
80 | 92 | ||
81 | fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> { | 93 | fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> { |
@@ -86,7 +98,7 @@ fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashM | |||
86 | 98 | ||
87 | let res = strukt | 99 | let res = strukt |
88 | .fields(ctx.db()) | 100 | .fields(ctx.db()) |
89 | .iter() | 101 | .into_iter() |
90 | .enumerate() | 102 | .enumerate() |
91 | .map(|(idx, field)| (field.name(ctx.db()).to_string(), idx)) | 103 | .map(|(idx, field)| (field.name(ctx.db()).to_string(), idx)) |
92 | .collect(); | 104 | .collect(); |
@@ -137,7 +149,6 @@ const test: Foo = Foo { foo: 1, bar: 0 }; | |||
137 | "#, | 149 | "#, |
138 | ) | 150 | ) |
139 | } | 151 | } |
140 | |||
141 | #[test] | 152 | #[test] |
142 | fn reorder_struct_pattern() { | 153 | fn reorder_struct_pattern() { |
143 | check_assist( | 154 | check_assist( |
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 f872d20c8..694d897d1 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 | |||
@@ -5,7 +5,6 @@ use itertools::Itertools; | |||
5 | use syntax::{ | 5 | use syntax::{ |
6 | ast::{self, make, AstNode, NameOwner}, | 6 | ast::{self, make, AstNode, NameOwner}, |
7 | SyntaxKind::{IDENT, WHITESPACE}, | 7 | SyntaxKind::{IDENT, WHITESPACE}, |
8 | TextSize, | ||
9 | }; | 8 | }; |
10 | 9 | ||
11 | use crate::{ | 10 | use crate::{ |
@@ -43,32 +42,28 @@ pub(crate) fn replace_derive_with_manual_impl( | |||
43 | ctx: &AssistContext, | 42 | ctx: &AssistContext, |
44 | ) -> Option<()> { | 43 | ) -> Option<()> { |
45 | let attr = ctx.find_node_at_offset::<ast::Attr>()?; | 44 | let attr = ctx.find_node_at_offset::<ast::Attr>()?; |
45 | let (name, args) = attr.as_simple_call()?; | ||
46 | if name != "derive" { | ||
47 | return None; | ||
48 | } | ||
46 | 49 | ||
47 | let has_derive = attr | 50 | if !args.syntax().text_range().contains(ctx.offset()) { |
48 | .syntax() | 51 | cov_mark::hit!(outside_of_attr_args); |
49 | .descendants_with_tokens() | ||
50 | .filter(|t| t.kind() == IDENT) | ||
51 | .find_map(syntax::NodeOrToken::into_token) | ||
52 | .filter(|t| t.text() == "derive") | ||
53 | .is_some(); | ||
54 | if !has_derive { | ||
55 | return None; | 52 | return None; |
56 | } | 53 | } |
57 | 54 | ||
58 | let trait_token = ctx.token_at_offset().find(|t| t.kind() == IDENT && t.text() != "derive")?; | 55 | let trait_token = args.syntax().token_at_offset(ctx.offset()).find(|t| t.kind() == IDENT)?; |
59 | let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text()))); | 56 | let trait_name = trait_token.text(); |
60 | 57 | ||
61 | let adt = attr.syntax().parent().and_then(ast::Adt::cast)?; | 58 | let adt = attr.syntax().parent().and_then(ast::Adt::cast)?; |
62 | let annotated_name = adt.name()?; | ||
63 | let insert_pos = adt.syntax().text_range().end(); | ||
64 | 59 | ||
65 | let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; | 60 | let current_module = ctx.sema.scope(adt.syntax()).module()?; |
66 | let current_crate = current_module.krate(); | 61 | let current_crate = current_module.krate(); |
67 | 62 | ||
68 | let found_traits = items_locator::items_with_name( | 63 | let found_traits = items_locator::items_with_name( |
69 | &ctx.sema, | 64 | &ctx.sema, |
70 | current_crate, | 65 | current_crate, |
71 | NameToImport::Exact(trait_token.text().to_string()), | 66 | NameToImport::Exact(trait_name.to_string()), |
72 | items_locator::AssocItemSearch::Exclude, | 67 | items_locator::AssocItemSearch::Exclude, |
73 | Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT), | 68 | Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT), |
74 | ) | 69 | ) |
@@ -86,10 +81,11 @@ pub(crate) fn replace_derive_with_manual_impl( | |||
86 | 81 | ||
87 | let mut no_traits_found = true; | 82 | let mut no_traits_found = true; |
88 | for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { | 83 | for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { |
89 | add_assist(acc, ctx, &attr, &trait_path, Some(trait_), &adt, &annotated_name, insert_pos)?; | 84 | add_assist(acc, ctx, &attr, &args, &trait_path, Some(trait_), &adt)?; |
90 | } | 85 | } |
91 | if no_traits_found { | 86 | if no_traits_found { |
92 | add_assist(acc, ctx, &attr, &trait_path, None, &adt, &annotated_name, insert_pos)?; | 87 | let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_name))); |
88 | add_assist(acc, ctx, &attr, &args, &trait_path, None, &adt)?; | ||
93 | } | 89 | } |
94 | Some(()) | 90 | Some(()) |
95 | } | 91 | } |
@@ -98,15 +94,14 @@ fn add_assist( | |||
98 | acc: &mut Assists, | 94 | acc: &mut Assists, |
99 | ctx: &AssistContext, | 95 | ctx: &AssistContext, |
100 | attr: &ast::Attr, | 96 | attr: &ast::Attr, |
97 | input: &ast::TokenTree, | ||
101 | trait_path: &ast::Path, | 98 | trait_path: &ast::Path, |
102 | trait_: Option<hir::Trait>, | 99 | trait_: Option<hir::Trait>, |
103 | adt: &ast::Adt, | 100 | adt: &ast::Adt, |
104 | annotated_name: &ast::Name, | ||
105 | insert_pos: TextSize, | ||
106 | ) -> Option<()> { | 101 | ) -> Option<()> { |
107 | let target = attr.syntax().text_range(); | 102 | let target = attr.syntax().text_range(); |
108 | let input = attr.token_tree()?; | 103 | let annotated_name = adt.name()?; |
109 | let label = format!("Convert to manual `impl {} for {}`", trait_path, annotated_name); | 104 | let label = format!("Convert to manual `impl {} for {}`", trait_path, annotated_name); |
110 | let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?; | 105 | let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?; |
111 | 106 | ||
112 | acc.add( | 107 | acc.add( |
@@ -114,8 +109,9 @@ fn add_assist( | |||
114 | label, | 109 | label, |
115 | target, | 110 | target, |
116 | |builder| { | 111 | |builder| { |
112 | let insert_pos = adt.syntax().text_range().end(); | ||
117 | let impl_def_with_items = | 113 | let impl_def_with_items = |
118 | impl_def_from_trait(&ctx.sema, annotated_name, trait_, trait_path); | 114 | impl_def_from_trait(&ctx.sema, &annotated_name, trait_, trait_path); |
119 | update_attribute(builder, &input, &trait_name, &attr); | 115 | update_attribute(builder, &input, &trait_name, &attr); |
120 | let trait_path = format!("{}", trait_path); | 116 | let trait_path = format!("{}", trait_path); |
121 | match (ctx.config.snippet_cap, impl_def_with_items) { | 117 | match (ctx.config.snippet_cap, impl_def_with_items) { |
@@ -216,7 +212,7 @@ mod tests { | |||
216 | fn add_custom_impl_debug() { | 212 | fn add_custom_impl_debug() { |
217 | check_assist( | 213 | check_assist( |
218 | replace_derive_with_manual_impl, | 214 | replace_derive_with_manual_impl, |
219 | " | 215 | r#" |
220 | mod fmt { | 216 | mod fmt { |
221 | pub struct Error; | 217 | pub struct Error; |
222 | pub type Result = Result<(), Error>; | 218 | pub type Result = Result<(), Error>; |
@@ -230,8 +226,8 @@ mod fmt { | |||
230 | struct Foo { | 226 | struct Foo { |
231 | bar: String, | 227 | bar: String, |
232 | } | 228 | } |
233 | ", | 229 | "#, |
234 | " | 230 | r#" |
235 | mod fmt { | 231 | mod fmt { |
236 | pub struct Error; | 232 | pub struct Error; |
237 | pub type Result = Result<(), Error>; | 233 | pub type Result = Result<(), Error>; |
@@ -250,14 +246,14 @@ impl fmt::Debug for Foo { | |||
250 | ${0:todo!()} | 246 | ${0:todo!()} |
251 | } | 247 | } |
252 | } | 248 | } |
253 | ", | 249 | "#, |
254 | ) | 250 | ) |
255 | } | 251 | } |
256 | #[test] | 252 | #[test] |
257 | fn add_custom_impl_all() { | 253 | fn add_custom_impl_all() { |
258 | check_assist( | 254 | check_assist( |
259 | replace_derive_with_manual_impl, | 255 | replace_derive_with_manual_impl, |
260 | " | 256 | r#" |
261 | mod foo { | 257 | mod foo { |
262 | pub trait Bar { | 258 | pub trait Bar { |
263 | type Qux; | 259 | type Qux; |
@@ -272,8 +268,8 @@ mod foo { | |||
272 | struct Foo { | 268 | struct Foo { |
273 | bar: String, | 269 | bar: String, |
274 | } | 270 | } |
275 | ", | 271 | "#, |
276 | " | 272 | r#" |
277 | mod foo { | 273 | mod foo { |
278 | pub trait Bar { | 274 | pub trait Bar { |
279 | type Qux; | 275 | type Qux; |
@@ -299,20 +295,20 @@ impl foo::Bar for Foo { | |||
299 | todo!() | 295 | todo!() |
300 | } | 296 | } |
301 | } | 297 | } |
302 | ", | 298 | "#, |
303 | ) | 299 | ) |
304 | } | 300 | } |
305 | #[test] | 301 | #[test] |
306 | fn add_custom_impl_for_unique_input() { | 302 | fn add_custom_impl_for_unique_input() { |
307 | check_assist( | 303 | check_assist( |
308 | replace_derive_with_manual_impl, | 304 | replace_derive_with_manual_impl, |
309 | " | 305 | r#" |
310 | #[derive(Debu$0g)] | 306 | #[derive(Debu$0g)] |
311 | struct Foo { | 307 | struct Foo { |
312 | bar: String, | 308 | bar: String, |
313 | } | 309 | } |
314 | ", | 310 | "#, |
315 | " | 311 | r#" |
316 | struct Foo { | 312 | struct Foo { |
317 | bar: String, | 313 | bar: String, |
318 | } | 314 | } |
@@ -320,7 +316,7 @@ struct Foo { | |||
320 | impl Debug for Foo { | 316 | impl Debug for Foo { |
321 | $0 | 317 | $0 |
322 | } | 318 | } |
323 | ", | 319 | "#, |
324 | ) | 320 | ) |
325 | } | 321 | } |
326 | 322 | ||
@@ -328,13 +324,13 @@ impl Debug for Foo { | |||
328 | fn add_custom_impl_for_with_visibility_modifier() { | 324 | fn add_custom_impl_for_with_visibility_modifier() { |
329 | check_assist( | 325 | check_assist( |
330 | replace_derive_with_manual_impl, | 326 | replace_derive_with_manual_impl, |
331 | " | 327 | r#" |
332 | #[derive(Debug$0)] | 328 | #[derive(Debug$0)] |
333 | pub struct Foo { | 329 | pub struct Foo { |
334 | bar: String, | 330 | bar: String, |
335 | } | 331 | } |
336 | ", | 332 | "#, |
337 | " | 333 | r#" |
338 | pub struct Foo { | 334 | pub struct Foo { |
339 | bar: String, | 335 | bar: String, |
340 | } | 336 | } |
@@ -342,7 +338,7 @@ pub struct Foo { | |||
342 | impl Debug for Foo { | 338 | impl Debug for Foo { |
343 | $0 | 339 | $0 |
344 | } | 340 | } |
345 | ", | 341 | "#, |
346 | ) | 342 | ) |
347 | } | 343 | } |
348 | 344 | ||
@@ -350,18 +346,18 @@ impl Debug for Foo { | |||
350 | fn add_custom_impl_when_multiple_inputs() { | 346 | fn add_custom_impl_when_multiple_inputs() { |
351 | check_assist( | 347 | check_assist( |
352 | replace_derive_with_manual_impl, | 348 | replace_derive_with_manual_impl, |
353 | " | 349 | r#" |
354 | #[derive(Display, Debug$0, Serialize)] | 350 | #[derive(Display, Debug$0, Serialize)] |
355 | struct Foo {} | 351 | struct Foo {} |
356 | ", | 352 | "#, |
357 | " | 353 | r#" |
358 | #[derive(Display, Serialize)] | 354 | #[derive(Display, Serialize)] |
359 | struct Foo {} | 355 | struct Foo {} |
360 | 356 | ||
361 | impl Debug for Foo { | 357 | impl Debug for Foo { |
362 | $0 | 358 | $0 |
363 | } | 359 | } |
364 | ", | 360 | "#, |
365 | ) | 361 | ) |
366 | } | 362 | } |
367 | 363 | ||
@@ -369,10 +365,10 @@ impl Debug for Foo { | |||
369 | fn test_ignore_derive_macro_without_input() { | 365 | fn test_ignore_derive_macro_without_input() { |
370 | check_assist_not_applicable( | 366 | check_assist_not_applicable( |
371 | replace_derive_with_manual_impl, | 367 | replace_derive_with_manual_impl, |
372 | " | 368 | r#" |
373 | #[derive($0)] | 369 | #[derive($0)] |
374 | struct Foo {} | 370 | struct Foo {} |
375 | ", | 371 | "#, |
376 | ) | 372 | ) |
377 | } | 373 | } |
378 | 374 | ||
@@ -380,18 +376,18 @@ struct Foo {} | |||
380 | fn test_ignore_if_cursor_on_param() { | 376 | fn test_ignore_if_cursor_on_param() { |
381 | check_assist_not_applicable( | 377 | check_assist_not_applicable( |
382 | replace_derive_with_manual_impl, | 378 | replace_derive_with_manual_impl, |
383 | " | 379 | r#" |
384 | #[derive$0(Debug)] | 380 | #[derive$0(Debug)] |
385 | struct Foo {} | 381 | struct Foo {} |
386 | ", | 382 | "#, |
387 | ); | 383 | ); |
388 | 384 | ||
389 | check_assist_not_applicable( | 385 | check_assist_not_applicable( |
390 | replace_derive_with_manual_impl, | 386 | replace_derive_with_manual_impl, |
391 | " | 387 | r#" |
392 | #[derive(Debug)$0] | 388 | #[derive(Debug)$0] |
393 | struct Foo {} | 389 | struct Foo {} |
394 | ", | 390 | "#, |
395 | ) | 391 | ) |
396 | } | 392 | } |
397 | 393 | ||
@@ -399,10 +395,22 @@ struct Foo {} | |||
399 | fn test_ignore_if_not_derive() { | 395 | fn test_ignore_if_not_derive() { |
400 | check_assist_not_applicable( | 396 | check_assist_not_applicable( |
401 | replace_derive_with_manual_impl, | 397 | replace_derive_with_manual_impl, |
402 | " | 398 | r#" |
403 | #[allow(non_camel_$0case_types)] | 399 | #[allow(non_camel_$0case_types)] |
404 | struct Foo {} | 400 | struct Foo {} |
405 | ", | 401 | "#, |
406 | ) | 402 | ) |
407 | } | 403 | } |
404 | |||
405 | #[test] | ||
406 | fn works_at_start_of_file() { | ||
407 | cov_mark::check!(outside_of_attr_args); | ||
408 | check_assist_not_applicable( | ||
409 | replace_derive_with_manual_impl, | ||
410 | r#" | ||
411 | $0#[derive(Debug)] | ||
412 | struct S; | ||
413 | "#, | ||
414 | ); | ||
415 | } | ||
408 | } | 416 | } |
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index 1c55b9fbf..88ae5c9a9 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs | |||
@@ -28,7 +28,9 @@ pub use assist_config::AssistConfig; | |||
28 | 28 | ||
29 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 29 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
30 | pub enum AssistKind { | 30 | pub enum AssistKind { |
31 | // FIXME: does the None variant make sense? Probably not. | ||
31 | None, | 32 | None, |
33 | |||
32 | QuickFix, | 34 | QuickFix, |
33 | Generate, | 35 | Generate, |
34 | Refactor, | 36 | Refactor, |
@@ -133,6 +135,7 @@ mod handlers { | |||
133 | mod generate_default_from_enum_variant; | 135 | mod generate_default_from_enum_variant; |
134 | mod generate_default_from_new; | 136 | mod generate_default_from_new; |
135 | mod generate_is_empty_from_len; | 137 | mod generate_is_empty_from_len; |
138 | mod generate_deref; | ||
136 | mod generate_derive; | 139 | mod generate_derive; |
137 | mod generate_enum_is_method; | 140 | mod generate_enum_is_method; |
138 | mod generate_enum_projection_method; | 141 | mod generate_enum_projection_method; |
@@ -201,6 +204,7 @@ mod handlers { | |||
201 | generate_default_from_enum_variant::generate_default_from_enum_variant, | 204 | generate_default_from_enum_variant::generate_default_from_enum_variant, |
202 | generate_default_from_new::generate_default_from_new, | 205 | generate_default_from_new::generate_default_from_new, |
203 | generate_is_empty_from_len::generate_is_empty_from_len, | 206 | generate_is_empty_from_len::generate_is_empty_from_len, |
207 | generate_deref::generate_deref, | ||
204 | generate_derive::generate_derive, | 208 | generate_derive::generate_derive, |
205 | generate_enum_is_method::generate_enum_is_method, | 209 | generate_enum_is_method::generate_enum_is_method, |
206 | generate_enum_projection_method::generate_enum_as_method, | 210 | generate_enum_projection_method::generate_enum_as_method, |
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs index a7a923beb..49533e7d2 100644 --- a/crates/ide_assists/src/tests.rs +++ b/crates/ide_assists/src/tests.rs | |||
@@ -84,7 +84,8 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) { | |||
84 | }); | 84 | }); |
85 | 85 | ||
86 | let actual = { | 86 | let actual = { |
87 | let source_change = assist.source_change.unwrap(); | 87 | let source_change = |
88 | assist.source_change.expect("Assist did not contain any source changes"); | ||
88 | let mut actual = before; | 89 | let mut actual = before; |
89 | if let Some(source_file_edit) = source_change.get_source_edit(file_id) { | 90 | if let Some(source_file_edit) = source_change.get_source_edit(file_id) { |
90 | source_file_edit.apply(&mut actual); | 91 | source_file_edit.apply(&mut actual); |
@@ -121,7 +122,8 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label: | |||
121 | 122 | ||
122 | match (assist, expected) { | 123 | match (assist, expected) { |
123 | (Some(assist), ExpectedResult::After(after)) => { | 124 | (Some(assist), ExpectedResult::After(after)) => { |
124 | let source_change = assist.source_change.unwrap(); | 125 | let source_change = |
126 | assist.source_change.expect("Assist did not contain any source changes"); | ||
125 | assert!(!source_change.source_file_edits.is_empty()); | 127 | assert!(!source_change.source_file_edits.is_empty()); |
126 | let skip_header = source_change.source_file_edits.len() == 1 | 128 | let skip_header = source_change.source_file_edits.len() == 1 |
127 | && source_change.file_system_edits.len() == 0; | 129 | && source_change.file_system_edits.len() == 0; |
@@ -191,6 +193,7 @@ fn assist_order_field_struct() { | |||
191 | let mut assists = assists.iter(); | 193 | let mut assists = assists.iter(); |
192 | 194 | ||
193 | assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); | 195 | assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); |
196 | assert_eq!(assists.next().expect("expected assist").label, "Generate `Deref` impl using `bar`"); | ||
194 | assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method"); | 197 | assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method"); |
195 | assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method"); | 198 | assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method"); |
196 | assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method"); | 199 | assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method"); |
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index f4a4749c8..59bcef8fb 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs | |||
@@ -593,6 +593,33 @@ impl Default for Example { | |||
593 | } | 593 | } |
594 | 594 | ||
595 | #[test] | 595 | #[test] |
596 | fn doctest_generate_deref() { | ||
597 | check_doc_test( | ||
598 | "generate_deref", | ||
599 | r#####" | ||
600 | struct A; | ||
601 | struct B { | ||
602 | $0a: A | ||
603 | } | ||
604 | "#####, | ||
605 | r#####" | ||
606 | struct A; | ||
607 | struct B { | ||
608 | a: A | ||
609 | } | ||
610 | |||
611 | impl std::ops::Deref for B { | ||
612 | type Target = A; | ||
613 | |||
614 | fn deref(&self) -> &Self::Target { | ||
615 | &self.a | ||
616 | } | ||
617 | } | ||
618 | "#####, | ||
619 | ) | ||
620 | } | ||
621 | |||
622 | #[test] | ||
596 | fn doctest_generate_derive() { | 623 | fn doctest_generate_derive() { |
597 | check_doc_test( | 624 | check_doc_test( |
598 | "generate_derive", | 625 | "generate_derive", |
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs index 1ad017198..8e211ae1e 100644 --- a/crates/ide_completion/src/completions/flyimport.rs +++ b/crates/ide_completion/src/completions/flyimport.rs | |||
@@ -113,6 +113,9 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) | |||
113 | if ctx.use_item_syntax.is_some() | 113 | if ctx.use_item_syntax.is_some() |
114 | || ctx.attribute_under_caret.is_some() | 114 | || ctx.attribute_under_caret.is_some() |
115 | || ctx.mod_declaration_under_caret.is_some() | 115 | || ctx.mod_declaration_under_caret.is_some() |
116 | || ctx.record_lit_syntax.is_some() | ||
117 | || ctx.has_trait_parent | ||
118 | || ctx.has_impl_parent | ||
116 | { | 119 | { |
117 | return None; | 120 | return None; |
118 | } | 121 | } |
@@ -1034,4 +1037,117 @@ fn main() { | |||
1034 | expect![[]], | 1037 | expect![[]], |
1035 | ); | 1038 | ); |
1036 | } | 1039 | } |
1040 | |||
1041 | #[test] | ||
1042 | fn no_fuzzy_during_fields_of_record_lit_syntax() { | ||
1043 | check( | ||
1044 | r#" | ||
1045 | mod m { | ||
1046 | pub fn some_fn() -> i32 { | ||
1047 | 42 | ||
1048 | } | ||
1049 | } | ||
1050 | struct Foo { | ||
1051 | some_field: i32, | ||
1052 | } | ||
1053 | fn main() { | ||
1054 | let _ = Foo { so$0 }; | ||
1055 | } | ||
1056 | "#, | ||
1057 | expect![[]], | ||
1058 | ); | ||
1059 | } | ||
1060 | |||
1061 | #[test] | ||
1062 | fn fuzzy_after_fields_of_record_lit_syntax() { | ||
1063 | check( | ||
1064 | r#" | ||
1065 | mod m { | ||
1066 | pub fn some_fn() -> i32 { | ||
1067 | 42 | ||
1068 | } | ||
1069 | } | ||
1070 | struct Foo { | ||
1071 | some_field: i32, | ||
1072 | } | ||
1073 | fn main() { | ||
1074 | let _ = Foo { some_field: so$0 }; | ||
1075 | } | ||
1076 | "#, | ||
1077 | expect![[r#" | ||
1078 | fn some_fn() (m::some_fn) fn() -> i32 | ||
1079 | "#]], | ||
1080 | ); | ||
1081 | } | ||
1082 | |||
1083 | #[test] | ||
1084 | fn no_flyimports_in_traits_and_impl_declarations() { | ||
1085 | check( | ||
1086 | r#" | ||
1087 | mod m { | ||
1088 | pub fn some_fn() -> i32 { | ||
1089 | 42 | ||
1090 | } | ||
1091 | } | ||
1092 | trait Foo { | ||
1093 | som$0 | ||
1094 | } | ||
1095 | "#, | ||
1096 | expect![[r#""#]], | ||
1097 | ); | ||
1098 | |||
1099 | check( | ||
1100 | r#" | ||
1101 | mod m { | ||
1102 | pub fn some_fn() -> i32 { | ||
1103 | 42 | ||
1104 | } | ||
1105 | } | ||
1106 | struct Foo; | ||
1107 | impl Foo { | ||
1108 | som$0 | ||
1109 | } | ||
1110 | "#, | ||
1111 | expect![[r#""#]], | ||
1112 | ); | ||
1113 | |||
1114 | check( | ||
1115 | r#" | ||
1116 | mod m { | ||
1117 | pub fn some_fn() -> i32 { | ||
1118 | 42 | ||
1119 | } | ||
1120 | } | ||
1121 | struct Foo; | ||
1122 | trait Bar {} | ||
1123 | impl Bar for Foo { | ||
1124 | som$0 | ||
1125 | } | ||
1126 | "#, | ||
1127 | expect![[r#""#]], | ||
1128 | ); | ||
1129 | } | ||
1130 | |||
1131 | #[test] | ||
1132 | fn no_inherent_candidates_proposed() { | ||
1133 | check( | ||
1134 | r#" | ||
1135 | mod baz { | ||
1136 | pub trait DefDatabase { | ||
1137 | fn method1(&self); | ||
1138 | } | ||
1139 | pub trait HirDatabase: DefDatabase { | ||
1140 | fn method2(&self); | ||
1141 | } | ||
1142 | } | ||
1143 | |||
1144 | mod bar { | ||
1145 | fn test(db: &dyn crate::baz::HirDatabase) { | ||
1146 | db.metho$0 | ||
1147 | } | ||
1148 | } | ||
1149 | "#, | ||
1150 | expect![[r#""#]], | ||
1151 | ); | ||
1152 | } | ||
1037 | } | 1153 | } |
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs index cc4ac9ea2..16991b688 100644 --- a/crates/ide_completion/src/item.rs +++ b/crates/ide_completion/src/item.rs | |||
@@ -29,7 +29,7 @@ pub struct CompletionItem { | |||
29 | /// Range of identifier that is being completed. | 29 | /// Range of identifier that is being completed. |
30 | /// | 30 | /// |
31 | /// It should be used primarily for UI, but we also use this to convert | 31 | /// It should be used primarily for UI, but we also use this to convert |
32 | /// genetic TextEdit into LSP's completion edit (see conv.rs). | 32 | /// generic TextEdit into LSP's completion edit (see conv.rs). |
33 | /// | 33 | /// |
34 | /// `source_range` must contain the completion offset. `insert_text` should | 34 | /// `source_range` must contain the completion offset. `insert_text` should |
35 | /// start with what `source_range` points to, or VSCode will filter out the | 35 | /// start with what `source_range` points to, or VSCode will filter out the |
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs index 831d543bb..6f3d5c5c5 100644 --- a/crates/ide_completion/src/lib.rs +++ b/crates/ide_completion/src/lib.rs | |||
@@ -106,6 +106,34 @@ pub use crate::{ | |||
106 | /// `foo` *should* be present among the completion variants. Filtering by | 106 | /// `foo` *should* be present among the completion variants. Filtering by |
107 | /// identifier prefix/fuzzy match should be done higher in the stack, together | 107 | /// identifier prefix/fuzzy match should be done higher in the stack, together |
108 | /// with ordering of completions (currently this is done by the client). | 108 | /// with ordering of completions (currently this is done by the client). |
109 | /// | ||
110 | /// # Hypothetical Completion Problem | ||
111 | /// | ||
112 | /// There's a curious unsolved problem in the current implementation. Often, you | ||
113 | /// want to compute completions on a *slightly different* text document. | ||
114 | /// | ||
115 | /// In the simplest case, when the code looks like `let x = `, you want to | ||
116 | /// insert a fake identifier to get a better syntax tree: `let x = complete_me`. | ||
117 | /// | ||
118 | /// We do this in `CompletionContext`, and it works OK-enough for *syntax* | ||
119 | /// analysis. However, we might want to, eg, ask for the type of `complete_me` | ||
120 | /// variable, and that's where our current infrastructure breaks down. salsa | ||
121 | /// doesn't allow such "phantom" inputs. | ||
122 | /// | ||
123 | /// Another case where this would be instrumental is macro expansion. We want to | ||
124 | /// insert a fake ident and re-expand code. There's `expand_hypothetical` as a | ||
125 | /// work-around for this. | ||
126 | /// | ||
127 | /// A different use-case is completion of injection (examples and links in doc | ||
128 | /// comments). When computing completion for a path in a doc-comment, you want | ||
129 | /// to inject a fake path expression into the item being documented and complete | ||
130 | /// that. | ||
131 | /// | ||
132 | /// IntelliJ has CodeFragment/Context infrastructure for that. You can create a | ||
133 | /// temporary PSI node, and say that the context ("parent") of this node is some | ||
134 | /// existing node. Asking for, eg, type of this `CodeFragment` node works | ||
135 | /// correctly, as the underlying infrastructure makes use of contexts to do | ||
136 | /// analysis. | ||
109 | pub fn completions( | 137 | pub fn completions( |
110 | db: &RootDatabase, | 138 | db: &RootDatabase, |
111 | config: &CompletionConfig, | 139 | config: &CompletionConfig, |
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs index 111e9325a..eac5ef6b9 100644 --- a/crates/ide_db/src/apply_change.rs +++ b/crates/ide_db/src/apply_change.rs | |||
@@ -152,6 +152,10 @@ impl RootDatabase { | |||
152 | hir::db::FileItemTreeQuery | 152 | hir::db::FileItemTreeQuery |
153 | hir::db::BlockDefMapQuery | 153 | hir::db::BlockDefMapQuery |
154 | hir::db::CrateDefMapQueryQuery | 154 | hir::db::CrateDefMapQueryQuery |
155 | hir::db::FieldsAttrsQuery | ||
156 | hir::db::VariantsAttrsQuery | ||
157 | hir::db::FieldsAttrsSourceMapQuery | ||
158 | hir::db::VariantsAttrsSourceMapQuery | ||
155 | hir::db::StructDataQuery | 159 | hir::db::StructDataQuery |
156 | hir::db::UnionDataQuery | 160 | hir::db::UnionDataQuery |
157 | hir::db::EnumDataQuery | 161 | hir::db::EnumDataQuery |
@@ -197,7 +201,7 @@ impl RootDatabase { | |||
197 | hir::db::InternImplTraitIdQuery | 201 | hir::db::InternImplTraitIdQuery |
198 | hir::db::InternClosureQuery | 202 | hir::db::InternClosureQuery |
199 | hir::db::AssociatedTyValueQuery | 203 | hir::db::AssociatedTyValueQuery |
200 | hir::db::TraitSolveQuery | 204 | hir::db::TraitSolveQueryQuery |
201 | 205 | ||
202 | // SymbolsDatabase | 206 | // SymbolsDatabase |
203 | crate::symbol_index::FileSymbolsQuery | 207 | crate::symbol_index::FileSymbolsQuery |
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs index e583a52f4..bad277a95 100644 --- a/crates/ide_db/src/call_info.rs +++ b/crates/ide_db/src/call_info.rs | |||
@@ -4,8 +4,9 @@ use either::Either; | |||
4 | use hir::{HasAttrs, HirDisplay, Semantics, Type}; | 4 | use hir::{HasAttrs, HirDisplay, Semantics, Type}; |
5 | use stdx::format_to; | 5 | use stdx::format_to; |
6 | use syntax::{ | 6 | use syntax::{ |
7 | algo, | ||
7 | ast::{self, ArgListOwner, NameOwner}, | 8 | ast::{self, ArgListOwner, NameOwner}, |
8 | match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize, | 9 | match_ast, AstNode, Direction, SyntaxNode, SyntaxToken, TextRange, TextSize, |
9 | }; | 10 | }; |
10 | 11 | ||
11 | use crate::RootDatabase; | 12 | use crate::RootDatabase; |
@@ -43,7 +44,12 @@ pub fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> | |||
43 | let sema = Semantics::new(db); | 44 | let sema = Semantics::new(db); |
44 | let file = sema.parse(position.file_id); | 45 | let file = sema.parse(position.file_id); |
45 | let file = file.syntax(); | 46 | let file = file.syntax(); |
46 | let token = file.token_at_offset(position.offset).next()?; | 47 | let token = file |
48 | .token_at_offset(position.offset) | ||
49 | .left_biased() | ||
50 | // if the cursor is sandwiched between two space tokens and the call is unclosed | ||
51 | // this prevents us from leaving the CallExpression | ||
52 | .and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?; | ||
47 | let token = sema.descend_into_macros(token); | 53 | let token = sema.descend_into_macros(token); |
48 | 54 | ||
49 | let (callable, active_parameter) = call_info_impl(&sema, token)?; | 55 | let (callable, active_parameter) = call_info_impl(&sema, token)?; |
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs index 281a081a3..be1cc12de 100644 --- a/crates/ide_db/src/call_info/tests.rs +++ b/crates/ide_db/src/call_info/tests.rs | |||
@@ -522,3 +522,30 @@ fn main(f: fn(i32, f64) -> char) { | |||
522 | "#]], | 522 | "#]], |
523 | ) | 523 | ) |
524 | } | 524 | } |
525 | |||
526 | #[test] | ||
527 | fn call_info_for_unclosed_call() { | ||
528 | check( | ||
529 | r#" | ||
530 | fn foo(foo: u32, bar: u32) {} | ||
531 | fn main() { | ||
532 | foo($0 | ||
533 | }"#, | ||
534 | expect![[r#" | ||
535 | fn foo(foo: u32, bar: u32) | ||
536 | (<foo: u32>, bar: u32) | ||
537 | "#]], | ||
538 | ); | ||
539 | // check with surrounding space | ||
540 | check( | ||
541 | r#" | ||
542 | fn foo(foo: u32, bar: u32) {} | ||
543 | fn main() { | ||
544 | foo( $0 | ||
545 | }"#, | ||
546 | expect![[r#" | ||
547 | fn foo(foo: u32, bar: u32) | ||
548 | (<foo: u32>, bar: u32) | ||
549 | "#]], | ||
550 | ) | ||
551 | } | ||
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index 66798ea3a..720de0d1f 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! A module with ide helpers for high-level ide features. | 1 | //! A module with ide helpers for high-level ide features. |
2 | pub mod insert_use; | 2 | pub mod insert_use; |
3 | pub mod import_assets; | 3 | pub mod import_assets; |
4 | pub mod rust_doc; | ||
4 | 5 | ||
5 | use std::collections::VecDeque; | 6 | use std::collections::VecDeque; |
6 | 7 | ||
@@ -113,6 +114,10 @@ impl FamousDefs<'_, '_> { | |||
113 | self.find_module("core:iter") | 114 | self.find_module("core:iter") |
114 | } | 115 | } |
115 | 116 | ||
117 | pub fn core_ops_Deref(&self) -> Option<Trait> { | ||
118 | self.find_trait("core:ops:Deref") | ||
119 | } | ||
120 | |||
116 | fn find_trait(&self, path: &str) -> Option<Trait> { | 121 | fn find_trait(&self, path: &str) -> Option<Trait> { |
117 | match self.find_def(path)? { | 122 | match self.find_def(path)? { |
118 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), | 123 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), |
diff --git a/crates/ide_db/src/helpers/famous_defs_fixture.rs b/crates/ide_db/src/helpers/famous_defs_fixture.rs index 4d79e064e..29ae12dcf 100644 --- a/crates/ide_db/src/helpers/famous_defs_fixture.rs +++ b/crates/ide_db/src/helpers/famous_defs_fixture.rs | |||
@@ -112,6 +112,12 @@ pub mod ops { | |||
112 | type Output; | 112 | type Output; |
113 | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; | 113 | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; |
114 | } | 114 | } |
115 | |||
116 | #[lang = "deref"] | ||
117 | pub trait Deref { | ||
118 | type Target: ?Sized; | ||
119 | fn deref(&self) -> &Self::Target; | ||
120 | } | ||
115 | } | 121 | } |
116 | 122 | ||
117 | pub mod option { | 123 | pub mod option { |
@@ -141,3 +147,5 @@ mod return_keyword {} | |||
141 | 147 | ||
142 | /// Docs for prim_str | 148 | /// Docs for prim_str |
143 | mod prim_str {} | 149 | mod prim_str {} |
150 | |||
151 | pub use core::ops; \ No newline at end of file | ||
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index 8ce648367..91d6a4665 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs | |||
@@ -436,6 +436,8 @@ fn trait_applicable_items( | |||
436 | }) | 436 | }) |
437 | .collect(); | 437 | .collect(); |
438 | 438 | ||
439 | let related_dyn_traits = | ||
440 | trait_candidate.receiver_ty.applicable_inherent_traits(db).collect::<FxHashSet<_>>(); | ||
439 | let mut located_imports = FxHashSet::default(); | 441 | let mut located_imports = FxHashSet::default(); |
440 | 442 | ||
441 | if trait_assoc_item { | 443 | if trait_assoc_item { |
@@ -451,12 +453,16 @@ fn trait_applicable_items( | |||
451 | return None; | 453 | return None; |
452 | } | 454 | } |
453 | } | 455 | } |
456 | let located_trait = assoc.containing_trait(db)?; | ||
457 | if related_dyn_traits.contains(&located_trait) { | ||
458 | return None; | ||
459 | } | ||
454 | 460 | ||
455 | let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); | 461 | let trait_item = ItemInNs::from(ModuleDef::from(located_trait)); |
456 | let original_item = assoc_to_item(assoc); | 462 | let original_item = assoc_to_item(assoc); |
457 | located_imports.insert(LocatedImport::new( | 463 | located_imports.insert(LocatedImport::new( |
458 | mod_path(item)?, | 464 | mod_path(trait_item)?, |
459 | item, | 465 | trait_item, |
460 | original_item, | 466 | original_item, |
461 | mod_path(original_item), | 467 | mod_path(original_item), |
462 | )); | 468 | )); |
@@ -473,11 +479,15 @@ fn trait_applicable_items( | |||
473 | |_, function| { | 479 | |_, function| { |
474 | let assoc = function.as_assoc_item(db)?; | 480 | let assoc = function.as_assoc_item(db)?; |
475 | if required_assoc_items.contains(&assoc) { | 481 | if required_assoc_items.contains(&assoc) { |
476 | let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); | 482 | let located_trait = assoc.containing_trait(db)?; |
483 | if related_dyn_traits.contains(&located_trait) { | ||
484 | return None; | ||
485 | } | ||
486 | let trait_item = ItemInNs::from(ModuleDef::from(located_trait)); | ||
477 | let original_item = assoc_to_item(assoc); | 487 | let original_item = assoc_to_item(assoc); |
478 | located_imports.insert(LocatedImport::new( | 488 | located_imports.insert(LocatedImport::new( |
479 | mod_path(item)?, | 489 | mod_path(trait_item)?, |
480 | item, | 490 | trait_item, |
481 | original_item, | 491 | original_item, |
482 | mod_path(original_item), | 492 | mod_path(original_item), |
483 | )); | 493 | )); |
diff --git a/crates/ide_db/src/helpers/rust_doc.rs b/crates/ide_db/src/helpers/rust_doc.rs new file mode 100644 index 000000000..e27e23867 --- /dev/null +++ b/crates/ide_db/src/helpers/rust_doc.rs | |||
@@ -0,0 +1,34 @@ | |||
1 | //! Rustdoc specific doc comment handling | ||
2 | |||
3 | // stripped down version of https://github.com/rust-lang/rust/blob/392ba2ba1a7d6c542d2459fb8133bebf62a4a423/src/librustdoc/html/markdown.rs#L810-L933 | ||
4 | pub fn is_rust_fence(s: &str) -> bool { | ||
5 | let mut seen_rust_tags = false; | ||
6 | let mut seen_other_tags = false; | ||
7 | |||
8 | let tokens = s | ||
9 | .trim() | ||
10 | .split(|c| c == ',' || c == ' ' || c == '\t') | ||
11 | .map(str::trim) | ||
12 | .filter(|t| !t.is_empty()); | ||
13 | |||
14 | for token in tokens { | ||
15 | match token { | ||
16 | "should_panic" | "no_run" | "ignore" | "allow_fail" => { | ||
17 | seen_rust_tags = !seen_other_tags | ||
18 | } | ||
19 | "rust" => seen_rust_tags = true, | ||
20 | "test_harness" | "compile_fail" => seen_rust_tags = !seen_other_tags || seen_rust_tags, | ||
21 | x if x.starts_with("edition") => {} | ||
22 | x if x.starts_with('E') && x.len() == 5 => { | ||
23 | if x[1..].parse::<u32>().is_ok() { | ||
24 | seen_rust_tags = !seen_other_tags || seen_rust_tags; | ||
25 | } else { | ||
26 | seen_other_tags = true; | ||
27 | } | ||
28 | } | ||
29 | _ => seen_other_tags = true, | ||
30 | } | ||
31 | } | ||
32 | |||
33 | !seen_other_tags || seen_rust_tags | ||
34 | } | ||
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs index 9ba98f7fb..a7c8c13c6 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs | |||
@@ -213,7 +213,7 @@ fn doc_comment_text(comment: &ast::Comment) -> SmolStr { | |||
213 | 213 | ||
214 | // Quote the string | 214 | // Quote the string |
215 | // Note that `tt::Literal` expect an escaped string | 215 | // Note that `tt::Literal` expect an escaped string |
216 | let text = format!("{:?}", text.escape_debug().to_string()); | 216 | let text = format!("\"{}\"", text.escape_debug()); |
217 | text.into() | 217 | text.into() |
218 | } | 218 | } |
219 | 219 | ||
diff --git a/crates/mbe/src/tests/expand.rs b/crates/mbe/src/tests/expand.rs index e02d038b6..3a1d840ea 100644 --- a/crates/mbe/src/tests/expand.rs +++ b/crates/mbe/src/tests/expand.rs | |||
@@ -936,7 +936,25 @@ fn test_meta_doc_comments() { | |||
936 | MultiLines Doc | 936 | MultiLines Doc |
937 | */ | 937 | */ |
938 | }"#, | 938 | }"#, |
939 | "# [doc = \" Single Line Doc 1\"] # [doc = \"\\\\n MultiLines Doc\\\\n \"] fn bar () {}", | 939 | "# [doc = \" Single Line Doc 1\"] # [doc = \"\\n MultiLines Doc\\n \"] fn bar () {}", |
940 | ); | ||
941 | } | ||
942 | |||
943 | #[test] | ||
944 | fn test_meta_extended_key_value_attributes() { | ||
945 | parse_macro( | ||
946 | r#" | ||
947 | macro_rules! foo { | ||
948 | (#[$i:meta]) => ( | ||
949 | #[$ i] | ||
950 | fn bar() {} | ||
951 | ) | ||
952 | } | ||
953 | "#, | ||
954 | ) | ||
955 | .assert_expand_items( | ||
956 | r#"foo! { #[doc = concat!("The `", "bla", "` lang item.")] }"#, | ||
957 | r#"# [doc = concat ! ("The `" , "bla" , "` lang item.")] fn bar () {}"#, | ||
940 | ); | 958 | ); |
941 | } | 959 | } |
942 | 960 | ||
@@ -959,7 +977,27 @@ fn test_meta_doc_comments_non_latin() { | |||
959 | 莊生曉夢迷蝴蝶,望帝春心託杜鵑。 | 977 | 莊生曉夢迷蝴蝶,望帝春心託杜鵑。 |
960 | */ | 978 | */ |
961 | }"#, | 979 | }"#, |
962 | "# [doc = \" 錦瑟無端五十弦,一弦一柱思華年。\"] # [doc = \"\\\\n 莊生曉夢迷蝴蝶,望帝春心託杜鵑。\\\\n \"] fn bar () {}", | 980 | "# [doc = \" 錦瑟無端五十弦,一弦一柱思華年。\"] # [doc = \"\\n 莊生曉夢迷蝴蝶,望帝春心託杜鵑。\\n \"] fn bar () {}", |
981 | ); | ||
982 | } | ||
983 | |||
984 | #[test] | ||
985 | fn test_meta_doc_comments_escaped_characters() { | ||
986 | parse_macro( | ||
987 | r#" | ||
988 | macro_rules! foo { | ||
989 | ($(#[$ i:meta])+) => ( | ||
990 | $(#[$ i])+ | ||
991 | fn bar() {} | ||
992 | ) | ||
993 | } | ||
994 | "#, | ||
995 | ) | ||
996 | .assert_expand_items( | ||
997 | r#"foo! { | ||
998 | /// \ " ' | ||
999 | }"#, | ||
1000 | r#"# [doc = " \\ \" \'"] fn bar () {}"#, | ||
963 | ); | 1001 | ); |
964 | } | 1002 | } |
965 | 1003 | ||
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs index cebb8f400..9bdf0b5fa 100644 --- a/crates/parser/src/grammar.rs +++ b/crates/parser/src/grammar.rs | |||
@@ -76,42 +76,7 @@ pub(crate) mod fragments { | |||
76 | 76 | ||
77 | // Parse a meta item , which excluded [], e.g : #[ MetaItem ] | 77 | // Parse a meta item , which excluded [], e.g : #[ MetaItem ] |
78 | pub(crate) fn meta_item(p: &mut Parser) { | 78 | pub(crate) fn meta_item(p: &mut Parser) { |
79 | fn is_delimiter(p: &mut Parser) -> bool { | 79 | attributes::meta(p); |
80 | matches!(p.current(), T!['{'] | T!['('] | T!['[']) | ||
81 | } | ||
82 | |||
83 | if is_delimiter(p) { | ||
84 | items::token_tree(p); | ||
85 | return; | ||
86 | } | ||
87 | |||
88 | let m = p.start(); | ||
89 | while !p.at(EOF) { | ||
90 | if is_delimiter(p) { | ||
91 | items::token_tree(p); | ||
92 | break; | ||
93 | } else { | ||
94 | // https://doc.rust-lang.org/reference/attributes.html | ||
95 | // https://doc.rust-lang.org/reference/paths.html#simple-paths | ||
96 | // The start of an meta must be a simple path | ||
97 | match p.current() { | ||
98 | IDENT | T![super] | T![self] | T![crate] => p.bump_any(), | ||
99 | T![=] => { | ||
100 | p.bump_any(); | ||
101 | match p.current() { | ||
102 | c if c.is_literal() => p.bump_any(), | ||
103 | T![true] | T![false] => p.bump_any(), | ||
104 | _ => {} | ||
105 | } | ||
106 | break; | ||
107 | } | ||
108 | _ if p.at(T![::]) => p.bump(T![::]), | ||
109 | _ => break, | ||
110 | } | ||
111 | } | ||
112 | } | ||
113 | |||
114 | m.complete(p, TOKEN_TREE); | ||
115 | } | 80 | } |
116 | 81 | ||
117 | pub(crate) fn item(p: &mut Parser) { | 82 | pub(crate) fn item(p: &mut Parser) { |
diff --git a/crates/parser/src/grammar/attributes.rs b/crates/parser/src/grammar/attributes.rs index 96791ffc2..124a10eb2 100644 --- a/crates/parser/src/grammar/attributes.rs +++ b/crates/parser/src/grammar/attributes.rs | |||
@@ -14,6 +14,21 @@ pub(super) fn outer_attrs(p: &mut Parser) { | |||
14 | } | 14 | } |
15 | } | 15 | } |
16 | 16 | ||
17 | pub(super) fn meta(p: &mut Parser) { | ||
18 | paths::use_path(p); | ||
19 | |||
20 | match p.current() { | ||
21 | T![=] => { | ||
22 | p.bump(T![=]); | ||
23 | if expressions::expr(p).0.is_none() { | ||
24 | p.error("expected expression"); | ||
25 | } | ||
26 | } | ||
27 | T!['('] | T!['['] | T!['{'] => items::token_tree(p), | ||
28 | _ => {} | ||
29 | } | ||
30 | } | ||
31 | |||
17 | fn attr(p: &mut Parser, inner: bool) { | 32 | fn attr(p: &mut Parser, inner: bool) { |
18 | let attr = p.start(); | 33 | let attr = p.start(); |
19 | assert!(p.at(T![#])); | 34 | assert!(p.at(T![#])); |
@@ -25,18 +40,7 @@ fn attr(p: &mut Parser, inner: bool) { | |||
25 | } | 40 | } |
26 | 41 | ||
27 | if p.eat(T!['[']) { | 42 | if p.eat(T!['[']) { |
28 | paths::use_path(p); | 43 | meta(p); |
29 | |||
30 | match p.current() { | ||
31 | T![=] => { | ||
32 | p.bump(T![=]); | ||
33 | if expressions::expr(p).0.is_none() { | ||
34 | p.error("expected expression"); | ||
35 | } | ||
36 | } | ||
37 | T!['('] | T!['['] | T!['{'] => items::token_tree(p), | ||
38 | _ => {} | ||
39 | } | ||
40 | 44 | ||
41 | if !p.eat(T![']']) { | 45 | if !p.eat(T![']']) { |
42 | p.error("expected `]`"); | 46 | p.error("expected `]`"); |
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs index da71498a8..3ab347834 100644 --- a/crates/parser/src/grammar/patterns.rs +++ b/crates/parser/src/grammar/patterns.rs | |||
@@ -206,13 +206,15 @@ fn record_pat_field_list(p: &mut Parser) { | |||
206 | T![.] if p.at(T![..]) => p.bump(T![..]), | 206 | T![.] if p.at(T![..]) => p.bump(T![..]), |
207 | T!['{'] => error_block(p, "expected ident"), | 207 | T!['{'] => error_block(p, "expected ident"), |
208 | 208 | ||
209 | c => { | 209 | _ => { |
210 | let m = p.start(); | 210 | let m = p.start(); |
211 | match c { | 211 | attributes::outer_attrs(p); |
212 | match p.current() { | ||
212 | // test record_pat_field | 213 | // test record_pat_field |
213 | // fn foo() { | 214 | // fn foo() { |
214 | // let S { 0: 1 } = (); | 215 | // let S { 0: 1 } = (); |
215 | // let S { x: 1 } = (); | 216 | // let S { x: 1 } = (); |
217 | // let S { #[cfg(any())] x: 1 } = (); | ||
216 | // } | 218 | // } |
217 | IDENT | INT_NUMBER if p.nth(1) == T![:] => { | 219 | IDENT | INT_NUMBER if p.nth(1) == T![:] => { |
218 | name_ref_or_index(p); | 220 | name_ref_or_index(p); |
diff --git a/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs index 94cbf7d85..6ae3e734f 100644 --- a/crates/parser/src/grammar/types.rs +++ b/crates/parser/src/grammar/types.rs | |||
@@ -283,17 +283,21 @@ pub(super) fn path_type(p: &mut Parser) { | |||
283 | // type B = crate::foo!(); | 283 | // type B = crate::foo!(); |
284 | fn path_or_macro_type_(p: &mut Parser, allow_bounds: bool) { | 284 | fn path_or_macro_type_(p: &mut Parser, allow_bounds: bool) { |
285 | assert!(paths::is_path_start(p)); | 285 | assert!(paths::is_path_start(p)); |
286 | let r = p.start(); | ||
286 | let m = p.start(); | 287 | let m = p.start(); |
288 | |||
287 | paths::type_path(p); | 289 | paths::type_path(p); |
288 | 290 | ||
289 | let kind = if p.at(T![!]) && !p.at(T![!=]) { | 291 | let kind = if p.at(T![!]) && !p.at(T![!=]) { |
290 | items::macro_call_after_excl(p); | 292 | items::macro_call_after_excl(p); |
291 | MACRO_CALL | 293 | m.complete(p, MACRO_CALL); |
294 | MACRO_TYPE | ||
292 | } else { | 295 | } else { |
296 | m.abandon(p); | ||
293 | PATH_TYPE | 297 | PATH_TYPE |
294 | }; | 298 | }; |
295 | 299 | ||
296 | let path = m.complete(p, kind); | 300 | let path = r.complete(p, kind); |
297 | 301 | ||
298 | if allow_bounds { | 302 | if allow_bounds { |
299 | opt_type_bounds_as_dyn_trait_type(p, path); | 303 | opt_type_bounds_as_dyn_trait_type(p, path); |
@@ -319,7 +323,7 @@ pub(super) fn path_type_(p: &mut Parser, allow_bounds: bool) { | |||
319 | fn opt_type_bounds_as_dyn_trait_type(p: &mut Parser, type_marker: CompletedMarker) { | 323 | fn opt_type_bounds_as_dyn_trait_type(p: &mut Parser, type_marker: CompletedMarker) { |
320 | assert!(matches!( | 324 | assert!(matches!( |
321 | type_marker.kind(), | 325 | type_marker.kind(), |
322 | SyntaxKind::PATH_TYPE | SyntaxKind::FOR_TYPE | SyntaxKind::MACRO_CALL | 326 | SyntaxKind::PATH_TYPE | SyntaxKind::FOR_TYPE | SyntaxKind::MACRO_TYPE |
323 | )); | 327 | )); |
324 | if !p.at(T![+]) { | 328 | if !p.at(T![+]) { |
325 | return; | 329 | return; |
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs index 22011cb33..f09ad37e3 100644 --- a/crates/paths/src/lib.rs +++ b/crates/paths/src/lib.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! Thin wrappers around `std::path`, distinguishing between absolute and | 1 | //! Thin wrappers around `std::path`, distinguishing between absolute and |
2 | //! relative paths. | 2 | //! relative paths. |
3 | use std::{ | 3 | use std::{ |
4 | borrow::Borrow, | ||
4 | convert::{TryFrom, TryInto}, | 5 | convert::{TryFrom, TryInto}, |
5 | ops, | 6 | ops, |
6 | path::{Component, Path, PathBuf}, | 7 | path::{Component, Path, PathBuf}, |
@@ -35,6 +36,12 @@ impl AsRef<AbsPath> for AbsPathBuf { | |||
35 | } | 36 | } |
36 | } | 37 | } |
37 | 38 | ||
39 | impl Borrow<AbsPath> for AbsPathBuf { | ||
40 | fn borrow(&self) -> &AbsPath { | ||
41 | self.as_path() | ||
42 | } | ||
43 | } | ||
44 | |||
38 | impl TryFrom<PathBuf> for AbsPathBuf { | 45 | impl TryFrom<PathBuf> for AbsPathBuf { |
39 | type Error = PathBuf; | 46 | type Error = PathBuf; |
40 | fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> { | 47 | fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> { |
diff --git a/crates/proc_macro_api/Cargo.toml b/crates/proc_macro_api/Cargo.toml index 16fd56c7e..1ba1e4abd 100644 --- a/crates/proc_macro_api/Cargo.toml +++ b/crates/proc_macro_api/Cargo.toml | |||
@@ -15,10 +15,11 @@ serde_json = { version = "1.0", features = ["unbounded_depth"] } | |||
15 | log = "0.4.8" | 15 | log = "0.4.8" |
16 | crossbeam-channel = "0.5.0" | 16 | crossbeam-channel = "0.5.0" |
17 | jod-thread = "0.1.1" | 17 | jod-thread = "0.1.1" |
18 | memmap = "0.7.0" | ||
19 | object = { version = "0.23.0", default-features = false, features = ["std", "read_core", "elf", "macho", "pe", "unaligned"] } | ||
20 | snap = "1.0" | ||
18 | 21 | ||
19 | tt = { path = "../tt", version = "0.0.0" } | 22 | tt = { path = "../tt", version = "0.0.0" } |
20 | base_db = { path = "../base_db", version = "0.0.0" } | 23 | base_db = { path = "../base_db", version = "0.0.0" } |
21 | stdx = { path = "../stdx", version = "0.0.0" } | 24 | stdx = { path = "../stdx", version = "0.0.0" } |
22 | snap = "1" | 25 | profile = { path = "../profile", version = "0.0.0" } |
23 | object = { version = "0.23.0", default-features = false, features = ["std", "read_core", "elf", "macho", "pe", "unaligned"] } | ||
24 | memmap = "0.7.0" | ||
diff --git a/crates/proc_macro_api/src/lib.rs b/crates/proc_macro_api/src/lib.rs index 2dd2a8541..654bd9943 100644 --- a/crates/proc_macro_api/src/lib.rs +++ b/crates/proc_macro_api/src/lib.rs | |||
@@ -77,6 +77,7 @@ impl ProcMacroClient { | |||
77 | } | 77 | } |
78 | 78 | ||
79 | pub fn by_dylib_path(&self, dylib_path: &Path) -> Vec<ProcMacro> { | 79 | pub fn by_dylib_path(&self, dylib_path: &Path) -> Vec<ProcMacro> { |
80 | let _p = profile::span("ProcMacroClient::by_dylib_path"); | ||
80 | match version::read_dylib_info(dylib_path) { | 81 | match version::read_dylib_info(dylib_path) { |
81 | Ok(info) => { | 82 | Ok(info) => { |
82 | if info.version.0 < 1 || info.version.1 < 47 { | 83 | if info.version.0 < 1 || info.version.1 < 47 { |
diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs index f7050be4e..faca336de 100644 --- a/crates/project_model/src/build_data.rs +++ b/crates/project_model/src/build_data.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | //! Handles build script specific information | 1 | //! Handles build script specific information |
2 | 2 | ||
3 | use std::{ | 3 | use std::{ |
4 | io::BufReader, | ||
5 | path::PathBuf, | 4 | path::PathBuf, |
6 | process::{Command, Stdio}, | 5 | process::{Command, Stdio}, |
7 | sync::Arc, | 6 | sync::Arc, |
@@ -13,12 +12,13 @@ use cargo_metadata::{BuildScript, Message}; | |||
13 | use itertools::Itertools; | 12 | use itertools::Itertools; |
14 | use paths::{AbsPath, AbsPathBuf}; | 13 | use paths::{AbsPath, AbsPathBuf}; |
15 | use rustc_hash::FxHashMap; | 14 | use rustc_hash::FxHashMap; |
16 | use stdx::JodChild; | 15 | use serde::Deserialize; |
16 | use stdx::format_to; | ||
17 | 17 | ||
18 | use crate::{cfg_flag::CfgFlag, CargoConfig}; | 18 | use crate::{cfg_flag::CfgFlag, CargoConfig}; |
19 | 19 | ||
20 | #[derive(Debug, Clone, Default, PartialEq, Eq)] | 20 | #[derive(Debug, Clone, Default, PartialEq, Eq)] |
21 | pub(crate) struct BuildData { | 21 | pub(crate) struct PackageBuildData { |
22 | /// List of config flags defined by this package's build script | 22 | /// List of config flags defined by this package's build script |
23 | pub(crate) cfgs: Vec<CfgFlag>, | 23 | pub(crate) cfgs: Vec<CfgFlag>, |
24 | /// List of cargo-related environment variables with their value | 24 | /// List of cargo-related environment variables with their value |
@@ -32,6 +32,17 @@ pub(crate) struct BuildData { | |||
32 | pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>, | 32 | pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>, |
33 | } | 33 | } |
34 | 34 | ||
35 | #[derive(Debug, Default, PartialEq, Eq, Clone)] | ||
36 | pub(crate) struct WorkspaceBuildData { | ||
37 | per_package: FxHashMap<String, PackageBuildData>, | ||
38 | error: Option<String>, | ||
39 | } | ||
40 | |||
41 | #[derive(Debug, Default, PartialEq, Eq, Clone)] | ||
42 | pub struct BuildDataResult { | ||
43 | per_workspace: FxHashMap<AbsPathBuf, WorkspaceBuildData>, | ||
44 | } | ||
45 | |||
35 | #[derive(Clone, Debug)] | 46 | #[derive(Clone, Debug)] |
36 | pub(crate) struct BuildDataConfig { | 47 | pub(crate) struct BuildDataConfig { |
37 | cargo_toml: AbsPathBuf, | 48 | cargo_toml: AbsPathBuf, |
@@ -47,19 +58,17 @@ impl PartialEq for BuildDataConfig { | |||
47 | 58 | ||
48 | impl Eq for BuildDataConfig {} | 59 | impl Eq for BuildDataConfig {} |
49 | 60 | ||
50 | #[derive(Debug, Default)] | 61 | #[derive(Debug)] |
51 | pub struct BuildDataCollector { | 62 | pub struct BuildDataCollector { |
63 | wrap_rustc: bool, | ||
52 | configs: FxHashMap<AbsPathBuf, BuildDataConfig>, | 64 | configs: FxHashMap<AbsPathBuf, BuildDataConfig>, |
53 | } | 65 | } |
54 | 66 | ||
55 | #[derive(Debug, Default, PartialEq, Eq)] | ||
56 | pub struct BuildDataResult { | ||
57 | data: FxHashMap<AbsPathBuf, BuildDataMap>, | ||
58 | } | ||
59 | |||
60 | pub(crate) type BuildDataMap = FxHashMap<String, BuildData>; | ||
61 | |||
62 | impl BuildDataCollector { | 67 | impl BuildDataCollector { |
68 | pub fn new(wrap_rustc: bool) -> Self { | ||
69 | Self { wrap_rustc, configs: FxHashMap::default() } | ||
70 | } | ||
71 | |||
63 | pub(crate) fn add_config(&mut self, workspace_root: &AbsPath, config: BuildDataConfig) { | 72 | pub(crate) fn add_config(&mut self, workspace_root: &AbsPath, config: BuildDataConfig) { |
64 | self.configs.insert(workspace_root.to_path_buf(), config); | 73 | self.configs.insert(workspace_root.to_path_buf(), config); |
65 | } | 74 | } |
@@ -67,23 +76,41 @@ impl BuildDataCollector { | |||
67 | pub fn collect(&mut self, progress: &dyn Fn(String)) -> Result<BuildDataResult> { | 76 | pub fn collect(&mut self, progress: &dyn Fn(String)) -> Result<BuildDataResult> { |
68 | let mut res = BuildDataResult::default(); | 77 | let mut res = BuildDataResult::default(); |
69 | for (path, config) in self.configs.iter() { | 78 | for (path, config) in self.configs.iter() { |
70 | res.data.insert( | 79 | let workspace_build_data = WorkspaceBuildData::collect( |
71 | path.clone(), | 80 | &config.cargo_toml, |
72 | collect_from_workspace( | 81 | &config.cargo_features, |
73 | &config.cargo_toml, | 82 | &config.packages, |
74 | &config.cargo_features, | 83 | self.wrap_rustc, |
75 | &config.packages, | 84 | progress, |
76 | progress, | 85 | )?; |
77 | )?, | 86 | res.per_workspace.insert(path.clone(), workspace_build_data); |
78 | ); | ||
79 | } | 87 | } |
80 | Ok(res) | 88 | Ok(res) |
81 | } | 89 | } |
82 | } | 90 | } |
83 | 91 | ||
92 | impl WorkspaceBuildData { | ||
93 | pub(crate) fn get(&self, package_id: &str) -> Option<&PackageBuildData> { | ||
94 | self.per_package.get(package_id) | ||
95 | } | ||
96 | } | ||
97 | |||
84 | impl BuildDataResult { | 98 | impl BuildDataResult { |
85 | pub(crate) fn get(&self, root: &AbsPath) -> Option<&BuildDataMap> { | 99 | pub(crate) fn get(&self, workspace_root: &AbsPath) -> Option<&WorkspaceBuildData> { |
86 | self.data.get(&root.to_path_buf()) | 100 | self.per_workspace.get(workspace_root) |
101 | } | ||
102 | pub fn error(&self) -> Option<String> { | ||
103 | let mut buf = String::new(); | ||
104 | for (_workspace_root, build_data) in &self.per_workspace { | ||
105 | if let Some(err) = &build_data.error { | ||
106 | format_to!(buf, "cargo check failed:\n{}", err); | ||
107 | } | ||
108 | } | ||
109 | if buf.is_empty() { | ||
110 | return None; | ||
111 | } | ||
112 | |||
113 | Some(buf) | ||
87 | } | 114 | } |
88 | } | 115 | } |
89 | 116 | ||
@@ -97,108 +124,155 @@ impl BuildDataConfig { | |||
97 | } | 124 | } |
98 | } | 125 | } |
99 | 126 | ||
100 | fn collect_from_workspace( | 127 | impl WorkspaceBuildData { |
101 | cargo_toml: &AbsPath, | 128 | fn collect( |
102 | cargo_features: &CargoConfig, | 129 | cargo_toml: &AbsPath, |
103 | packages: &Vec<cargo_metadata::Package>, | 130 | cargo_features: &CargoConfig, |
104 | progress: &dyn Fn(String), | 131 | packages: &Vec<cargo_metadata::Package>, |
105 | ) -> Result<BuildDataMap> { | 132 | wrap_rustc: bool, |
106 | let mut cmd = Command::new(toolchain::cargo()); | 133 | progress: &dyn Fn(String), |
107 | cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"]) | 134 | ) -> Result<WorkspaceBuildData> { |
108 | .arg(cargo_toml.as_ref()); | 135 | let mut cmd = Command::new(toolchain::cargo()); |
109 | 136 | ||
110 | // --all-targets includes tests, benches and examples in addition to the | 137 | if wrap_rustc { |
111 | // default lib and bins. This is an independent concept from the --targets | 138 | // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use |
112 | // flag below. | 139 | // that to compile only proc macros and build scripts during the initial |
113 | cmd.arg("--all-targets"); | 140 | // `cargo check`. |
114 | 141 | let myself = std::env::current_exe()?; | |
115 | if let Some(target) = &cargo_features.target { | 142 | cmd.env("RUSTC_WRAPPER", myself); |
116 | cmd.args(&["--target", target]); | 143 | cmd.env("RA_RUSTC_WRAPPER", "1"); |
117 | } | 144 | } |
118 | 145 | ||
119 | if cargo_features.all_features { | 146 | cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"]) |
120 | cmd.arg("--all-features"); | 147 | .arg(cargo_toml.as_ref()); |
121 | } else { | 148 | |
122 | if cargo_features.no_default_features { | 149 | // --all-targets includes tests, benches and examples in addition to the |
123 | // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` | 150 | // default lib and bins. This is an independent concept from the --targets |
124 | // https://github.com/oli-obk/cargo_metadata/issues/79 | 151 | // flag below. |
125 | cmd.arg("--no-default-features"); | 152 | cmd.arg("--all-targets"); |
153 | |||
154 | if let Some(target) = &cargo_features.target { | ||
155 | cmd.args(&["--target", target]); | ||
126 | } | 156 | } |
127 | if !cargo_features.features.is_empty() { | 157 | |
128 | cmd.arg("--features"); | 158 | if cargo_features.all_features { |
129 | cmd.arg(cargo_features.features.join(" ")); | 159 | cmd.arg("--all-features"); |
160 | } else { | ||
161 | if cargo_features.no_default_features { | ||
162 | // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` | ||
163 | // https://github.com/oli-obk/cargo_metadata/issues/79 | ||
164 | cmd.arg("--no-default-features"); | ||
165 | } | ||
166 | if !cargo_features.features.is_empty() { | ||
167 | cmd.arg("--features"); | ||
168 | cmd.arg(cargo_features.features.join(" ")); | ||
169 | } | ||
130 | } | 170 | } |
131 | } | ||
132 | 171 | ||
133 | cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null()); | 172 | cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null()); |
134 | 173 | ||
135 | let mut child = cmd.spawn().map(JodChild)?; | 174 | let mut res = WorkspaceBuildData::default(); |
136 | let child_stdout = child.stdout.take().unwrap(); | 175 | |
137 | let stdout = BufReader::new(child_stdout); | 176 | let mut callback_err = None; |
138 | 177 | let output = stdx::process::streaming_output( | |
139 | let mut res = BuildDataMap::default(); | 178 | cmd, |
140 | for message in cargo_metadata::Message::parse_stream(stdout).flatten() { | 179 | &mut |line| { |
141 | match message { | 180 | if callback_err.is_some() { |
142 | Message::BuildScriptExecuted(BuildScript { | 181 | return; |
143 | package_id, out_dir, cfgs, env, .. | 182 | } |
144 | }) => { | 183 | |
145 | let cfgs = { | 184 | // Copy-pasted from existing cargo_metadata. It seems like we |
146 | let mut acc = Vec::new(); | 185 | // should be using sered_stacker here? |
147 | for cfg in cfgs { | 186 | let mut deserializer = serde_json::Deserializer::from_str(&line); |
148 | match cfg.parse::<CfgFlag>() { | 187 | deserializer.disable_recursion_limit(); |
149 | Ok(it) => acc.push(it), | 188 | let message = Message::deserialize(&mut deserializer) |
150 | Err(err) => { | 189 | .unwrap_or(Message::TextLine(line.to_string())); |
151 | anyhow::bail!("invalid cfg from cargo-metadata: {}", err) | 190 | |
191 | match message { | ||
192 | Message::BuildScriptExecuted(BuildScript { | ||
193 | package_id, | ||
194 | out_dir, | ||
195 | cfgs, | ||
196 | env, | ||
197 | .. | ||
198 | }) => { | ||
199 | let cfgs = { | ||
200 | let mut acc = Vec::new(); | ||
201 | for cfg in cfgs { | ||
202 | match cfg.parse::<CfgFlag>() { | ||
203 | Ok(it) => acc.push(it), | ||
204 | Err(err) => { | ||
205 | callback_err = Some(anyhow::format_err!( | ||
206 | "invalid cfg from cargo-metadata: {}", | ||
207 | err | ||
208 | )); | ||
209 | return; | ||
210 | } | ||
211 | }; | ||
152 | } | 212 | } |
213 | acc | ||
153 | }; | 214 | }; |
215 | let package_build_data = | ||
216 | res.per_package.entry(package_id.repr.clone()).or_default(); | ||
217 | // cargo_metadata crate returns default (empty) path for | ||
218 | // older cargos, which is not absolute, so work around that. | ||
219 | if !out_dir.as_str().is_empty() { | ||
220 | let out_dir = | ||
221 | AbsPathBuf::assert(PathBuf::from(out_dir.into_os_string())); | ||
222 | package_build_data.out_dir = Some(out_dir); | ||
223 | package_build_data.cfgs = cfgs; | ||
224 | } | ||
225 | |||
226 | package_build_data.envs = env; | ||
154 | } | 227 | } |
155 | acc | 228 | Message::CompilerArtifact(message) => { |
156 | }; | 229 | progress(format!("metadata {}", message.target.name)); |
157 | let res = res.entry(package_id.repr.clone()).or_default(); | 230 | |
158 | // cargo_metadata crate returns default (empty) path for | 231 | if message.target.kind.contains(&"proc-macro".to_string()) { |
159 | // older cargos, which is not absolute, so work around that. | 232 | let package_id = message.package_id; |
160 | if !out_dir.as_str().is_empty() { | 233 | // Skip rmeta file |
161 | let out_dir = AbsPathBuf::assert(PathBuf::from(out_dir.into_os_string())); | 234 | if let Some(filename) = |
162 | res.out_dir = Some(out_dir); | 235 | message.filenames.iter().find(|name| is_dylib(name)) |
163 | res.cfgs = cfgs; | 236 | { |
164 | } | 237 | let filename = AbsPathBuf::assert(PathBuf::from(&filename)); |
165 | 238 | let package_build_data = | |
166 | res.envs = env; | 239 | res.per_package.entry(package_id.repr.clone()).or_default(); |
167 | } | 240 | package_build_data.proc_macro_dylib_path = Some(filename); |
168 | Message::CompilerArtifact(message) => { | 241 | } |
169 | progress(format!("metadata {}", message.target.name)); | 242 | } |
170 | |||
171 | if message.target.kind.contains(&"proc-macro".to_string()) { | ||
172 | let package_id = message.package_id; | ||
173 | // Skip rmeta file | ||
174 | if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) { | ||
175 | let filename = AbsPathBuf::assert(PathBuf::from(&filename)); | ||
176 | let res = res.entry(package_id.repr.clone()).or_default(); | ||
177 | res.proc_macro_dylib_path = Some(filename); | ||
178 | } | 243 | } |
244 | Message::CompilerMessage(message) => { | ||
245 | progress(message.target.name.clone()); | ||
246 | } | ||
247 | Message::BuildFinished(_) => {} | ||
248 | Message::TextLine(_) => {} | ||
249 | _ => {} | ||
250 | } | ||
251 | }, | ||
252 | &mut |_| (), | ||
253 | )?; | ||
254 | |||
255 | for package in packages { | ||
256 | let package_build_data = res.per_package.entry(package.id.repr.clone()).or_default(); | ||
257 | inject_cargo_env(package, package_build_data); | ||
258 | if let Some(out_dir) = &package_build_data.out_dir { | ||
259 | // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() | ||
260 | if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { | ||
261 | package_build_data.envs.push(("OUT_DIR".to_string(), out_dir)); | ||
179 | } | 262 | } |
180 | } | 263 | } |
181 | Message::CompilerMessage(message) => { | ||
182 | progress(message.target.name.clone()); | ||
183 | } | ||
184 | Message::BuildFinished(_) => {} | ||
185 | Message::TextLine(_) => {} | ||
186 | _ => {} | ||
187 | } | 264 | } |
188 | } | ||
189 | 265 | ||
190 | for package in packages { | 266 | if !output.status.success() { |
191 | let build_data = res.entry(package.id.repr.clone()).or_default(); | 267 | let mut stderr = String::from_utf8(output.stderr).unwrap_or_default(); |
192 | inject_cargo_env(package, build_data); | 268 | if stderr.is_empty() { |
193 | if let Some(out_dir) = &build_data.out_dir { | 269 | stderr = "cargo check failed".to_string(); |
194 | // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() | ||
195 | if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { | ||
196 | build_data.envs.push(("OUT_DIR".to_string(), out_dir)); | ||
197 | } | 270 | } |
271 | res.error = Some(stderr) | ||
198 | } | 272 | } |
199 | } | ||
200 | 273 | ||
201 | Ok(res) | 274 | Ok(res) |
275 | } | ||
202 | } | 276 | } |
203 | 277 | ||
204 | // FIXME: File a better way to know if it is a dylib | 278 | // FIXME: File a better way to know if it is a dylib |
@@ -212,7 +286,7 @@ fn is_dylib(path: &Utf8Path) -> bool { | |||
212 | /// Recreates the compile-time environment variables that Cargo sets. | 286 | /// Recreates the compile-time environment variables that Cargo sets. |
213 | /// | 287 | /// |
214 | /// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates> | 288 | /// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates> |
215 | fn inject_cargo_env(package: &cargo_metadata::Package, build_data: &mut BuildData) { | 289 | fn inject_cargo_env(package: &cargo_metadata::Package, build_data: &mut PackageBuildData) { |
216 | let env = &mut build_data.envs; | 290 | let env = &mut build_data.envs; |
217 | 291 | ||
218 | // FIXME: Missing variables: | 292 | // FIXME: Missing variables: |
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs index 1b53fcc30..2fcd0f8fa 100644 --- a/crates/project_model/src/workspace.rs +++ b/crates/project_model/src/workspace.rs | |||
@@ -12,7 +12,7 @@ use proc_macro_api::ProcMacroClient; | |||
12 | use rustc_hash::{FxHashMap, FxHashSet}; | 12 | use rustc_hash::{FxHashMap, FxHashSet}; |
13 | 13 | ||
14 | use crate::{ | 14 | use crate::{ |
15 | build_data::{BuildData, BuildDataMap, BuildDataResult}, | 15 | build_data::{BuildDataResult, PackageBuildData, WorkspaceBuildData}, |
16 | cargo_workspace, | 16 | cargo_workspace, |
17 | cfg_flag::CfgFlag, | 17 | cfg_flag::CfgFlag, |
18 | rustc_cfg, | 18 | rustc_cfg, |
@@ -354,10 +354,10 @@ fn cargo_to_crate_graph( | |||
354 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, | 354 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, |
355 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, | 355 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, |
356 | cargo: &CargoWorkspace, | 356 | cargo: &CargoWorkspace, |
357 | build_data_map: Option<&BuildDataMap>, | 357 | build_data_map: Option<&WorkspaceBuildData>, |
358 | sysroot: &Sysroot, | 358 | sysroot: &Sysroot, |
359 | rustc: &Option<CargoWorkspace>, | 359 | rustc: &Option<CargoWorkspace>, |
360 | rustc_build_data_map: Option<&BuildDataMap>, | 360 | rustc_build_data_map: Option<&WorkspaceBuildData>, |
361 | ) -> CrateGraph { | 361 | ) -> CrateGraph { |
362 | let _p = profile::span("cargo_to_crate_graph"); | 362 | let _p = profile::span("cargo_to_crate_graph"); |
363 | let mut crate_graph = CrateGraph::default(); | 363 | let mut crate_graph = CrateGraph::default(); |
@@ -464,7 +464,7 @@ fn handle_rustc_crates( | |||
464 | rustc_workspace: &CargoWorkspace, | 464 | rustc_workspace: &CargoWorkspace, |
465 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, | 465 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, |
466 | crate_graph: &mut CrateGraph, | 466 | crate_graph: &mut CrateGraph, |
467 | rustc_build_data_map: Option<&FxHashMap<String, BuildData>>, | 467 | rustc_build_data_map: Option<&WorkspaceBuildData>, |
468 | cfg_options: &CfgOptions, | 468 | cfg_options: &CfgOptions, |
469 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, | 469 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, |
470 | pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>, | 470 | pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>, |
@@ -555,7 +555,7 @@ fn handle_rustc_crates( | |||
555 | fn add_target_crate_root( | 555 | fn add_target_crate_root( |
556 | crate_graph: &mut CrateGraph, | 556 | crate_graph: &mut CrateGraph, |
557 | pkg: &cargo_workspace::PackageData, | 557 | pkg: &cargo_workspace::PackageData, |
558 | build_data: Option<&BuildData>, | 558 | build_data: Option<&PackageBuildData>, |
559 | cfg_options: &CfgOptions, | 559 | cfg_options: &CfgOptions, |
560 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, | 560 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, |
561 | file_id: FileId, | 561 | file_id: FileId, |
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 3130785cc..0571a912c 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -22,7 +22,7 @@ env_logger = { version = "0.8.1", default-features = false } | |||
22 | itertools = "0.10.0" | 22 | itertools = "0.10.0" |
23 | jod-thread = "0.1.0" | 23 | jod-thread = "0.1.0" |
24 | log = "0.4.8" | 24 | log = "0.4.8" |
25 | lsp-types = { version = "0.88.0", features = ["proposed"] } | 25 | lsp-types = { version = "0.89.0", features = ["proposed"] } |
26 | parking_lot = "0.11.0" | 26 | parking_lot = "0.11.0" |
27 | xflags = "0.2.1" | 27 | xflags = "0.2.1" |
28 | oorandom = "11.1.2" | 28 | oorandom = "11.1.2" |
diff --git a/crates/rust-analyzer/build.rs b/crates/rust-analyzer/build.rs index 999dc5928..13b903891 100644 --- a/crates/rust-analyzer/build.rs +++ b/crates/rust-analyzer/build.rs | |||
@@ -39,7 +39,8 @@ fn set_rerun() { | |||
39 | } | 39 | } |
40 | 40 | ||
41 | fn rev() -> Option<String> { | 41 | fn rev() -> Option<String> { |
42 | let output = Command::new("git").args(&["describe", "--tags"]).output().ok()?; | 42 | let output = |
43 | Command::new("git").args(&["describe", "--tags", "--exclude", "nightly"]).output().ok()?; | ||
43 | let stdout = String::from_utf8(output.stdout).ok()?; | 44 | let stdout = String::from_utf8(output.stdout).ok()?; |
44 | Some(stdout) | 45 | Some(stdout) |
45 | } | 46 | } |
diff --git a/crates/rust-analyzer/src/benchmarks.rs b/crates/rust-analyzer/src/benchmarks.rs index bf569b40b..bdd94b1c4 100644 --- a/crates/rust-analyzer/src/benchmarks.rs +++ b/crates/rust-analyzer/src/benchmarks.rs | |||
@@ -30,8 +30,11 @@ fn benchmark_integrated_highlighting() { | |||
30 | let file = "./crates/ide_db/src/apply_change.rs"; | 30 | let file = "./crates/ide_db/src/apply_change.rs"; |
31 | 31 | ||
32 | let cargo_config = Default::default(); | 32 | let cargo_config = Default::default(); |
33 | let load_cargo_config = | 33 | let load_cargo_config = LoadCargoConfig { |
34 | LoadCargoConfig { load_out_dirs_from_check: true, with_proc_macro: false }; | 34 | load_out_dirs_from_check: true, |
35 | wrap_rustc: false, | ||
36 | with_proc_macro: false, | ||
37 | }; | ||
35 | 38 | ||
36 | let (mut host, vfs, _proc_macro) = { | 39 | let (mut host, vfs, _proc_macro) = { |
37 | let _it = stdx::timeit("workspace loading"); | 40 | let _it = stdx::timeit("workspace loading"); |
diff --git a/crates/rust-analyzer/src/bin/flags.rs b/crates/rust-analyzer/src/bin/flags.rs index b05fc00b9..63953098a 100644 --- a/crates/rust-analyzer/src/bin/flags.rs +++ b/crates/rust-analyzer/src/bin/flags.rs | |||
@@ -71,6 +71,8 @@ xflags::xflags! { | |||
71 | optional --load-output-dirs | 71 | optional --load-output-dirs |
72 | /// Use proc-macro-srv for proc-macro expanding. | 72 | /// Use proc-macro-srv for proc-macro expanding. |
73 | optional --with-proc-macro | 73 | optional --with-proc-macro |
74 | /// Only resolve names, don't run type inference. | ||
75 | optional --skip-inference | ||
74 | } | 76 | } |
75 | 77 | ||
76 | cmd diagnostics | 78 | cmd diagnostics |
@@ -158,6 +160,7 @@ pub struct AnalysisStats { | |||
158 | pub no_sysroot: bool, | 160 | pub no_sysroot: bool, |
159 | pub load_output_dirs: bool, | 161 | pub load_output_dirs: bool, |
160 | pub with_proc_macro: bool, | 162 | pub with_proc_macro: bool, |
163 | pub skip_inference: bool, | ||
161 | } | 164 | } |
162 | 165 | ||
163 | #[derive(Debug)] | 166 | #[derive(Debug)] |
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 3b9b9e8b4..f0abb5b15 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs | |||
@@ -3,6 +3,7 @@ | |||
3 | //! Based on cli flags, either spawns an LSP server, or runs a batch analysis | 3 | //! Based on cli flags, either spawns an LSP server, or runs a batch analysis |
4 | mod flags; | 4 | mod flags; |
5 | mod logger; | 5 | mod logger; |
6 | mod rustc_wrapper; | ||
6 | 7 | ||
7 | use std::{convert::TryFrom, env, fs, path::Path, process}; | 8 | use std::{convert::TryFrom, env, fs, path::Path, process}; |
8 | 9 | ||
@@ -26,6 +27,20 @@ static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc; | |||
26 | static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; | 27 | static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; |
27 | 28 | ||
28 | fn main() { | 29 | fn main() { |
30 | if std::env::var("RA_RUSTC_WRAPPER").is_ok() { | ||
31 | let mut args = std::env::args_os(); | ||
32 | let _me = args.next().unwrap(); | ||
33 | let rustc = args.next().unwrap(); | ||
34 | let code = match rustc_wrapper::run_rustc_skipping_cargo_checking(rustc, args.collect()) { | ||
35 | Ok(rustc_wrapper::ExitCode(code)) => code.unwrap_or(102), | ||
36 | Err(err) => { | ||
37 | eprintln!("{}", err); | ||
38 | 101 | ||
39 | } | ||
40 | }; | ||
41 | process::exit(code); | ||
42 | } | ||
43 | |||
29 | if let Err(err) = try_main() { | 44 | if let Err(err) = try_main() { |
30 | log::error!("Unexpected error: {}", err); | 45 | log::error!("Unexpected error: {}", err); |
31 | eprintln!("{}", err); | 46 | eprintln!("{}", err); |
@@ -78,6 +93,7 @@ fn try_main() -> Result<()> { | |||
78 | path: cmd.path, | 93 | path: cmd.path, |
79 | load_output_dirs: cmd.load_output_dirs, | 94 | load_output_dirs: cmd.load_output_dirs, |
80 | with_proc_macro: cmd.with_proc_macro, | 95 | with_proc_macro: cmd.with_proc_macro, |
96 | skip_inference: cmd.skip_inference, | ||
81 | } | 97 | } |
82 | .run(verbosity)?, | 98 | .run(verbosity)?, |
83 | 99 | ||
diff --git a/crates/rust-analyzer/src/bin/rustc_wrapper.rs b/crates/rust-analyzer/src/bin/rustc_wrapper.rs new file mode 100644 index 000000000..2f6d4706d --- /dev/null +++ b/crates/rust-analyzer/src/bin/rustc_wrapper.rs | |||
@@ -0,0 +1,46 @@ | |||
1 | //! We setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself during the | ||
2 | //! initial `cargo check`. That way, we avoid checking the actual project, and | ||
3 | //! only build proc macros and build.rs. | ||
4 | //! | ||
5 | //! Code taken from IntelliJ :0) | ||
6 | //! https://github.com/intellij-rust/intellij-rust/blob/master/native-helper/src/main.rs | ||
7 | use std::{ | ||
8 | ffi::OsString, | ||
9 | io, | ||
10 | process::{Command, Stdio}, | ||
11 | }; | ||
12 | |||
13 | /// ExitCode/ExitStatus are impossible to create :(. | ||
14 | pub(crate) struct ExitCode(pub(crate) Option<i32>); | ||
15 | |||
16 | pub(crate) fn run_rustc_skipping_cargo_checking( | ||
17 | rustc_executable: OsString, | ||
18 | args: Vec<OsString>, | ||
19 | ) -> io::Result<ExitCode> { | ||
20 | let is_cargo_check = args.iter().any(|arg| { | ||
21 | let arg = arg.to_string_lossy(); | ||
22 | // `cargo check` invokes `rustc` with `--emit=metadata` argument. | ||
23 | // | ||
24 | // https://doc.rust-lang.org/rustc/command-line-arguments.html#--emit-specifies-the-types-of-output-files-to-generate | ||
25 | // link — Generates the crates specified by --crate-type. The default | ||
26 | // output filenames depend on the crate type and platform. This | ||
27 | // is the default if --emit is not specified. | ||
28 | // metadata — Generates a file containing metadata about the crate. | ||
29 | // The default output filename is CRATE_NAME.rmeta. | ||
30 | arg.starts_with("--emit=") && arg.contains("metadata") && !arg.contains("link") | ||
31 | }); | ||
32 | if is_cargo_check { | ||
33 | return Ok(ExitCode(Some(0))); | ||
34 | } | ||
35 | run_rustc(rustc_executable, args) | ||
36 | } | ||
37 | |||
38 | fn run_rustc(rustc_executable: OsString, args: Vec<OsString>) -> io::Result<ExitCode> { | ||
39 | let mut child = Command::new(rustc_executable) | ||
40 | .args(args) | ||
41 | .stdin(Stdio::inherit()) | ||
42 | .stdout(Stdio::inherit()) | ||
43 | .stderr(Stdio::inherit()) | ||
44 | .spawn()?; | ||
45 | Ok(ExitCode(child.wait()?.code())) | ||
46 | } | ||
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index 7a5bcb8c7..3c87782f2 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs | |||
@@ -57,7 +57,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti | |||
57 | document_range_formatting_provider: None, | 57 | document_range_formatting_provider: None, |
58 | document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { | 58 | document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { |
59 | first_trigger_character: "=".to_string(), | 59 | first_trigger_character: "=".to_string(), |
60 | more_trigger_character: Some(vec![".".to_string(), ">".to_string()]), | 60 | more_trigger_character: Some(vec![".".to_string(), ">".to_string(), "{".to_string()]), |
61 | }), | 61 | }), |
62 | selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)), | 62 | selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)), |
63 | folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)), | 63 | folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)), |
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 9c59e7ee4..3f3134562 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs | |||
@@ -9,10 +9,11 @@ use std::{ | |||
9 | 9 | ||
10 | use hir::{ | 10 | use hir::{ |
11 | db::{AstDatabase, DefDatabase, HirDatabase}, | 11 | db::{AstDatabase, DefDatabase, HirDatabase}, |
12 | AssocItem, Crate, HasSource, HirDisplay, ModuleDef, | 12 | AssocItem, Crate, Function, HasSource, HirDisplay, ModuleDef, |
13 | }; | 13 | }; |
14 | use hir_def::FunctionId; | 14 | use hir_def::FunctionId; |
15 | use hir_ty::TypeWalk; | 15 | use hir_ty::{TyExt, TypeWalk}; |
16 | use ide::{AnalysisHost, RootDatabase}; | ||
16 | use ide_db::base_db::{ | 17 | use ide_db::base_db::{ |
17 | salsa::{self, ParallelDatabase}, | 18 | salsa::{self, ParallelDatabase}, |
18 | SourceDatabaseExt, | 19 | SourceDatabaseExt, |
@@ -24,6 +25,7 @@ use rayon::prelude::*; | |||
24 | use rustc_hash::FxHashSet; | 25 | use rustc_hash::FxHashSet; |
25 | use stdx::format_to; | 26 | use stdx::format_to; |
26 | use syntax::AstNode; | 27 | use syntax::AstNode; |
28 | use vfs::Vfs; | ||
27 | 29 | ||
28 | use crate::cli::{ | 30 | use crate::cli::{ |
29 | load_cargo::{load_workspace_at, LoadCargoConfig}, | 31 | load_cargo::{load_workspace_at, LoadCargoConfig}, |
@@ -51,6 +53,7 @@ pub struct AnalysisStatsCmd { | |||
51 | pub path: PathBuf, | 53 | pub path: PathBuf, |
52 | pub load_output_dirs: bool, | 54 | pub load_output_dirs: bool, |
53 | pub with_proc_macro: bool, | 55 | pub with_proc_macro: bool, |
56 | pub skip_inference: bool, | ||
54 | } | 57 | } |
55 | 58 | ||
56 | impl AnalysisStatsCmd { | 59 | impl AnalysisStatsCmd { |
@@ -65,6 +68,7 @@ impl AnalysisStatsCmd { | |||
65 | cargo_config.no_sysroot = self.no_sysroot; | 68 | cargo_config.no_sysroot = self.no_sysroot; |
66 | let load_cargo_config = LoadCargoConfig { | 69 | let load_cargo_config = LoadCargoConfig { |
67 | load_out_dirs_from_check: self.load_output_dirs, | 70 | load_out_dirs_from_check: self.load_output_dirs, |
71 | wrap_rustc: false, | ||
68 | with_proc_macro: self.with_proc_macro, | 72 | with_proc_macro: self.with_proc_macro, |
69 | }; | 73 | }; |
70 | let (host, vfs, _proc_macro) = | 74 | let (host, vfs, _proc_macro) = |
@@ -128,6 +132,39 @@ impl AnalysisStatsCmd { | |||
128 | shuffle(&mut rng, &mut funcs); | 132 | shuffle(&mut rng, &mut funcs); |
129 | } | 133 | } |
130 | 134 | ||
135 | if !self.skip_inference { | ||
136 | self.run_inference(&host, db, &vfs, &funcs, verbosity); | ||
137 | } | ||
138 | |||
139 | let total_span = analysis_sw.elapsed(); | ||
140 | eprintln!("{:<20} {}", "Total:", total_span); | ||
141 | report_metric("total time", total_span.time.as_millis() as u64, "ms"); | ||
142 | if let Some(instructions) = total_span.instructions { | ||
143 | report_metric("total instructions", instructions, "#instr"); | ||
144 | } | ||
145 | if let Some(memory) = total_span.memory { | ||
146 | report_metric("total memory", memory.allocated.megabytes() as u64, "MB"); | ||
147 | } | ||
148 | |||
149 | if env::var("RA_COUNT").is_ok() { | ||
150 | eprintln!("{}", profile::countme::get_all()); | ||
151 | } | ||
152 | |||
153 | if self.memory_usage && verbosity.is_verbose() { | ||
154 | print_memory_usage(host, vfs); | ||
155 | } | ||
156 | |||
157 | Ok(()) | ||
158 | } | ||
159 | |||
160 | fn run_inference( | ||
161 | &self, | ||
162 | host: &AnalysisHost, | ||
163 | db: &RootDatabase, | ||
164 | vfs: &Vfs, | ||
165 | funcs: &[Function], | ||
166 | verbosity: Verbosity, | ||
167 | ) { | ||
131 | let mut bar = match verbosity { | 168 | let mut bar = match verbosity { |
132 | Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(), | 169 | Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(), |
133 | _ if self.parallel => ProgressReport::hidden(), | 170 | _ if self.parallel => ProgressReport::hidden(), |
@@ -154,7 +191,7 @@ impl AnalysisStatsCmd { | |||
154 | let mut num_exprs_unknown = 0; | 191 | let mut num_exprs_unknown = 0; |
155 | let mut num_exprs_partially_unknown = 0; | 192 | let mut num_exprs_partially_unknown = 0; |
156 | let mut num_type_mismatches = 0; | 193 | let mut num_type_mismatches = 0; |
157 | for f in funcs { | 194 | for f in funcs.iter().copied() { |
158 | let name = f.name(db); | 195 | let name = f.name(db); |
159 | let full_name = f | 196 | let full_name = f |
160 | .module(db) | 197 | .module(db) |
@@ -296,26 +333,6 @@ impl AnalysisStatsCmd { | |||
296 | report_metric("type mismatches", num_type_mismatches, "#"); | 333 | report_metric("type mismatches", num_type_mismatches, "#"); |
297 | 334 | ||
298 | eprintln!("{:<20} {}", "Inference:", inference_sw.elapsed()); | 335 | eprintln!("{:<20} {}", "Inference:", inference_sw.elapsed()); |
299 | |||
300 | let total_span = analysis_sw.elapsed(); | ||
301 | eprintln!("{:<20} {}", "Total:", total_span); | ||
302 | report_metric("total time", total_span.time.as_millis() as u64, "ms"); | ||
303 | if let Some(instructions) = total_span.instructions { | ||
304 | report_metric("total instructions", instructions, "#instr"); | ||
305 | } | ||
306 | if let Some(memory) = total_span.memory { | ||
307 | report_metric("total memory", memory.allocated.megabytes() as u64, "MB"); | ||
308 | } | ||
309 | |||
310 | if env::var("RA_COUNT").is_ok() { | ||
311 | eprintln!("{}", profile::countme::get_all()); | ||
312 | } | ||
313 | |||
314 | if self.memory_usage && verbosity.is_verbose() { | ||
315 | print_memory_usage(host, vfs); | ||
316 | } | ||
317 | |||
318 | Ok(()) | ||
319 | } | 336 | } |
320 | 337 | ||
321 | fn stop_watch(&self) -> StopWatch { | 338 | fn stop_watch(&self) -> StopWatch { |
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs index 8b985716b..74f784338 100644 --- a/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/crates/rust-analyzer/src/cli/diagnostics.rs | |||
@@ -34,7 +34,8 @@ pub fn diagnostics( | |||
34 | with_proc_macro: bool, | 34 | with_proc_macro: bool, |
35 | ) -> Result<()> { | 35 | ) -> Result<()> { |
36 | let cargo_config = Default::default(); | 36 | let cargo_config = Default::default(); |
37 | let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check, with_proc_macro }; | 37 | let load_cargo_config = |
38 | LoadCargoConfig { load_out_dirs_from_check, with_proc_macro, wrap_rustc: false }; | ||
38 | let (host, _vfs, _proc_macro) = | 39 | let (host, _vfs, _proc_macro) = |
39 | load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?; | 40 | load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?; |
40 | let db = host.raw_database(); | 41 | let db = host.raw_database(); |
@@ -56,7 +57,8 @@ pub fn diagnostics( | |||
56 | let crate_name = | 57 | let crate_name = |
57 | module.krate().display_name(db).as_deref().unwrap_or("unknown").to_string(); | 58 | module.krate().display_name(db).as_deref().unwrap_or("unknown").to_string(); |
58 | println!("processing crate: {}, module: {}", crate_name, _vfs.file_path(file_id)); | 59 | println!("processing crate: {}, module: {}", crate_name, _vfs.file_path(file_id)); |
59 | for diagnostic in analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap() | 60 | for diagnostic in |
61 | analysis.diagnostics(&DiagnosticsConfig::default(), false, file_id).unwrap() | ||
60 | { | 62 | { |
61 | if matches!(diagnostic.severity, Severity::Error) { | 63 | if matches!(diagnostic.severity, Severity::Error) { |
62 | found_error = true; | 64 | found_error = true; |
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 310c36904..75bad1112 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs | |||
@@ -15,6 +15,7 @@ use crate::reload::{ProjectFolders, SourceRootConfig}; | |||
15 | 15 | ||
16 | pub struct LoadCargoConfig { | 16 | pub struct LoadCargoConfig { |
17 | pub load_out_dirs_from_check: bool, | 17 | pub load_out_dirs_from_check: bool, |
18 | pub wrap_rustc: bool, | ||
18 | pub with_proc_macro: bool, | 19 | pub with_proc_macro: bool, |
19 | } | 20 | } |
20 | 21 | ||
@@ -52,7 +53,7 @@ pub fn load_workspace( | |||
52 | }; | 53 | }; |
53 | 54 | ||
54 | let build_data = if config.load_out_dirs_from_check { | 55 | let build_data = if config.load_out_dirs_from_check { |
55 | let mut collector = BuildDataCollector::default(); | 56 | let mut collector = BuildDataCollector::new(config.wrap_rustc); |
56 | ws.collect_build_data_configs(&mut collector); | 57 | ws.collect_build_data_configs(&mut collector); |
57 | Some(collector.collect(progress)?) | 58 | Some(collector.collect(progress)?) |
58 | } else { | 59 | } else { |
@@ -136,8 +137,11 @@ mod tests { | |||
136 | fn test_loading_rust_analyzer() -> Result<()> { | 137 | fn test_loading_rust_analyzer() -> Result<()> { |
137 | let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap(); | 138 | let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap(); |
138 | let cargo_config = Default::default(); | 139 | let cargo_config = Default::default(); |
139 | let load_cargo_config = | 140 | let load_cargo_config = LoadCargoConfig { |
140 | LoadCargoConfig { load_out_dirs_from_check: false, with_proc_macro: false }; | 141 | load_out_dirs_from_check: false, |
142 | wrap_rustc: false, | ||
143 | with_proc_macro: false, | ||
144 | }; | ||
141 | let (host, _vfs, _proc_macro) = | 145 | let (host, _vfs, _proc_macro) = |
142 | load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?; | 146 | load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?; |
143 | 147 | ||
diff --git a/crates/rust-analyzer/src/cli/ssr.rs b/crates/rust-analyzer/src/cli/ssr.rs index 79f426fff..1fd9b5a9b 100644 --- a/crates/rust-analyzer/src/cli/ssr.rs +++ b/crates/rust-analyzer/src/cli/ssr.rs | |||
@@ -9,8 +9,11 @@ use ide_ssr::{MatchFinder, SsrPattern, SsrRule}; | |||
9 | pub fn apply_ssr_rules(rules: Vec<SsrRule>) -> Result<()> { | 9 | pub fn apply_ssr_rules(rules: Vec<SsrRule>) -> Result<()> { |
10 | use ide_db::base_db::SourceDatabaseExt; | 10 | use ide_db::base_db::SourceDatabaseExt; |
11 | let cargo_config = Default::default(); | 11 | let cargo_config = Default::default(); |
12 | let load_cargo_config = | 12 | let load_cargo_config = LoadCargoConfig { |
13 | LoadCargoConfig { load_out_dirs_from_check: true, with_proc_macro: true }; | 13 | load_out_dirs_from_check: true, |
14 | wrap_rustc: false, | ||
15 | with_proc_macro: true, | ||
16 | }; | ||
14 | let (host, vfs, _proc_macro) = | 17 | let (host, vfs, _proc_macro) = |
15 | load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?; | 18 | load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?; |
16 | let db = host.raw_database(); | 19 | let db = host.raw_database(); |
@@ -37,7 +40,7 @@ pub fn search_for_patterns(patterns: Vec<SsrPattern>, debug_snippet: Option<Stri | |||
37 | use ide_db::symbol_index::SymbolsDatabase; | 40 | use ide_db::symbol_index::SymbolsDatabase; |
38 | let cargo_config = Default::default(); | 41 | let cargo_config = Default::default(); |
39 | let load_cargo_config = | 42 | let load_cargo_config = |
40 | LoadCargoConfig { load_out_dirs_from_check: true, with_proc_macro: true }; | 43 | LoadCargoConfig { load_out_dirs_from_check: true, wrap_rustc: true, with_proc_macro: true }; |
41 | let (host, _vfs, _proc_macro) = | 44 | let (host, _vfs, _proc_macro) = |
42 | load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?; | 45 | load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?; |
43 | let db = host.raw_database(); | 46 | let db = host.raw_database(); |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index cda272fd4..1109d2daf 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -17,7 +17,7 @@ use ide_db::helpers::{ | |||
17 | }; | 17 | }; |
18 | use lsp_types::{ClientCapabilities, MarkupKind}; | 18 | use lsp_types::{ClientCapabilities, MarkupKind}; |
19 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; | 19 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; |
20 | use rustc_hash::FxHashSet; | 20 | use rustc_hash::{FxHashMap, FxHashSet}; |
21 | use serde::{de::DeserializeOwned, Deserialize}; | 21 | use serde::{de::DeserializeOwned, Deserialize}; |
22 | use vfs::AbsPathBuf; | 22 | use vfs::AbsPathBuf; |
23 | 23 | ||
@@ -48,6 +48,9 @@ config_data! { | |||
48 | /// Run build scripts (`build.rs`) for more precise code analysis. | 48 | /// Run build scripts (`build.rs`) for more precise code analysis. |
49 | cargo_runBuildScripts | | 49 | cargo_runBuildScripts | |
50 | cargo_loadOutDirsFromCheck: bool = "true", | 50 | cargo_loadOutDirsFromCheck: bool = "true", |
51 | /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to | ||
52 | /// avoid compiling unnecessary things. | ||
53 | cargo_useRustcWrapperForBuildScripts: bool = "true", | ||
51 | /// Do not activate the `default` feature. | 54 | /// Do not activate the `default` feature. |
52 | cargo_noDefaultFeatures: bool = "false", | 55 | cargo_noDefaultFeatures: bool = "false", |
53 | /// Compilation target (target triple). | 56 | /// Compilation target (target triple). |
@@ -96,6 +99,9 @@ config_data! { | |||
96 | diagnostics_enableExperimental: bool = "true", | 99 | diagnostics_enableExperimental: bool = "true", |
97 | /// List of rust-analyzer diagnostics to disable. | 100 | /// List of rust-analyzer diagnostics to disable. |
98 | diagnostics_disabled: FxHashSet<String> = "[]", | 101 | diagnostics_disabled: FxHashSet<String> = "[]", |
102 | /// Map of prefixes to be substituted when parsing diagnostic file paths. | ||
103 | /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. | ||
104 | diagnostics_remapPrefix: FxHashMap<String, String> = "{}", | ||
99 | /// List of warnings that should be displayed with info severity. | 105 | /// List of warnings that should be displayed with info severity. |
100 | /// | 106 | /// |
101 | /// The warnings will be indicated by a blue squiggly underline in code | 107 | /// The warnings will be indicated by a blue squiggly underline in code |
@@ -397,6 +403,17 @@ impl Config { | |||
397 | pub fn will_rename(&self) -> bool { | 403 | pub fn will_rename(&self) -> bool { |
398 | try_or!(self.caps.workspace.as_ref()?.file_operations.as_ref()?.will_rename?, false) | 404 | try_or!(self.caps.workspace.as_ref()?.file_operations.as_ref()?.will_rename?, false) |
399 | } | 405 | } |
406 | pub fn change_annotation_support(&self) -> bool { | ||
407 | try_!(self | ||
408 | .caps | ||
409 | .workspace | ||
410 | .as_ref()? | ||
411 | .workspace_edit | ||
412 | .as_ref()? | ||
413 | .change_annotation_support | ||
414 | .as_ref()?) | ||
415 | .is_some() | ||
416 | } | ||
400 | pub fn code_action_resolve(&self) -> bool { | 417 | pub fn code_action_resolve(&self) -> bool { |
401 | try_or!( | 418 | try_or!( |
402 | self.caps | 419 | self.caps |
@@ -445,8 +462,8 @@ impl Config { | |||
445 | pub fn hover_actions(&self) -> bool { | 462 | pub fn hover_actions(&self) -> bool { |
446 | self.experimental("hoverActions") | 463 | self.experimental("hoverActions") |
447 | } | 464 | } |
448 | pub fn status_notification(&self) -> bool { | 465 | pub fn server_status_notification(&self) -> bool { |
449 | self.experimental("statusNotification") | 466 | self.experimental("serverStatusNotification") |
450 | } | 467 | } |
451 | 468 | ||
452 | pub fn publish_diagnostics(&self) -> bool { | 469 | pub fn publish_diagnostics(&self) -> bool { |
@@ -460,6 +477,7 @@ impl Config { | |||
460 | } | 477 | } |
461 | pub fn diagnostics_map(&self) -> DiagnosticsMapConfig { | 478 | pub fn diagnostics_map(&self) -> DiagnosticsMapConfig { |
462 | DiagnosticsMapConfig { | 479 | DiagnosticsMapConfig { |
480 | remap_prefix: self.data.diagnostics_remapPrefix.clone(), | ||
463 | warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(), | 481 | warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(), |
464 | warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(), | 482 | warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(), |
465 | } | 483 | } |
@@ -493,6 +511,9 @@ impl Config { | |||
493 | pub fn run_build_scripts(&self) -> bool { | 511 | pub fn run_build_scripts(&self) -> bool { |
494 | self.data.cargo_runBuildScripts || self.data.procMacro_enable | 512 | self.data.cargo_runBuildScripts || self.data.procMacro_enable |
495 | } | 513 | } |
514 | pub fn wrap_rustc(&self) -> bool { | ||
515 | self.data.cargo_useRustcWrapperForBuildScripts | ||
516 | } | ||
496 | pub fn cargo(&self) -> CargoConfig { | 517 | pub fn cargo(&self) -> CargoConfig { |
497 | let rustc_source = self.data.rustcSource.as_ref().map(|rustc_src| { | 518 | let rustc_source = self.data.rustcSource.as_ref().map(|rustc_src| { |
498 | if rustc_src == "discover" { | 519 | if rustc_src == "discover" { |
@@ -656,6 +677,19 @@ impl Config { | |||
656 | pub fn code_lens_refresh(&self) -> bool { | 677 | pub fn code_lens_refresh(&self) -> bool { |
657 | try_or!(self.caps.workspace.as_ref()?.code_lens.as_ref()?.refresh_support?, false) | 678 | try_or!(self.caps.workspace.as_ref()?.code_lens.as_ref()?.refresh_support?, false) |
658 | } | 679 | } |
680 | pub fn insert_replace_support(&self) -> bool { | ||
681 | try_or!( | ||
682 | self.caps | ||
683 | .text_document | ||
684 | .as_ref()? | ||
685 | .completion | ||
686 | .as_ref()? | ||
687 | .completion_item | ||
688 | .as_ref()? | ||
689 | .insert_replace_support?, | ||
690 | false | ||
691 | ) | ||
692 | } | ||
659 | } | 693 | } |
660 | 694 | ||
661 | #[derive(Deserialize, Debug, Clone)] | 695 | #[derive(Deserialize, Debug, Clone)] |
@@ -805,6 +839,9 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json | |||
805 | "items": { "type": "string" }, | 839 | "items": { "type": "string" }, |
806 | "uniqueItems": true, | 840 | "uniqueItems": true, |
807 | }, | 841 | }, |
842 | "FxHashMap<String, String>" => set! { | ||
843 | "type": "object", | ||
844 | }, | ||
808 | "Option<usize>" => set! { | 845 | "Option<usize>" => set! { |
809 | "type": ["null", "integer"], | 846 | "type": ["null", "integer"], |
810 | "minimum": 0, | 847 | "minimum": 0, |
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs index f01548c50..d4b9db362 100644 --- a/crates/rust-analyzer/src/diagnostics.rs +++ b/crates/rust-analyzer/src/diagnostics.rs | |||
@@ -12,6 +12,7 @@ pub(crate) type CheckFixes = Arc<FxHashMap<FileId, Vec<Fix>>>; | |||
12 | 12 | ||
13 | #[derive(Debug, Default, Clone)] | 13 | #[derive(Debug, Default, Clone)] |
14 | pub struct DiagnosticsMapConfig { | 14 | pub struct DiagnosticsMapConfig { |
15 | pub remap_prefix: FxHashMap<String, String>, | ||
15 | pub warnings_as_info: Vec<String>, | 16 | pub warnings_as_info: Vec<String>, |
16 | pub warnings_as_hint: Vec<String>, | 17 | pub warnings_as_hint: Vec<String>, |
17 | } | 18 | } |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt index 23ec2efba..227d96d51 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt | |||
@@ -326,6 +326,7 @@ | |||
326 | }, | 326 | }, |
327 | ), | 327 | ), |
328 | document_changes: None, | 328 | document_changes: None, |
329 | change_annotations: None, | ||
329 | }, | 330 | }, |
330 | ), | 331 | ), |
331 | is_preferred: Some( | 332 | is_preferred: Some( |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt index b6acb5f42..f8adfad3b 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt | |||
@@ -179,6 +179,7 @@ | |||
179 | }, | 179 | }, |
180 | ), | 180 | ), |
181 | document_changes: None, | 181 | document_changes: None, |
182 | change_annotations: None, | ||
182 | }, | 183 | }, |
183 | ), | 184 | ), |
184 | is_preferred: Some( | 185 | is_preferred: Some( |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt index d765257c4..5a70d2ed7 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt | |||
@@ -179,6 +179,7 @@ | |||
179 | }, | 179 | }, |
180 | ), | 180 | ), |
181 | document_changes: None, | 181 | document_changes: None, |
182 | change_annotations: None, | ||
182 | }, | 183 | }, |
183 | ), | 184 | ), |
184 | is_preferred: Some( | 185 | is_preferred: Some( |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt index 6b0d94878..04ca0c9c2 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt | |||
@@ -179,6 +179,7 @@ | |||
179 | }, | 179 | }, |
180 | ), | 180 | ), |
181 | document_changes: None, | 181 | document_changes: None, |
182 | change_annotations: None, | ||
182 | }, | 183 | }, |
183 | ), | 184 | ), |
184 | is_preferred: Some( | 185 | is_preferred: Some( |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt index a0cfb8d33..57d2f1ae3 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt | |||
@@ -339,6 +339,7 @@ | |||
339 | }, | 339 | }, |
340 | ), | 340 | ), |
341 | document_changes: None, | 341 | document_changes: None, |
342 | change_annotations: None, | ||
342 | }, | 343 | }, |
343 | ), | 344 | ), |
344 | is_preferred: Some( | 345 | is_preferred: Some( |
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index e2f319f6b..82dd0da9a 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs | |||
@@ -1,6 +1,9 @@ | |||
1 | //! This module provides the functionality needed to convert diagnostics from | 1 | //! This module provides the functionality needed to convert diagnostics from |
2 | //! `cargo check` json format to the LSP diagnostic format. | 2 | //! `cargo check` json format to the LSP diagnostic format. |
3 | use std::{collections::HashMap, path::Path}; | 3 | use std::{ |
4 | collections::HashMap, | ||
5 | path::{Path, PathBuf}, | ||
6 | }; | ||
4 | 7 | ||
5 | use flycheck::{DiagnosticLevel, DiagnosticSpan}; | 8 | use flycheck::{DiagnosticLevel, DiagnosticSpan}; |
6 | use stdx::format_to; | 9 | use stdx::format_to; |
@@ -41,8 +44,12 @@ fn is_dummy_macro_file(file_name: &str) -> bool { | |||
41 | } | 44 | } |
42 | 45 | ||
43 | /// Converts a Rust span to a LSP location | 46 | /// Converts a Rust span to a LSP location |
44 | fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { | 47 | fn location( |
45 | let file_name = workspace_root.join(&span.file_name); | 48 | config: &DiagnosticsMapConfig, |
49 | workspace_root: &Path, | ||
50 | span: &DiagnosticSpan, | ||
51 | ) -> lsp_types::Location { | ||
52 | let file_name = resolve_path(config, workspace_root, &span.file_name); | ||
46 | let uri = url_from_abs_path(&file_name); | 53 | let uri = url_from_abs_path(&file_name); |
47 | 54 | ||
48 | // FIXME: this doesn't handle UTF16 offsets correctly | 55 | // FIXME: this doesn't handle UTF16 offsets correctly |
@@ -58,32 +65,50 @@ fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location | |||
58 | /// | 65 | /// |
59 | /// This takes locations pointing into the standard library, or generally outside the current | 66 | /// This takes locations pointing into the standard library, or generally outside the current |
60 | /// workspace into account and tries to avoid those, in case macros are involved. | 67 | /// workspace into account and tries to avoid those, in case macros are involved. |
61 | fn primary_location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { | 68 | fn primary_location( |
69 | config: &DiagnosticsMapConfig, | ||
70 | workspace_root: &Path, | ||
71 | span: &DiagnosticSpan, | ||
72 | ) -> lsp_types::Location { | ||
62 | let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span)); | 73 | let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span)); |
63 | for span in span_stack.clone() { | 74 | for span in span_stack.clone() { |
64 | let abs_path = workspace_root.join(&span.file_name); | 75 | let abs_path = resolve_path(config, workspace_root, &span.file_name); |
65 | if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) { | 76 | if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) { |
66 | return location(workspace_root, span); | 77 | return location(config, workspace_root, span); |
67 | } | 78 | } |
68 | } | 79 | } |
69 | 80 | ||
70 | // Fall back to the outermost macro invocation if no suitable span comes up. | 81 | // Fall back to the outermost macro invocation if no suitable span comes up. |
71 | let last_span = span_stack.last().unwrap(); | 82 | let last_span = span_stack.last().unwrap(); |
72 | location(workspace_root, last_span) | 83 | location(config, workspace_root, last_span) |
73 | } | 84 | } |
74 | 85 | ||
75 | /// Converts a secondary Rust span to a LSP related information | 86 | /// Converts a secondary Rust span to a LSP related information |
76 | /// | 87 | /// |
77 | /// If the span is unlabelled this will return `None`. | 88 | /// If the span is unlabelled this will return `None`. |
78 | fn diagnostic_related_information( | 89 | fn diagnostic_related_information( |
90 | config: &DiagnosticsMapConfig, | ||
79 | workspace_root: &Path, | 91 | workspace_root: &Path, |
80 | span: &DiagnosticSpan, | 92 | span: &DiagnosticSpan, |
81 | ) -> Option<lsp_types::DiagnosticRelatedInformation> { | 93 | ) -> Option<lsp_types::DiagnosticRelatedInformation> { |
82 | let message = span.label.clone()?; | 94 | let message = span.label.clone()?; |
83 | let location = location(workspace_root, span); | 95 | let location = location(config, workspace_root, span); |
84 | Some(lsp_types::DiagnosticRelatedInformation { location, message }) | 96 | Some(lsp_types::DiagnosticRelatedInformation { location, message }) |
85 | } | 97 | } |
86 | 98 | ||
99 | /// Resolves paths applying any matching path prefix remappings, and then | ||
100 | /// joining the path to the workspace root. | ||
101 | fn resolve_path(config: &DiagnosticsMapConfig, workspace_root: &Path, file_name: &str) -> PathBuf { | ||
102 | match config | ||
103 | .remap_prefix | ||
104 | .iter() | ||
105 | .find_map(|(from, to)| file_name.strip_prefix(from).map(|file_name| (to, file_name))) | ||
106 | { | ||
107 | Some((to, file_name)) => workspace_root.join(format!("{}{}", to, file_name)), | ||
108 | None => workspace_root.join(file_name), | ||
109 | } | ||
110 | } | ||
111 | |||
87 | struct SubDiagnostic { | 112 | struct SubDiagnostic { |
88 | related: lsp_types::DiagnosticRelatedInformation, | 113 | related: lsp_types::DiagnosticRelatedInformation, |
89 | suggested_fix: Option<lsp_ext::CodeAction>, | 114 | suggested_fix: Option<lsp_ext::CodeAction>, |
@@ -95,6 +120,7 @@ enum MappedRustChildDiagnostic { | |||
95 | } | 120 | } |
96 | 121 | ||
97 | fn map_rust_child_diagnostic( | 122 | fn map_rust_child_diagnostic( |
123 | config: &DiagnosticsMapConfig, | ||
98 | workspace_root: &Path, | 124 | workspace_root: &Path, |
99 | rd: &flycheck::Diagnostic, | 125 | rd: &flycheck::Diagnostic, |
100 | ) -> MappedRustChildDiagnostic { | 126 | ) -> MappedRustChildDiagnostic { |
@@ -108,7 +134,7 @@ fn map_rust_child_diagnostic( | |||
108 | let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new(); | 134 | let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new(); |
109 | for &span in &spans { | 135 | for &span in &spans { |
110 | if let Some(suggested_replacement) = &span.suggested_replacement { | 136 | if let Some(suggested_replacement) = &span.suggested_replacement { |
111 | let location = location(workspace_root, span); | 137 | let location = location(config, workspace_root, span); |
112 | let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone()); | 138 | let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone()); |
113 | edit_map.entry(location.uri).or_default().push(edit); | 139 | edit_map.entry(location.uri).or_default().push(edit); |
114 | } | 140 | } |
@@ -117,7 +143,7 @@ fn map_rust_child_diagnostic( | |||
117 | if edit_map.is_empty() { | 143 | if edit_map.is_empty() { |
118 | MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { | 144 | MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { |
119 | related: lsp_types::DiagnosticRelatedInformation { | 145 | related: lsp_types::DiagnosticRelatedInformation { |
120 | location: location(workspace_root, spans[0]), | 146 | location: location(config, workspace_root, spans[0]), |
121 | message: rd.message.clone(), | 147 | message: rd.message.clone(), |
122 | }, | 148 | }, |
123 | suggested_fix: None, | 149 | suggested_fix: None, |
@@ -125,7 +151,7 @@ fn map_rust_child_diagnostic( | |||
125 | } else { | 151 | } else { |
126 | MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { | 152 | MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { |
127 | related: lsp_types::DiagnosticRelatedInformation { | 153 | related: lsp_types::DiagnosticRelatedInformation { |
128 | location: location(workspace_root, spans[0]), | 154 | location: location(config, workspace_root, spans[0]), |
129 | message: rd.message.clone(), | 155 | message: rd.message.clone(), |
130 | }, | 156 | }, |
131 | suggested_fix: Some(lsp_ext::CodeAction { | 157 | suggested_fix: Some(lsp_ext::CodeAction { |
@@ -136,6 +162,7 @@ fn map_rust_child_diagnostic( | |||
136 | // FIXME: there's no good reason to use edit_map here.... | 162 | // FIXME: there's no good reason to use edit_map here.... |
137 | changes: Some(edit_map), | 163 | changes: Some(edit_map), |
138 | document_changes: None, | 164 | document_changes: None, |
165 | change_annotations: None, | ||
139 | }), | 166 | }), |
140 | is_preferred: Some(true), | 167 | is_preferred: Some(true), |
141 | data: None, | 168 | data: None, |
@@ -189,7 +216,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
189 | let mut tags = Vec::new(); | 216 | let mut tags = Vec::new(); |
190 | 217 | ||
191 | for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) { | 218 | for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) { |
192 | let related = diagnostic_related_information(workspace_root, secondary_span); | 219 | let related = diagnostic_related_information(config, workspace_root, secondary_span); |
193 | if let Some(related) = related { | 220 | if let Some(related) = related { |
194 | subdiagnostics.push(SubDiagnostic { related, suggested_fix: None }); | 221 | subdiagnostics.push(SubDiagnostic { related, suggested_fix: None }); |
195 | } | 222 | } |
@@ -197,7 +224,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
197 | 224 | ||
198 | let mut message = rd.message.clone(); | 225 | let mut message = rd.message.clone(); |
199 | for child in &rd.children { | 226 | for child in &rd.children { |
200 | let child = map_rust_child_diagnostic(workspace_root, &child); | 227 | let child = map_rust_child_diagnostic(config, workspace_root, &child); |
201 | match child { | 228 | match child { |
202 | MappedRustChildDiagnostic::SubDiagnostic(sub) => { | 229 | MappedRustChildDiagnostic::SubDiagnostic(sub) => { |
203 | subdiagnostics.push(sub); | 230 | subdiagnostics.push(sub); |
@@ -241,7 +268,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
241 | primary_spans | 268 | primary_spans |
242 | .iter() | 269 | .iter() |
243 | .flat_map(|primary_span| { | 270 | .flat_map(|primary_span| { |
244 | let primary_location = primary_location(workspace_root, &primary_span); | 271 | let primary_location = primary_location(config, workspace_root, &primary_span); |
245 | 272 | ||
246 | let mut message = message.clone(); | 273 | let mut message = message.clone(); |
247 | if needs_primary_span_label { | 274 | if needs_primary_span_label { |
@@ -271,7 +298,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
271 | // generated that code. | 298 | // generated that code. |
272 | let is_in_macro_call = i != 0; | 299 | let is_in_macro_call = i != 0; |
273 | 300 | ||
274 | let secondary_location = location(workspace_root, &span); | 301 | let secondary_location = location(config, workspace_root, &span); |
275 | if secondary_location == primary_location { | 302 | if secondary_location == primary_location { |
276 | continue; | 303 | continue; |
277 | } | 304 | } |
diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs index 5b02b2598..712d5a9c2 100644 --- a/crates/rust-analyzer/src/from_proto.rs +++ b/crates/rust-analyzer/src/from_proto.rs | |||
@@ -42,27 +42,27 @@ pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> Tex | |||
42 | TextRange::new(start, end) | 42 | TextRange::new(start, end) |
43 | } | 43 | } |
44 | 44 | ||
45 | pub(crate) fn file_id(world: &GlobalStateSnapshot, url: &lsp_types::Url) -> Result<FileId> { | 45 | pub(crate) fn file_id(snap: &GlobalStateSnapshot, url: &lsp_types::Url) -> Result<FileId> { |
46 | world.url_to_file_id(url) | 46 | snap.url_to_file_id(url) |
47 | } | 47 | } |
48 | 48 | ||
49 | pub(crate) fn file_position( | 49 | pub(crate) fn file_position( |
50 | world: &GlobalStateSnapshot, | 50 | snap: &GlobalStateSnapshot, |
51 | tdpp: lsp_types::TextDocumentPositionParams, | 51 | tdpp: lsp_types::TextDocumentPositionParams, |
52 | ) -> Result<FilePosition> { | 52 | ) -> Result<FilePosition> { |
53 | let file_id = file_id(world, &tdpp.text_document.uri)?; | 53 | let file_id = file_id(snap, &tdpp.text_document.uri)?; |
54 | let line_index = world.file_line_index(file_id)?; | 54 | let line_index = snap.file_line_index(file_id)?; |
55 | let offset = offset(&line_index, tdpp.position); | 55 | let offset = offset(&line_index, tdpp.position); |
56 | Ok(FilePosition { file_id, offset }) | 56 | Ok(FilePosition { file_id, offset }) |
57 | } | 57 | } |
58 | 58 | ||
59 | pub(crate) fn file_range( | 59 | pub(crate) fn file_range( |
60 | world: &GlobalStateSnapshot, | 60 | snap: &GlobalStateSnapshot, |
61 | text_document_identifier: lsp_types::TextDocumentIdentifier, | 61 | text_document_identifier: lsp_types::TextDocumentIdentifier, |
62 | range: lsp_types::Range, | 62 | range: lsp_types::Range, |
63 | ) -> Result<FileRange> { | 63 | ) -> Result<FileRange> { |
64 | let file_id = file_id(world, &text_document_identifier.uri)?; | 64 | let file_id = file_id(snap, &text_document_identifier.uri)?; |
65 | let line_index = world.file_line_index(file_id)?; | 65 | let line_index = snap.file_line_index(file_id)?; |
66 | let range = text_range(&line_index, range); | 66 | let range = text_range(&line_index, range); |
67 | Ok(FileRange { file_id, range }) | 67 | Ok(FileRange { file_id, range }) |
68 | } | 68 | } |
@@ -82,7 +82,7 @@ pub(crate) fn assist_kind(kind: lsp_types::CodeActionKind) -> Option<AssistKind> | |||
82 | } | 82 | } |
83 | 83 | ||
84 | pub(crate) fn annotation( | 84 | pub(crate) fn annotation( |
85 | world: &GlobalStateSnapshot, | 85 | snap: &GlobalStateSnapshot, |
86 | code_lens: lsp_types::CodeLens, | 86 | code_lens: lsp_types::CodeLens, |
87 | ) -> Result<Annotation> { | 87 | ) -> Result<Annotation> { |
88 | let data = code_lens.data.unwrap(); | 88 | let data = code_lens.data.unwrap(); |
@@ -91,25 +91,25 @@ pub(crate) fn annotation( | |||
91 | match resolve { | 91 | match resolve { |
92 | lsp_ext::CodeLensResolveData::Impls(params) => { | 92 | lsp_ext::CodeLensResolveData::Impls(params) => { |
93 | let file_id = | 93 | let file_id = |
94 | world.url_to_file_id(¶ms.text_document_position_params.text_document.uri)?; | 94 | snap.url_to_file_id(¶ms.text_document_position_params.text_document.uri)?; |
95 | let line_index = world.file_line_index(file_id)?; | 95 | let line_index = snap.file_line_index(file_id)?; |
96 | 96 | ||
97 | Ok(Annotation { | 97 | Ok(Annotation { |
98 | range: text_range(&line_index, code_lens.range), | 98 | range: text_range(&line_index, code_lens.range), |
99 | kind: AnnotationKind::HasImpls { | 99 | kind: AnnotationKind::HasImpls { |
100 | position: file_position(world, params.text_document_position_params)?, | 100 | position: file_position(snap, params.text_document_position_params)?, |
101 | data: None, | 101 | data: None, |
102 | }, | 102 | }, |
103 | }) | 103 | }) |
104 | } | 104 | } |
105 | lsp_ext::CodeLensResolveData::References(params) => { | 105 | lsp_ext::CodeLensResolveData::References(params) => { |
106 | let file_id = world.url_to_file_id(¶ms.text_document.uri)?; | 106 | let file_id = snap.url_to_file_id(¶ms.text_document.uri)?; |
107 | let line_index = world.file_line_index(file_id)?; | 107 | let line_index = snap.file_line_index(file_id)?; |
108 | 108 | ||
109 | Ok(Annotation { | 109 | Ok(Annotation { |
110 | range: text_range(&line_index, code_lens.range), | 110 | range: text_range(&line_index, code_lens.range), |
111 | kind: AnnotationKind::HasReferences { | 111 | kind: AnnotationKind::HasReferences { |
112 | position: file_position(world, params)?, | 112 | position: file_position(snap, params)?, |
113 | data: None, | 113 | data: None, |
114 | }, | 114 | }, |
115 | }) | 115 | }) |
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 52c249713..adeb7a97e 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -23,6 +23,7 @@ use crate::{ | |||
23 | document::DocumentData, | 23 | document::DocumentData, |
24 | from_proto, | 24 | from_proto, |
25 | line_index::{LineEndings, LineIndex}, | 25 | line_index::{LineEndings, LineIndex}, |
26 | lsp_ext, | ||
26 | main_loop::Task, | 27 | main_loop::Task, |
27 | op_queue::OpQueue, | 28 | op_queue::OpQueue, |
28 | reload::SourceRootConfig, | 29 | reload::SourceRootConfig, |
@@ -32,20 +33,6 @@ use crate::{ | |||
32 | Result, | 33 | Result, |
33 | }; | 34 | }; |
34 | 35 | ||
35 | #[derive(Eq, PartialEq, Copy, Clone)] | ||
36 | pub(crate) enum Status { | ||
37 | Loading, | ||
38 | Ready { partial: bool }, | ||
39 | Invalid, | ||
40 | NeedsReload, | ||
41 | } | ||
42 | |||
43 | impl Default for Status { | ||
44 | fn default() -> Self { | ||
45 | Status::Loading | ||
46 | } | ||
47 | } | ||
48 | |||
49 | // Enforces drop order | 36 | // Enforces drop order |
50 | pub(crate) struct Handle<H, C> { | 37 | pub(crate) struct Handle<H, C> { |
51 | pub(crate) handle: H, | 38 | pub(crate) handle: H, |
@@ -67,24 +54,37 @@ pub(crate) struct GlobalState { | |||
67 | req_queue: ReqQueue, | 54 | req_queue: ReqQueue, |
68 | pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>, | 55 | pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>, |
69 | pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>, | 56 | pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>, |
70 | pub(crate) vfs_config_version: u32, | ||
71 | pub(crate) flycheck: Vec<FlycheckHandle>, | ||
72 | pub(crate) flycheck_sender: Sender<flycheck::Message>, | ||
73 | pub(crate) flycheck_receiver: Receiver<flycheck::Message>, | ||
74 | pub(crate) config: Arc<Config>, | 57 | pub(crate) config: Arc<Config>, |
75 | pub(crate) analysis_host: AnalysisHost, | 58 | pub(crate) analysis_host: AnalysisHost, |
76 | pub(crate) diagnostics: DiagnosticCollection, | 59 | pub(crate) diagnostics: DiagnosticCollection, |
77 | pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>, | 60 | pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>, |
78 | pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>, | 61 | pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>, |
79 | pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, | ||
80 | pub(crate) shutdown_requested: bool, | 62 | pub(crate) shutdown_requested: bool, |
81 | pub(crate) status: Status, | 63 | pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>, |
82 | pub(crate) source_root_config: SourceRootConfig, | 64 | pub(crate) source_root_config: SourceRootConfig, |
83 | pub(crate) proc_macro_client: Option<ProcMacroClient>, | 65 | pub(crate) proc_macro_client: Option<ProcMacroClient>, |
66 | |||
67 | pub(crate) flycheck: Vec<FlycheckHandle>, | ||
68 | pub(crate) flycheck_sender: Sender<flycheck::Message>, | ||
69 | pub(crate) flycheck_receiver: Receiver<flycheck::Message>, | ||
70 | |||
71 | pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, | ||
72 | pub(crate) vfs_config_version: u32, | ||
73 | pub(crate) vfs_progress_config_version: u32, | ||
74 | pub(crate) vfs_progress_n_total: usize, | ||
75 | pub(crate) vfs_progress_n_done: usize, | ||
76 | |||
77 | /// For both `workspaces` and `workspace_build_data`, the field stores the | ||
78 | /// data we actually use, while the `OpQueue` stores the result of the last | ||
79 | /// fetch. | ||
80 | /// | ||
81 | /// If the fetch (partially) fails, we do not update the values. | ||
84 | pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, | 82 | pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, |
85 | pub(crate) fetch_workspaces_queue: OpQueue<()>, | 83 | pub(crate) fetch_workspaces_queue: OpQueue<(), Vec<anyhow::Result<ProjectWorkspace>>>, |
86 | pub(crate) workspace_build_data: Option<BuildDataResult>, | 84 | pub(crate) workspace_build_data: Option<BuildDataResult>, |
87 | pub(crate) fetch_build_data_queue: OpQueue<BuildDataCollector>, | 85 | pub(crate) fetch_build_data_queue: |
86 | OpQueue<BuildDataCollector, Option<anyhow::Result<BuildDataResult>>>, | ||
87 | |||
88 | latest_requests: Arc<RwLock<LatestRequests>>, | 88 | latest_requests: Arc<RwLock<LatestRequests>>, |
89 | } | 89 | } |
90 | 90 | ||
@@ -121,25 +121,32 @@ impl GlobalState { | |||
121 | GlobalState { | 121 | GlobalState { |
122 | sender, | 122 | sender, |
123 | req_queue: ReqQueue::default(), | 123 | req_queue: ReqQueue::default(), |
124 | vfs_config_version: 0, | ||
125 | task_pool, | 124 | task_pool, |
126 | loader, | 125 | loader, |
127 | flycheck: Vec::new(), | ||
128 | flycheck_sender, | ||
129 | flycheck_receiver, | ||
130 | config: Arc::new(config), | 126 | config: Arc::new(config), |
131 | analysis_host, | 127 | analysis_host, |
132 | diagnostics: Default::default(), | 128 | diagnostics: Default::default(), |
133 | mem_docs: FxHashMap::default(), | 129 | mem_docs: FxHashMap::default(), |
134 | semantic_tokens_cache: Arc::new(Default::default()), | 130 | semantic_tokens_cache: Arc::new(Default::default()), |
135 | vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))), | ||
136 | shutdown_requested: false, | 131 | shutdown_requested: false, |
137 | status: Status::default(), | 132 | last_reported_status: None, |
138 | source_root_config: SourceRootConfig::default(), | 133 | source_root_config: SourceRootConfig::default(), |
139 | proc_macro_client: None, | 134 | proc_macro_client: None, |
135 | |||
136 | flycheck: Vec::new(), | ||
137 | flycheck_sender, | ||
138 | flycheck_receiver, | ||
139 | |||
140 | vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))), | ||
141 | vfs_config_version: 0, | ||
142 | vfs_progress_config_version: 0, | ||
143 | vfs_progress_n_total: 0, | ||
144 | vfs_progress_n_done: 0, | ||
145 | |||
140 | workspaces: Arc::new(Vec::new()), | 146 | workspaces: Arc::new(Vec::new()), |
141 | fetch_workspaces_queue: OpQueue::default(), | 147 | fetch_workspaces_queue: OpQueue::default(), |
142 | workspace_build_data: None, | 148 | workspace_build_data: None, |
149 | |||
143 | fetch_build_data_queue: OpQueue::default(), | 150 | fetch_build_data_queue: OpQueue::default(), |
144 | latest_requests: Default::default(), | 151 | latest_requests: Default::default(), |
145 | } | 152 | } |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 53d29ddfc..1f59402e5 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -17,7 +17,7 @@ use lsp_server::ErrorCode; | |||
17 | use lsp_types::{ | 17 | use lsp_types::{ |
18 | CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, | 18 | CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, |
19 | CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, | 19 | CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, |
20 | CodeActionKind, CodeLens, CompletionItem, Diagnostic, DiagnosticTag, DocumentFormattingParams, | 20 | CodeLens, CompletionItem, Diagnostic, DiagnosticTag, DocumentFormattingParams, |
21 | DocumentHighlight, FoldingRange, FoldingRangeParams, HoverContents, Location, NumberOrString, | 21 | DocumentHighlight, FoldingRange, FoldingRangeParams, HoverContents, Location, NumberOrString, |
22 | Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensDeltaParams, | 22 | Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensDeltaParams, |
23 | SemanticTokensFullDeltaResult, SemanticTokensParams, SemanticTokensRangeParams, | 23 | SemanticTokensFullDeltaResult, SemanticTokensParams, SemanticTokensRangeParams, |
@@ -36,7 +36,7 @@ use crate::{ | |||
36 | diff::diff, | 36 | diff::diff, |
37 | from_proto, | 37 | from_proto, |
38 | global_state::{GlobalState, GlobalStateSnapshot}, | 38 | global_state::{GlobalState, GlobalStateSnapshot}, |
39 | line_index::{LineEndings, LineIndex}, | 39 | line_index::LineEndings, |
40 | lsp_ext::{self, InlayHint, InlayHintsParams}, | 40 | lsp_ext::{self, InlayHint, InlayHintsParams}, |
41 | lsp_utils::all_edits_are_disjoint, | 41 | lsp_utils::all_edits_are_disjoint, |
42 | to_proto, LspError, Result, | 42 | to_proto, LspError, Result, |
@@ -231,7 +231,6 @@ pub(crate) fn handle_on_enter( | |||
231 | Ok(Some(edit)) | 231 | Ok(Some(edit)) |
232 | } | 232 | } |
233 | 233 | ||
234 | // Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`. | ||
235 | pub(crate) fn handle_on_type_formatting( | 234 | pub(crate) fn handle_on_type_formatting( |
236 | snap: GlobalStateSnapshot, | 235 | snap: GlobalStateSnapshot, |
237 | params: lsp_types::DocumentOnTypeFormattingParams, | 236 | params: lsp_types::DocumentOnTypeFormattingParams, |
@@ -665,10 +664,13 @@ pub(crate) fn handle_completion( | |||
665 | }; | 664 | }; |
666 | let line_index = snap.file_line_index(position.file_id)?; | 665 | let line_index = snap.file_line_index(position.file_id)?; |
667 | 666 | ||
667 | let insert_replace_support = | ||
668 | snap.config.insert_replace_support().then(|| text_document_position.position); | ||
668 | let items: Vec<CompletionItem> = items | 669 | let items: Vec<CompletionItem> = items |
669 | .into_iter() | 670 | .into_iter() |
670 | .flat_map(|item| { | 671 | .flat_map(|item| { |
671 | let mut new_completion_items = to_proto::completion_item(&line_index, item.clone()); | 672 | let mut new_completion_items = |
673 | to_proto::completion_item(insert_replace_support, &line_index, item.clone()); | ||
672 | 674 | ||
673 | if completion_config.enable_imports_on_the_fly { | 675 | if completion_config.enable_imports_on_the_fly { |
674 | for new_item in &mut new_completion_items { | 676 | for new_item in &mut new_completion_items { |
@@ -927,19 +929,22 @@ pub(crate) fn handle_formatting( | |||
927 | let captured_stderr = String::from_utf8(output.stderr).unwrap_or_default(); | 929 | let captured_stderr = String::from_utf8(output.stderr).unwrap_or_default(); |
928 | 930 | ||
929 | if !output.status.success() { | 931 | if !output.status.success() { |
930 | match output.status.code() { | 932 | let rustfmt_not_installed = |
931 | Some(1) if !captured_stderr.contains("not installed") => { | 933 | captured_stderr.contains("not installed") || captured_stderr.contains("not available"); |
934 | |||
935 | return match output.status.code() { | ||
936 | Some(1) if !rustfmt_not_installed => { | ||
932 | // While `rustfmt` doesn't have a specific exit code for parse errors this is the | 937 | // While `rustfmt` doesn't have a specific exit code for parse errors this is the |
933 | // likely cause exiting with 1. Most Language Servers swallow parse errors on | 938 | // likely cause exiting with 1. Most Language Servers swallow parse errors on |
934 | // formatting because otherwise an error is surfaced to the user on top of the | 939 | // formatting because otherwise an error is surfaced to the user on top of the |
935 | // syntax error diagnostics they're already receiving. This is especially jarring | 940 | // syntax error diagnostics they're already receiving. This is especially jarring |
936 | // if they have format on save enabled. | 941 | // if they have format on save enabled. |
937 | log::info!("rustfmt exited with status 1, assuming parse error and ignoring"); | 942 | log::info!("rustfmt exited with status 1, assuming parse error and ignoring"); |
938 | return Ok(None); | 943 | Ok(None) |
939 | } | 944 | } |
940 | _ => { | 945 | _ => { |
941 | // Something else happened - e.g. `rustfmt` is missing or caught a signal | 946 | // Something else happened - e.g. `rustfmt` is missing or caught a signal |
942 | return Err(LspError::new( | 947 | Err(LspError::new( |
943 | -32900, | 948 | -32900, |
944 | format!( | 949 | format!( |
945 | r#"rustfmt exited with: | 950 | r#"rustfmt exited with: |
@@ -949,9 +954,9 @@ pub(crate) fn handle_formatting( | |||
949 | output.status, captured_stdout, captured_stderr, | 954 | output.status, captured_stdout, captured_stderr, |
950 | ), | 955 | ), |
951 | ) | 956 | ) |
952 | .into()); | 957 | .into()) |
953 | } | 958 | } |
954 | } | 959 | }; |
955 | } | 960 | } |
956 | 961 | ||
957 | let (new_text, new_line_endings) = LineEndings::normalize(captured_stdout); | 962 | let (new_text, new_line_endings) = LineEndings::normalize(captured_stdout); |
@@ -977,84 +982,52 @@ pub(crate) fn handle_code_action( | |||
977 | params: lsp_types::CodeActionParams, | 982 | params: lsp_types::CodeActionParams, |
978 | ) -> Result<Option<Vec<lsp_ext::CodeAction>>> { | 983 | ) -> Result<Option<Vec<lsp_ext::CodeAction>>> { |
979 | let _p = profile::span("handle_code_action"); | 984 | let _p = profile::span("handle_code_action"); |
980 | // We intentionally don't support command-based actions, as those either | 985 | |
981 | // requires custom client-code anyway, or requires server-initiated edits. | ||
982 | // Server initiated edits break causality, so we avoid those as well. | ||
983 | if !snap.config.code_action_literals() { | 986 | if !snap.config.code_action_literals() { |
987 | // We intentionally don't support command-based actions, as those either | ||
988 | // require either custom client-code or server-initiated edits. Server | ||
989 | // initiated edits break causality, so we avoid those. | ||
984 | return Ok(None); | 990 | return Ok(None); |
985 | } | 991 | } |
986 | 992 | ||
987 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | 993 | let line_index = |
988 | let line_index = snap.file_line_index(file_id)?; | 994 | snap.file_line_index(from_proto::file_id(&snap, ¶ms.text_document.uri)?)?; |
989 | let range = from_proto::text_range(&line_index, params.range); | 995 | let frange = from_proto::file_range(&snap, params.text_document.clone(), params.range)?; |
990 | let frange = FileRange { file_id, range }; | ||
991 | 996 | ||
992 | let mut assists_config = snap.config.assist(); | 997 | let mut assists_config = snap.config.assist(); |
993 | assists_config.allowed = params | 998 | assists_config.allowed = params |
994 | .clone() | ||
995 | .context | 999 | .context |
996 | .only | 1000 | .only |
1001 | .clone() | ||
997 | .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); | 1002 | .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); |
998 | 1003 | ||
999 | let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); | 1004 | let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); |
1000 | 1005 | ||
1001 | let include_quick_fixes = match ¶ms.context.only { | 1006 | let code_action_resolve_cap = snap.config.code_action_resolve(); |
1002 | Some(v) => v.iter().any(|it| { | 1007 | let assists = snap.analysis.assists_with_fixes( |
1003 | it == &lsp_types::CodeActionKind::EMPTY || it == &lsp_types::CodeActionKind::QUICKFIX | 1008 | &assists_config, |
1004 | }), | 1009 | &snap.config.diagnostics(), |
1005 | None => true, | 1010 | !code_action_resolve_cap, |
1006 | }; | 1011 | frange, |
1007 | if include_quick_fixes { | 1012 | )?; |
1008 | add_quick_fixes(&snap, frange, &line_index, &mut res)?; | 1013 | for (index, assist) in assists.into_iter().enumerate() { |
1009 | } | 1014 | let resolve_data = |
1010 | 1015 | if code_action_resolve_cap { Some((index, params.clone())) } else { None }; | |
1011 | if snap.config.code_action_resolve() { | 1016 | let code_action = to_proto::code_action(&snap, assist, resolve_data)?; |
1012 | for (index, assist) in | 1017 | res.push(code_action) |
1013 | snap.analysis.assists(&assists_config, false, frange)?.into_iter().enumerate() | ||
1014 | { | ||
1015 | res.push(to_proto::unresolved_code_action(&snap, params.clone(), assist, index)?); | ||
1016 | } | ||
1017 | } else { | ||
1018 | for assist in snap.analysis.assists(&assists_config, true, frange)?.into_iter() { | ||
1019 | res.push(to_proto::resolved_code_action(&snap, assist)?); | ||
1020 | } | ||
1021 | } | ||
1022 | |||
1023 | Ok(Some(res)) | ||
1024 | } | ||
1025 | |||
1026 | fn add_quick_fixes( | ||
1027 | snap: &GlobalStateSnapshot, | ||
1028 | frange: FileRange, | ||
1029 | line_index: &LineIndex, | ||
1030 | acc: &mut Vec<lsp_ext::CodeAction>, | ||
1031 | ) -> Result<()> { | ||
1032 | let diagnostics = snap.analysis.diagnostics(&snap.config.diagnostics(), frange.file_id)?; | ||
1033 | |||
1034 | for fix in diagnostics | ||
1035 | .into_iter() | ||
1036 | .filter_map(|d| d.fix) | ||
1037 | .filter(|fix| fix.fix_trigger_range.intersect(frange.range).is_some()) | ||
1038 | { | ||
1039 | let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?; | ||
1040 | let action = lsp_ext::CodeAction { | ||
1041 | title: fix.label.to_string(), | ||
1042 | group: None, | ||
1043 | kind: Some(CodeActionKind::QUICKFIX), | ||
1044 | edit: Some(edit), | ||
1045 | is_preferred: Some(false), | ||
1046 | data: None, | ||
1047 | }; | ||
1048 | acc.push(action); | ||
1049 | } | 1018 | } |
1050 | 1019 | ||
1020 | // Fixes from `cargo check`. | ||
1051 | for fix in snap.check_fixes.get(&frange.file_id).into_iter().flatten() { | 1021 | for fix in snap.check_fixes.get(&frange.file_id).into_iter().flatten() { |
1022 | // FIXME: this mapping is awkward and shouldn't exist. Refactor | ||
1023 | // `snap.check_fixes` to not convert to LSP prematurely. | ||
1052 | let fix_range = from_proto::text_range(&line_index, fix.range); | 1024 | let fix_range = from_proto::text_range(&line_index, fix.range); |
1053 | if fix_range.intersect(frange.range).is_some() { | 1025 | if fix_range.intersect(frange.range).is_some() { |
1054 | acc.push(fix.action.clone()); | 1026 | res.push(fix.action.clone()); |
1055 | } | 1027 | } |
1056 | } | 1028 | } |
1057 | Ok(()) | 1029 | |
1030 | Ok(Some(res)) | ||
1058 | } | 1031 | } |
1059 | 1032 | ||
1060 | pub(crate) fn handle_code_action_resolve( | 1033 | pub(crate) fn handle_code_action_resolve( |
@@ -1079,12 +1052,18 @@ pub(crate) fn handle_code_action_resolve( | |||
1079 | .only | 1052 | .only |
1080 | .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); | 1053 | .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); |
1081 | 1054 | ||
1082 | let assists = snap.analysis.assists(&assists_config, true, frange)?; | 1055 | let assists = snap.analysis.assists_with_fixes( |
1056 | &assists_config, | ||
1057 | &snap.config.diagnostics(), | ||
1058 | true, | ||
1059 | frange, | ||
1060 | )?; | ||
1061 | |||
1083 | let (id, index) = split_once(¶ms.id, ':').unwrap(); | 1062 | let (id, index) = split_once(¶ms.id, ':').unwrap(); |
1084 | let index = index.parse::<usize>().unwrap(); | 1063 | let index = index.parse::<usize>().unwrap(); |
1085 | let assist = &assists[index]; | 1064 | let assist = &assists[index]; |
1086 | assert!(assist.id.0 == id); | 1065 | assert!(assist.id.0 == id); |
1087 | let edit = to_proto::resolved_code_action(&snap, assist.clone())?.edit; | 1066 | let edit = to_proto::code_action(&snap, assist.clone(), None)?.edit; |
1088 | code_action.edit = edit; | 1067 | code_action.edit = edit; |
1089 | Ok(code_action) | 1068 | Ok(code_action) |
1090 | } | 1069 | } |
@@ -1203,7 +1182,7 @@ pub(crate) fn publish_diagnostics( | |||
1203 | 1182 | ||
1204 | let diagnostics: Vec<Diagnostic> = snap | 1183 | let diagnostics: Vec<Diagnostic> = snap |
1205 | .analysis | 1184 | .analysis |
1206 | .diagnostics(&snap.config.diagnostics(), file_id)? | 1185 | .diagnostics(&snap.config.diagnostics(), false, file_id)? |
1207 | .into_iter() | 1186 | .into_iter() |
1208 | .map(|d| Diagnostic { | 1187 | .map(|d| Diagnostic { |
1209 | range: to_proto::range(&line_index, d.range), | 1188 | range: to_proto::range(&line_index, d.range), |
@@ -1431,7 +1410,7 @@ pub(crate) fn handle_open_cargo_toml( | |||
1431 | pub(crate) fn handle_move_item( | 1410 | pub(crate) fn handle_move_item( |
1432 | snap: GlobalStateSnapshot, | 1411 | snap: GlobalStateSnapshot, |
1433 | params: lsp_ext::MoveItemParams, | 1412 | params: lsp_ext::MoveItemParams, |
1434 | ) -> Result<Option<lsp_types::TextDocumentEdit>> { | 1413 | ) -> Result<Vec<lsp_ext::SnippetTextEdit>> { |
1435 | let _p = profile::span("handle_move_item"); | 1414 | let _p = profile::span("handle_move_item"); |
1436 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | 1415 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
1437 | let range = from_proto::file_range(&snap, params.text_document, params.range)?; | 1416 | let range = from_proto::file_range(&snap, params.text_document, params.range)?; |
@@ -1442,8 +1421,11 @@ pub(crate) fn handle_move_item( | |||
1442 | }; | 1421 | }; |
1443 | 1422 | ||
1444 | match snap.analysis.move_item(range, direction)? { | 1423 | match snap.analysis.move_item(range, direction)? { |
1445 | Some(text_edit) => Ok(Some(to_proto::text_document_edit(&snap, file_id, text_edit)?)), | 1424 | Some(text_edit) => { |
1446 | None => Ok(None), | 1425 | let line_index = snap.file_line_index(file_id)?; |
1426 | Ok(to_proto::snippet_text_edit_vec(&line_index, true, text_edit)) | ||
1427 | } | ||
1428 | None => Ok(vec![]), | ||
1447 | } | 1429 | } |
1448 | } | 1430 | } |
1449 | 1431 | ||
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 0e1fec209..b8835a534 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -241,26 +241,26 @@ pub struct SsrParams { | |||
241 | pub selections: Vec<lsp_types::Range>, | 241 | pub selections: Vec<lsp_types::Range>, |
242 | } | 242 | } |
243 | 243 | ||
244 | pub enum StatusNotification {} | 244 | pub enum ServerStatusNotification {} |
245 | 245 | ||
246 | #[derive(Serialize, Deserialize)] | 246 | impl Notification for ServerStatusNotification { |
247 | #[serde(rename_all = "camelCase")] | 247 | type Params = ServerStatusParams; |
248 | pub enum Status { | 248 | const METHOD: &'static str = "experimental/serverStatus"; |
249 | Loading, | ||
250 | ReadyPartial, | ||
251 | Ready, | ||
252 | NeedsReload, | ||
253 | Invalid, | ||
254 | } | 249 | } |
255 | 250 | ||
256 | #[derive(Deserialize, Serialize)] | 251 | #[derive(Deserialize, Serialize, PartialEq, Eq, Clone)] |
257 | pub struct StatusParams { | 252 | pub struct ServerStatusParams { |
258 | pub status: Status, | 253 | pub health: Health, |
254 | pub quiescent: bool, | ||
255 | pub message: Option<String>, | ||
259 | } | 256 | } |
260 | 257 | ||
261 | impl Notification for StatusNotification { | 258 | #[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] |
262 | type Params = StatusParams; | 259 | #[serde(rename_all = "camelCase")] |
263 | const METHOD: &'static str = "rust-analyzer/status"; | 260 | pub enum Health { |
261 | Ok, | ||
262 | Warning, | ||
263 | Error, | ||
264 | } | 264 | } |
265 | 265 | ||
266 | pub enum CodeActionRequest {} | 266 | pub enum CodeActionRequest {} |
@@ -312,6 +312,9 @@ pub struct SnippetWorkspaceEdit { | |||
312 | pub changes: Option<HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>>>, | 312 | pub changes: Option<HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>>>, |
313 | #[serde(skip_serializing_if = "Option::is_none")] | 313 | #[serde(skip_serializing_if = "Option::is_none")] |
314 | pub document_changes: Option<Vec<SnippetDocumentChangeOperation>>, | 314 | pub document_changes: Option<Vec<SnippetDocumentChangeOperation>>, |
315 | #[serde(skip_serializing_if = "Option::is_none")] | ||
316 | pub change_annotations: | ||
317 | Option<HashMap<lsp_types::ChangeAnnotationIdentifier, lsp_types::ChangeAnnotation>>, | ||
315 | } | 318 | } |
316 | 319 | ||
317 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] | 320 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] |
@@ -335,6 +338,9 @@ pub struct SnippetTextEdit { | |||
335 | pub new_text: String, | 338 | pub new_text: String, |
336 | #[serde(skip_serializing_if = "Option::is_none")] | 339 | #[serde(skip_serializing_if = "Option::is_none")] |
337 | pub insert_text_format: Option<lsp_types::InsertTextFormat>, | 340 | pub insert_text_format: Option<lsp_types::InsertTextFormat>, |
341 | /// The annotation id if this is an annotated | ||
342 | #[serde(skip_serializing_if = "Option::is_none")] | ||
343 | pub annotation_id: Option<lsp_types::ChangeAnnotationIdentifier>, | ||
338 | } | 344 | } |
339 | 345 | ||
340 | pub enum HoverRequest {} | 346 | pub enum HoverRequest {} |
@@ -407,7 +413,7 @@ pub enum MoveItem {} | |||
407 | 413 | ||
408 | impl Request for MoveItem { | 414 | impl Request for MoveItem { |
409 | type Params = MoveItemParams; | 415 | type Params = MoveItemParams; |
410 | type Result = Option<lsp_types::TextDocumentEdit>; | 416 | type Result = Vec<SnippetTextEdit>; |
411 | const METHOD: &'static str = "experimental/moveItem"; | 417 | const METHOD: &'static str = "experimental/moveItem"; |
412 | } | 418 | } |
413 | 419 | ||
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs index 2ac487632..73c4193e8 100644 --- a/crates/rust-analyzer/src/lsp_utils.rs +++ b/crates/rust-analyzer/src/lsp_utils.rs | |||
@@ -150,8 +150,16 @@ pub(crate) fn all_edits_are_disjoint( | |||
150 | edit_ranges.push(edit.range); | 150 | edit_ranges.push(edit.range); |
151 | } | 151 | } |
152 | Some(lsp_types::CompletionTextEdit::InsertAndReplace(edit)) => { | 152 | Some(lsp_types::CompletionTextEdit::InsertAndReplace(edit)) => { |
153 | edit_ranges.push(edit.insert); | 153 | let replace = edit.replace; |
154 | edit_ranges.push(edit.replace); | 154 | let insert = edit.insert; |
155 | if replace.start != insert.start | ||
156 | || insert.start > insert.end | ||
157 | || insert.end > replace.end | ||
158 | { | ||
159 | // insert has to be a prefix of replace but it is not | ||
160 | return false; | ||
161 | } | ||
162 | edit_ranges.push(replace); | ||
155 | } | 163 | } |
156 | None => {} | 164 | None => {} |
157 | } | 165 | } |
@@ -314,18 +322,6 @@ mod tests { | |||
314 | Some(CompletionTextEdit::InsertAndReplace(InsertReplaceEdit { | 322 | Some(CompletionTextEdit::InsertAndReplace(InsertReplaceEdit { |
315 | new_text: "new_text".to_string(), | 323 | new_text: "new_text".to_string(), |
316 | insert: disjoint_edit.range, | 324 | insert: disjoint_edit.range, |
317 | replace: joint_edit.range, | ||
318 | })); | ||
319 | completion_with_joint_edits.additional_text_edits = None; | ||
320 | assert!( | ||
321 | !all_edits_are_disjoint(&completion_with_joint_edits, &[]), | ||
322 | "Completion with disjoint edits fails the validation even with empty extra edits" | ||
323 | ); | ||
324 | |||
325 | completion_with_joint_edits.text_edit = | ||
326 | Some(CompletionTextEdit::InsertAndReplace(InsertReplaceEdit { | ||
327 | new_text: "new_text".to_string(), | ||
328 | insert: disjoint_edit.range, | ||
329 | replace: disjoint_edit_2.range, | 325 | replace: disjoint_edit_2.range, |
330 | })); | 326 | })); |
331 | completion_with_joint_edits.additional_text_edits = Some(vec![joint_edit]); | 327 | completion_with_joint_edits.additional_text_edits = Some(vec![joint_edit]); |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index e88f16cc1..6ea775d68 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -2,6 +2,7 @@ | |||
2 | //! requests/replies and notifications back to the client. | 2 | //! requests/replies and notifications back to the client. |
3 | use std::{ | 3 | use std::{ |
4 | env, fmt, | 4 | env, fmt, |
5 | sync::Arc, | ||
5 | time::{Duration, Instant}, | 6 | time::{Duration, Instant}, |
6 | }; | 7 | }; |
7 | 8 | ||
@@ -12,6 +13,7 @@ use ide::{Canceled, FileId}; | |||
12 | use ide_db::base_db::VfsPath; | 13 | use ide_db::base_db::VfsPath; |
13 | use lsp_server::{Connection, Notification, Request, Response}; | 14 | use lsp_server::{Connection, Notification, Request, Response}; |
14 | use lsp_types::notification::Notification as _; | 15 | use lsp_types::notification::Notification as _; |
16 | use project_model::BuildDataCollector; | ||
15 | use vfs::ChangeKind; | 17 | use vfs::ChangeKind; |
16 | 18 | ||
17 | use crate::{ | 19 | use crate::{ |
@@ -19,7 +21,7 @@ use crate::{ | |||
19 | dispatch::{NotificationDispatcher, RequestDispatcher}, | 21 | dispatch::{NotificationDispatcher, RequestDispatcher}, |
20 | document::DocumentData, | 22 | document::DocumentData, |
21 | from_proto, | 23 | from_proto, |
22 | global_state::{file_id_to_url, url_to_file_id, GlobalState, Status}, | 24 | global_state::{file_id_to_url, url_to_file_id, GlobalState}, |
23 | handlers, lsp_ext, | 25 | handlers, lsp_ext, |
24 | lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress}, | 26 | lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress}, |
25 | reload::{BuildDataProgress, ProjectWorkspaceProgress}, | 27 | reload::{BuildDataProgress, ProjectWorkspaceProgress}, |
@@ -187,7 +189,7 @@ impl GlobalState { | |||
187 | log::info!("task queue len: {}", task_queue_len); | 189 | log::info!("task queue len: {}", task_queue_len); |
188 | } | 190 | } |
189 | 191 | ||
190 | let mut new_status = self.status; | 192 | let was_quiescent = self.is_quiescent(); |
191 | match event { | 193 | match event { |
192 | Event::Lsp(msg) => match msg { | 194 | Event::Lsp(msg) => match msg { |
193 | lsp_server::Message::Request(req) => self.on_request(loop_start, req)?, | 195 | lsp_server::Message::Request(req) => self.on_request(loop_start, req)?, |
@@ -227,12 +229,26 @@ impl GlobalState { | |||
227 | (Progress::Report, Some(msg)) | 229 | (Progress::Report, Some(msg)) |
228 | } | 230 | } |
229 | ProjectWorkspaceProgress::End(workspaces) => { | 231 | ProjectWorkspaceProgress::End(workspaces) => { |
230 | self.fetch_workspaces_completed(); | 232 | self.fetch_workspaces_completed(workspaces); |
231 | self.switch_workspaces(workspaces, None); | 233 | |
234 | let old = Arc::clone(&self.workspaces); | ||
235 | self.switch_workspaces(); | ||
236 | let workspaces_updated = !Arc::ptr_eq(&old, &self.workspaces); | ||
237 | |||
238 | if self.config.run_build_scripts() && workspaces_updated { | ||
239 | let mut collector = | ||
240 | BuildDataCollector::new(self.config.wrap_rustc()); | ||
241 | for ws in self.workspaces.iter() { | ||
242 | ws.collect_build_data_configs(&mut collector); | ||
243 | } | ||
244 | self.fetch_build_data_request(collector) | ||
245 | } | ||
246 | |||
232 | (Progress::End, None) | 247 | (Progress::End, None) |
233 | } | 248 | } |
234 | }; | 249 | }; |
235 | self.report_progress("fetching", state, msg, None); | 250 | |
251 | self.report_progress("Fetching", state, msg, None); | ||
236 | } | 252 | } |
237 | Task::FetchBuildData(progress) => { | 253 | Task::FetchBuildData(progress) => { |
238 | let (state, msg) = match progress { | 254 | let (state, msg) = match progress { |
@@ -240,19 +256,21 @@ impl GlobalState { | |||
240 | BuildDataProgress::Report(msg) => { | 256 | BuildDataProgress::Report(msg) => { |
241 | (Some(Progress::Report), Some(msg)) | 257 | (Some(Progress::Report), Some(msg)) |
242 | } | 258 | } |
243 | BuildDataProgress::End(collector) => { | 259 | BuildDataProgress::End(build_data_result) => { |
244 | self.fetch_build_data_completed(); | 260 | self.fetch_build_data_completed(build_data_result); |
245 | let workspaces = | 261 | |
246 | (*self.workspaces).clone().into_iter().map(Ok).collect(); | 262 | self.switch_workspaces(); |
247 | self.switch_workspaces(workspaces, Some(collector)); | 263 | |
248 | (Some(Progress::End), None) | 264 | (Some(Progress::End), None) |
249 | } | 265 | } |
250 | }; | 266 | }; |
267 | |||
251 | if let Some(state) = state { | 268 | if let Some(state) = state { |
252 | self.report_progress("loading", state, msg, None); | 269 | self.report_progress("Loading", state, msg, None); |
253 | } | 270 | } |
254 | } | 271 | } |
255 | } | 272 | } |
273 | |||
256 | // Coalesce multiple task events into one loop turn | 274 | // Coalesce multiple task events into one loop turn |
257 | task = match self.task_pool.receiver.try_recv() { | 275 | task = match self.task_pool.receiver.try_recv() { |
258 | Ok(task) => task, | 276 | Ok(task) => task, |
@@ -280,7 +298,7 @@ impl GlobalState { | |||
280 | } | 298 | } |
281 | }; | 299 | }; |
282 | 300 | ||
283 | self.report_progress("indexing", state, message, Some(fraction)); | 301 | self.report_progress("Indexing", state, message, Some(fraction)); |
284 | } | 302 | } |
285 | } | 303 | } |
286 | Event::Vfs(mut task) => { | 304 | Event::Vfs(mut task) => { |
@@ -298,30 +316,25 @@ impl GlobalState { | |||
298 | } | 316 | } |
299 | vfs::loader::Message::Progress { n_total, n_done, config_version } => { | 317 | vfs::loader::Message::Progress { n_total, n_done, config_version } => { |
300 | always!(config_version <= self.vfs_config_version); | 318 | always!(config_version <= self.vfs_config_version); |
301 | if n_total == 0 { | 319 | |
302 | new_status = Status::Invalid; | 320 | self.vfs_progress_config_version = config_version; |
321 | self.vfs_progress_n_total = n_total; | ||
322 | self.vfs_progress_n_done = n_done; | ||
323 | |||
324 | let state = if n_done == 0 { | ||
325 | Progress::Begin | ||
326 | } else if n_done < n_total { | ||
327 | Progress::Report | ||
303 | } else { | 328 | } else { |
304 | let state = if n_done == 0 { | 329 | assert_eq!(n_done, n_total); |
305 | new_status = Status::Loading; | 330 | Progress::End |
306 | Progress::Begin | 331 | }; |
307 | } else if n_done < n_total { | 332 | self.report_progress( |
308 | Progress::Report | 333 | "Roots Scanned", |
309 | } else { | 334 | state, |
310 | assert_eq!(n_done, n_total); | 335 | Some(format!("{}/{}", n_done, n_total)), |
311 | new_status = Status::Ready { | 336 | Some(Progress::fraction(n_done, n_total)), |
312 | partial: self.config.run_build_scripts() | 337 | ) |
313 | && self.workspace_build_data.is_none() | ||
314 | || config_version < self.vfs_config_version, | ||
315 | }; | ||
316 | Progress::End | ||
317 | }; | ||
318 | self.report_progress( | ||
319 | "roots scanned", | ||
320 | state, | ||
321 | Some(format!("{}/{}", n_done, n_total)), | ||
322 | Some(Progress::fraction(n_done, n_total)), | ||
323 | ) | ||
324 | } | ||
325 | } | 338 | } |
326 | } | 339 | } |
327 | // Coalesce many VFS event into a single loop turn | 340 | // Coalesce many VFS event into a single loop turn |
@@ -397,18 +410,14 @@ impl GlobalState { | |||
397 | } | 410 | } |
398 | 411 | ||
399 | let state_changed = self.process_changes(); | 412 | let state_changed = self.process_changes(); |
400 | let prev_status = self.status; | 413 | |
401 | if prev_status != new_status { | 414 | if self.is_quiescent() && !was_quiescent { |
402 | self.transition(new_status); | ||
403 | } | ||
404 | let is_ready = matches!(self.status, Status::Ready { .. }); | ||
405 | if prev_status == Status::Loading && is_ready { | ||
406 | for flycheck in &self.flycheck { | 415 | for flycheck in &self.flycheck { |
407 | flycheck.update(); | 416 | flycheck.update(); |
408 | } | 417 | } |
409 | } | 418 | } |
410 | 419 | ||
411 | if is_ready && (state_changed || prev_status == Status::Loading) { | 420 | if self.is_quiescent() && (!was_quiescent || state_changed) { |
412 | self.update_file_notifications_on_threadpool(); | 421 | self.update_file_notifications_on_threadpool(); |
413 | 422 | ||
414 | // Refresh semantic tokens if the client supports it. | 423 | // Refresh semantic tokens if the client supports it. |
@@ -437,9 +446,13 @@ impl GlobalState { | |||
437 | } | 446 | } |
438 | } | 447 | } |
439 | 448 | ||
440 | self.fetch_workspaces_if_needed(); | 449 | if self.config.cargo_autoreload() { |
450 | self.fetch_workspaces_if_needed(); | ||
451 | } | ||
441 | self.fetch_build_data_if_needed(); | 452 | self.fetch_build_data_if_needed(); |
442 | 453 | ||
454 | self.report_new_status_if_needed(); | ||
455 | |||
443 | let loop_duration = loop_start.elapsed(); | 456 | let loop_duration = loop_start.elapsed(); |
444 | if loop_duration > Duration::from_millis(100) { | 457 | if loop_duration > Duration::from_millis(100) { |
445 | log::warn!("overly long loop turn: {:?}", loop_duration); | 458 | log::warn!("overly long loop turn: {:?}", loop_duration); |
@@ -466,18 +479,23 @@ impl GlobalState { | |||
466 | return Ok(()); | 479 | return Ok(()); |
467 | } | 480 | } |
468 | 481 | ||
469 | if self.status == Status::Loading && req.method != "shutdown" { | 482 | // Avoid flashing a bunch of unresolved references during initial load. |
483 | if self.workspaces.is_empty() && !self.is_quiescent() { | ||
470 | self.respond(lsp_server::Response::new_err( | 484 | self.respond(lsp_server::Response::new_err( |
471 | req.id, | 485 | req.id, |
472 | // FIXME: i32 should impl From<ErrorCode> (from() guarantees lossless conversion) | 486 | // FIXME: i32 should impl From<ErrorCode> (from() guarantees lossless conversion) |
473 | lsp_server::ErrorCode::ContentModified as i32, | 487 | lsp_server::ErrorCode::ContentModified as i32, |
474 | "Rust Analyzer is still loading...".to_owned(), | 488 | "waiting for cargo metadata or cargo check".to_owned(), |
475 | )); | 489 | )); |
476 | return Ok(()); | 490 | return Ok(()); |
477 | } | 491 | } |
478 | 492 | ||
479 | RequestDispatcher { req: Some(req), global_state: self } | 493 | RequestDispatcher { req: Some(req), global_state: self } |
480 | .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.fetch_workspaces_request()))? | 494 | .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| { |
495 | s.fetch_workspaces_request(); | ||
496 | s.fetch_workspaces_if_needed(); | ||
497 | Ok(()) | ||
498 | })? | ||
481 | .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))? | 499 | .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))? |
482 | .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))? | 500 | .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))? |
483 | .on_sync::<lsp_types::request::Shutdown>(|s, ()| { | 501 | .on_sync::<lsp_types::request::Shutdown>(|s, ()| { |
diff --git a/crates/rust-analyzer/src/markdown.rs b/crates/rust-analyzer/src/markdown.rs index 865eaae9b..35eaffba8 100644 --- a/crates/rust-analyzer/src/markdown.rs +++ b/crates/rust-analyzer/src/markdown.rs | |||
@@ -1,17 +1,7 @@ | |||
1 | //! Transforms markdown | 1 | //! Transforms markdown |
2 | use ide_db::helpers::rust_doc::is_rust_fence; | ||
2 | 3 | ||
3 | const RUSTDOC_FENCE: &str = "```"; | 4 | const RUSTDOC_FENCE: &str = "```"; |
4 | const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC: &[&str] = &[ | ||
5 | "", | ||
6 | "rust", | ||
7 | "should_panic", | ||
8 | "ignore", | ||
9 | "no_run", | ||
10 | "compile_fail", | ||
11 | "edition2015", | ||
12 | "edition2018", | ||
13 | "edition2021", | ||
14 | ]; | ||
15 | 5 | ||
16 | pub(crate) fn format_docs(src: &str) -> String { | 6 | pub(crate) fn format_docs(src: &str) -> String { |
17 | let mut processed_lines = Vec::new(); | 7 | let mut processed_lines = Vec::new(); |
@@ -27,9 +17,7 @@ pub(crate) fn format_docs(src: &str) -> String { | |||
27 | in_code_block ^= true; | 17 | in_code_block ^= true; |
28 | 18 | ||
29 | if in_code_block { | 19 | if in_code_block { |
30 | is_rust = header | 20 | is_rust = is_rust_fence(header); |
31 | .split(',') | ||
32 | .all(|sub| RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC.contains(&sub.trim())); | ||
33 | 21 | ||
34 | if is_rust { | 22 | if is_rust { |
35 | line = "```rust"; | 23 | line = "```rust"; |
@@ -82,6 +70,12 @@ mod tests { | |||
82 | } | 70 | } |
83 | 71 | ||
84 | #[test] | 72 | #[test] |
73 | fn test_format_docs_handles_error_codes() { | ||
74 | let comment = "```compile_fail,E0641\nlet b = 0 as *const _;\n```"; | ||
75 | assert_eq!(format_docs(comment), "```rust\nlet b = 0 as *const _;\n```"); | ||
76 | } | ||
77 | |||
78 | #[test] | ||
85 | fn test_format_docs_skips_comments_in_rust_block() { | 79 | fn test_format_docs_skips_comments_in_rust_block() { |
86 | let comment = | 80 | let comment = |
87 | "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```"; | 81 | "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```"; |
diff --git a/crates/rust-analyzer/src/op_queue.rs b/crates/rust-analyzer/src/op_queue.rs index 761b9ad39..1d612a933 100644 --- a/crates/rust-analyzer/src/op_queue.rs +++ b/crates/rust-analyzer/src/op_queue.rs | |||
@@ -1,29 +1,43 @@ | |||
1 | //! Bookkeeping to make sure only one long-running operation is executed. | 1 | //! Bookkeeping to make sure only one long-running operation is being executed |
2 | //! at a time. | ||
2 | 3 | ||
3 | pub(crate) struct OpQueue<D> { | 4 | pub(crate) struct OpQueue<Args, Output> { |
4 | op_scheduled: Option<D>, | 5 | op_requested: Option<Args>, |
5 | op_in_progress: bool, | 6 | op_in_progress: bool, |
7 | last_op_result: Output, | ||
6 | } | 8 | } |
7 | 9 | ||
8 | impl<D> Default for OpQueue<D> { | 10 | impl<Args, Output: Default> Default for OpQueue<Args, Output> { |
9 | fn default() -> Self { | 11 | fn default() -> Self { |
10 | Self { op_scheduled: None, op_in_progress: false } | 12 | Self { op_requested: None, op_in_progress: false, last_op_result: Default::default() } |
11 | } | 13 | } |
12 | } | 14 | } |
13 | 15 | ||
14 | impl<D> OpQueue<D> { | 16 | impl<Args, Output> OpQueue<Args, Output> { |
15 | pub(crate) fn request_op(&mut self, data: D) { | 17 | pub(crate) fn request_op(&mut self, data: Args) { |
16 | self.op_scheduled = Some(data); | 18 | self.op_requested = Some(data); |
17 | } | 19 | } |
18 | pub(crate) fn should_start_op(&mut self) -> Option<D> { | 20 | pub(crate) fn should_start_op(&mut self) -> Option<Args> { |
19 | if self.op_in_progress { | 21 | if self.op_in_progress { |
20 | return None; | 22 | return None; |
21 | } | 23 | } |
22 | self.op_in_progress = self.op_scheduled.is_some(); | 24 | self.op_in_progress = self.op_requested.is_some(); |
23 | self.op_scheduled.take() | 25 | self.op_requested.take() |
24 | } | 26 | } |
25 | pub(crate) fn op_completed(&mut self) { | 27 | pub(crate) fn op_completed(&mut self, result: Output) { |
26 | assert!(self.op_in_progress); | 28 | assert!(self.op_in_progress); |
27 | self.op_in_progress = false; | 29 | self.op_in_progress = false; |
30 | self.last_op_result = result; | ||
31 | } | ||
32 | |||
33 | #[allow(unused)] | ||
34 | pub(crate) fn last_op_result(&self) -> &Output { | ||
35 | &self.last_op_result | ||
36 | } | ||
37 | pub(crate) fn op_in_progress(&self) -> bool { | ||
38 | self.op_in_progress | ||
39 | } | ||
40 | pub(crate) fn op_requested(&self) -> bool { | ||
41 | self.op_requested.is_some() | ||
28 | } | 42 | } |
29 | } | 43 | } |
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 76fdbcddd..0ae2758cc 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs | |||
@@ -9,11 +9,10 @@ use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind}; | |||
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | config::{Config, FilesWatcher, LinkedProject}, | 11 | config::{Config, FilesWatcher, LinkedProject}, |
12 | global_state::{GlobalState, Status}, | 12 | global_state::GlobalState, |
13 | lsp_ext, | 13 | lsp_ext, |
14 | main_loop::Task, | 14 | main_loop::Task, |
15 | }; | 15 | }; |
16 | use lsp_ext::StatusParams; | ||
17 | 16 | ||
18 | #[derive(Debug)] | 17 | #[derive(Debug)] |
19 | pub(crate) enum ProjectWorkspaceProgress { | 18 | pub(crate) enum ProjectWorkspaceProgress { |
@@ -30,6 +29,13 @@ pub(crate) enum BuildDataProgress { | |||
30 | } | 29 | } |
31 | 30 | ||
32 | impl GlobalState { | 31 | impl GlobalState { |
32 | pub(crate) fn is_quiescent(&self) -> bool { | ||
33 | !(self.fetch_workspaces_queue.op_in_progress() | ||
34 | || self.fetch_build_data_queue.op_in_progress() | ||
35 | || self.vfs_progress_config_version < self.vfs_config_version | ||
36 | || self.vfs_progress_n_done < self.vfs_progress_n_total) | ||
37 | } | ||
38 | |||
33 | pub(crate) fn update_configuration(&mut self, config: Config) { | 39 | pub(crate) fn update_configuration(&mut self, config: Config) { |
34 | let _p = profile::span("GlobalState::update_configuration"); | 40 | let _p = profile::span("GlobalState::update_configuration"); |
35 | let old_config = mem::replace(&mut self.config, Arc::new(config)); | 41 | let old_config = mem::replace(&mut self.config, Arc::new(config)); |
@@ -46,25 +52,17 @@ impl GlobalState { | |||
46 | if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) { | 52 | if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) { |
47 | return; | 53 | return; |
48 | } | 54 | } |
49 | match self.status { | ||
50 | Status::Loading | Status::NeedsReload => return, | ||
51 | Status::Ready { .. } | Status::Invalid => (), | ||
52 | } | ||
53 | log::info!( | 55 | log::info!( |
54 | "Reloading workspace because of the following changes: {}", | 56 | "Requesting workspace reload because of the following changes: {}", |
55 | itertools::join( | 57 | itertools::join( |
56 | changes | 58 | changes |
57 | .iter() | 59 | .iter() |
58 | .filter(|(path, kind)| is_interesting(path, *kind)) | 60 | .filter(|(path, kind)| is_interesting(path, *kind)) |
59 | .map(|(path, kind)| format!("{}/{:?}", path.display(), kind)), | 61 | .map(|(path, kind)| format!("{}: {:?}", path.display(), kind)), |
60 | ", " | 62 | ", " |
61 | ) | 63 | ) |
62 | ); | 64 | ); |
63 | if self.config.cargo_autoreload() { | 65 | self.fetch_workspaces_request(); |
64 | self.fetch_workspaces_request(); | ||
65 | } else { | ||
66 | self.transition(Status::NeedsReload); | ||
67 | } | ||
68 | 66 | ||
69 | fn is_interesting(path: &AbsPath, change_kind: ChangeKind) -> bool { | 67 | fn is_interesting(path: &AbsPath, change_kind: ChangeKind) -> bool { |
70 | const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"]; | 68 | const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"]; |
@@ -101,46 +99,41 @@ impl GlobalState { | |||
101 | false | 99 | false |
102 | } | 100 | } |
103 | } | 101 | } |
104 | pub(crate) fn transition(&mut self, new_status: Status) { | 102 | pub(crate) fn report_new_status_if_needed(&mut self) { |
105 | self.status = new_status; | 103 | let mut status = lsp_ext::ServerStatusParams { |
106 | if self.config.status_notification() { | 104 | health: lsp_ext::Health::Ok, |
107 | let lsp_status = match new_status { | 105 | quiescent: self.is_quiescent(), |
108 | Status::Loading => lsp_ext::Status::Loading, | 106 | message: None, |
109 | Status::Ready { partial: true } => lsp_ext::Status::ReadyPartial, | 107 | }; |
110 | Status::Ready { partial: false } => lsp_ext::Status::Ready, | 108 | |
111 | Status::Invalid => lsp_ext::Status::Invalid, | 109 | if let Some(error) = self.build_data_error() { |
112 | Status::NeedsReload => lsp_ext::Status::NeedsReload, | 110 | status.health = lsp_ext::Health::Warning; |
113 | }; | 111 | status.message = Some(error) |
114 | self.send_notification::<lsp_ext::StatusNotification>(StatusParams { | 112 | } |
115 | status: lsp_status, | 113 | if !self.config.cargo_autoreload() |
116 | }); | 114 | && self.is_quiescent() |
115 | && self.fetch_workspaces_queue.op_requested() | ||
116 | { | ||
117 | status.health = lsp_ext::Health::Warning; | ||
118 | status.message = Some("Workspace reload required".to_string()) | ||
117 | } | 119 | } |
118 | } | ||
119 | 120 | ||
120 | pub(crate) fn fetch_build_data_request(&mut self, build_data_collector: BuildDataCollector) { | 121 | if let Some(error) = self.fetch_workspace_error() { |
121 | self.fetch_build_data_queue.request_op(build_data_collector); | 122 | status.health = lsp_ext::Health::Error; |
122 | } | 123 | status.message = Some(error) |
124 | } | ||
123 | 125 | ||
124 | pub(crate) fn fetch_build_data_if_needed(&mut self) { | 126 | if self.last_reported_status.as_ref() != Some(&status) { |
125 | let mut build_data_collector = match self.fetch_build_data_queue.should_start_op() { | 127 | self.last_reported_status = Some(status.clone()); |
126 | Some(it) => it, | ||
127 | None => return, | ||
128 | }; | ||
129 | self.task_pool.handle.spawn_with_sender(move |sender| { | ||
130 | sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap(); | ||
131 | 128 | ||
132 | let progress = { | 129 | if let (lsp_ext::Health::Error, Some(message)) = (status.health, &status.message) { |
133 | let sender = sender.clone(); | 130 | self.show_message(lsp_types::MessageType::Error, message.clone()); |
134 | move |msg| { | 131 | } |
135 | sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap() | 132 | |
136 | } | 133 | if self.config.server_status_notification() { |
137 | }; | 134 | self.send_notification::<lsp_ext::ServerStatusNotification>(status); |
138 | let res = build_data_collector.collect(&progress); | 135 | } |
139 | sender.send(Task::FetchBuildData(BuildDataProgress::End(res))).unwrap(); | 136 | } |
140 | }); | ||
141 | } | ||
142 | pub(crate) fn fetch_build_data_completed(&mut self) { | ||
143 | self.fetch_build_data_queue.op_completed() | ||
144 | } | 137 | } |
145 | 138 | ||
146 | pub(crate) fn fetch_workspaces_request(&mut self) { | 139 | pub(crate) fn fetch_workspaces_request(&mut self) { |
@@ -194,54 +187,69 @@ impl GlobalState { | |||
194 | } | 187 | } |
195 | }); | 188 | }); |
196 | } | 189 | } |
197 | pub(crate) fn fetch_workspaces_completed(&mut self) { | 190 | pub(crate) fn fetch_workspaces_completed( |
198 | self.fetch_workspaces_queue.op_completed() | 191 | &mut self, |
192 | workspaces: Vec<anyhow::Result<ProjectWorkspace>>, | ||
193 | ) { | ||
194 | self.fetch_workspaces_queue.op_completed(workspaces) | ||
195 | } | ||
196 | |||
197 | pub(crate) fn fetch_build_data_request(&mut self, build_data_collector: BuildDataCollector) { | ||
198 | self.fetch_build_data_queue.request_op(build_data_collector); | ||
199 | } | 199 | } |
200 | pub(crate) fn fetch_build_data_if_needed(&mut self) { | ||
201 | let mut build_data_collector = match self.fetch_build_data_queue.should_start_op() { | ||
202 | Some(it) => it, | ||
203 | None => return, | ||
204 | }; | ||
205 | self.task_pool.handle.spawn_with_sender(move |sender| { | ||
206 | sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap(); | ||
200 | 207 | ||
201 | pub(crate) fn switch_workspaces( | 208 | let progress = { |
209 | let sender = sender.clone(); | ||
210 | move |msg| { | ||
211 | sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap() | ||
212 | } | ||
213 | }; | ||
214 | let res = build_data_collector.collect(&progress); | ||
215 | sender.send(Task::FetchBuildData(BuildDataProgress::End(res))).unwrap(); | ||
216 | }); | ||
217 | } | ||
218 | pub(crate) fn fetch_build_data_completed( | ||
202 | &mut self, | 219 | &mut self, |
203 | workspaces: Vec<anyhow::Result<ProjectWorkspace>>, | 220 | build_data: anyhow::Result<BuildDataResult>, |
204 | workspace_build_data: Option<anyhow::Result<BuildDataResult>>, | ||
205 | ) { | 221 | ) { |
206 | let _p = profile::span("GlobalState::switch_workspaces"); | 222 | self.fetch_build_data_queue.op_completed(Some(build_data)) |
207 | log::info!("will switch workspaces: {:?}", workspaces); | 223 | } |
208 | 224 | ||
209 | let mut has_errors = false; | 225 | pub(crate) fn switch_workspaces(&mut self) { |
210 | let workspaces = workspaces | 226 | let _p = profile::span("GlobalState::switch_workspaces"); |
211 | .into_iter() | 227 | log::info!("will switch workspaces"); |
212 | .filter_map(|res| { | ||
213 | res.map_err(|err| { | ||
214 | has_errors = true; | ||
215 | log::error!("failed to load workspace: {:#}", err); | ||
216 | if self.workspaces.is_empty() { | ||
217 | self.show_message( | ||
218 | lsp_types::MessageType::Error, | ||
219 | format!("rust-analyzer failed to load workspace: {:#}", err), | ||
220 | ); | ||
221 | } | ||
222 | }) | ||
223 | .ok() | ||
224 | }) | ||
225 | .collect::<Vec<_>>(); | ||
226 | 228 | ||
227 | let workspace_build_data = match workspace_build_data { | 229 | if let Some(error_message) = self.fetch_workspace_error() { |
228 | Some(Ok(it)) => Some(it), | 230 | log::error!("failed to switch workspaces: {}", error_message); |
229 | Some(Err(err)) => { | 231 | if !self.workspaces.is_empty() { |
230 | log::error!("failed to fetch build data: {:#}", err); | ||
231 | self.show_message( | ||
232 | lsp_types::MessageType::Error, | ||
233 | format!("rust-analyzer failed to fetch build data: {:#}", err), | ||
234 | ); | ||
235 | return; | 232 | return; |
236 | } | 233 | } |
237 | None => None, | 234 | } |
238 | }; | ||
239 | 235 | ||
240 | if *self.workspaces == workspaces && self.workspace_build_data == workspace_build_data { | 236 | if let Some(error_message) = self.build_data_error() { |
241 | return; | 237 | log::error!("failed to switch build data: {}", error_message); |
242 | } | 238 | } |
243 | 239 | ||
244 | if !self.workspaces.is_empty() && has_errors { | 240 | let workspaces = self |
241 | .fetch_workspaces_queue | ||
242 | .last_op_result() | ||
243 | .iter() | ||
244 | .filter_map(|res| res.as_ref().ok().cloned()) | ||
245 | .collect::<Vec<_>>(); | ||
246 | |||
247 | let workspace_build_data = match self.fetch_build_data_queue.last_op_result() { | ||
248 | Some(Ok(it)) => Some(it.clone()), | ||
249 | None | Some(Err(_)) => None, | ||
250 | }; | ||
251 | |||
252 | if *self.workspaces == workspaces && self.workspace_build_data == workspace_build_data { | ||
245 | return; | 253 | return; |
246 | } | 254 | } |
247 | 255 | ||
@@ -314,6 +322,7 @@ impl GlobalState { | |||
314 | let loader = &mut self.loader; | 322 | let loader = &mut self.loader; |
315 | let mem_docs = &self.mem_docs; | 323 | let mem_docs = &self.mem_docs; |
316 | let mut load = |path: &AbsPath| { | 324 | let mut load = |path: &AbsPath| { |
325 | let _p = profile::span("GlobalState::load"); | ||
317 | let vfs_path = vfs::VfsPath::from(path.to_path_buf()); | 326 | let vfs_path = vfs::VfsPath::from(path.to_path_buf()); |
318 | if !mem_docs.contains_key(&vfs_path) { | 327 | if !mem_docs.contains_key(&vfs_path) { |
319 | let contents = loader.handle.load_sync(path); | 328 | let contents = loader.handle.load_sync(path); |
@@ -337,14 +346,6 @@ impl GlobalState { | |||
337 | }; | 346 | }; |
338 | change.set_crate_graph(crate_graph); | 347 | change.set_crate_graph(crate_graph); |
339 | 348 | ||
340 | if self.config.run_build_scripts() && workspace_build_data.is_none() { | ||
341 | let mut collector = BuildDataCollector::default(); | ||
342 | for ws in &workspaces { | ||
343 | ws.collect_build_data_configs(&mut collector); | ||
344 | } | ||
345 | self.fetch_build_data_request(collector) | ||
346 | } | ||
347 | |||
348 | self.source_root_config = project_folders.source_root_config; | 349 | self.source_root_config = project_folders.source_root_config; |
349 | self.workspaces = Arc::new(workspaces); | 350 | self.workspaces = Arc::new(workspaces); |
350 | self.workspace_build_data = workspace_build_data; | 351 | self.workspace_build_data = workspace_build_data; |
@@ -355,6 +356,32 @@ impl GlobalState { | |||
355 | log::info!("did switch workspaces"); | 356 | log::info!("did switch workspaces"); |
356 | } | 357 | } |
357 | 358 | ||
359 | fn fetch_workspace_error(&self) -> Option<String> { | ||
360 | let mut buf = String::new(); | ||
361 | |||
362 | for ws in self.fetch_workspaces_queue.last_op_result() { | ||
363 | if let Err(err) = ws { | ||
364 | stdx::format_to!(buf, "rust-analyzer failed to load workspace: {:#}\n", err); | ||
365 | } | ||
366 | } | ||
367 | |||
368 | if buf.is_empty() { | ||
369 | return None; | ||
370 | } | ||
371 | |||
372 | Some(buf) | ||
373 | } | ||
374 | |||
375 | fn build_data_error(&self) -> Option<String> { | ||
376 | match self.fetch_build_data_queue.last_op_result() { | ||
377 | Some(Err(err)) => { | ||
378 | Some(format!("rust-analyzer failed to fetch build data: {:#}\n", err)) | ||
379 | } | ||
380 | Some(Ok(data)) => data.error(), | ||
381 | None => None, | ||
382 | } | ||
383 | } | ||
384 | |||
358 | fn reload_flycheck(&mut self) { | 385 | fn reload_flycheck(&mut self) { |
359 | let _p = profile::span("GlobalState::reload_flycheck"); | 386 | let _p = profile::span("GlobalState::reload_flycheck"); |
360 | let config = match self.config.flycheck() { | 387 | let config = match self.config.flycheck() { |
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs index 2dc8a42f1..ecab89b2a 100644 --- a/crates/rust-analyzer/src/semantic_tokens.rs +++ b/crates/rust-analyzer/src/semantic_tokens.rs | |||
@@ -39,7 +39,9 @@ macro_rules! define_semantic_token_types { | |||
39 | 39 | ||
40 | define_semantic_token_types![ | 40 | define_semantic_token_types![ |
41 | (ANGLE, "angle"), | 41 | (ANGLE, "angle"), |
42 | (ARITHMETIC, "arithmetic"), | ||
42 | (ATTRIBUTE, "attribute"), | 43 | (ATTRIBUTE, "attribute"), |
44 | (BITWISE, "bitwise"), | ||
43 | (BOOLEAN, "boolean"), | 45 | (BOOLEAN, "boolean"), |
44 | (BRACE, "brace"), | 46 | (BRACE, "brace"), |
45 | (BRACKET, "bracket"), | 47 | (BRACKET, "bracket"), |
@@ -47,6 +49,7 @@ define_semantic_token_types![ | |||
47 | (CHAR_LITERAL, "characterLiteral"), | 49 | (CHAR_LITERAL, "characterLiteral"), |
48 | (COLON, "colon"), | 50 | (COLON, "colon"), |
49 | (COMMA, "comma"), | 51 | (COMMA, "comma"), |
52 | (COMPARISON, "comparison"), | ||
50 | (CONST_PARAMETER, "constParameter"), | 53 | (CONST_PARAMETER, "constParameter"), |
51 | (DOT, "dot"), | 54 | (DOT, "dot"), |
52 | (ESCAPE_SEQUENCE, "escapeSequence"), | 55 | (ESCAPE_SEQUENCE, "escapeSequence"), |
@@ -54,6 +57,8 @@ define_semantic_token_types![ | |||
54 | (GENERIC, "generic"), | 57 | (GENERIC, "generic"), |
55 | (LABEL, "label"), | 58 | (LABEL, "label"), |
56 | (LIFETIME, "lifetime"), | 59 | (LIFETIME, "lifetime"), |
60 | (LOGICAL, "logical"), | ||
61 | (OPERATOR, "operator"), | ||
57 | (PARENTHESIS, "parenthesis"), | 62 | (PARENTHESIS, "parenthesis"), |
58 | (PUNCTUATION, "punctuation"), | 63 | (PUNCTUATION, "punctuation"), |
59 | (SELF_KEYWORD, "selfKeyword"), | 64 | (SELF_KEYWORD, "selfKeyword"), |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index c3820944b..c2361b32e 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -1,15 +1,16 @@ | |||
1 | //! Conversion of rust-analyzer specific types to lsp_types equivalents. | 1 | //! Conversion of rust-analyzer specific types to lsp_types equivalents. |
2 | use std::{ | 2 | use std::{ |
3 | iter::once, | ||
3 | path::{self, Path}, | 4 | path::{self, Path}, |
4 | sync::atomic::{AtomicU32, Ordering}, | 5 | sync::atomic::{AtomicU32, Ordering}, |
5 | }; | 6 | }; |
6 | 7 | ||
7 | use ide::{ | 8 | use ide::{ |
8 | Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, | 9 | Annotation, AnnotationKind, Assist, AssistKind, CallInfo, Cancelable, CompletionItem, |
9 | CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, | 10 | CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, |
10 | Highlight, HlMod, HlPunct, HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat, | 11 | Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint, |
11 | Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity, SourceChange, | 12 | InlayKind, InsertTextFormat, Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, |
12 | StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, | 13 | Severity, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, |
13 | }; | 14 | }; |
14 | use itertools::Itertools; | 15 | use itertools::Itertools; |
15 | use serde_json::to_value; | 16 | use serde_json::to_value; |
@@ -145,6 +146,23 @@ pub(crate) fn text_edit(line_index: &LineIndex, indel: Indel) -> lsp_types::Text | |||
145 | lsp_types::TextEdit { range, new_text } | 146 | lsp_types::TextEdit { range, new_text } |
146 | } | 147 | } |
147 | 148 | ||
149 | pub(crate) fn completion_text_edit( | ||
150 | line_index: &LineIndex, | ||
151 | insert_replace_support: Option<lsp_types::Position>, | ||
152 | indel: Indel, | ||
153 | ) -> lsp_types::CompletionTextEdit { | ||
154 | let text_edit = text_edit(line_index, indel); | ||
155 | match insert_replace_support { | ||
156 | Some(cursor_pos) => lsp_types::InsertReplaceEdit { | ||
157 | new_text: text_edit.new_text, | ||
158 | insert: lsp_types::Range { start: text_edit.range.start, end: cursor_pos }, | ||
159 | replace: text_edit.range, | ||
160 | } | ||
161 | .into(), | ||
162 | None => text_edit.into(), | ||
163 | } | ||
164 | } | ||
165 | |||
148 | pub(crate) fn snippet_text_edit( | 166 | pub(crate) fn snippet_text_edit( |
149 | line_index: &LineIndex, | 167 | line_index: &LineIndex, |
150 | is_snippet: bool, | 168 | is_snippet: bool, |
@@ -157,6 +175,7 @@ pub(crate) fn snippet_text_edit( | |||
157 | range: text_edit.range, | 175 | range: text_edit.range, |
158 | new_text: text_edit.new_text, | 176 | new_text: text_edit.new_text, |
159 | insert_text_format, | 177 | insert_text_format, |
178 | annotation_id: None, | ||
160 | } | 179 | } |
161 | } | 180 | } |
162 | 181 | ||
@@ -179,6 +198,7 @@ pub(crate) fn snippet_text_edit_vec( | |||
179 | } | 198 | } |
180 | 199 | ||
181 | pub(crate) fn completion_item( | 200 | pub(crate) fn completion_item( |
201 | insert_replace_support: Option<lsp_types::Position>, | ||
182 | line_index: &LineIndex, | 202 | line_index: &LineIndex, |
183 | item: CompletionItem, | 203 | item: CompletionItem, |
184 | ) -> Vec<lsp_types::CompletionItem> { | 204 | ) -> Vec<lsp_types::CompletionItem> { |
@@ -190,7 +210,7 @@ pub(crate) fn completion_item( | |||
190 | for indel in item.text_edit().iter() { | 210 | for indel in item.text_edit().iter() { |
191 | if indel.delete.contains_range(source_range) { | 211 | if indel.delete.contains_range(source_range) { |
192 | text_edit = Some(if indel.delete == source_range { | 212 | text_edit = Some(if indel.delete == source_range { |
193 | self::text_edit(line_index, indel.clone()) | 213 | self::completion_text_edit(line_index, insert_replace_support, indel.clone()) |
194 | } else { | 214 | } else { |
195 | assert!(source_range.end() == indel.delete.end()); | 215 | assert!(source_range.end() == indel.delete.end()); |
196 | let range1 = TextRange::new(indel.delete.start(), source_range.start()); | 216 | let range1 = TextRange::new(indel.delete.start(), source_range.start()); |
@@ -198,7 +218,7 @@ pub(crate) fn completion_item( | |||
198 | let indel1 = Indel::replace(range1, String::new()); | 218 | let indel1 = Indel::replace(range1, String::new()); |
199 | let indel2 = Indel::replace(range2, indel.insert.clone()); | 219 | let indel2 = Indel::replace(range2, indel.insert.clone()); |
200 | additional_text_edits.push(self::text_edit(line_index, indel1)); | 220 | additional_text_edits.push(self::text_edit(line_index, indel1)); |
201 | self::text_edit(line_index, indel2) | 221 | self::completion_text_edit(line_index, insert_replace_support, indel2) |
202 | }) | 222 | }) |
203 | } else { | 223 | } else { |
204 | assert!(source_range.intersect(indel.delete).is_none()); | 224 | assert!(source_range.intersect(indel.delete).is_none()); |
@@ -213,7 +233,7 @@ pub(crate) fn completion_item( | |||
213 | detail: item.detail().map(|it| it.to_string()), | 233 | detail: item.detail().map(|it| it.to_string()), |
214 | filter_text: Some(item.lookup().to_string()), | 234 | filter_text: Some(item.lookup().to_string()), |
215 | kind: item.kind().map(completion_item_kind), | 235 | kind: item.kind().map(completion_item_kind), |
216 | text_edit: Some(text_edit.into()), | 236 | text_edit: Some(text_edit), |
217 | additional_text_edits: Some(additional_text_edits), | 237 | additional_text_edits: Some(additional_text_edits), |
218 | documentation: item.documentation().map(documentation), | 238 | documentation: item.documentation().map(documentation), |
219 | deprecated: Some(item.deprecated()), | 239 | deprecated: Some(item.deprecated()), |
@@ -445,7 +465,13 @@ fn semantic_token_type_and_modifiers( | |||
445 | HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER, | 465 | HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER, |
446 | HlTag::Keyword => lsp_types::SemanticTokenType::KEYWORD, | 466 | HlTag::Keyword => lsp_types::SemanticTokenType::KEYWORD, |
447 | HlTag::None => semantic_tokens::GENERIC, | 467 | HlTag::None => semantic_tokens::GENERIC, |
448 | HlTag::Operator => lsp_types::SemanticTokenType::OPERATOR, | 468 | HlTag::Operator(op) => match op { |
469 | HlOperator::Bitwise => semantic_tokens::BITWISE, | ||
470 | HlOperator::Arithmetic => semantic_tokens::ARITHMETIC, | ||
471 | HlOperator::Logical => semantic_tokens::LOGICAL, | ||
472 | HlOperator::Comparison => semantic_tokens::COMPARISON, | ||
473 | HlOperator::Other => semantic_tokens::OPERATOR, | ||
474 | }, | ||
449 | HlTag::StringLiteral => lsp_types::SemanticTokenType::STRING, | 475 | HlTag::StringLiteral => lsp_types::SemanticTokenType::STRING, |
450 | HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE, | 476 | HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE, |
451 | HlTag::Punctuation(punct) => match punct { | 477 | HlTag::Punctuation(punct) => match punct { |
@@ -497,7 +523,8 @@ pub(crate) fn folding_range( | |||
497 | | FoldKind::Block | 523 | | FoldKind::Block |
498 | | FoldKind::ArgList | 524 | | FoldKind::ArgList |
499 | | FoldKind::Consts | 525 | | FoldKind::Consts |
500 | | FoldKind::Statics => None, | 526 | | FoldKind::Statics |
527 | | FoldKind::Array => None, | ||
501 | }; | 528 | }; |
502 | 529 | ||
503 | let range = range(line_index, fold.range); | 530 | let range = range(line_index, fold.range); |
@@ -663,16 +690,8 @@ pub(crate) fn goto_definition_response( | |||
663 | } | 690 | } |
664 | } | 691 | } |
665 | 692 | ||
666 | pub(crate) fn text_document_edit( | 693 | fn outside_workspace_annotation_id() -> String { |
667 | snap: &GlobalStateSnapshot, | 694 | String::from("OutsideWorkspace") |
668 | file_id: FileId, | ||
669 | edit: TextEdit, | ||
670 | ) -> Result<lsp_types::TextDocumentEdit> { | ||
671 | let text_document = optional_versioned_text_document_identifier(snap, file_id); | ||
672 | let line_index = snap.file_line_index(file_id)?; | ||
673 | let edits = | ||
674 | edit.into_iter().map(|it| lsp_types::OneOf::Left(text_edit(&line_index, it))).collect(); | ||
675 | Ok(lsp_types::TextDocumentEdit { text_document, edits }) | ||
676 | } | 695 | } |
677 | 696 | ||
678 | pub(crate) fn snippet_text_document_edit( | 697 | pub(crate) fn snippet_text_document_edit( |
@@ -683,14 +702,21 @@ pub(crate) fn snippet_text_document_edit( | |||
683 | ) -> Result<lsp_ext::SnippetTextDocumentEdit> { | 702 | ) -> Result<lsp_ext::SnippetTextDocumentEdit> { |
684 | let text_document = optional_versioned_text_document_identifier(snap, file_id); | 703 | let text_document = optional_versioned_text_document_identifier(snap, file_id); |
685 | let line_index = snap.file_line_index(file_id)?; | 704 | let line_index = snap.file_line_index(file_id)?; |
686 | let edits = edit.into_iter().map(|it| snippet_text_edit(&line_index, is_snippet, it)).collect(); | 705 | let mut edits: Vec<_> = |
706 | edit.into_iter().map(|it| snippet_text_edit(&line_index, is_snippet, it)).collect(); | ||
707 | |||
708 | if snap.analysis.is_library_file(file_id)? && snap.config.change_annotation_support() { | ||
709 | for edit in &mut edits { | ||
710 | edit.annotation_id = Some(outside_workspace_annotation_id()) | ||
711 | } | ||
712 | } | ||
687 | Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits }) | 713 | Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits }) |
688 | } | 714 | } |
689 | 715 | ||
690 | pub(crate) fn snippet_text_document_ops( | 716 | pub(crate) fn snippet_text_document_ops( |
691 | snap: &GlobalStateSnapshot, | 717 | snap: &GlobalStateSnapshot, |
692 | file_system_edit: FileSystemEdit, | 718 | file_system_edit: FileSystemEdit, |
693 | ) -> Vec<lsp_ext::SnippetDocumentChangeOperation> { | 719 | ) -> Cancelable<Vec<lsp_ext::SnippetDocumentChangeOperation>> { |
694 | let mut ops = Vec::new(); | 720 | let mut ops = Vec::new(); |
695 | match file_system_edit { | 721 | match file_system_edit { |
696 | FileSystemEdit::CreateFile { dst, initial_contents } => { | 722 | FileSystemEdit::CreateFile { dst, initial_contents } => { |
@@ -708,6 +734,7 @@ pub(crate) fn snippet_text_document_ops( | |||
708 | range: lsp_types::Range::default(), | 734 | range: lsp_types::Range::default(), |
709 | new_text: initial_contents, | 735 | new_text: initial_contents, |
710 | insert_text_format: Some(lsp_types::InsertTextFormat::PlainText), | 736 | insert_text_format: Some(lsp_types::InsertTextFormat::PlainText), |
737 | annotation_id: None, | ||
711 | }; | 738 | }; |
712 | let edit_file = | 739 | let edit_file = |
713 | lsp_ext::SnippetTextDocumentEdit { text_document, edits: vec![text_edit] }; | 740 | lsp_ext::SnippetTextDocumentEdit { text_document, edits: vec![text_edit] }; |
@@ -717,16 +744,19 @@ pub(crate) fn snippet_text_document_ops( | |||
717 | FileSystemEdit::MoveFile { src, dst } => { | 744 | FileSystemEdit::MoveFile { src, dst } => { |
718 | let old_uri = snap.file_id_to_url(src); | 745 | let old_uri = snap.file_id_to_url(src); |
719 | let new_uri = snap.anchored_path(&dst); | 746 | let new_uri = snap.anchored_path(&dst); |
720 | let rename_file = lsp_types::ResourceOp::Rename(lsp_types::RenameFile { | 747 | let mut rename_file = |
721 | old_uri, | 748 | lsp_types::RenameFile { old_uri, new_uri, options: None, annotation_id: None }; |
722 | new_uri, | 749 | if snap.analysis.is_library_file(src) == Ok(true) |
723 | options: None, | 750 | && snap.config.change_annotation_support() |
724 | annotation_id: None, | 751 | { |
725 | }); | 752 | rename_file.annotation_id = Some(outside_workspace_annotation_id()) |
726 | ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(rename_file)) | 753 | } |
754 | ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(lsp_types::ResourceOp::Rename( | ||
755 | rename_file, | ||
756 | ))) | ||
727 | } | 757 | } |
728 | } | 758 | } |
729 | ops | 759 | Ok(ops) |
730 | } | 760 | } |
731 | 761 | ||
732 | pub(crate) fn snippet_workspace_edit( | 762 | pub(crate) fn snippet_workspace_edit( |
@@ -734,16 +764,35 @@ pub(crate) fn snippet_workspace_edit( | |||
734 | source_change: SourceChange, | 764 | source_change: SourceChange, |
735 | ) -> Result<lsp_ext::SnippetWorkspaceEdit> { | 765 | ) -> Result<lsp_ext::SnippetWorkspaceEdit> { |
736 | let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new(); | 766 | let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new(); |
767 | |||
737 | for op in source_change.file_system_edits { | 768 | for op in source_change.file_system_edits { |
738 | let ops = snippet_text_document_ops(snap, op); | 769 | let ops = snippet_text_document_ops(snap, op)?; |
739 | document_changes.extend_from_slice(&ops); | 770 | document_changes.extend_from_slice(&ops); |
740 | } | 771 | } |
741 | for (file_id, edit) in source_change.source_file_edits { | 772 | for (file_id, edit) in source_change.source_file_edits { |
742 | let edit = snippet_text_document_edit(&snap, source_change.is_snippet, file_id, edit)?; | 773 | let edit = snippet_text_document_edit(&snap, source_change.is_snippet, file_id, edit)?; |
743 | document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); | 774 | document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); |
744 | } | 775 | } |
745 | let workspace_edit = | 776 | let mut workspace_edit = lsp_ext::SnippetWorkspaceEdit { |
746 | lsp_ext::SnippetWorkspaceEdit { changes: None, document_changes: Some(document_changes) }; | 777 | changes: None, |
778 | document_changes: Some(document_changes), | ||
779 | change_annotations: None, | ||
780 | }; | ||
781 | if snap.config.change_annotation_support() { | ||
782 | workspace_edit.change_annotations = Some( | ||
783 | once(( | ||
784 | outside_workspace_annotation_id(), | ||
785 | lsp_types::ChangeAnnotation { | ||
786 | label: String::from("Edit outside of the workspace"), | ||
787 | needs_confirmation: Some(true), | ||
788 | description: Some(String::from( | ||
789 | "This edit lies outside of the workspace and may affect dependencies", | ||
790 | )), | ||
791 | }, | ||
792 | )) | ||
793 | .collect(), | ||
794 | ) | ||
795 | } | ||
747 | Ok(workspace_edit) | 796 | Ok(workspace_edit) |
748 | } | 797 | } |
749 | 798 | ||
@@ -771,16 +820,7 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit { | |||
771 | lsp_types::DocumentChangeOperation::Edit( | 820 | lsp_types::DocumentChangeOperation::Edit( |
772 | lsp_types::TextDocumentEdit { | 821 | lsp_types::TextDocumentEdit { |
773 | text_document: edit.text_document, | 822 | text_document: edit.text_document, |
774 | edits: edit | 823 | edits: edit.edits.into_iter().map(From::from).collect(), |
775 | .edits | ||
776 | .into_iter() | ||
777 | .map(|edit| { | ||
778 | lsp_types::OneOf::Left(lsp_types::TextEdit { | ||
779 | range: edit.range, | ||
780 | new_text: edit.new_text, | ||
781 | }) | ||
782 | }) | ||
783 | .collect(), | ||
784 | }, | 824 | }, |
785 | ) | 825 | ) |
786 | } | 826 | } |
@@ -788,7 +828,23 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit { | |||
788 | .collect(), | 828 | .collect(), |
789 | ) | 829 | ) |
790 | }), | 830 | }), |
791 | change_annotations: None, | 831 | change_annotations: snippet_workspace_edit.change_annotations, |
832 | } | ||
833 | } | ||
834 | } | ||
835 | |||
836 | impl From<lsp_ext::SnippetTextEdit> | ||
837 | for lsp_types::OneOf<lsp_types::TextEdit, lsp_types::AnnotatedTextEdit> | ||
838 | { | ||
839 | fn from( | ||
840 | lsp_ext::SnippetTextEdit { annotation_id, insert_text_format:_, new_text, range }: lsp_ext::SnippetTextEdit, | ||
841 | ) -> Self { | ||
842 | match annotation_id { | ||
843 | Some(annotation_id) => lsp_types::OneOf::Right(lsp_types::AnnotatedTextEdit { | ||
844 | text_edit: lsp_types::TextEdit { range, new_text }, | ||
845 | annotation_id, | ||
846 | }), | ||
847 | None => lsp_types::OneOf::Left(lsp_types::TextEdit { range, new_text }), | ||
792 | } | 848 | } |
793 | } | 849 | } |
794 | } | 850 | } |
@@ -824,40 +880,31 @@ pub(crate) fn code_action_kind(kind: AssistKind) -> lsp_types::CodeActionKind { | |||
824 | } | 880 | } |
825 | } | 881 | } |
826 | 882 | ||
827 | pub(crate) fn unresolved_code_action( | 883 | pub(crate) fn code_action( |
828 | snap: &GlobalStateSnapshot, | 884 | snap: &GlobalStateSnapshot, |
829 | code_action_params: lsp_types::CodeActionParams, | ||
830 | assist: Assist, | 885 | assist: Assist, |
831 | index: usize, | 886 | resolve_data: Option<(usize, lsp_types::CodeActionParams)>, |
832 | ) -> Result<lsp_ext::CodeAction> { | 887 | ) -> Result<lsp_ext::CodeAction> { |
833 | assert!(assist.source_change.is_none()); | 888 | let mut res = lsp_ext::CodeAction { |
834 | let res = lsp_ext::CodeAction { | ||
835 | title: assist.label.to_string(), | 889 | title: assist.label.to_string(), |
836 | group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0), | 890 | group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0), |
837 | kind: Some(code_action_kind(assist.id.1)), | 891 | kind: Some(code_action_kind(assist.id.1)), |
838 | edit: None, | 892 | edit: None, |
839 | is_preferred: None, | 893 | is_preferred: None, |
840 | data: Some(lsp_ext::CodeActionData { | ||
841 | id: format!("{}:{}", assist.id.0, index.to_string()), | ||
842 | code_action_params, | ||
843 | }), | ||
844 | }; | ||
845 | Ok(res) | ||
846 | } | ||
847 | |||
848 | pub(crate) fn resolved_code_action( | ||
849 | snap: &GlobalStateSnapshot, | ||
850 | assist: Assist, | ||
851 | ) -> Result<lsp_ext::CodeAction> { | ||
852 | let change = assist.source_change.unwrap(); | ||
853 | let res = lsp_ext::CodeAction { | ||
854 | edit: Some(snippet_workspace_edit(snap, change)?), | ||
855 | title: assist.label.to_string(), | ||
856 | group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0), | ||
857 | kind: Some(code_action_kind(assist.id.1)), | ||
858 | is_preferred: None, | ||
859 | data: None, | 894 | data: None, |
860 | }; | 895 | }; |
896 | match (assist.source_change, resolve_data) { | ||
897 | (Some(it), _) => res.edit = Some(snippet_workspace_edit(snap, it)?), | ||
898 | (None, Some((index, code_action_params))) => { | ||
899 | res.data = Some(lsp_ext::CodeActionData { | ||
900 | id: format!("{}:{}", assist.id.0, index.to_string()), | ||
901 | code_action_params, | ||
902 | }); | ||
903 | } | ||
904 | (None, None) => { | ||
905 | stdx::never!("assist should always be resolved if client can't do lazy resolving") | ||
906 | } | ||
907 | }; | ||
861 | Ok(res) | 908 | Ok(res) |
862 | } | 909 | } |
863 | 910 | ||
@@ -1135,7 +1182,7 @@ mod tests { | |||
1135 | .unwrap() | 1182 | .unwrap() |
1136 | .into_iter() | 1183 | .into_iter() |
1137 | .filter(|c| c.label().ends_with("arg")) | 1184 | .filter(|c| c.label().ends_with("arg")) |
1138 | .map(|c| completion_item(&line_index, c)) | 1185 | .map(|c| completion_item(None, &line_index, c)) |
1139 | .flat_map(|comps| comps.into_iter().map(|c| (c.label, c.sort_text))) | 1186 | .flat_map(|comps| comps.into_iter().map(|c| (c.label, c.sort_text))) |
1140 | .collect(); | 1187 | .collect(); |
1141 | expect_test::expect![[r#" | 1188 | expect_test::expect![[r#" |
diff --git a/crates/rust-analyzer/tests/rust-analyzer/main.rs b/crates/rust-analyzer/tests/rust-analyzer/main.rs index 4442cbff6..9e89209ea 100644 --- a/crates/rust-analyzer/tests/rust-analyzer/main.rs +++ b/crates/rust-analyzer/tests/rust-analyzer/main.rs | |||
@@ -340,7 +340,6 @@ fn main() {} | |||
340 | } | 340 | } |
341 | ] | 341 | ] |
342 | }, | 342 | }, |
343 | "isPreferred": false, | ||
344 | "kind": "quickfix", | 343 | "kind": "quickfix", |
345 | "title": "Create module" | 344 | "title": "Create module" |
346 | }]), | 345 | }]), |
@@ -411,7 +410,6 @@ fn main() {{}} | |||
411 | } | 410 | } |
412 | ] | 411 | ] |
413 | }, | 412 | }, |
414 | "isPreferred": false, | ||
415 | "kind": "quickfix", | 413 | "kind": "quickfix", |
416 | "title": "Create module" | 414 | "title": "Create module" |
417 | }]), | 415 | }]), |
diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs index 95bf26f01..75e677762 100644 --- a/crates/rust-analyzer/tests/rust-analyzer/support.rs +++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs | |||
@@ -32,8 +32,12 @@ impl<'a> Project<'a> { | |||
32 | tmp_dir: None, | 32 | tmp_dir: None, |
33 | roots: vec![], | 33 | roots: vec![], |
34 | config: serde_json::json!({ | 34 | config: serde_json::json!({ |
35 | // Loading standard library is costly, let's ignore it by default | 35 | "cargo": { |
36 | "cargo": { "noSysroot": true } | 36 | // Loading standard library is costly, let's ignore it by default |
37 | "noSysroot": true, | ||
38 | // Can't use test binary as rustc wrapper. | ||
39 | "useRustcWrapperForBuildScripts": false, | ||
40 | } | ||
37 | }), | 41 | }), |
38 | } | 42 | } |
39 | } | 43 | } |
@@ -49,7 +53,17 @@ impl<'a> Project<'a> { | |||
49 | } | 53 | } |
50 | 54 | ||
51 | pub(crate) fn with_config(mut self, config: serde_json::Value) -> Project<'a> { | 55 | pub(crate) fn with_config(mut self, config: serde_json::Value) -> Project<'a> { |
52 | self.config = config; | 56 | fn merge(dst: &mut serde_json::Value, src: serde_json::Value) { |
57 | match (dst, src) { | ||
58 | (Value::Object(dst), Value::Object(src)) => { | ||
59 | for (k, v) in src { | ||
60 | merge(dst.entry(k).or_insert(v.clone()), v) | ||
61 | } | ||
62 | } | ||
63 | (dst, src) => *dst = src, | ||
64 | } | ||
65 | } | ||
66 | merge(&mut self.config, config); | ||
53 | self | 67 | self |
54 | } | 68 | } |
55 | 69 | ||
@@ -103,7 +117,7 @@ impl<'a> Project<'a> { | |||
103 | ..Default::default() | 117 | ..Default::default() |
104 | }), | 118 | }), |
105 | experimental: Some(json!({ | 119 | experimental: Some(json!({ |
106 | "statusNotification": true, | 120 | "serverStatusNotification": true, |
107 | })), | 121 | })), |
108 | ..Default::default() | 122 | ..Default::default() |
109 | }, | 123 | }, |
@@ -154,6 +168,7 @@ impl Server { | |||
154 | self.send_notification(r) | 168 | self.send_notification(r) |
155 | } | 169 | } |
156 | 170 | ||
171 | #[track_caller] | ||
157 | pub(crate) fn request<R>(&self, params: R::Params, expected_resp: Value) | 172 | pub(crate) fn request<R>(&self, params: R::Params, expected_resp: Value) |
158 | where | 173 | where |
159 | R: lsp_types::request::Request, | 174 | R: lsp_types::request::Request, |
@@ -213,13 +228,12 @@ impl Server { | |||
213 | } | 228 | } |
214 | pub(crate) fn wait_until_workspace_is_loaded(self) -> Server { | 229 | pub(crate) fn wait_until_workspace_is_loaded(self) -> Server { |
215 | self.wait_for_message_cond(1, &|msg: &Message| match msg { | 230 | self.wait_for_message_cond(1, &|msg: &Message| match msg { |
216 | Message::Notification(n) if n.method == "rust-analyzer/status" => { | 231 | Message::Notification(n) if n.method == "experimental/serverStatus" => { |
217 | let status = n | 232 | let status = n |
218 | .clone() | 233 | .clone() |
219 | .extract::<lsp_ext::StatusParams>("rust-analyzer/status") | 234 | .extract::<lsp_ext::ServerStatusParams>("experimental/serverStatus") |
220 | .unwrap() | 235 | .unwrap(); |
221 | .status; | 236 | status.quiescent |
222 | matches!(status, lsp_ext::Status::Ready) | ||
223 | } | 237 | } |
224 | _ => false, | 238 | _ => false, |
225 | }) | 239 | }) |
diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml index d28b5e658..f78c5da7c 100644 --- a/crates/stdx/Cargo.toml +++ b/crates/stdx/Cargo.toml | |||
@@ -10,10 +10,15 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | libc = "0.2.93" | ||
13 | backtrace = { version = "0.3.44", optional = true } | 14 | backtrace = { version = "0.3.44", optional = true } |
14 | always-assert = { version = "0.1.2", features = ["log"] } | 15 | always-assert = { version = "0.1.2", features = ["log"] } |
15 | # Think twice before adding anything here | 16 | # Think twice before adding anything here |
16 | 17 | ||
18 | [target.'cfg(windows)'.dependencies] | ||
19 | miow = "0.3.6" | ||
20 | winapi = "0.3.9" | ||
21 | |||
17 | [features] | 22 | [features] |
18 | # Uncomment to enable for the whole crate graph | 23 | # Uncomment to enable for the whole crate graph |
19 | # default = [ "backtrace" ] | 24 | # default = [ "backtrace" ] |
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index d26be4853..857567a85 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs | |||
@@ -1,7 +1,8 @@ | |||
1 | //! Missing batteries for standard libraries. | 1 | //! Missing batteries for standard libraries. |
2 | use std::{cmp::Ordering, ops, process, time::Instant}; | 2 | use std::{cmp::Ordering, ops, time::Instant}; |
3 | 3 | ||
4 | mod macros; | 4 | mod macros; |
5 | pub mod process; | ||
5 | pub mod panic_context; | 6 | pub mod panic_context; |
6 | 7 | ||
7 | pub use always_assert::{always, never}; | 8 | pub use always_assert::{always, never}; |
@@ -178,17 +179,30 @@ where | |||
178 | start..start + len | 179 | start..start + len |
179 | } | 180 | } |
180 | 181 | ||
181 | pub struct JodChild(pub process::Child); | 182 | pub fn defer<F: FnOnce()>(f: F) -> impl Drop { |
183 | struct D<F: FnOnce()>(Option<F>); | ||
184 | impl<F: FnOnce()> Drop for D<F> { | ||
185 | fn drop(&mut self) { | ||
186 | if let Some(f) = self.0.take() { | ||
187 | f() | ||
188 | } | ||
189 | } | ||
190 | } | ||
191 | D(Some(f)) | ||
192 | } | ||
193 | |||
194 | #[repr(transparent)] | ||
195 | pub struct JodChild(pub std::process::Child); | ||
182 | 196 | ||
183 | impl ops::Deref for JodChild { | 197 | impl ops::Deref for JodChild { |
184 | type Target = process::Child; | 198 | type Target = std::process::Child; |
185 | fn deref(&self) -> &process::Child { | 199 | fn deref(&self) -> &std::process::Child { |
186 | &self.0 | 200 | &self.0 |
187 | } | 201 | } |
188 | } | 202 | } |
189 | 203 | ||
190 | impl ops::DerefMut for JodChild { | 204 | impl ops::DerefMut for JodChild { |
191 | fn deref_mut(&mut self) -> &mut process::Child { | 205 | fn deref_mut(&mut self) -> &mut std::process::Child { |
192 | &mut self.0 | 206 | &mut self.0 |
193 | } | 207 | } |
194 | } | 208 | } |
@@ -200,6 +214,13 @@ impl Drop for JodChild { | |||
200 | } | 214 | } |
201 | } | 215 | } |
202 | 216 | ||
217 | impl JodChild { | ||
218 | pub fn into_inner(self) -> std::process::Child { | ||
219 | // SAFETY: repr transparent | ||
220 | unsafe { std::mem::transmute::<JodChild, std::process::Child>(self) } | ||
221 | } | ||
222 | } | ||
223 | |||
203 | #[cfg(test)] | 224 | #[cfg(test)] |
204 | mod tests { | 225 | mod tests { |
205 | use super::*; | 226 | use super::*; |
diff --git a/crates/stdx/src/process.rs b/crates/stdx/src/process.rs new file mode 100644 index 000000000..b0fa12f76 --- /dev/null +++ b/crates/stdx/src/process.rs | |||
@@ -0,0 +1,238 @@ | |||
1 | //! Read both stdout and stderr of child without deadlocks. | ||
2 | //! | ||
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 | ||
5 | |||
6 | use std::{ | ||
7 | io, | ||
8 | process::{Command, Output, Stdio}, | ||
9 | }; | ||
10 | |||
11 | pub fn streaming_output( | ||
12 | mut cmd: Command, | ||
13 | on_stdout_line: &mut dyn FnMut(&str), | ||
14 | on_stderr_line: &mut dyn FnMut(&str), | ||
15 | ) -> io::Result<Output> { | ||
16 | let mut stdout = Vec::new(); | ||
17 | let mut stderr = Vec::new(); | ||
18 | |||
19 | let cmd = cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null()); | ||
20 | |||
21 | let status = { | ||
22 | let mut child = cmd.spawn()?; | ||
23 | let out = child.stdout.take().unwrap(); | ||
24 | let err = child.stderr.take().unwrap(); | ||
25 | imp::read2(out, err, &mut |is_out, data, eof| { | ||
26 | let idx = if eof { | ||
27 | data.len() | ||
28 | } else { | ||
29 | match data.iter().rposition(|b| *b == b'\n') { | ||
30 | Some(i) => i + 1, | ||
31 | None => return, | ||
32 | } | ||
33 | }; | ||
34 | { | ||
35 | // scope for new_lines | ||
36 | let new_lines = { | ||
37 | let dst = if is_out { &mut stdout } else { &mut stderr }; | ||
38 | let start = dst.len(); | ||
39 | let data = data.drain(..idx); | ||
40 | dst.extend(data); | ||
41 | &dst[start..] | ||
42 | }; | ||
43 | for line in String::from_utf8_lossy(new_lines).lines() { | ||
44 | if is_out { | ||
45 | on_stdout_line(line) | ||
46 | } else { | ||
47 | on_stderr_line(line) | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | })?; | ||
52 | child.wait()? | ||
53 | }; | ||
54 | |||
55 | Ok(Output { status, stdout, stderr }) | ||
56 | } | ||
57 | |||
58 | #[cfg(unix)] | ||
59 | mod imp { | ||
60 | use std::{ | ||
61 | io::{self, prelude::*}, | ||
62 | mem, | ||
63 | os::unix::prelude::*, | ||
64 | process::{ChildStderr, ChildStdout}, | ||
65 | }; | ||
66 | |||
67 | pub(crate) fn read2( | ||
68 | mut out_pipe: ChildStdout, | ||
69 | mut err_pipe: ChildStderr, | ||
70 | data: &mut dyn FnMut(bool, &mut Vec<u8>, bool), | ||
71 | ) -> io::Result<()> { | ||
72 | unsafe { | ||
73 | libc::fcntl(out_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK); | ||
74 | libc::fcntl(err_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK); | ||
75 | } | ||
76 | |||
77 | let mut out_done = false; | ||
78 | let mut err_done = false; | ||
79 | let mut out = Vec::new(); | ||
80 | let mut err = Vec::new(); | ||
81 | |||
82 | let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() }; | ||
83 | fds[0].fd = out_pipe.as_raw_fd(); | ||
84 | fds[0].events = libc::POLLIN; | ||
85 | fds[1].fd = err_pipe.as_raw_fd(); | ||
86 | fds[1].events = libc::POLLIN; | ||
87 | let mut nfds = 2; | ||
88 | let mut errfd = 1; | ||
89 | |||
90 | while nfds > 0 { | ||
91 | // wait for either pipe to become readable using `select` | ||
92 | let r = unsafe { libc::poll(fds.as_mut_ptr(), nfds, -1) }; | ||
93 | if r == -1 { | ||
94 | let err = io::Error::last_os_error(); | ||
95 | if err.kind() == io::ErrorKind::Interrupted { | ||
96 | continue; | ||
97 | } | ||
98 | return Err(err); | ||
99 | } | ||
100 | |||
101 | // Read as much as we can from each pipe, ignoring EWOULDBLOCK or | ||
102 | // EAGAIN. If we hit EOF, then this will happen because the underlying | ||
103 | // reader will return Ok(0), in which case we'll see `Ok` ourselves. In | ||
104 | // this case we flip the other fd back into blocking mode and read | ||
105 | // whatever's leftover on that file descriptor. | ||
106 | let handle = |res: io::Result<_>| match res { | ||
107 | Ok(_) => Ok(true), | ||
108 | Err(e) => { | ||
109 | if e.kind() == io::ErrorKind::WouldBlock { | ||
110 | Ok(false) | ||
111 | } else { | ||
112 | Err(e) | ||
113 | } | ||
114 | } | ||
115 | }; | ||
116 | if !err_done && fds[errfd].revents != 0 && handle(err_pipe.read_to_end(&mut err))? { | ||
117 | err_done = true; | ||
118 | nfds -= 1; | ||
119 | } | ||
120 | data(false, &mut err, err_done); | ||
121 | if !out_done && fds[0].revents != 0 && handle(out_pipe.read_to_end(&mut out))? { | ||
122 | out_done = true; | ||
123 | fds[0].fd = err_pipe.as_raw_fd(); | ||
124 | errfd = 0; | ||
125 | nfds -= 1; | ||
126 | } | ||
127 | data(true, &mut out, out_done); | ||
128 | } | ||
129 | Ok(()) | ||
130 | } | ||
131 | } | ||
132 | |||
133 | #[cfg(windows)] | ||
134 | mod imp { | ||
135 | use std::{ | ||
136 | io, | ||
137 | os::windows::prelude::*, | ||
138 | process::{ChildStderr, ChildStdout}, | ||
139 | slice, | ||
140 | }; | ||
141 | |||
142 | use miow::{ | ||
143 | iocp::{CompletionPort, CompletionStatus}, | ||
144 | pipe::NamedPipe, | ||
145 | Overlapped, | ||
146 | }; | ||
147 | use winapi::shared::winerror::ERROR_BROKEN_PIPE; | ||
148 | |||
149 | struct Pipe<'a> { | ||
150 | dst: &'a mut Vec<u8>, | ||
151 | overlapped: Overlapped, | ||
152 | pipe: NamedPipe, | ||
153 | done: bool, | ||
154 | } | ||
155 | |||
156 | pub(crate) fn read2( | ||
157 | out_pipe: ChildStdout, | ||
158 | err_pipe: ChildStderr, | ||
159 | data: &mut dyn FnMut(bool, &mut Vec<u8>, bool), | ||
160 | ) -> io::Result<()> { | ||
161 | let mut out = Vec::new(); | ||
162 | let mut err = Vec::new(); | ||
163 | |||
164 | let port = CompletionPort::new(1)?; | ||
165 | port.add_handle(0, &out_pipe)?; | ||
166 | port.add_handle(1, &err_pipe)?; | ||
167 | |||
168 | unsafe { | ||
169 | let mut out_pipe = Pipe::new(out_pipe, &mut out); | ||
170 | let mut err_pipe = Pipe::new(err_pipe, &mut err); | ||
171 | |||
172 | out_pipe.read()?; | ||
173 | err_pipe.read()?; | ||
174 | |||
175 | let mut status = [CompletionStatus::zero(), CompletionStatus::zero()]; | ||
176 | |||
177 | while !out_pipe.done || !err_pipe.done { | ||
178 | for status in port.get_many(&mut status, None)? { | ||
179 | if status.token() == 0 { | ||
180 | out_pipe.complete(status); | ||
181 | data(true, out_pipe.dst, out_pipe.done); | ||
182 | out_pipe.read()?; | ||
183 | } else { | ||
184 | err_pipe.complete(status); | ||
185 | data(false, err_pipe.dst, err_pipe.done); | ||
186 | err_pipe.read()?; | ||
187 | } | ||
188 | } | ||
189 | } | ||
190 | |||
191 | Ok(()) | ||
192 | } | ||
193 | } | ||
194 | |||
195 | impl<'a> Pipe<'a> { | ||
196 | unsafe fn new<P: IntoRawHandle>(p: P, dst: &'a mut Vec<u8>) -> Pipe<'a> { | ||
197 | Pipe { | ||
198 | dst, | ||
199 | pipe: NamedPipe::from_raw_handle(p.into_raw_handle()), | ||
200 | overlapped: Overlapped::zero(), | ||
201 | done: false, | ||
202 | } | ||
203 | } | ||
204 | |||
205 | unsafe fn read(&mut self) -> io::Result<()> { | ||
206 | let dst = slice_to_end(self.dst); | ||
207 | match self.pipe.read_overlapped(dst, self.overlapped.raw()) { | ||
208 | Ok(_) => Ok(()), | ||
209 | Err(e) => { | ||
210 | if e.raw_os_error() == Some(ERROR_BROKEN_PIPE as i32) { | ||
211 | self.done = true; | ||
212 | Ok(()) | ||
213 | } else { | ||
214 | Err(e) | ||
215 | } | ||
216 | } | ||
217 | } | ||
218 | } | ||
219 | |||
220 | unsafe fn complete(&mut self, status: &CompletionStatus) { | ||
221 | let prev = self.dst.len(); | ||
222 | self.dst.set_len(prev + status.bytes_transferred() as usize); | ||
223 | if status.bytes_transferred() == 0 { | ||
224 | self.done = true; | ||
225 | } | ||
226 | } | ||
227 | } | ||
228 | |||
229 | unsafe fn slice_to_end(v: &mut Vec<u8>) -> &mut [u8] { | ||
230 | if v.capacity() == 0 { | ||
231 | v.reserve(16); | ||
232 | } | ||
233 | if v.capacity() == v.len() { | ||
234 | v.reserve(1); | ||
235 | } | ||
236 | slice::from_raw_parts_mut(v.as_mut_ptr().add(v.len()), v.capacity() - v.len()) | ||
237 | } | ||
238 | } | ||
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index 9f01acc26..556f80882 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml | |||
@@ -13,10 +13,10 @@ doctest = false | |||
13 | [dependencies] | 13 | [dependencies] |
14 | cov-mark = { version = "1.1", features = ["thread-local"] } | 14 | cov-mark = { version = "1.1", features = ["thread-local"] } |
15 | itertools = "0.10.0" | 15 | itertools = "0.10.0" |
16 | rowan = "0.13.0-pre.3" | 16 | rowan = "=0.13.0-pre.3" |
17 | rustc_lexer = { version = "710.0.0", package = "rustc-ap-rustc_lexer" } | 17 | rustc_lexer = { version = "716.0.0", package = "rustc-ap-rustc_lexer" } |
18 | rustc-hash = "1.1.0" | 18 | rustc-hash = "1.1.0" |
19 | arrayvec = "0.6" | 19 | arrayvec = "0.7" |
20 | once_cell = "1.3.1" | 20 | once_cell = "1.3.1" |
21 | indexmap = "1.4.0" | 21 | indexmap = "1.4.0" |
22 | smol_str = { version = "0.1.15", features = ["serde"] } | 22 | smol_str = { version = "0.1.15", features = ["serde"] } |
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index 529bd0eb1..04f97f368 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs | |||
@@ -14,10 +14,29 @@ use crate::{ | |||
14 | use super::NameOwner; | 14 | use super::NameOwner; |
15 | 15 | ||
16 | pub trait GenericParamsOwnerEdit: ast::GenericParamsOwner + AstNodeEdit { | 16 | pub trait GenericParamsOwnerEdit: ast::GenericParamsOwner + AstNodeEdit { |
17 | fn get_or_create_generic_param_list(&self) -> ast::GenericParamList; | ||
17 | fn get_or_create_where_clause(&self) -> ast::WhereClause; | 18 | fn get_or_create_where_clause(&self) -> ast::WhereClause; |
18 | } | 19 | } |
19 | 20 | ||
20 | impl GenericParamsOwnerEdit for ast::Fn { | 21 | impl GenericParamsOwnerEdit for ast::Fn { |
22 | fn get_or_create_generic_param_list(&self) -> ast::GenericParamList { | ||
23 | match self.generic_param_list() { | ||
24 | Some(it) => it, | ||
25 | None => { | ||
26 | let position = if let Some(name) = self.name() { | ||
27 | Position::after(name.syntax) | ||
28 | } else if let Some(fn_token) = self.fn_token() { | ||
29 | Position::after(fn_token) | ||
30 | } else if let Some(param_list) = self.param_list() { | ||
31 | Position::before(param_list.syntax) | ||
32 | } else { | ||
33 | Position::last_child_of(self.syntax()) | ||
34 | }; | ||
35 | create_generic_param_list(position) | ||
36 | } | ||
37 | } | ||
38 | } | ||
39 | |||
21 | fn get_or_create_where_clause(&self) -> WhereClause { | 40 | fn get_or_create_where_clause(&self) -> WhereClause { |
22 | if self.where_clause().is_none() { | 41 | if self.where_clause().is_none() { |
23 | let position = if let Some(ty) = self.ret_type() { | 42 | let position = if let Some(ty) = self.ret_type() { |
@@ -34,6 +53,20 @@ impl GenericParamsOwnerEdit for ast::Fn { | |||
34 | } | 53 | } |
35 | 54 | ||
36 | impl GenericParamsOwnerEdit for ast::Impl { | 55 | impl GenericParamsOwnerEdit for ast::Impl { |
56 | fn get_or_create_generic_param_list(&self) -> ast::GenericParamList { | ||
57 | match self.generic_param_list() { | ||
58 | Some(it) => it, | ||
59 | None => { | ||
60 | let position = if let Some(imp_token) = self.impl_token() { | ||
61 | Position::after(imp_token) | ||
62 | } else { | ||
63 | Position::last_child_of(self.syntax()) | ||
64 | }; | ||
65 | create_generic_param_list(position) | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | |||
37 | fn get_or_create_where_clause(&self) -> WhereClause { | 70 | fn get_or_create_where_clause(&self) -> WhereClause { |
38 | if self.where_clause().is_none() { | 71 | if self.where_clause().is_none() { |
39 | let position = if let Some(items) = self.assoc_item_list() { | 72 | let position = if let Some(items) = self.assoc_item_list() { |
@@ -48,6 +81,22 @@ impl GenericParamsOwnerEdit for ast::Impl { | |||
48 | } | 81 | } |
49 | 82 | ||
50 | impl GenericParamsOwnerEdit for ast::Trait { | 83 | impl GenericParamsOwnerEdit for ast::Trait { |
84 | fn get_or_create_generic_param_list(&self) -> ast::GenericParamList { | ||
85 | match self.generic_param_list() { | ||
86 | Some(it) => it, | ||
87 | None => { | ||
88 | let position = if let Some(name) = self.name() { | ||
89 | Position::after(name.syntax) | ||
90 | } else if let Some(trait_token) = self.trait_token() { | ||
91 | Position::after(trait_token) | ||
92 | } else { | ||
93 | Position::last_child_of(self.syntax()) | ||
94 | }; | ||
95 | create_generic_param_list(position) | ||
96 | } | ||
97 | } | ||
98 | } | ||
99 | |||
51 | fn get_or_create_where_clause(&self) -> WhereClause { | 100 | fn get_or_create_where_clause(&self) -> WhereClause { |
52 | if self.where_clause().is_none() { | 101 | if self.where_clause().is_none() { |
53 | let position = if let Some(items) = self.assoc_item_list() { | 102 | let position = if let Some(items) = self.assoc_item_list() { |
@@ -62,6 +111,22 @@ impl GenericParamsOwnerEdit for ast::Trait { | |||
62 | } | 111 | } |
63 | 112 | ||
64 | impl GenericParamsOwnerEdit for ast::Struct { | 113 | impl GenericParamsOwnerEdit for ast::Struct { |
114 | fn get_or_create_generic_param_list(&self) -> ast::GenericParamList { | ||
115 | match self.generic_param_list() { | ||
116 | Some(it) => it, | ||
117 | None => { | ||
118 | let position = if let Some(name) = self.name() { | ||
119 | Position::after(name.syntax) | ||
120 | } else if let Some(struct_token) = self.struct_token() { | ||
121 | Position::after(struct_token) | ||
122 | } else { | ||
123 | Position::last_child_of(self.syntax()) | ||
124 | }; | ||
125 | create_generic_param_list(position) | ||
126 | } | ||
127 | } | ||
128 | } | ||
129 | |||
65 | fn get_or_create_where_clause(&self) -> WhereClause { | 130 | fn get_or_create_where_clause(&self) -> WhereClause { |
66 | if self.where_clause().is_none() { | 131 | if self.where_clause().is_none() { |
67 | let tfl = self.field_list().and_then(|fl| match fl { | 132 | let tfl = self.field_list().and_then(|fl| match fl { |
@@ -84,6 +149,22 @@ impl GenericParamsOwnerEdit for ast::Struct { | |||
84 | } | 149 | } |
85 | 150 | ||
86 | impl GenericParamsOwnerEdit for ast::Enum { | 151 | impl GenericParamsOwnerEdit for ast::Enum { |
152 | fn get_or_create_generic_param_list(&self) -> ast::GenericParamList { | ||
153 | match self.generic_param_list() { | ||
154 | Some(it) => it, | ||
155 | None => { | ||
156 | let position = if let Some(name) = self.name() { | ||
157 | Position::after(name.syntax) | ||
158 | } else if let Some(enum_token) = self.enum_token() { | ||
159 | Position::after(enum_token) | ||
160 | } else { | ||
161 | Position::last_child_of(self.syntax()) | ||
162 | }; | ||
163 | create_generic_param_list(position) | ||
164 | } | ||
165 | } | ||
166 | } | ||
167 | |||
87 | fn get_or_create_where_clause(&self) -> WhereClause { | 168 | fn get_or_create_where_clause(&self) -> WhereClause { |
88 | if self.where_clause().is_none() { | 169 | if self.where_clause().is_none() { |
89 | let position = if let Some(gpl) = self.generic_param_list() { | 170 | let position = if let Some(gpl) = self.generic_param_list() { |
@@ -104,6 +185,37 @@ fn create_where_clause(position: Position) { | |||
104 | ted::insert(position, where_clause.syntax()); | 185 | ted::insert(position, where_clause.syntax()); |
105 | } | 186 | } |
106 | 187 | ||
188 | fn create_generic_param_list(position: Position) -> ast::GenericParamList { | ||
189 | let gpl = make::generic_param_list(empty()).clone_for_update(); | ||
190 | ted::insert_raw(position, gpl.syntax()); | ||
191 | gpl | ||
192 | } | ||
193 | |||
194 | impl ast::GenericParamList { | ||
195 | pub fn add_generic_param(&self, generic_param: ast::GenericParam) { | ||
196 | match self.generic_params().last() { | ||
197 | Some(last_param) => { | ||
198 | let mut elems = Vec::new(); | ||
199 | if !last_param | ||
200 | .syntax() | ||
201 | .siblings_with_tokens(Direction::Next) | ||
202 | .any(|it| it.kind() == T![,]) | ||
203 | { | ||
204 | elems.push(make::token(T![,]).into()); | ||
205 | elems.push(make::tokens::single_space().into()); | ||
206 | }; | ||
207 | elems.push(generic_param.syntax().clone().into()); | ||
208 | let after_last_param = Position::after(last_param.syntax()); | ||
209 | ted::insert_all(after_last_param, elems); | ||
210 | } | ||
211 | None => { | ||
212 | let after_l_angle = Position::after(self.l_angle_token().unwrap()); | ||
213 | ted::insert(after_l_angle, generic_param.syntax()) | ||
214 | } | ||
215 | } | ||
216 | } | ||
217 | } | ||
218 | |||
107 | impl ast::WhereClause { | 219 | impl ast::WhereClause { |
108 | pub fn add_predicate(&self, predicate: ast::WherePred) { | 220 | pub fn add_predicate(&self, predicate: ast::WherePred) { |
109 | if let Some(pred) = self.predicates().last() { | 221 | if let Some(pred) = self.predicates().last() { |
@@ -164,3 +276,44 @@ impl ast::Use { | |||
164 | ted::remove(self.syntax()) | 276 | ted::remove(self.syntax()) |
165 | } | 277 | } |
166 | } | 278 | } |
279 | |||
280 | #[cfg(test)] | ||
281 | mod tests { | ||
282 | use std::fmt; | ||
283 | |||
284 | use crate::SourceFile; | ||
285 | |||
286 | use super::*; | ||
287 | |||
288 | fn ast_mut_from_text<N: AstNode>(text: &str) -> N { | ||
289 | let parse = SourceFile::parse(text); | ||
290 | parse.tree().syntax().descendants().find_map(N::cast).unwrap().clone_for_update() | ||
291 | } | ||
292 | |||
293 | #[test] | ||
294 | fn test_create_generic_param_list() { | ||
295 | fn check_create_gpl<N: GenericParamsOwnerEdit + fmt::Display>(before: &str, after: &str) { | ||
296 | let gpl_owner = ast_mut_from_text::<N>(before); | ||
297 | gpl_owner.get_or_create_generic_param_list(); | ||
298 | assert_eq!(gpl_owner.to_string(), after); | ||
299 | } | ||
300 | |||
301 | check_create_gpl::<ast::Fn>("fn foo", "fn foo<>"); | ||
302 | check_create_gpl::<ast::Fn>("fn foo() {}", "fn foo<>() {}"); | ||
303 | |||
304 | check_create_gpl::<ast::Impl>("impl", "impl<>"); | ||
305 | check_create_gpl::<ast::Impl>("impl Struct {}", "impl<> Struct {}"); | ||
306 | check_create_gpl::<ast::Impl>("impl Trait for Struct {}", "impl<> Trait for Struct {}"); | ||
307 | |||
308 | check_create_gpl::<ast::Trait>("trait Trait<>", "trait Trait<>"); | ||
309 | check_create_gpl::<ast::Trait>("trait Trait<> {}", "trait Trait<> {}"); | ||
310 | |||
311 | check_create_gpl::<ast::Struct>("struct A", "struct A<>"); | ||
312 | check_create_gpl::<ast::Struct>("struct A;", "struct A<>;"); | ||
313 | check_create_gpl::<ast::Struct>("struct A();", "struct A<>();"); | ||
314 | check_create_gpl::<ast::Struct>("struct A {}", "struct A<> {}"); | ||
315 | |||
316 | check_create_gpl::<ast::Enum>("enum E", "enum E<>"); | ||
317 | check_create_gpl::<ast::Enum>("enum E {", "enum E<> {"); | ||
318 | } | ||
319 | } | ||
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 3a588e540..222b7e212 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -29,9 +29,13 @@ pub fn ty(text: &str) -> ast::Type { | |||
29 | pub fn ty_unit() -> ast::Type { | 29 | pub fn ty_unit() -> ast::Type { |
30 | ty("()") | 30 | ty("()") |
31 | } | 31 | } |
32 | // FIXME: handle types of length == 1 | ||
33 | pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { | 32 | pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { |
34 | let contents = types.into_iter().join(", "); | 33 | let mut count: usize = 0; |
34 | let mut contents = types.into_iter().inspect(|_| count += 1).join(", "); | ||
35 | if count == 1 { | ||
36 | contents.push(','); | ||
37 | } | ||
38 | |||
35 | ty(&format!("({})", contents)) | 39 | ty(&format!("({})", contents)) |
36 | } | 40 | } |
37 | // FIXME: handle path to type | 41 | // FIXME: handle path to type |
@@ -301,13 +305,23 @@ pub fn wildcard_pat() -> ast::WildcardPat { | |||
301 | } | 305 | } |
302 | } | 306 | } |
303 | 307 | ||
308 | pub fn literal_pat(lit: &str) -> ast::LiteralPat { | ||
309 | return from_text(lit); | ||
310 | |||
311 | fn from_text(text: &str) -> ast::LiteralPat { | ||
312 | ast_from_text(&format!("fn f() {{ match x {{ {} => {{}} }} }}", text)) | ||
313 | } | ||
314 | } | ||
315 | |||
304 | /// Creates a tuple of patterns from an iterator of patterns. | 316 | /// Creates a tuple of patterns from an iterator of patterns. |
305 | /// | 317 | /// |
306 | /// Invariant: `pats` must be length > 1 | 318 | /// Invariant: `pats` must be length > 0 |
307 | /// | ||
308 | /// FIXME handle `pats` length == 1 | ||
309 | pub fn tuple_pat(pats: impl IntoIterator<Item = ast::Pat>) -> ast::TuplePat { | 319 | pub fn tuple_pat(pats: impl IntoIterator<Item = ast::Pat>) -> ast::TuplePat { |
310 | let pats_str = pats.into_iter().map(|p| p.to_string()).join(", "); | 320 | let mut count: usize = 0; |
321 | let mut pats_str = pats.into_iter().inspect(|_| count += 1).join(", "); | ||
322 | if count == 1 { | ||
323 | pats_str.push(','); | ||
324 | } | ||
311 | return from_text(&format!("({})", pats_str)); | 325 | return from_text(&format!("({})", pats_str)); |
312 | 326 | ||
313 | fn from_text(text: &str) -> ast::TuplePat { | 327 | fn from_text(text: &str) -> ast::TuplePat { |
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index ae98dbd26..171099661 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs | |||
@@ -125,6 +125,18 @@ pub enum AttrKind { | |||
125 | Outer, | 125 | Outer, |
126 | } | 126 | } |
127 | 127 | ||
128 | impl AttrKind { | ||
129 | /// Returns `true` if the attr_kind is [`Inner`]. | ||
130 | pub fn is_inner(&self) -> bool { | ||
131 | matches!(self, Self::Inner) | ||
132 | } | ||
133 | |||
134 | /// Returns `true` if the attr_kind is [`Outer`]. | ||
135 | pub fn is_outer(&self) -> bool { | ||
136 | matches!(self, Self::Outer) | ||
137 | } | ||
138 | } | ||
139 | |||
128 | impl ast::Attr { | 140 | impl ast::Attr { |
129 | pub fn as_simple_atom(&self) -> Option<SmolStr> { | 141 | pub fn as_simple_atom(&self) -> Option<SmolStr> { |
130 | if self.eq_token().is_some() || self.token_tree().is_some() { | 142 | if self.eq_token().is_some() || self.token_tree().is_some() { |
diff --git a/crates/syntax/src/ted.rs b/crates/syntax/src/ted.rs index 177d4ff67..450f2e447 100644 --- a/crates/syntax/src/ted.rs +++ b/crates/syntax/src/ted.rs | |||
@@ -165,6 +165,13 @@ fn ws_between(left: &SyntaxElement, right: &SyntaxElement) -> Option<SyntaxToken | |||
165 | if right.kind() == T![;] || right.kind() == T![,] { | 165 | if right.kind() == T![;] || right.kind() == T![,] { |
166 | return None; | 166 | return None; |
167 | } | 167 | } |
168 | if left.kind() == T![<] || right.kind() == T![>] { | ||
169 | return None; | ||
170 | } | ||
171 | if left.kind() == T![&] && right.kind() == SyntaxKind::LIFETIME { | ||
172 | return None; | ||
173 | } | ||
174 | |||
168 | if right.kind() == SyntaxKind::USE { | 175 | if right.kind() == SyntaxKind::USE { |
169 | let indent = IndentLevel::from_element(left); | 176 | let indent = IndentLevel::from_element(left); |
170 | return Some(make::tokens::whitespace(&format!("\n{}", indent))); | 177 | return Some(make::tokens::whitespace(&format!("\n{}", indent))); |
diff --git a/crates/syntax/src/validation/block.rs b/crates/syntax/src/validation/block.rs index ad9901468..40170014f 100644 --- a/crates/syntax/src/validation/block.rs +++ b/crates/syntax/src/validation/block.rs | |||
@@ -13,7 +13,7 @@ pub(crate) fn validate_block_expr(block: ast::BlockExpr, errors: &mut Vec<Syntax | |||
13 | _ => {} | 13 | _ => {} |
14 | } | 14 | } |
15 | } | 15 | } |
16 | errors.extend(block.attrs().map(|attr| { | 16 | errors.extend(block.attrs().filter(|attr| attr.kind().is_inner()).map(|attr| { |
17 | SyntaxError::new( | 17 | SyntaxError::new( |
18 | "A block in this position cannot accept inner attributes", | 18 | "A block in this position cannot accept inner attributes", |
19 | attr.syntax().text_range(), | 19 | attr.syntax().text_range(), |
diff --git a/crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast b/crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast index 3016a6574..1ff3f7656 100644 --- a/crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast +++ b/crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast | |||
@@ -7,15 +7,16 @@ [email protected] | |||
7 | [email protected] " " | 7 | [email protected] " " |
8 | [email protected] "=" | 8 | [email protected] "=" |
9 | [email protected] " " | 9 | [email protected] " " |
10 | [email protected] | 10 | [email protected] |
11 | [email protected] | 11 | [email protected] |
12 | [email protected] | 12 | [email protected] |
13 | [email protected] | 13 | [email protected] |
14 | [email protected] "foo" | 14 | [email protected] |
15 | [email protected] "!" | 15 | [email protected] "foo" |
16 | [email protected] | 16 | [email protected] "!" |
17 | [email protected] "(" | 17 | [email protected] |
18 | [email protected] ")" | 18 | [email protected] "(" |
19 | [email protected] ")" | ||
19 | [email protected] ";" | 20 | [email protected] ";" |
20 | [email protected] "\n" | 21 | [email protected] "\n" |
21 | [email protected] | 22 | [email protected] |
@@ -26,19 +27,20 @@ [email protected] | |||
26 | [email protected] " " | 27 | [email protected] " " |
27 | [email protected] "=" | 28 | [email protected] "=" |
28 | [email protected] " " | 29 | [email protected] " " |
29 | [email protected] | 30 | [email protected] |
30 | [email protected] | 31 | [email protected] |
31 | [email protected] | 32 | [email protected] |
32 | [email protected] | 33 | [email protected] |
33 | [email protected] | 34 | [email protected] |
34 | [email protected] "crate" | 35 | [email protected] |
35 | [email protected] "::" | 36 | [email protected] "crate" |
36 | [email protected] | 37 | [email protected] "::" |
37 | [email protected] | 38 | [email protected] |
38 | [email protected] "foo" | 39 | [email protected] |
39 | [email protected] "!" | 40 | [email protected] "foo" |
40 | [email protected] | 41 | [email protected] "!" |
41 | [email protected] "(" | 42 | [email protected] |
42 | [email protected] ")" | 43 | [email protected] "(" |
44 | [email protected] ")" | ||
43 | [email protected] ";" | 45 | [email protected] ";" |
44 | [email protected] "\n" | 46 | [email protected] "\n" |
diff --git a/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast index 925409bdf..e9202a612 100644 --- a/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast +++ b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast | |||
@@ -1,5 +1,5 @@ | |||
1 | SOURCE_FILE@0..63 | 1 | SOURCE_FILE@0..102 |
2 | FN@0..62 | 2 | FN@0..101 |
3 | [email protected] "fn" | 3 | [email protected] "fn" |
4 | [email protected] " " | 4 | [email protected] " " |
5 | [email protected] | 5 | [email protected] |
@@ -8,7 +8,7 @@ [email protected] | |||
8 | [email protected] "(" | 8 | [email protected] "(" |
9 | [email protected] ")" | 9 | [email protected] ")" |
10 | [email protected] " " | 10 | [email protected] " " |
11 | BLOCK_EXPR@9..62 | 11 | BLOCK_EXPR@9..101 |
12 | [email protected] "{" | 12 | [email protected] "{" |
13 | [email protected] "\n " | 13 | [email protected] "\n " |
14 | [email protected] | 14 | [email protected] |
@@ -70,6 +70,52 @@ [email protected] | |||
70 | [email protected] "(" | 70 | [email protected] "(" |
71 | [email protected] ")" | 71 | [email protected] ")" |
72 | [email protected] ";" | 72 | [email protected] ";" |
73 | [email protected] "\n" | 73 | [email protected] "\n " |
74 | [email protected] "}" | 74 | [email protected] |
75 | [email protected] "\n" | 75 | [email protected] "let" |
76 | [email protected] " " | ||
77 | [email protected] | ||
78 | [email protected] | ||
79 | [email protected] | ||
80 | [email protected] | ||
81 | [email protected] "S" | ||
82 | [email protected] " " | ||
83 | [email protected] | ||
84 | [email protected] "{" | ||
85 | [email protected] " " | ||
86 | [email protected] | ||
87 | [email protected] | ||
88 | [email protected] "#" | ||
89 | [email protected] "[" | ||
90 | [email protected] | ||
91 | [email protected] | ||
92 | [email protected] | ||
93 | [email protected] "cfg" | ||
94 | [email protected] | ||
95 | [email protected] "(" | ||
96 | [email protected] "any" | ||
97 | [email protected] | ||
98 | [email protected] "(" | ||
99 | [email protected] ")" | ||
100 | [email protected] ")" | ||
101 | [email protected] "]" | ||
102 | [email protected] " " | ||
103 | [email protected] | ||
104 | [email protected] "x" | ||
105 | [email protected] ":" | ||
106 | [email protected] " " | ||
107 | [email protected] | ||
108 | [email protected] | ||
109 | [email protected] "1" | ||
110 | [email protected] " " | ||
111 | [email protected] "}" | ||
112 | [email protected] " " | ||
113 | [email protected] "=" | ||
114 | [email protected] " " | ||
115 | [email protected] | ||
116 | [email protected] "(" | ||
117 | [email protected] ")" | ||
118 | [email protected] ";" | ||
119 | [email protected] "\n" | ||
120 | [email protected] "}" | ||
121 | [email protected] "\n" | ||
diff --git a/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs index 26b1d5f89..53cfdc22d 100644 --- a/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs +++ b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs | |||
@@ -1,4 +1,5 @@ | |||
1 | fn foo() { | 1 | fn foo() { |
2 | let S { 0: 1 } = (); | 2 | let S { 0: 1 } = (); |
3 | let S { x: 1 } = (); | 3 | let S { x: 1 } = (); |
4 | let S { #[cfg(any())] x: 1 } = (); | ||
4 | } | 5 | } |
diff --git a/crates/syntax/test_data/parser/ok/0045_block_attrs.rast b/crates/syntax/test_data/parser/ok/0045_block_attrs.rast new file mode 100644 index 000000000..50ab52d32 --- /dev/null +++ b/crates/syntax/test_data/parser/ok/0045_block_attrs.rast | |||
@@ -0,0 +1,218 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "fn" | ||
4 | [email protected] " " | ||
5 | [email protected] | ||
6 | [email protected] "inner" | ||
7 | [email protected] | ||
8 | [email protected] "(" | ||
9 | [email protected] ")" | ||
10 | [email protected] " " | ||
11 | [email protected] | ||
12 | [email protected] "{" | ||
13 | [email protected] "\n " | ||
14 | [email protected] | ||
15 | [email protected] "#" | ||
16 | [email protected] "!" | ||
17 | [email protected] "[" | ||
18 | [email protected] | ||
19 | [email protected] | ||
20 | [email protected] | ||
21 | [email protected] "doc" | ||
22 | [email protected] | ||
23 | [email protected] "(" | ||
24 | [email protected] "\"Inner attributes all ..." | ||
25 | [email protected] ")" | ||
26 | [email protected] "]" | ||
27 | [email protected] "\n " | ||
28 | [email protected] "//! As are ModuleDoc ..." | ||
29 | [email protected] "\n " | ||
30 | [email protected] | ||
31 | [email protected] | ||
32 | [email protected] "{" | ||
33 | [email protected] "\n " | ||
34 | [email protected] | ||
35 | [email protected] "#" | ||
36 | [email protected] "!" | ||
37 | [email protected] "[" | ||
38 | [email protected] | ||
39 | [email protected] | ||
40 | [email protected] | ||
41 | [email protected] "doc" | ||
42 | [email protected] | ||
43 | [email protected] "(" | ||
44 | [email protected] "\"Inner attributes are ..." | ||
45 | [email protected] ")" | ||
46 | [email protected] "]" | ||
47 | [email protected] "\n " | ||
48 | [email protected] | ||
49 | [email protected] "#" | ||
50 | [email protected] "!" | ||
51 | [email protected] "[" | ||
52 | [email protected] | ||
53 | [email protected] | ||
54 | [email protected] | ||
55 | [email protected] "doc" | ||
56 | [email protected] | ||
57 | [email protected] "(" | ||
58 | [email protected] "\"Being validated is n ..." | ||
59 | [email protected] ")" | ||
60 | [email protected] "]" | ||
61 | [email protected] "\n " | ||
62 | [email protected] "//! As are ModuleDoc ..." | ||
63 | [email protected] "\n " | ||
64 | [email protected] "}" | ||
65 | [email protected] ";" | ||
66 | [email protected] "\n " | ||
67 | [email protected] | ||
68 | [email protected] "{" | ||
69 | [email protected] "\n " | ||
70 | [email protected] | ||
71 | [email protected] "#" | ||
72 | [email protected] "!" | ||
73 | [email protected] "[" | ||
74 | [email protected] | ||
75 | [email protected] | ||
76 | [email protected] | ||
77 | [email protected] "doc" | ||
78 | [email protected] | ||
79 | [email protected] "(" | ||
80 | [email protected] "\"Inner attributes are ..." | ||
81 | [email protected] ")" | ||
82 | [email protected] "]" | ||
83 | [email protected] "\n " | ||
84 | [email protected] "//! As are ModuleDoc ..." | ||
85 | [email protected] "\n " | ||
86 | [email protected] "}" | ||
87 | [email protected] "\n" | ||
88 | [email protected] "}" | ||
89 | [email protected] "\n\n" | ||
90 | [email protected] | ||
91 | [email protected] "fn" | ||
92 | [email protected] " " | ||
93 | [email protected] | ||
94 | [email protected] "outer" | ||
95 | [email protected] | ||
96 | [email protected] "(" | ||
97 | [email protected] ")" | ||
98 | [email protected] " " | ||
99 | [email protected] | ||
100 | [email protected] "{" | ||
101 | [email protected] "\n " | ||
102 | [email protected] | ||
103 | [email protected] "let" | ||
104 | [email protected] " " | ||
105 | [email protected] | ||
106 | [email protected] "_" | ||
107 | [email protected] " " | ||
108 | [email protected] "=" | ||
109 | [email protected] " " | ||
110 | [email protected] | ||
111 | [email protected] | ||
112 | [email protected] "#" | ||
113 | [email protected] "[" | ||
114 | [email protected] | ||
115 | [email protected] | ||
116 | [email protected] | ||
117 | [email protected] "doc" | ||
118 | [email protected] | ||
119 | [email protected] "(" | ||
120 | [email protected] "\"Outer attributes are ..." | ||
121 | [email protected] ")" | ||
122 | [email protected] "]" | ||
123 | [email protected] " " | ||
124 | [email protected] "{" | ||
125 | [email protected] "}" | ||
126 | [email protected] ";" | ||
127 | [email protected] "\n" | ||
128 | [email protected] "}" | ||
129 | [email protected] "\n\n" | ||
130 | [email protected] "// https://github.com ..." | ||
131 | [email protected] "\n" | ||
132 | [email protected] | ||
133 | [email protected] "impl" | ||
134 | [email protected] " " | ||
135 | [email protected] | ||
136 | [email protected] | ||
137 | [email protected] | ||
138 | [email protected] | ||
139 | [email protected] "Whatever" | ||
140 | [email protected] " " | ||
141 | [email protected] | ||
142 | [email protected] "{" | ||
143 | [email protected] "\n " | ||
144 | [email protected] | ||
145 | [email protected] "fn" | ||
146 | [email protected] " " | ||
147 | [email protected] | ||
148 | [email protected] "salsa_event" | ||
149 | [email protected] | ||
150 | [email protected] "(" | ||
151 | [email protected] | ||
152 | [email protected] "&" | ||
153 | [email protected] | ||
154 | [email protected] "self" | ||
155 | [email protected] "," | ||
156 | [email protected] " " | ||
157 | [email protected] | ||
158 | [email protected] | ||
159 | [email protected] | ||
160 | [email protected] "event_fn" | ||
161 | [email protected] ":" | ||
162 | [email protected] " " | ||
163 | [email protected] | ||
164 | [email protected] "impl" | ||
165 | [email protected] " " | ||
166 | [email protected] | ||
167 | [email protected] | ||
168 | [email protected] | ||
169 | [email protected] | ||
170 | [email protected] | ||
171 | [email protected] | ||
172 | [email protected] "Fn" | ||
173 | [email protected] | ||
174 | [email protected] "(" | ||
175 | [email protected] ")" | ||
176 | [email protected] " " | ||
177 | [email protected] | ||
178 | [email protected] "->" | ||
179 | [email protected] " " | ||
180 | [email protected] | ||
181 | [email protected] | ||
182 | [email protected] | ||
183 | [email protected] | ||
184 | [email protected] "Event" | ||
185 | [email protected] | ||
186 | [email protected] "<" | ||
187 | [email protected] | ||
188 | [email protected] | ||
189 | [email protected] | ||
190 | [email protected] | ||
191 | [email protected] | ||
192 | [email protected] "Self" | ||
193 | [email protected] ">" | ||
194 | [email protected] ")" | ||
195 | [email protected] " " | ||
196 | [email protected] | ||
197 | [email protected] "{" | ||
198 | [email protected] "\n " | ||
199 | [email protected] | ||
200 | [email protected] "#" | ||
201 | [email protected] "!" | ||
202 | [email protected] "[" | ||
203 | [email protected] | ||
204 | [email protected] | ||
205 | [email protected] | ||
206 | [email protected] "allow" | ||
207 | [email protected] | ||
208 | [email protected] "(" | ||
209 | [email protected] "unused_variables" | ||
210 | [email protected] ")" | ||
211 | [email protected] "]" | ||
212 | [email protected] " " | ||
213 | [email protected] "// this is `inner_at ..." | ||
214 | [email protected] "\n " | ||
215 | [email protected] "}" | ||
216 | [email protected] "\n" | ||
217 | [email protected] "}" | ||
218 | [email protected] "\n" | ||
diff --git a/crates/syntax/test_data/parser/ok/0045_block_inner_attrs.rs b/crates/syntax/test_data/parser/ok/0045_block_attrs.rs index 88df8138e..ed4593759 100644 --- a/crates/syntax/test_data/parser/ok/0045_block_inner_attrs.rs +++ b/crates/syntax/test_data/parser/ok/0045_block_attrs.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | fn block() { | 1 | fn inner() { |
2 | #![doc("Inner attributes allowed here")] | 2 | #![doc("Inner attributes allowed here")] |
3 | //! As are ModuleDoc style comments | 3 | //! As are ModuleDoc style comments |
4 | { | 4 | { |
@@ -12,6 +12,10 @@ fn block() { | |||
12 | } | 12 | } |
13 | } | 13 | } |
14 | 14 | ||
15 | fn outer() { | ||
16 | let _ = #[doc("Outer attributes are always allowed")] {}; | ||
17 | } | ||
18 | |||
15 | // https://github.com/rust-analyzer/rust-analyzer/issues/689 | 19 | // https://github.com/rust-analyzer/rust-analyzer/issues/689 |
16 | impl Whatever { | 20 | impl Whatever { |
17 | fn salsa_event(&self, event_fn: impl Fn() -> Event<Self>) { | 21 | fn salsa_event(&self, event_fn: impl Fn() -> Event<Self>) { |
diff --git a/crates/syntax/test_data/parser/ok/0045_block_inner_attrs.rast b/crates/syntax/test_data/parser/ok/0045_block_inner_attrs.rast deleted file mode 100644 index 6afed5f05..000000000 --- a/crates/syntax/test_data/parser/ok/0045_block_inner_attrs.rast +++ /dev/null | |||
@@ -1,178 +0,0 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "fn" | ||
4 | [email protected] " " | ||
5 | [email protected] | ||
6 | [email protected] "block" | ||
7 | [email protected] | ||
8 | [email protected] "(" | ||
9 | [email protected] ")" | ||
10 | [email protected] " " | ||
11 | [email protected] | ||
12 | [email protected] "{" | ||
13 | [email protected] "\n " | ||
14 | [email protected] | ||
15 | [email protected] "#" | ||
16 | [email protected] "!" | ||
17 | [email protected] "[" | ||
18 | [email protected] | ||
19 | [email protected] | ||
20 | [email protected] | ||
21 | [email protected] "doc" | ||
22 | [email protected] | ||
23 | [email protected] "(" | ||
24 | [email protected] "\"Inner attributes all ..." | ||
25 | [email protected] ")" | ||
26 | [email protected] "]" | ||
27 | [email protected] "\n " | ||
28 | [email protected] "//! As are ModuleDoc ..." | ||
29 | [email protected] "\n " | ||
30 | [email protected] | ||
31 | [email protected] | ||
32 | [email protected] "{" | ||
33 | [email protected] "\n " | ||
34 | [email protected] | ||
35 | [email protected] "#" | ||
36 | [email protected] "!" | ||
37 | [email protected] "[" | ||
38 | [email protected] | ||
39 | [email protected] | ||
40 | [email protected] | ||
41 | [email protected] "doc" | ||
42 | [email protected] | ||
43 | [email protected] "(" | ||
44 | [email protected] "\"Inner attributes are ..." | ||
45 | [email protected] ")" | ||
46 | [email protected] "]" | ||
47 | [email protected] "\n " | ||
48 | [email protected] | ||
49 | [email protected] "#" | ||
50 | [email protected] "!" | ||
51 | [email protected] "[" | ||
52 | [email protected] | ||
53 | [email protected] | ||
54 | [email protected] | ||
55 | [email protected] "doc" | ||
56 | [email protected] | ||
57 | [email protected] "(" | ||
58 | [email protected] "\"Being validated is n ..." | ||
59 | [email protected] ")" | ||
60 | [email protected] "]" | ||
61 | [email protected] "\n " | ||
62 | [email protected] "//! As are ModuleDoc ..." | ||
63 | [email protected] "\n " | ||
64 | [email protected] "}" | ||
65 | [email protected] ";" | ||
66 | [email protected] "\n " | ||
67 | [email protected] | ||
68 | [email protected] "{" | ||
69 | [email protected] "\n " | ||
70 | [email protected] | ||
71 | [email protected] "#" | ||
72 | [email protected] "!" | ||
73 | [email protected] "[" | ||
74 | [email protected] | ||
75 | [email protected] | ||
76 | [email protected] | ||
77 | [email protected] "doc" | ||
78 | [email protected] | ||
79 | [email protected] "(" | ||
80 | [email protected] "\"Inner attributes are ..." | ||
81 | [email protected] ")" | ||
82 | [email protected] "]" | ||
83 | [email protected] "\n " | ||
84 | [email protected] "//! As are ModuleDoc ..." | ||
85 | [email protected] "\n " | ||
86 | [email protected] "}" | ||
87 | [email protected] "\n" | ||
88 | [email protected] "}" | ||
89 | [email protected] "\n\n" | ||
90 | [email protected] "// https://github.com ..." | ||
91 | [email protected] "\n" | ||
92 | [email protected] | ||
93 | [email protected] "impl" | ||
94 | [email protected] " " | ||
95 | [email protected] | ||
96 | [email protected] | ||
97 | [email protected] | ||
98 | [email protected] | ||
99 | [email protected] "Whatever" | ||
100 | [email protected] " " | ||
101 | [email protected] | ||
102 | [email protected] "{" | ||
103 | [email protected] "\n " | ||
104 | [email protected] | ||
105 | [email protected] "fn" | ||
106 | [email protected] " " | ||
107 | [email protected] | ||
108 | [email protected] "salsa_event" | ||
109 | [email protected] | ||
110 | [email protected] "(" | ||
111 | [email protected] | ||
112 | [email protected] "&" | ||
113 | [email protected] | ||
114 | [email protected] "self" | ||
115 | [email protected] "," | ||
116 | [email protected] " " | ||
117 | [email protected] | ||
118 | [email protected] | ||
119 | [email protected] | ||
120 | [email protected] "event_fn" | ||
121 | [email protected] ":" | ||
122 | [email protected] " " | ||
123 | [email protected] | ||
124 | [email protected] "impl" | ||
125 | [email protected] " " | ||
126 | [email protected] | ||
127 | [email protected] | ||
128 | [email protected] | ||
129 | [email protected] | ||
130 | [email protected] | ||
131 | [email protected] | ||
132 | [email protected] "Fn" | ||
133 | [email protected] | ||
134 | [email protected] "(" | ||
135 | [email protected] ")" | ||
136 | [email protected] " " | ||
137 | [email protected] | ||
138 | [email protected] "->" | ||
139 | [email protected] " " | ||
140 | [email protected] | ||
141 | [email protected] | ||
142 | [email protected] | ||
143 | [email protected] | ||
144 | [email protected] "Event" | ||
145 | [email protected] | ||
146 | [email protected] "<" | ||
147 | [email protected] | ||
148 | [email protected] | ||
149 | [email protected] | ||
150 | [email protected] | ||
151 | [email protected] | ||
152 | [email protected] "Self" | ||
153 | [email protected] ">" | ||
154 | [email protected] ")" | ||
155 | [email protected] " " | ||
156 | [email protected] | ||
157 | [email protected] "{" | ||
158 | [email protected] "\n " | ||
159 | [email protected] | ||
160 | [email protected] "#" | ||
161 | [email protected] "!" | ||
162 | [email protected] "[" | ||
163 | [email protected] | ||
164 | [email protected] | ||
165 | [email protected] | ||
166 | [email protected] "allow" | ||
167 | [email protected] | ||
168 | [email protected] "(" | ||
169 | [email protected] "unused_variables" | ||
170 | [email protected] ")" | ||
171 | [email protected] "]" | ||
172 | [email protected] " " | ||
173 | [email protected] "// this is `inner_at ..." | ||
174 | [email protected] "\n " | ||
175 | [email protected] "}" | ||
176 | [email protected] "\n" | ||
177 | [email protected] "}" | ||
178 | [email protected] "\n" | ||
diff --git a/crates/test_utils/src/assert_linear.rs b/crates/test_utils/src/assert_linear.rs new file mode 100644 index 000000000..6ecc232e1 --- /dev/null +++ b/crates/test_utils/src/assert_linear.rs | |||
@@ -0,0 +1,112 @@ | |||
1 | //! Checks that a set of measurements looks like a linear function rather than | ||
2 | //! like a quadratic function. Algorithm: | ||
3 | //! | ||
4 | //! 1. Linearly scale input to be in [0; 1) | ||
5 | //! 2. Using linear regression, compute the best linear function approximating | ||
6 | //! the input. | ||
7 | //! 3. Compute RMSE and maximal absolute error. | ||
8 | //! 4. Check that errors are within tolerances and that the constant term is not | ||
9 | //! too negative. | ||
10 | //! | ||
11 | //! Ideally, we should use a proper "model selection" to directly compare | ||
12 | //! quadratic and linear models, but that sounds rather complicated: | ||
13 | //! | ||
14 | //! https://stats.stackexchange.com/questions/21844/selecting-best-model-based-on-linear-quadratic-and-cubic-fit-of-data | ||
15 | //! | ||
16 | //! We might get false positives on a VM, but never false negatives. So, if the | ||
17 | //! first round fails, we repeat the ordeal three more times and fail only if | ||
18 | //! every time there's a fault. | ||
19 | use stdx::format_to; | ||
20 | |||
21 | #[derive(Default)] | ||
22 | pub struct AssertLinear { | ||
23 | rounds: Vec<Round>, | ||
24 | } | ||
25 | |||
26 | #[derive(Default)] | ||
27 | struct Round { | ||
28 | samples: Vec<(f64, f64)>, | ||
29 | plot: String, | ||
30 | linear: bool, | ||
31 | } | ||
32 | |||
33 | impl AssertLinear { | ||
34 | pub fn next_round(&mut self) -> bool { | ||
35 | if let Some(round) = self.rounds.last_mut() { | ||
36 | round.finish(); | ||
37 | } | ||
38 | if self.rounds.iter().any(|it| it.linear) || self.rounds.len() == 4 { | ||
39 | return false; | ||
40 | } | ||
41 | self.rounds.push(Round::default()); | ||
42 | true | ||
43 | } | ||
44 | |||
45 | pub fn sample(&mut self, x: f64, y: f64) { | ||
46 | self.rounds.last_mut().unwrap().samples.push((x, y)) | ||
47 | } | ||
48 | } | ||
49 | |||
50 | impl Drop for AssertLinear { | ||
51 | fn drop(&mut self) { | ||
52 | assert!(!self.rounds.is_empty()); | ||
53 | if self.rounds.iter().all(|it| !it.linear) { | ||
54 | for round in &self.rounds { | ||
55 | eprintln!("\n{}", round.plot); | ||
56 | } | ||
57 | panic!("Doesn't look linear!") | ||
58 | } | ||
59 | } | ||
60 | } | ||
61 | |||
62 | impl Round { | ||
63 | fn finish(&mut self) { | ||
64 | let (mut xs, mut ys): (Vec<_>, Vec<_>) = self.samples.iter().copied().unzip(); | ||
65 | normalize(&mut xs); | ||
66 | normalize(&mut ys); | ||
67 | let xy = xs.iter().copied().zip(ys.iter().copied()); | ||
68 | |||
69 | // Linear regression: finding a and b to fit y = a + b*x. | ||
70 | |||
71 | let mean_x = mean(&xs); | ||
72 | let mean_y = mean(&ys); | ||
73 | |||
74 | let b = { | ||
75 | let mut num = 0.0; | ||
76 | let mut denom = 0.0; | ||
77 | for (x, y) in xy.clone() { | ||
78 | num += (x - mean_x) * (y - mean_y); | ||
79 | denom += (x - mean_x).powi(2); | ||
80 | } | ||
81 | num / denom | ||
82 | }; | ||
83 | |||
84 | let a = mean_y - b * mean_x; | ||
85 | |||
86 | self.plot = format!("y_pred = {:.3} + {:.3} * x\n\nx y y_pred\n", a, b); | ||
87 | |||
88 | let mut se = 0.0; | ||
89 | let mut max_error = 0.0f64; | ||
90 | for (x, y) in xy { | ||
91 | let y_pred = a + b * x; | ||
92 | se += (y - y_pred).powi(2); | ||
93 | max_error = max_error.max((y_pred - y).abs()); | ||
94 | |||
95 | format_to!(self.plot, "{:.3} {:.3} {:.3}\n", x, y, y_pred); | ||
96 | } | ||
97 | |||
98 | let rmse = (se / xs.len() as f64).sqrt(); | ||
99 | format_to!(self.plot, "\nrmse = {:.3} max error = {:.3}", rmse, max_error); | ||
100 | |||
101 | self.linear = rmse < 0.05 && max_error < 0.1 && a > -0.1; | ||
102 | |||
103 | fn normalize(xs: &mut Vec<f64>) { | ||
104 | let max = xs.iter().copied().max_by(|a, b| a.partial_cmp(b).unwrap()).unwrap(); | ||
105 | xs.iter_mut().for_each(|it| *it /= max); | ||
106 | } | ||
107 | |||
108 | fn mean(xs: &[f64]) -> f64 { | ||
109 | xs.iter().copied().sum::<f64>() / (xs.len() as f64) | ||
110 | } | ||
111 | } | ||
112 | } | ||
diff --git a/crates/test_utils/src/bench_fixture.rs b/crates/test_utils/src/bench_fixture.rs index 3a37c4473..979156263 100644 --- a/crates/test_utils/src/bench_fixture.rs +++ b/crates/test_utils/src/bench_fixture.rs | |||
@@ -8,7 +8,10 @@ use crate::project_root; | |||
8 | 8 | ||
9 | pub fn big_struct() -> String { | 9 | pub fn big_struct() -> String { |
10 | let n = 1_000; | 10 | let n = 1_000; |
11 | big_struct_n(n) | ||
12 | } | ||
11 | 13 | ||
14 | pub fn big_struct_n(n: u32) -> String { | ||
12 | let mut buf = "pub struct RegisterBlock {".to_string(); | 15 | let mut buf = "pub struct RegisterBlock {".to_string(); |
13 | for i in 0..n { | 16 | for i in 0..n { |
14 | format_to!(buf, " /// Doc comment for {}.\n", i); | 17 | format_to!(buf, " /// Doc comment for {}.\n", i); |
diff --git a/crates/test_utils/src/fixture.rs b/crates/test_utils/src/fixture.rs index 6bc824e94..099baeca2 100644 --- a/crates/test_utils/src/fixture.rs +++ b/crates/test_utils/src/fixture.rs | |||
@@ -1,5 +1,65 @@ | |||
1 | //! Defines `Fixture` -- a convenient way to describe the initial state of | 1 | //! Defines `Fixture` -- a convenient way to describe the initial state of |
2 | //! rust-analyzer database from a single string. | 2 | //! rust-analyzer database from a single string. |
3 | //! | ||
4 | //! Fixtures are strings containing rust source code with optional metadata. | ||
5 | //! A fixture without metadata is parsed into a single source file. | ||
6 | //! Use this to test functionality local to one file. | ||
7 | //! | ||
8 | //! Simple Example: | ||
9 | //! ``` | ||
10 | //! r#" | ||
11 | //! fn main() { | ||
12 | //! println!("Hello World") | ||
13 | //! } | ||
14 | //! "# | ||
15 | //! ``` | ||
16 | //! | ||
17 | //! Metadata can be added to a fixture after a `//-` comment. | ||
18 | //! The basic form is specifying filenames, | ||
19 | //! which is also how to define multiple files in a single test fixture | ||
20 | //! | ||
21 | //! Example using two files in the same crate: | ||
22 | //! ``` | ||
23 | //! " | ||
24 | //! //- /main.rs | ||
25 | //! mod foo; | ||
26 | //! fn main() { | ||
27 | //! foo::bar(); | ||
28 | //! } | ||
29 | //! | ||
30 | //! //- /foo.rs | ||
31 | //! pub fn bar() {} | ||
32 | //! " | ||
33 | //! ``` | ||
34 | //! | ||
35 | //! Example using two crates with one file each, with one crate depending on the other: | ||
36 | //! ``` | ||
37 | //! r#" | ||
38 | //! //- /main.rs crate:a deps:b | ||
39 | //! fn main() { | ||
40 | //! b::foo(); | ||
41 | //! } | ||
42 | //! //- /lib.rs crate:b | ||
43 | //! pub fn b() { | ||
44 | //! println!("Hello World") | ||
45 | //! } | ||
46 | //! "# | ||
47 | //! ``` | ||
48 | //! | ||
49 | //! Metadata allows specifying all settings and variables | ||
50 | //! that are available in a real rust project: | ||
51 | //! - crate names via `crate:cratename` | ||
52 | //! - dependencies via `deps:dep1,dep2` | ||
53 | //! - configuration settings via `cfg:dbg=false,opt_level=2` | ||
54 | //! - environment variables via `env:PATH=/bin,RUST_LOG=debug` | ||
55 | //! | ||
56 | //! Example using all available metadata: | ||
57 | //! ``` | ||
58 | //! " | ||
59 | //! //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo | ||
60 | //! fn insert_source_code_here() {} | ||
61 | //! " | ||
62 | //! ``` | ||
3 | 63 | ||
4 | use rustc_hash::FxHashMap; | 64 | use rustc_hash::FxHashMap; |
5 | use stdx::{lines_with_ends, split_once, trim_indent}; | 65 | use stdx::{lines_with_ends, split_once, trim_indent}; |
@@ -24,7 +84,7 @@ impl Fixture { | |||
24 | /// //- some meta | 84 | /// //- some meta |
25 | /// line 1 | 85 | /// line 1 |
26 | /// line 2 | 86 | /// line 2 |
27 | /// // - other meta | 87 | /// //- other meta |
28 | /// ``` | 88 | /// ``` |
29 | pub fn parse(ra_fixture: &str) -> Vec<Fixture> { | 89 | pub fn parse(ra_fixture: &str) -> Vec<Fixture> { |
30 | let fixture = trim_indent(ra_fixture); | 90 | let fixture = trim_indent(ra_fixture); |
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index c5f859790..72466c957 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs | |||
@@ -8,6 +8,7 @@ | |||
8 | 8 | ||
9 | pub mod bench_fixture; | 9 | pub mod bench_fixture; |
10 | mod fixture; | 10 | mod fixture; |
11 | mod assert_linear; | ||
11 | 12 | ||
12 | use std::{ | 13 | use std::{ |
13 | convert::{TryFrom, TryInto}, | 14 | convert::{TryFrom, TryInto}, |
@@ -22,7 +23,7 @@ use text_size::{TextRange, TextSize}; | |||
22 | pub use dissimilar::diff as __diff; | 23 | pub use dissimilar::diff as __diff; |
23 | pub use rustc_hash::FxHashMap; | 24 | pub use rustc_hash::FxHashMap; |
24 | 25 | ||
25 | pub use crate::fixture::Fixture; | 26 | pub use crate::{assert_linear::AssertLinear, fixture::Fixture}; |
26 | 27 | ||
27 | pub const CURSOR_MARKER: &str = "$0"; | 28 | pub const CURSOR_MARKER: &str = "$0"; |
28 | pub const ESCAPED_CURSOR_MARKER: &str = "\\$0"; | 29 | pub const ESCAPED_CURSOR_MARKER: &str = "\\$0"; |
diff --git a/crates/vfs/Cargo.toml b/crates/vfs/Cargo.toml index c318a68f7..894944b18 100644 --- a/crates/vfs/Cargo.toml +++ b/crates/vfs/Cargo.toml | |||
@@ -14,3 +14,4 @@ rustc-hash = "1.0" | |||
14 | fst = "0.4" | 14 | fst = "0.4" |
15 | 15 | ||
16 | paths = { path = "../paths", version = "0.0.0" } | 16 | paths = { path = "../paths", version = "0.0.0" } |
17 | indexmap = "1.6.2" | ||
diff --git a/crates/vfs/src/path_interner.rs b/crates/vfs/src/path_interner.rs index 2189e5e25..6e049f0d4 100644 --- a/crates/vfs/src/path_interner.rs +++ b/crates/vfs/src/path_interner.rs | |||
@@ -1,15 +1,22 @@ | |||
1 | //! Maps paths to compact integer ids. We don't care about clearings paths which | 1 | //! Maps paths to compact integer ids. We don't care about clearings paths which |
2 | //! no longer exist -- the assumption is total size of paths we ever look at is | 2 | //! no longer exist -- the assumption is total size of paths we ever look at is |
3 | //! not too big. | 3 | //! not too big. |
4 | use rustc_hash::FxHashMap; | 4 | use std::hash::BuildHasherDefault; |
5 | |||
6 | use indexmap::IndexSet; | ||
7 | use rustc_hash::FxHasher; | ||
5 | 8 | ||
6 | use crate::{FileId, VfsPath}; | 9 | use crate::{FileId, VfsPath}; |
7 | 10 | ||
8 | /// Structure to map between [`VfsPath`] and [`FileId`]. | 11 | /// Structure to map between [`VfsPath`] and [`FileId`]. |
9 | #[derive(Default)] | ||
10 | pub(crate) struct PathInterner { | 12 | pub(crate) struct PathInterner { |
11 | map: FxHashMap<VfsPath, FileId>, | 13 | map: IndexSet<VfsPath, BuildHasherDefault<FxHasher>>, |
12 | vec: Vec<VfsPath>, | 14 | } |
15 | |||
16 | impl Default for PathInterner { | ||
17 | fn default() -> Self { | ||
18 | Self { map: IndexSet::default() } | ||
19 | } | ||
13 | } | 20 | } |
14 | 21 | ||
15 | impl PathInterner { | 22 | impl PathInterner { |
@@ -17,7 +24,7 @@ impl PathInterner { | |||
17 | /// | 24 | /// |
18 | /// If `path` does not exists in `self`, returns [`None`]. | 25 | /// If `path` does not exists in `self`, returns [`None`]. |
19 | pub(crate) fn get(&self, path: &VfsPath) -> Option<FileId> { | 26 | pub(crate) fn get(&self, path: &VfsPath) -> Option<FileId> { |
20 | self.map.get(path).copied() | 27 | self.map.get_index_of(path).map(|i| FileId(i as u32)) |
21 | } | 28 | } |
22 | 29 | ||
23 | /// Insert `path` in `self`. | 30 | /// Insert `path` in `self`. |
@@ -25,13 +32,9 @@ impl PathInterner { | |||
25 | /// - If `path` already exists in `self`, returns its associated id; | 32 | /// - If `path` already exists in `self`, returns its associated id; |
26 | /// - Else, returns a newly allocated id. | 33 | /// - Else, returns a newly allocated id. |
27 | pub(crate) fn intern(&mut self, path: VfsPath) -> FileId { | 34 | pub(crate) fn intern(&mut self, path: VfsPath) -> FileId { |
28 | if let Some(id) = self.get(&path) { | 35 | let (id, _added) = self.map.insert_full(path); |
29 | return id; | 36 | assert!(id < u32::MAX as usize); |
30 | } | 37 | FileId(id as u32) |
31 | let id = FileId(self.vec.len() as u32); | ||
32 | self.map.insert(path.clone(), id); | ||
33 | self.vec.push(path); | ||
34 | id | ||
35 | } | 38 | } |
36 | 39 | ||
37 | /// Returns the path corresponding to `id`. | 40 | /// Returns the path corresponding to `id`. |
@@ -40,6 +43,6 @@ impl PathInterner { | |||
40 | /// | 43 | /// |
41 | /// Panics if `id` does not exists in `self`. | 44 | /// Panics if `id` does not exists in `self`. |
42 | pub(crate) fn lookup(&self, id: FileId) -> &VfsPath { | 45 | pub(crate) fn lookup(&self, id: FileId) -> &VfsPath { |
43 | &self.vec[id.0 as usize] | 46 | self.map.get_index(id.0 as usize).unwrap() |
44 | } | 47 | } |
45 | } | 48 | } |