diff options
68 files changed, 1593 insertions, 2158 deletions
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3adf9e6ca..c915b9d14 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml | |||
@@ -34,7 +34,7 @@ jobs: | |||
34 | name: Rust | 34 | name: Rust |
35 | runs-on: ${{ matrix.os }} | 35 | runs-on: ${{ matrix.os }} |
36 | env: | 36 | env: |
37 | CC: deny_c | 37 | CC: deny_c |
38 | 38 | ||
39 | strategy: | 39 | strategy: |
40 | fail-fast: false | 40 | fail-fast: false |
@@ -64,20 +64,16 @@ jobs: | |||
64 | - if: matrix.os == 'ubuntu-latest' | 64 | - if: matrix.os == 'ubuntu-latest' |
65 | run: sudo chown -R $(whoami):$(id -ng) ~/.cargo/ | 65 | run: sudo chown -R $(whoami):$(id -ng) ~/.cargo/ |
66 | 66 | ||
67 | - name: Cache cargo registry | 67 | - name: Cache cargo directories |
68 | uses: actions/cache@v1 | 68 | uses: actions/cache@v2 |
69 | with: | 69 | with: |
70 | path: ~/.cargo/registry | 70 | path: | |
71 | key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} | 71 | ~/.cargo/registry |
72 | 72 | ~/.cargo/git | |
73 | - name: Cache cargo index | 73 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} |
74 | uses: actions/cache@v1 | ||
75 | with: | ||
76 | path: ~/.cargo/git | ||
77 | key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} | ||
78 | 74 | ||
79 | - name: Cache cargo target dir | 75 | - name: Cache cargo target dir |
80 | uses: actions/cache@v1 | 76 | uses: actions/cache@v2 |
81 | with: | 77 | with: |
82 | path: target | 78 | path: target |
83 | key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} | 79 | key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} |
@@ -95,6 +91,36 @@ jobs: | |||
95 | if: matrix.os == 'windows-latest' | 91 | if: matrix.os == 'windows-latest' |
96 | run: Remove-Item ./target/debug/xtask.exe, ./target/debug/deps/xtask.exe | 92 | run: Remove-Item ./target/debug/xtask.exe, ./target/debug/deps/xtask.exe |
97 | 93 | ||
94 | # Weird target to catch non-portable code | ||
95 | rust-power: | ||
96 | name: Rust Power | ||
97 | runs-on: ubuntu-latest | ||
98 | |||
99 | steps: | ||
100 | - name: Checkout repository | ||
101 | uses: actions/checkout@v2 | ||
102 | |||
103 | - name: Install Rust toolchain | ||
104 | uses: actions-rs/toolchain@v1 | ||
105 | with: | ||
106 | toolchain: stable | ||
107 | profile: minimal | ||
108 | override: true | ||
109 | target: 'powerpc-unknown-linux-gnu' | ||
110 | |||
111 | - run: sudo chown -R $(whoami):$(id -ng) ~/.cargo/ | ||
112 | |||
113 | - name: Cache cargo directories | ||
114 | uses: actions/cache@v2 | ||
115 | with: | ||
116 | path: | | ||
117 | ~/.cargo/registry | ||
118 | ~/.cargo/git | ||
119 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} | ||
120 | |||
121 | - name: Check | ||
122 | run: cargo check --target=powerpc-unknown-linux-gnu --all-targets | ||
123 | |||
98 | typescript: | 124 | typescript: |
99 | name: TypeScript | 125 | name: TypeScript |
100 | strategy: | 126 | strategy: |
@@ -103,7 +129,7 @@ jobs: | |||
103 | os: [ubuntu-latest, windows-latest, macos-latest] | 129 | os: [ubuntu-latest, windows-latest, macos-latest] |
104 | 130 | ||
105 | runs-on: ${{ matrix.os }} | 131 | runs-on: ${{ matrix.os }} |
106 | 132 | ||
107 | steps: | 133 | steps: |
108 | - name: Checkout repository | 134 | - name: Checkout repository |
109 | uses: actions/checkout@v2 | 135 | uses: actions/checkout@v2 |
diff --git a/Cargo.lock b/Cargo.lock index a05c62e45..6cc44e0da 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -730,9 +730,9 @@ dependencies = [ | |||
730 | 730 | ||
731 | [[package]] | 731 | [[package]] |
732 | name = "lsp-types" | 732 | name = "lsp-types" |
733 | version = "0.76.0" | 733 | version = "0.76.1" |
734 | source = "registry+https://github.com/rust-lang/crates.io-index" | 734 | source = "registry+https://github.com/rust-lang/crates.io-index" |
735 | checksum = "af5586f0631c7f7826c3ea39377c326d7b4623138be7ab1204dab22e47717449" | 735 | checksum = "55cfa1593e04985972e018890b2e1a9ed25c71efc800067fbf0990a9432421c8" |
736 | dependencies = [ | 736 | dependencies = [ |
737 | "base64", | 737 | "base64", |
738 | "bitflags", | 738 | "bitflags", |
diff --git a/crates/expect/Cargo.toml b/crates/expect/Cargo.toml index f8fa29e4e..77775630d 100644 --- a/crates/expect/Cargo.toml +++ b/crates/expect/Cargo.toml | |||
@@ -3,6 +3,7 @@ name = "expect" | |||
3 | version = "0.1.0" | 3 | version = "0.1.0" |
4 | authors = ["rust-analyzer developers"] | 4 | authors = ["rust-analyzer developers"] |
5 | edition = "2018" | 5 | edition = "2018" |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml index dc26b8ce7..bea485694 100644 --- a/crates/flycheck/Cargo.toml +++ b/crates/flycheck/Cargo.toml | |||
@@ -3,6 +3,7 @@ edition = "2018" | |||
3 | name = "flycheck" | 3 | name = "flycheck" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/paths/Cargo.toml b/crates/paths/Cargo.toml index 646ee7fd5..cbe2c26e2 100644 --- a/crates/paths/Cargo.toml +++ b/crates/paths/Cargo.toml | |||
@@ -3,6 +3,7 @@ name = "paths" | |||
3 | version = "0.1.0" | 3 | version = "0.1.0" |
4 | authors = ["rust-analyzer developers"] | 4 | authors = ["rust-analyzer developers"] |
5 | edition = "2018" | 5 | edition = "2018" |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/ra_arena/Cargo.toml b/crates/ra_arena/Cargo.toml index d287dbb73..66c3738f4 100644 --- a/crates/ra_arena/Cargo.toml +++ b/crates/ra_arena/Cargo.toml | |||
@@ -3,6 +3,7 @@ edition = "2018" | |||
3 | name = "ra_arena" | 3 | name = "ra_arena" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/ra_assists/Cargo.toml b/crates/ra_assists/Cargo.toml index 3bcf58ba4..bd2905f08 100644 --- a/crates/ra_assists/Cargo.toml +++ b/crates/ra_assists/Cargo.toml | |||
@@ -3,6 +3,7 @@ edition = "2018" | |||
3 | name = "ra_assists" | 3 | name = "ra_assists" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs index d6aaf53f1..f185e61e5 100644 --- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs | |||
@@ -158,6 +158,9 @@ fn add_missing_impl_members_inner( | |||
158 | .map(|it| ast_transform::apply(&*ast_transform, it)) | 158 | .map(|it| ast_transform::apply(&*ast_transform, it)) |
159 | .map(|it| match it { | 159 | .map(|it| match it { |
160 | ast::AssocItem::FnDef(def) => ast::AssocItem::FnDef(add_body(def)), | 160 | ast::AssocItem::FnDef(def) => ast::AssocItem::FnDef(add_body(def)), |
161 | ast::AssocItem::TypeAliasDef(def) => { | ||
162 | ast::AssocItem::TypeAliasDef(def.remove_bounds()) | ||
163 | } | ||
161 | _ => it, | 164 | _ => it, |
162 | }) | 165 | }) |
163 | .map(|it| edit::remove_attrs_and_docs(&it)); | 166 | .map(|it| edit::remove_attrs_and_docs(&it)); |
@@ -684,4 +687,26 @@ impl Foo<T> for S<T> { | |||
684 | }"#, | 687 | }"#, |
685 | ) | 688 | ) |
686 | } | 689 | } |
690 | |||
691 | #[test] | ||
692 | fn test_assoc_type_bounds_are_removed() { | ||
693 | check_assist( | ||
694 | add_missing_impl_members, | ||
695 | r#" | ||
696 | trait Tr { | ||
697 | type Ty: Copy + 'static; | ||
698 | } | ||
699 | |||
700 | impl Tr for ()<|> { | ||
701 | }"#, | ||
702 | r#" | ||
703 | trait Tr { | ||
704 | type Ty: Copy + 'static; | ||
705 | } | ||
706 | |||
707 | impl Tr for () { | ||
708 | $0type Ty; | ||
709 | }"#, | ||
710 | ) | ||
711 | } | ||
687 | } | 712 | } |
diff --git a/crates/ra_cfg/Cargo.toml b/crates/ra_cfg/Cargo.toml index 9165076a5..6425cd6d6 100644 --- a/crates/ra_cfg/Cargo.toml +++ b/crates/ra_cfg/Cargo.toml | |||
@@ -3,6 +3,7 @@ edition = "2018" | |||
3 | name = "ra_cfg" | 3 | name = "ra_cfg" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml index 889142442..5f334d04f 100644 --- a/crates/ra_db/Cargo.toml +++ b/crates/ra_db/Cargo.toml | |||
@@ -3,6 +3,7 @@ edition = "2018" | |||
3 | name = "ra_db" | 3 | name = "ra_db" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/ra_fmt/Cargo.toml b/crates/ra_fmt/Cargo.toml index e9d057afc..b4ef93f2b 100644 --- a/crates/ra_fmt/Cargo.toml +++ b/crates/ra_fmt/Cargo.toml | |||
@@ -4,6 +4,7 @@ name = "ra_fmt" | |||
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | publish = false | 6 | publish = false |
7 | license = "MIT OR Apache-2.0" | ||
7 | 8 | ||
8 | [lib] | 9 | [lib] |
9 | doctest = false | 10 | doctest = false |
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml index 512676c99..c260bb193 100644 --- a/crates/ra_hir/Cargo.toml +++ b/crates/ra_hir/Cargo.toml | |||
@@ -3,6 +3,7 @@ edition = "2018" | |||
3 | name = "ra_hir" | 3 | name = "ra_hir" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 9222009fe..42c9ca189 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -25,11 +25,8 @@ use hir_expand::{ | |||
25 | use hir_ty::{ | 25 | use hir_ty::{ |
26 | autoderef, | 26 | autoderef, |
27 | display::{HirDisplayError, HirFormatter}, | 27 | display::{HirDisplayError, HirFormatter}, |
28 | expr::ExprValidator, | 28 | method_resolution, ApplicationTy, Canonical, GenericPredicate, InEnvironment, Substs, |
29 | method_resolution, | 29 | TraitEnvironment, Ty, TyDefId, TypeCtor, |
30 | unsafe_validation::UnsafeValidator, | ||
31 | ApplicationTy, Canonical, GenericPredicate, InEnvironment, Substs, TraitEnvironment, Ty, | ||
32 | TyDefId, TypeCtor, | ||
33 | }; | 30 | }; |
34 | use ra_db::{CrateId, Edition, FileId}; | 31 | use ra_db::{CrateId, Edition, FileId}; |
35 | use ra_prof::profile; | 32 | use ra_prof::profile; |
@@ -680,13 +677,7 @@ impl Function { | |||
680 | } | 677 | } |
681 | 678 | ||
682 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | 679 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { |
683 | let _p = profile("Function::diagnostics"); | 680 | hir_ty::diagnostics::validate_body(db, self.id.into(), sink) |
684 | let infer = db.infer(self.id.into()); | ||
685 | infer.add_diagnostics(db, self.id, sink); | ||
686 | let mut validator = ExprValidator::new(self.id, infer.clone(), sink); | ||
687 | validator.validate_body(db); | ||
688 | let mut validator = UnsafeValidator::new(self.id, infer, sink); | ||
689 | validator.validate_body(db); | ||
690 | } | 681 | } |
691 | } | 682 | } |
692 | 683 | ||
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index f74b78b23..d76345525 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs | |||
@@ -18,7 +18,7 @@ use hir_def::{ | |||
18 | }; | 18 | }; |
19 | use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; | 19 | use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; |
20 | use hir_ty::{ | 20 | use hir_ty::{ |
21 | expr::{record_literal_missing_fields, record_pattern_missing_fields}, | 21 | diagnostics::{record_literal_missing_fields, record_pattern_missing_fields}, |
22 | InferenceResult, Substs, Ty, | 22 | InferenceResult, Substs, Ty, |
23 | }; | 23 | }; |
24 | use ra_syntax::{ | 24 | use ra_syntax::{ |
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml index cf5ebbaf5..1ec619b42 100644 --- a/crates/ra_hir_def/Cargo.toml +++ b/crates/ra_hir_def/Cargo.toml | |||
@@ -3,6 +3,7 @@ edition = "2018" | |||
3 | name = "ra_hir_def" | 3 | name = "ra_hir_def" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index aa335f1e3..88a8ef9bf 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs | |||
@@ -27,6 +27,7 @@ pub struct FunctionData { | |||
27 | /// can be called as a method. | 27 | /// can be called as a method. |
28 | pub has_self_param: bool, | 28 | pub has_self_param: bool, |
29 | pub is_unsafe: bool, | 29 | pub is_unsafe: bool, |
30 | pub is_varargs: bool, | ||
30 | pub visibility: RawVisibility, | 31 | pub visibility: RawVisibility, |
31 | } | 32 | } |
32 | 33 | ||
@@ -43,6 +44,7 @@ impl FunctionData { | |||
43 | attrs: item_tree.attrs(ModItem::from(loc.id.value).into()).clone(), | 44 | attrs: item_tree.attrs(ModItem::from(loc.id.value).into()).clone(), |
44 | has_self_param: func.has_self_param, | 45 | has_self_param: func.has_self_param, |
45 | is_unsafe: func.is_unsafe, | 46 | is_unsafe: func.is_unsafe, |
47 | is_varargs: func.is_varargs, | ||
46 | visibility: item_tree[func.visibility].clone(), | 48 | visibility: item_tree[func.visibility].clone(), |
47 | }) | 49 | }) |
48 | } | 50 | } |
diff --git a/crates/ra_hir_def/src/item_tree.rs b/crates/ra_hir_def/src/item_tree.rs index 3e603bd55..da79d8ffd 100644 --- a/crates/ra_hir_def/src/item_tree.rs +++ b/crates/ra_hir_def/src/item_tree.rs | |||
@@ -503,6 +503,7 @@ pub struct Function { | |||
503 | pub has_self_param: bool, | 503 | pub has_self_param: bool, |
504 | pub is_unsafe: bool, | 504 | pub is_unsafe: bool, |
505 | pub params: Box<[TypeRef]>, | 505 | pub params: Box<[TypeRef]>, |
506 | pub is_varargs: bool, | ||
506 | pub ret_type: TypeRef, | 507 | pub ret_type: TypeRef, |
507 | pub ast_id: FileAstId<ast::FnDef>, | 508 | pub ast_id: FileAstId<ast::FnDef>, |
508 | } | 509 | } |
diff --git a/crates/ra_hir_def/src/item_tree/lower.rs b/crates/ra_hir_def/src/item_tree/lower.rs index 0b1509e13..f79b8fca3 100644 --- a/crates/ra_hir_def/src/item_tree/lower.rs +++ b/crates/ra_hir_def/src/item_tree/lower.rs | |||
@@ -219,21 +219,20 @@ impl Ctx { | |||
219 | fn lower_tuple_fields(&mut self, fields: &ast::TupleFieldDefList) -> IdRange<Field> { | 219 | fn lower_tuple_fields(&mut self, fields: &ast::TupleFieldDefList) -> IdRange<Field> { |
220 | let start = self.next_field_idx(); | 220 | let start = self.next_field_idx(); |
221 | for (i, field) in fields.fields().enumerate() { | 221 | for (i, field) in fields.fields().enumerate() { |
222 | if let Some(data) = self.lower_tuple_field(i, &field) { | 222 | let data = self.lower_tuple_field(i, &field); |
223 | let idx = self.data().fields.alloc(data); | 223 | let idx = self.data().fields.alloc(data); |
224 | self.add_attrs(idx.into(), Attrs::new(&field, &self.hygiene)); | 224 | self.add_attrs(idx.into(), Attrs::new(&field, &self.hygiene)); |
225 | } | ||
226 | } | 225 | } |
227 | let end = self.next_field_idx(); | 226 | let end = self.next_field_idx(); |
228 | IdRange::new(start..end) | 227 | IdRange::new(start..end) |
229 | } | 228 | } |
230 | 229 | ||
231 | fn lower_tuple_field(&mut self, idx: usize, field: &ast::TupleFieldDef) -> Option<Field> { | 230 | fn lower_tuple_field(&mut self, idx: usize, field: &ast::TupleFieldDef) -> Field { |
232 | let name = Name::new_tuple_field(idx); | 231 | let name = Name::new_tuple_field(idx); |
233 | let visibility = self.lower_visibility(field); | 232 | let visibility = self.lower_visibility(field); |
234 | let type_ref = self.lower_type_ref(&field.type_ref()?); | 233 | let type_ref = self.lower_type_ref_opt(field.type_ref()); |
235 | let res = Field { name, type_ref, visibility }; | 234 | let res = Field { name, type_ref, visibility }; |
236 | Some(res) | 235 | res |
237 | } | 236 | } |
238 | 237 | ||
239 | fn lower_union(&mut self, union: &ast::UnionDef) -> Option<FileItemTreeId<Union>> { | 238 | fn lower_union(&mut self, union: &ast::UnionDef) -> Option<FileItemTreeId<Union>> { |
@@ -314,6 +313,14 @@ impl Ctx { | |||
314 | params.push(type_ref); | 313 | params.push(type_ref); |
315 | } | 314 | } |
316 | } | 315 | } |
316 | |||
317 | let mut is_varargs = false; | ||
318 | if let Some(params) = func.param_list() { | ||
319 | if let Some(last) = params.params().last() { | ||
320 | is_varargs = last.dotdotdot_token().is_some(); | ||
321 | } | ||
322 | } | ||
323 | |||
317 | let ret_type = match func.ret_type().and_then(|rt| rt.type_ref()) { | 324 | let ret_type = match func.ret_type().and_then(|rt| rt.type_ref()) { |
318 | Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref), | 325 | Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref), |
319 | _ => TypeRef::unit(), | 326 | _ => TypeRef::unit(), |
@@ -335,6 +342,7 @@ impl Ctx { | |||
335 | has_self_param, | 342 | has_self_param, |
336 | is_unsafe: func.unsafe_token().is_some(), | 343 | is_unsafe: func.unsafe_token().is_some(), |
337 | params: params.into_boxed_slice(), | 344 | params: params.into_boxed_slice(), |
345 | is_varargs, | ||
338 | ret_type, | 346 | ret_type, |
339 | ast_id, | 347 | ast_id, |
340 | }; | 348 | }; |
diff --git a/crates/ra_hir_def/src/item_tree/tests.rs b/crates/ra_hir_def/src/item_tree/tests.rs index a0431dbce..f26982985 100644 --- a/crates/ra_hir_def/src/item_tree/tests.rs +++ b/crates/ra_hir_def/src/item_tree/tests.rs | |||
@@ -240,9 +240,9 @@ fn smoke() { | |||
240 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_const"))] }, input: None }]) }] | 240 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_const"))] }, input: None }]) }] |
241 | > Const { name: Some(Name(Text("CONST"))), visibility: RawVisibilityId("pub(self)"), type_ref: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("u8"))] }, generic_args: [None] }), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ConstDef>(9) } | 241 | > Const { name: Some(Name(Text("CONST"))), visibility: RawVisibilityId("pub(self)"), type_ref: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("u8"))] }, generic_args: [None] }), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ConstDef>(9) } |
242 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_method"))] }, input: None }]) }] | 242 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_method"))] }, input: None }]) }] |
243 | > Function { name: Name(Text("method")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: true, is_unsafe: false, params: [Reference(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Self"))] }, generic_args: [None] }), Shared)], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(10) } | 243 | > Function { name: Name(Text("method")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: true, is_unsafe: false, params: [Reference(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Self"))] }, generic_args: [None] }), Shared)], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(10) } |
244 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_dfl_method"))] }, input: None }]) }] | 244 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_dfl_method"))] }, input: None }]) }] |
245 | > Function { name: Name(Text("dfl_method")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: true, is_unsafe: false, params: [Reference(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Self"))] }, generic_args: [None] }), Mut)], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(11) } | 245 | > Function { name: Name(Text("dfl_method")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: true, is_unsafe: false, params: [Reference(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Self"))] }, generic_args: [None] }), Mut)], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(11) } |
246 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct0"))] }, input: None }]) }] | 246 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct0"))] }, input: None }]) }] |
247 | Struct { name: Name(Text("Struct0")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(1), fields: Unit, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(3), kind: Unit } | 247 | Struct { name: Name(Text("Struct0")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(1), fields: Unit, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(3), kind: Unit } |
248 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct1"))] }, input: None }]) }] | 248 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct1"))] }, input: None }]) }] |
@@ -275,12 +275,12 @@ fn simple_inner_items() { | |||
275 | 275 | ||
276 | top-level items: | 276 | top-level items: |
277 | Impl { generic_params: GenericParamsId(0), target_trait: Some(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("D"))] }, generic_args: [None] })), target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Response"))] }, generic_args: [Some(GenericArgs { args: [Type(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("T"))] }, generic_args: [None] }))], has_self_type: false, bindings: [] })] }), is_negative: false, items: [Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ImplDef>(0) } | 277 | Impl { generic_params: GenericParamsId(0), target_trait: Some(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("D"))] }, generic_args: [None] })), target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Response"))] }, generic_args: [Some(GenericArgs { args: [Type(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("T"))] }, generic_args: [None] }))], has_self_type: false, bindings: [] })] }), is_negative: false, items: [Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ImplDef>(0) } |
278 | > Function { name: Name(Text("foo")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) } | 278 | > Function { name: Name(Text("foo")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) } |
279 | 279 | ||
280 | inner items: | 280 | inner items: |
281 | 281 | ||
282 | for AST FileAstId::<ra_syntax::ast::generated::nodes::ModuleItem>(2): | 282 | for AST FileAstId::<ra_syntax::ast::generated::nodes::ModuleItem>(2): |
283 | Function { name: Name(Text("end")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(1), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) } | 283 | Function { name: Name(Text("end")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(1), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) } |
284 | 284 | ||
285 | "#]], | 285 | "#]], |
286 | ); | 286 | ); |
@@ -303,9 +303,9 @@ fn extern_attrs() { | |||
303 | 303 | ||
304 | top-level items: | 304 | top-level items: |
305 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }, Attr { path: ModPath { kind: Plain, segments: [Name(Text("block_attr"))] }, input: None }]) }] | 305 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }, Attr { path: ModPath { kind: Plain, segments: [Name(Text("block_attr"))] }, input: None }]) }] |
306 | Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: true, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) } | 306 | Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: true, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) } |
307 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }, Attr { path: ModPath { kind: Plain, segments: [Name(Text("block_attr"))] }, input: None }]) }] | 307 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }, Attr { path: ModPath { kind: Plain, segments: [Name(Text("block_attr"))] }, input: None }]) }] |
308 | Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: true, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) } | 308 | Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: true, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) } |
309 | "##]], | 309 | "##]], |
310 | ); | 310 | ); |
311 | } | 311 | } |
@@ -329,9 +329,9 @@ fn trait_attrs() { | |||
329 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("trait_attr"))] }, input: None }]) }] | 329 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("trait_attr"))] }, input: None }]) }] |
330 | Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::TraitDef>(0) } | 330 | Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::TraitDef>(0) } |
331 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }]) }] | 331 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }]) }] |
332 | > Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) } | 332 | > Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) } |
333 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }]) }] | 333 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }]) }] |
334 | > Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) } | 334 | > Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) } |
335 | "##]], | 335 | "##]], |
336 | ); | 336 | ); |
337 | } | 337 | } |
@@ -355,9 +355,9 @@ fn impl_attrs() { | |||
355 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("impl_attr"))] }, input: None }]) }] | 355 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("impl_attr"))] }, input: None }]) }] |
356 | Impl { generic_params: GenericParamsId(4294967295), target_trait: None, target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Ty"))] }, generic_args: [None] }), is_negative: false, items: [Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ImplDef>(0) } | 356 | Impl { generic_params: GenericParamsId(4294967295), target_trait: None, target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Ty"))] }, generic_args: [None] }), is_negative: false, items: [Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ImplDef>(0) } |
357 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }]) }] | 357 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }]) }] |
358 | > Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) } | 358 | > Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) } |
359 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }]) }] | 359 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }]) }] |
360 | > Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) } | 360 | > Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) } |
361 | "##]], | 361 | "##]], |
362 | ); | 362 | ); |
363 | } | 363 | } |
@@ -408,13 +408,13 @@ fn inner_item_attrs() { | |||
408 | inner attrs: Attrs { entries: None } | 408 | inner attrs: Attrs { entries: None } |
409 | 409 | ||
410 | top-level items: | 410 | top-level items: |
411 | Function { name: Name(Text("foo")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(0) } | 411 | Function { name: Name(Text("foo")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(0) } |
412 | 412 | ||
413 | inner items: | 413 | inner items: |
414 | 414 | ||
415 | for AST FileAstId::<ra_syntax::ast::generated::nodes::ModuleItem>(1): | 415 | for AST FileAstId::<ra_syntax::ast::generated::nodes::ModuleItem>(1): |
416 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_inner"))] }, input: None }]) }] | 416 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_inner"))] }, input: None }]) }] |
417 | Function { name: Name(Text("inner")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) } | 417 | Function { name: Name(Text("inner")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) } |
418 | 418 | ||
419 | "##]], | 419 | "##]], |
420 | ); | 420 | ); |
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index a35ac1024..e55cc1e55 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs | |||
@@ -717,6 +717,11 @@ impl DefCollector<'_> { | |||
717 | macro_call_id: MacroCallId, | 717 | macro_call_id: MacroCallId, |
718 | depth: usize, | 718 | depth: usize, |
719 | ) { | 719 | ) { |
720 | if depth > 100 { | ||
721 | mark::hit!(macro_expansion_overflow); | ||
722 | log::warn!("macro expansion is too deep"); | ||
723 | return; | ||
724 | } | ||
720 | let file_id: HirFileId = macro_call_id.as_file(); | 725 | let file_id: HirFileId = macro_call_id.as_file(); |
721 | let item_tree = self.db.item_tree(file_id); | 726 | let item_tree = self.db.item_tree(file_id); |
722 | let mod_dir = self.mod_dirs[&module_id].clone(); | 727 | let mod_dir = self.mod_dirs[&module_id].clone(); |
diff --git a/crates/ra_hir_def/src/nameres/tests/macros.rs b/crates/ra_hir_def/src/nameres/tests/macros.rs index 84480d9f6..c52341a07 100644 --- a/crates/ra_hir_def/src/nameres/tests/macros.rs +++ b/crates/ra_hir_def/src/nameres/tests/macros.rs | |||
@@ -660,3 +660,27 @@ fn expand_multiple_derive() { | |||
660 | ); | 660 | ); |
661 | assert_eq!(map.modules[map.root].scope.impls().len(), 2); | 661 | assert_eq!(map.modules[map.root].scope.impls().len(), 2); |
662 | } | 662 | } |
663 | |||
664 | #[test] | ||
665 | fn macro_expansion_overflow() { | ||
666 | mark::check!(macro_expansion_overflow); | ||
667 | compute_crate_def_map( | ||
668 | " | ||
669 | macro_rules! a { | ||
670 | ($e:expr; $($t:tt)*) => { | ||
671 | b!($($t)*); | ||
672 | }; | ||
673 | () => {}; | ||
674 | } | ||
675 | |||
676 | macro_rules! b { | ||
677 | (static = $e:expr; $($t:tt)*) => { | ||
678 | a!($e; $($t)*); | ||
679 | }; | ||
680 | () => {}; | ||
681 | } | ||
682 | |||
683 | b! { static = #[] (); } | ||
684 | ", | ||
685 | ); | ||
686 | } | ||
diff --git a/crates/ra_hir_def/src/type_ref.rs b/crates/ra_hir_def/src/type_ref.rs index 86a77b704..e90b2a0b9 100644 --- a/crates/ra_hir_def/src/type_ref.rs +++ b/crates/ra_hir_def/src/type_ref.rs | |||
@@ -63,7 +63,7 @@ pub enum TypeRef { | |||
63 | Array(Box<TypeRef> /*, Expr*/), | 63 | Array(Box<TypeRef> /*, Expr*/), |
64 | Slice(Box<TypeRef>), | 64 | Slice(Box<TypeRef>), |
65 | /// A fn pointer. Last element of the vector is the return type. | 65 | /// A fn pointer. Last element of the vector is the return type. |
66 | Fn(Vec<TypeRef>), | 66 | Fn(Vec<TypeRef>, bool /*varargs*/), |
67 | // For | 67 | // For |
68 | ImplTrait(Vec<TypeBound>), | 68 | ImplTrait(Vec<TypeBound>), |
69 | DynTrait(Vec<TypeBound>), | 69 | DynTrait(Vec<TypeBound>), |
@@ -118,7 +118,12 @@ impl TypeRef { | |||
118 | .and_then(|rt| rt.type_ref()) | 118 | .and_then(|rt| rt.type_ref()) |
119 | .map(|it| TypeRef::from_ast(ctx, it)) | 119 | .map(|it| TypeRef::from_ast(ctx, it)) |
120 | .unwrap_or_else(|| TypeRef::Tuple(Vec::new())); | 120 | .unwrap_or_else(|| TypeRef::Tuple(Vec::new())); |
121 | let mut is_varargs = false; | ||
121 | let mut params = if let Some(pl) = inner.param_list() { | 122 | let mut params = if let Some(pl) = inner.param_list() { |
123 | if let Some(param) = pl.params().last() { | ||
124 | is_varargs = param.dotdotdot_token().is_some(); | ||
125 | } | ||
126 | |||
122 | pl.params() | 127 | pl.params() |
123 | .map(|p| p.ascribed_type()) | 128 | .map(|p| p.ascribed_type()) |
124 | .map(|it| TypeRef::from_ast_opt(&ctx, it)) | 129 | .map(|it| TypeRef::from_ast_opt(&ctx, it)) |
@@ -127,7 +132,7 @@ impl TypeRef { | |||
127 | Vec::new() | 132 | Vec::new() |
128 | }; | 133 | }; |
129 | params.push(ret_ty); | 134 | params.push(ret_ty); |
130 | TypeRef::Fn(params) | 135 | TypeRef::Fn(params, is_varargs) |
131 | } | 136 | } |
132 | // for types are close enough for our purposes to the inner type for now... | 137 | // for types are close enough for our purposes to the inner type for now... |
133 | ast::TypeRef::ForType(inner) => TypeRef::from_ast_opt(&ctx, inner.type_ref()), | 138 | ast::TypeRef::ForType(inner) => TypeRef::from_ast_opt(&ctx, inner.type_ref()), |
@@ -158,7 +163,9 @@ impl TypeRef { | |||
158 | fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) { | 163 | fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) { |
159 | f(type_ref); | 164 | f(type_ref); |
160 | match type_ref { | 165 | match type_ref { |
161 | TypeRef::Fn(types) | TypeRef::Tuple(types) => types.iter().for_each(|t| go(t, f)), | 166 | TypeRef::Fn(types, _) | TypeRef::Tuple(types) => { |
167 | types.iter().for_each(|t| go(t, f)) | ||
168 | } | ||
162 | TypeRef::RawPtr(type_ref, _) | 169 | TypeRef::RawPtr(type_ref, _) |
163 | | TypeRef::Reference(type_ref, _) | 170 | | TypeRef::Reference(type_ref, _) |
164 | | TypeRef::Array(type_ref) | 171 | | TypeRef::Array(type_ref) |
diff --git a/crates/ra_hir_expand/Cargo.toml b/crates/ra_hir_expand/Cargo.toml index e5c9f3e99..6da0e2a16 100644 --- a/crates/ra_hir_expand/Cargo.toml +++ b/crates/ra_hir_expand/Cargo.toml | |||
@@ -3,6 +3,7 @@ edition = "2018" | |||
3 | name = "ra_hir_expand" | 3 | name = "ra_hir_expand" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml index 90368220b..78f5e55bb 100644 --- a/crates/ra_hir_ty/Cargo.toml +++ b/crates/ra_hir_ty/Cargo.toml | |||
@@ -3,6 +3,7 @@ edition = "2018" | |||
3 | name = "ra_hir_ty" | 3 | name = "ra_hir_ty" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index 5b0dda634..d3ee9cf55 100644 --- a/crates/ra_hir_ty/src/diagnostics.rs +++ b/crates/ra_hir_ty/src/diagnostics.rs | |||
@@ -1,13 +1,30 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | mod expr; | ||
3 | mod match_check; | ||
4 | mod unsafe_check; | ||
2 | 5 | ||
3 | use std::any::Any; | 6 | use std::any::Any; |
4 | 7 | ||
8 | use hir_def::DefWithBodyId; | ||
9 | use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; | ||
5 | use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile}; | 10 | use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile}; |
11 | use ra_prof::profile; | ||
6 | use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; | 12 | use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; |
7 | use stdx::format_to; | 13 | use stdx::format_to; |
8 | 14 | ||
9 | pub use hir_def::{diagnostics::UnresolvedModule, expr::MatchArm, path::Path}; | 15 | use crate::db::HirDatabase; |
10 | pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; | 16 | |
17 | pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields}; | ||
18 | |||
19 | pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) { | ||
20 | let _p = profile("validate_body"); | ||
21 | let infer = db.infer(owner); | ||
22 | infer.add_diagnostics(db, owner, sink); | ||
23 | let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink); | ||
24 | validator.validate_body(db); | ||
25 | let mut validator = unsafe_check::UnsafeValidator::new(owner, infer, sink); | ||
26 | validator.validate_body(db); | ||
27 | } | ||
11 | 28 | ||
12 | #[derive(Debug)] | 29 | #[derive(Debug)] |
13 | pub struct NoSuchField { | 30 | pub struct NoSuchField { |
@@ -227,3 +244,235 @@ impl AstDiagnostic for MismatchedArgCount { | |||
227 | ast::CallExpr::cast(node).unwrap() | 244 | ast::CallExpr::cast(node).unwrap() |
228 | } | 245 | } |
229 | } | 246 | } |
247 | |||
248 | #[cfg(test)] | ||
249 | mod tests { | ||
250 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId}; | ||
251 | use hir_expand::diagnostics::{Diagnostic, DiagnosticSink}; | ||
252 | use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; | ||
253 | use ra_syntax::{TextRange, TextSize}; | ||
254 | use rustc_hash::FxHashMap; | ||
255 | |||
256 | use crate::{diagnostics::validate_body, test_db::TestDB}; | ||
257 | |||
258 | impl TestDB { | ||
259 | fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) { | ||
260 | let crate_graph = self.crate_graph(); | ||
261 | for krate in crate_graph.iter() { | ||
262 | let crate_def_map = self.crate_def_map(krate); | ||
263 | |||
264 | let mut fns = Vec::new(); | ||
265 | for (module_id, _) in crate_def_map.modules.iter() { | ||
266 | for decl in crate_def_map[module_id].scope.declarations() { | ||
267 | if let ModuleDefId::FunctionId(f) = decl { | ||
268 | fns.push(f) | ||
269 | } | ||
270 | } | ||
271 | |||
272 | for impl_id in crate_def_map[module_id].scope.impls() { | ||
273 | let impl_data = self.impl_data(impl_id); | ||
274 | for item in impl_data.items.iter() { | ||
275 | if let AssocItemId::FunctionId(f) = item { | ||
276 | fns.push(*f) | ||
277 | } | ||
278 | } | ||
279 | } | ||
280 | } | ||
281 | |||
282 | for f in fns { | ||
283 | let mut sink = DiagnosticSink::new(&mut cb); | ||
284 | validate_body(self, f.into(), &mut sink); | ||
285 | } | ||
286 | } | ||
287 | } | ||
288 | } | ||
289 | |||
290 | pub(crate) fn check_diagnostics(ra_fixture: &str) { | ||
291 | let db = TestDB::with_files(ra_fixture); | ||
292 | let annotations = db.extract_annotations(); | ||
293 | |||
294 | let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); | ||
295 | db.diagnostics(|d| { | ||
296 | // FXIME: macros... | ||
297 | let file_id = d.source().file_id.original_file(&db); | ||
298 | let range = d.syntax_node(&db).text_range(); | ||
299 | let message = d.message().to_owned(); | ||
300 | actual.entry(file_id).or_default().push((range, message)); | ||
301 | }); | ||
302 | |||
303 | for (file_id, diags) in actual.iter_mut() { | ||
304 | diags.sort_by_key(|it| it.0.start()); | ||
305 | let text = db.file_text(*file_id); | ||
306 | // For multiline spans, place them on line start | ||
307 | for (range, content) in diags { | ||
308 | if text[*range].contains('\n') { | ||
309 | *range = TextRange::new(range.start(), range.start() + TextSize::from(1)); | ||
310 | *content = format!("... {}", content); | ||
311 | } | ||
312 | } | ||
313 | } | ||
314 | |||
315 | assert_eq!(annotations, actual); | ||
316 | } | ||
317 | |||
318 | #[test] | ||
319 | fn no_such_field_diagnostics() { | ||
320 | check_diagnostics( | ||
321 | r#" | ||
322 | struct S { foo: i32, bar: () } | ||
323 | impl S { | ||
324 | fn new() -> S { | ||
325 | S { | ||
326 | //^... Missing structure fields: | ||
327 | //| - bar | ||
328 | foo: 92, | ||
329 | baz: 62, | ||
330 | //^^^^^^^ no such field | ||
331 | } | ||
332 | } | ||
333 | } | ||
334 | "#, | ||
335 | ); | ||
336 | } | ||
337 | #[test] | ||
338 | fn no_such_field_with_feature_flag_diagnostics() { | ||
339 | check_diagnostics( | ||
340 | r#" | ||
341 | //- /lib.rs crate:foo cfg:feature=foo | ||
342 | struct MyStruct { | ||
343 | my_val: usize, | ||
344 | #[cfg(feature = "foo")] | ||
345 | bar: bool, | ||
346 | } | ||
347 | |||
348 | impl MyStruct { | ||
349 | #[cfg(feature = "foo")] | ||
350 | pub(crate) fn new(my_val: usize, bar: bool) -> Self { | ||
351 | Self { my_val, bar } | ||
352 | } | ||
353 | #[cfg(not(feature = "foo"))] | ||
354 | pub(crate) fn new(my_val: usize, _bar: bool) -> Self { | ||
355 | Self { my_val } | ||
356 | } | ||
357 | } | ||
358 | "#, | ||
359 | ); | ||
360 | } | ||
361 | |||
362 | #[test] | ||
363 | fn no_such_field_enum_with_feature_flag_diagnostics() { | ||
364 | check_diagnostics( | ||
365 | r#" | ||
366 | //- /lib.rs crate:foo cfg:feature=foo | ||
367 | enum Foo { | ||
368 | #[cfg(not(feature = "foo"))] | ||
369 | Buz, | ||
370 | #[cfg(feature = "foo")] | ||
371 | Bar, | ||
372 | Baz | ||
373 | } | ||
374 | |||
375 | fn test_fn(f: Foo) { | ||
376 | match f { | ||
377 | Foo::Bar => {}, | ||
378 | Foo::Baz => {}, | ||
379 | } | ||
380 | } | ||
381 | "#, | ||
382 | ); | ||
383 | } | ||
384 | |||
385 | #[test] | ||
386 | fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() { | ||
387 | check_diagnostics( | ||
388 | r#" | ||
389 | //- /lib.rs crate:foo cfg:feature=foo | ||
390 | struct S { | ||
391 | #[cfg(feature = "foo")] | ||
392 | foo: u32, | ||
393 | #[cfg(not(feature = "foo"))] | ||
394 | bar: u32, | ||
395 | } | ||
396 | |||
397 | impl S { | ||
398 | #[cfg(feature = "foo")] | ||
399 | fn new(foo: u32) -> Self { | ||
400 | Self { foo } | ||
401 | } | ||
402 | #[cfg(not(feature = "foo"))] | ||
403 | fn new(bar: u32) -> Self { | ||
404 | Self { bar } | ||
405 | } | ||
406 | fn new2(bar: u32) -> Self { | ||
407 | #[cfg(feature = "foo")] | ||
408 | { Self { foo: bar } } | ||
409 | #[cfg(not(feature = "foo"))] | ||
410 | { Self { bar } } | ||
411 | } | ||
412 | fn new2(val: u32) -> Self { | ||
413 | Self { | ||
414 | #[cfg(feature = "foo")] | ||
415 | foo: val, | ||
416 | #[cfg(not(feature = "foo"))] | ||
417 | bar: val, | ||
418 | } | ||
419 | } | ||
420 | } | ||
421 | "#, | ||
422 | ); | ||
423 | } | ||
424 | |||
425 | #[test] | ||
426 | fn no_such_field_with_type_macro() { | ||
427 | check_diagnostics( | ||
428 | r#" | ||
429 | macro_rules! Type { () => { u32 }; } | ||
430 | struct Foo { bar: Type![] } | ||
431 | |||
432 | impl Foo { | ||
433 | fn new() -> Self { | ||
434 | Foo { bar: 0 } | ||
435 | } | ||
436 | } | ||
437 | "#, | ||
438 | ); | ||
439 | } | ||
440 | |||
441 | #[test] | ||
442 | fn missing_record_pat_field_diagnostic() { | ||
443 | check_diagnostics( | ||
444 | r#" | ||
445 | struct S { foo: i32, bar: () } | ||
446 | fn baz(s: S) { | ||
447 | let S { foo: _ } = s; | ||
448 | //^^^^^^^^^^ Missing structure fields: | ||
449 | // | - bar | ||
450 | } | ||
451 | "#, | ||
452 | ); | ||
453 | } | ||
454 | |||
455 | #[test] | ||
456 | fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() { | ||
457 | check_diagnostics( | ||
458 | r" | ||
459 | struct S { foo: i32, bar: () } | ||
460 | fn baz(s: S) -> i32 { | ||
461 | match s { | ||
462 | S { foo, .. } => foo, | ||
463 | } | ||
464 | } | ||
465 | ", | ||
466 | ) | ||
467 | } | ||
468 | |||
469 | #[test] | ||
470 | fn break_outside_of_loop() { | ||
471 | check_diagnostics( | ||
472 | r#" | ||
473 | fn foo() { break; } | ||
474 | //^^^^^ break outside of loop | ||
475 | "#, | ||
476 | ); | ||
477 | } | ||
478 | } | ||
diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/diagnostics/expr.rs index d44562b22..557d01cdc 100644 --- a/crates/ra_hir_ty/src/expr.rs +++ b/crates/ra_hir_ty/src/diagnostics/expr.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use hir_def::{path::path, resolver::HasResolver, AdtId, FunctionId}; | 5 | use hir_def::{path::path, resolver::HasResolver, AdtId, DefWithBodyId}; |
6 | use hir_expand::diagnostics::DiagnosticSink; | 6 | use hir_expand::diagnostics::DiagnosticSink; |
7 | use ra_syntax::{ast, AstPtr}; | 7 | use ra_syntax::{ast, AstPtr}; |
8 | use rustc_hash::FxHashSet; | 8 | use rustc_hash::FxHashSet; |
@@ -10,9 +10,9 @@ use rustc_hash::FxHashSet; | |||
10 | use crate::{ | 10 | use crate::{ |
11 | db::HirDatabase, | 11 | db::HirDatabase, |
12 | diagnostics::{ | 12 | diagnostics::{ |
13 | match_check::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness}, | ||
13 | MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields, | 14 | MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields, |
14 | }, | 15 | }, |
15 | match_checking::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness}, | ||
16 | utils::variant_data, | 16 | utils::variant_data, |
17 | ApplicationTy, InferenceResult, Ty, TypeCtor, | 17 | ApplicationTy, InferenceResult, Ty, TypeCtor, |
18 | }; | 18 | }; |
@@ -30,23 +30,23 @@ pub use hir_def::{ | |||
30 | LocalFieldId, Lookup, VariantId, | 30 | LocalFieldId, Lookup, VariantId, |
31 | }; | 31 | }; |
32 | 32 | ||
33 | pub struct ExprValidator<'a, 'b: 'a> { | 33 | pub(super) struct ExprValidator<'a, 'b: 'a> { |
34 | func: FunctionId, | 34 | owner: DefWithBodyId, |
35 | infer: Arc<InferenceResult>, | 35 | infer: Arc<InferenceResult>, |
36 | sink: &'a mut DiagnosticSink<'b>, | 36 | sink: &'a mut DiagnosticSink<'b>, |
37 | } | 37 | } |
38 | 38 | ||
39 | impl<'a, 'b> ExprValidator<'a, 'b> { | 39 | impl<'a, 'b> ExprValidator<'a, 'b> { |
40 | pub fn new( | 40 | pub(super) fn new( |
41 | func: FunctionId, | 41 | owner: DefWithBodyId, |
42 | infer: Arc<InferenceResult>, | 42 | infer: Arc<InferenceResult>, |
43 | sink: &'a mut DiagnosticSink<'b>, | 43 | sink: &'a mut DiagnosticSink<'b>, |
44 | ) -> ExprValidator<'a, 'b> { | 44 | ) -> ExprValidator<'a, 'b> { |
45 | ExprValidator { func, infer, sink } | 45 | ExprValidator { owner, infer, sink } |
46 | } | 46 | } |
47 | 47 | ||
48 | pub fn validate_body(&mut self, db: &dyn HirDatabase) { | 48 | pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { |
49 | let body = db.body(self.func.into()); | 49 | let body = db.body(self.owner.into()); |
50 | 50 | ||
51 | for (id, expr) in body.exprs.iter() { | 51 | for (id, expr) in body.exprs.iter() { |
52 | if let Some((variant_def, missed_fields, true)) = | 52 | if let Some((variant_def, missed_fields, true)) = |
@@ -96,7 +96,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
96 | missed_fields: Vec<LocalFieldId>, | 96 | missed_fields: Vec<LocalFieldId>, |
97 | ) { | 97 | ) { |
98 | // XXX: only look at source_map if we do have missing fields | 98 | // XXX: only look at source_map if we do have missing fields |
99 | let (_, source_map) = db.body_with_source_map(self.func.into()); | 99 | let (_, source_map) = db.body_with_source_map(self.owner.into()); |
100 | 100 | ||
101 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | 101 | if let Ok(source_ptr) = source_map.expr_syntax(id) { |
102 | let root = source_ptr.file_syntax(db.upcast()); | 102 | let root = source_ptr.file_syntax(db.upcast()); |
@@ -125,7 +125,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
125 | missed_fields: Vec<LocalFieldId>, | 125 | missed_fields: Vec<LocalFieldId>, |
126 | ) { | 126 | ) { |
127 | // XXX: only look at source_map if we do have missing fields | 127 | // XXX: only look at source_map if we do have missing fields |
128 | let (_, source_map) = db.body_with_source_map(self.func.into()); | 128 | let (_, source_map) = db.body_with_source_map(self.owner.into()); |
129 | 129 | ||
130 | if let Ok(source_ptr) = source_map.pat_syntax(id) { | 130 | if let Ok(source_ptr) = source_map.pat_syntax(id) { |
131 | if let Some(expr) = source_ptr.value.as_ref().left() { | 131 | if let Some(expr) = source_ptr.value.as_ref().left() { |
@@ -175,13 +175,17 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
175 | }; | 175 | }; |
176 | 176 | ||
177 | let sig = db.callable_item_signature(callee); | 177 | let sig = db.callable_item_signature(callee); |
178 | if sig.value.is_varargs { | ||
179 | return None; | ||
180 | } | ||
181 | |||
178 | let params = sig.value.params(); | 182 | let params = sig.value.params(); |
179 | 183 | ||
180 | let mut param_count = params.len(); | 184 | let mut param_count = params.len(); |
181 | let mut arg_count = args.len(); | 185 | let mut arg_count = args.len(); |
182 | 186 | ||
183 | if arg_count != param_count { | 187 | if arg_count != param_count { |
184 | let (_, source_map) = db.body_with_source_map(self.func.into()); | 188 | let (_, source_map) = db.body_with_source_map(self.owner.into()); |
185 | if let Ok(source_ptr) = source_map.expr_syntax(call_id) { | 189 | if let Ok(source_ptr) = source_map.expr_syntax(call_id) { |
186 | if is_method_call { | 190 | if is_method_call { |
187 | param_count -= 1; | 191 | param_count -= 1; |
@@ -208,7 +212,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
208 | infer: Arc<InferenceResult>, | 212 | infer: Arc<InferenceResult>, |
209 | ) { | 213 | ) { |
210 | let (body, source_map): (Arc<Body>, Arc<BodySourceMap>) = | 214 | let (body, source_map): (Arc<Body>, Arc<BodySourceMap>) = |
211 | db.body_with_source_map(self.func.into()); | 215 | db.body_with_source_map(self.owner.into()); |
212 | 216 | ||
213 | let match_expr_ty = match infer.type_of_expr.get(match_expr) { | 217 | let match_expr_ty = match infer.type_of_expr.get(match_expr) { |
214 | Some(ty) => ty, | 218 | Some(ty) => ty, |
@@ -289,7 +293,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
289 | 293 | ||
290 | let core_result_path = path![core::result::Result]; | 294 | let core_result_path = path![core::result::Result]; |
291 | 295 | ||
292 | let resolver = self.func.resolver(db.upcast()); | 296 | let resolver = self.owner.resolver(db.upcast()); |
293 | let core_result_enum = match resolver.resolve_known_enum(db.upcast(), &core_result_path) { | 297 | let core_result_enum = match resolver.resolve_known_enum(db.upcast(), &core_result_path) { |
294 | Some(it) => it, | 298 | Some(it) => it, |
295 | _ => return, | 299 | _ => return, |
@@ -304,7 +308,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
304 | }; | 308 | }; |
305 | 309 | ||
306 | if params.len() == 2 && params[0] == mismatch.actual { | 310 | if params.len() == 2 && params[0] == mismatch.actual { |
307 | let (_, source_map) = db.body_with_source_map(self.func.into()); | 311 | let (_, source_map) = db.body_with_source_map(self.owner.into()); |
308 | 312 | ||
309 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | 313 | if let Ok(source_ptr) = source_map.expr_syntax(id) { |
310 | self.sink | 314 | self.sink |
@@ -376,146 +380,166 @@ pub fn record_pattern_missing_fields( | |||
376 | 380 | ||
377 | #[cfg(test)] | 381 | #[cfg(test)] |
378 | mod tests { | 382 | mod tests { |
379 | use expect::{expect, Expect}; | 383 | use crate::diagnostics::tests::check_diagnostics; |
380 | use ra_db::fixture::WithFixture; | ||
381 | |||
382 | use crate::{diagnostics::MismatchedArgCount, test_db::TestDB}; | ||
383 | |||
384 | fn check_diagnostic(ra_fixture: &str, expect: Expect) { | ||
385 | let msg = TestDB::with_single_file(ra_fixture).0.diagnostic::<MismatchedArgCount>().0; | ||
386 | expect.assert_eq(&msg); | ||
387 | } | ||
388 | |||
389 | fn check_no_diagnostic(ra_fixture: &str) { | ||
390 | let (s, diagnostic_count) = | ||
391 | TestDB::with_single_file(ra_fixture).0.diagnostic::<MismatchedArgCount>(); | ||
392 | |||
393 | assert_eq!(0, diagnostic_count, "expected no diagnostic, found one: {}", s); | ||
394 | } | ||
395 | 384 | ||
396 | #[test] | 385 | #[test] |
397 | fn simple_free_fn_zero() { | 386 | fn simple_free_fn_zero() { |
398 | check_diagnostic( | 387 | check_diagnostics( |
399 | r" | 388 | r#" |
400 | fn zero() {} | 389 | fn zero() {} |
401 | fn f() { zero(1); } | 390 | fn f() { zero(1); } |
402 | ", | 391 | //^^^^^^^ Expected 0 arguments, found 1 |
403 | expect![["\"zero(1)\": Expected 0 arguments, found 1\n"]], | 392 | "#, |
404 | ); | 393 | ); |
405 | 394 | ||
406 | check_no_diagnostic( | 395 | check_diagnostics( |
407 | r" | 396 | r#" |
408 | fn zero() {} | 397 | fn zero() {} |
409 | fn f() { zero(); } | 398 | fn f() { zero(); } |
410 | ", | 399 | "#, |
411 | ); | 400 | ); |
412 | } | 401 | } |
413 | 402 | ||
414 | #[test] | 403 | #[test] |
415 | fn simple_free_fn_one() { | 404 | fn simple_free_fn_one() { |
416 | check_diagnostic( | 405 | check_diagnostics( |
417 | r" | 406 | r#" |
418 | fn one(arg: u8) {} | 407 | fn one(arg: u8) {} |
419 | fn f() { one(); } | 408 | fn f() { one(); } |
420 | ", | 409 | //^^^^^ Expected 1 argument, found 0 |
421 | expect![["\"one()\": Expected 1 argument, found 0\n"]], | 410 | "#, |
422 | ); | 411 | ); |
423 | 412 | ||
424 | check_no_diagnostic( | 413 | check_diagnostics( |
425 | r" | 414 | r#" |
426 | fn one(arg: u8) {} | 415 | fn one(arg: u8) {} |
427 | fn f() { one(1); } | 416 | fn f() { one(1); } |
428 | ", | 417 | "#, |
429 | ); | 418 | ); |
430 | } | 419 | } |
431 | 420 | ||
432 | #[test] | 421 | #[test] |
433 | fn method_as_fn() { | 422 | fn method_as_fn() { |
434 | check_diagnostic( | 423 | check_diagnostics( |
435 | r" | 424 | r#" |
436 | struct S; | 425 | struct S; |
437 | impl S { | 426 | impl S { fn method(&self) {} } |
438 | fn method(&self) {} | 427 | |
439 | } | 428 | fn f() { |
440 | 429 | S::method(); | |
441 | fn f() { | 430 | } //^^^^^^^^^^^ Expected 1 argument, found 0 |
442 | S::method(); | 431 | "#, |
443 | } | ||
444 | ", | ||
445 | expect![["\"S::method()\": Expected 1 argument, found 0\n"]], | ||
446 | ); | 432 | ); |
447 | 433 | ||
448 | check_no_diagnostic( | 434 | check_diagnostics( |
449 | r" | 435 | r#" |
450 | struct S; | 436 | struct S; |
451 | impl S { | 437 | impl S { fn method(&self) {} } |
452 | fn method(&self) {} | ||
453 | } | ||
454 | 438 | ||
455 | fn f() { | 439 | fn f() { |
456 | S::method(&S); | 440 | S::method(&S); |
457 | S.method(); | 441 | S.method(); |
458 | } | 442 | } |
459 | ", | 443 | "#, |
460 | ); | 444 | ); |
461 | } | 445 | } |
462 | 446 | ||
463 | #[test] | 447 | #[test] |
464 | fn method_with_arg() { | 448 | fn method_with_arg() { |
465 | check_diagnostic( | 449 | check_diagnostics( |
466 | r" | 450 | r#" |
467 | struct S; | 451 | struct S; |
468 | impl S { | 452 | impl S { fn method(&self, arg: u8) {} } |
469 | fn method(&self, arg: u8) {} | ||
470 | } | ||
471 | 453 | ||
472 | fn f() { | 454 | fn f() { |
473 | S.method(); | 455 | S.method(); |
474 | } | 456 | } //^^^^^^^^^^ Expected 1 argument, found 0 |
475 | ", | 457 | "#, |
476 | expect![["\"S.method()\": Expected 1 argument, found 0\n"]], | ||
477 | ); | 458 | ); |
478 | 459 | ||
479 | check_no_diagnostic( | 460 | check_diagnostics( |
480 | r" | 461 | r#" |
481 | struct S; | 462 | struct S; |
482 | impl S { | 463 | impl S { fn method(&self, arg: u8) {} } |
483 | fn method(&self, arg: u8) {} | ||
484 | } | ||
485 | 464 | ||
486 | fn f() { | 465 | fn f() { |
487 | S::method(&S, 0); | 466 | S::method(&S, 0); |
488 | S.method(1); | 467 | S.method(1); |
489 | } | 468 | } |
490 | ", | 469 | "#, |
491 | ); | 470 | ); |
492 | } | 471 | } |
493 | 472 | ||
494 | #[test] | 473 | #[test] |
495 | fn tuple_struct() { | 474 | fn tuple_struct() { |
496 | check_diagnostic( | 475 | check_diagnostics( |
497 | r" | 476 | r#" |
498 | struct Tup(u8, u16); | 477 | struct Tup(u8, u16); |
499 | fn f() { | 478 | fn f() { |
500 | Tup(0); | 479 | Tup(0); |
501 | } | 480 | } //^^^^^^ Expected 2 arguments, found 1 |
502 | ", | 481 | "#, |
503 | expect![["\"Tup(0)\": Expected 2 arguments, found 1\n"]], | ||
504 | ) | 482 | ) |
505 | } | 483 | } |
506 | 484 | ||
507 | #[test] | 485 | #[test] |
508 | fn enum_variant() { | 486 | fn enum_variant() { |
509 | check_diagnostic( | 487 | check_diagnostics( |
510 | r" | 488 | r#" |
511 | enum En { | 489 | enum En { Variant(u8, u16), } |
512 | Variant(u8, u16), | 490 | fn f() { |
513 | } | 491 | En::Variant(0); |
514 | fn f() { | 492 | } //^^^^^^^^^^^^^^ Expected 2 arguments, found 1 |
515 | En::Variant(0); | 493 | "#, |
516 | } | 494 | ) |
517 | ", | 495 | } |
518 | expect![["\"En::Variant(0)\": Expected 2 arguments, found 1\n"]], | 496 | |
497 | #[test] | ||
498 | fn enum_variant_type_macro() { | ||
499 | check_diagnostics( | ||
500 | r#" | ||
501 | macro_rules! Type { | ||
502 | () => { u32 }; | ||
503 | } | ||
504 | enum Foo { | ||
505 | Bar(Type![]) | ||
506 | } | ||
507 | impl Foo { | ||
508 | fn new() { | ||
509 | Foo::Bar(0); | ||
510 | Foo::Bar(0, 1); | ||
511 | //^^^^^^^^^^^^^^ Expected 1 argument, found 2 | ||
512 | Foo::Bar(); | ||
513 | //^^^^^^^^^^ Expected 1 argument, found 0 | ||
514 | } | ||
515 | } | ||
516 | "#, | ||
517 | ); | ||
518 | } | ||
519 | |||
520 | #[test] | ||
521 | fn varargs() { | ||
522 | check_diagnostics( | ||
523 | r#" | ||
524 | extern "C" { | ||
525 | fn fixed(fixed: u8); | ||
526 | fn varargs(fixed: u8, ...); | ||
527 | fn varargs2(...); | ||
528 | } | ||
529 | |||
530 | fn f() { | ||
531 | unsafe { | ||
532 | fixed(0); | ||
533 | fixed(0, 1); | ||
534 | //^^^^^^^^^^^ Expected 1 argument, found 2 | ||
535 | varargs(0); | ||
536 | varargs(0, 1); | ||
537 | varargs2(); | ||
538 | varargs2(0); | ||
539 | varargs2(0, 1); | ||
540 | } | ||
541 | } | ||
542 | "#, | ||
519 | ) | 543 | ) |
520 | } | 544 | } |
521 | } | 545 | } |
diff --git a/crates/ra_hir_ty/src/match_checking.rs b/crates/ra_hir_ty/src/diagnostics/match_check.rs index 5495ce284..507edcb7d 100644 --- a/crates/ra_hir_ty/src/match_checking.rs +++ b/crates/ra_hir_ty/src/diagnostics/match_check.rs | |||
@@ -41,9 +41,9 @@ | |||
41 | //! ```ignore | 41 | //! ```ignore |
42 | //! // x: (Option<bool>, Result<()>) | 42 | //! // x: (Option<bool>, Result<()>) |
43 | //! match x { | 43 | //! match x { |
44 | //! (Some(true), _) => {} | 44 | //! (Some(true), _) => (), |
45 | //! (None, Err(())) => {} | 45 | //! (None, Err(())) => (), |
46 | //! (None, Err(_)) => {} | 46 | //! (None, Err(_)) => (), |
47 | //! } | 47 | //! } |
48 | //! ``` | 48 | //! ``` |
49 | //! | 49 | //! |
@@ -218,15 +218,16 @@ | |||
218 | //! ``` | 218 | //! ``` |
219 | use std::sync::Arc; | 219 | use std::sync::Arc; |
220 | 220 | ||
221 | use smallvec::{smallvec, SmallVec}; | 221 | use hir_def::{ |
222 | 222 | adt::VariantData, | |
223 | use crate::{ | 223 | body::Body, |
224 | db::HirDatabase, | 224 | expr::{Expr, Literal, Pat, PatId}, |
225 | expr::{Body, Expr, Literal, Pat, PatId}, | 225 | AdtId, EnumVariantId, VariantId, |
226 | ApplicationTy, InferenceResult, Ty, TypeCtor, | ||
227 | }; | 226 | }; |
228 | use hir_def::{adt::VariantData, AdtId, EnumVariantId, VariantId}; | ||
229 | use ra_arena::Idx; | 227 | use ra_arena::Idx; |
228 | use smallvec::{smallvec, SmallVec}; | ||
229 | |||
230 | use crate::{db::HirDatabase, ApplicationTy, InferenceResult, Ty, TypeCtor}; | ||
230 | 231 | ||
231 | #[derive(Debug, Clone, Copy)] | 232 | #[derive(Debug, Clone, Copy)] |
232 | /// Either a pattern from the source code being analyzed, represented as | 233 | /// Either a pattern from the source code being analyzed, represented as |
@@ -271,7 +272,7 @@ impl From<&PatId> for PatIdOrWild { | |||
271 | } | 272 | } |
272 | 273 | ||
273 | #[derive(Debug, Clone, Copy, PartialEq)] | 274 | #[derive(Debug, Clone, Copy, PartialEq)] |
274 | pub enum MatchCheckErr { | 275 | pub(super) enum MatchCheckErr { |
275 | NotImplemented, | 276 | NotImplemented, |
276 | MalformedMatchArm, | 277 | MalformedMatchArm, |
277 | /// Used when type inference cannot resolve the type of | 278 | /// Used when type inference cannot resolve the type of |
@@ -286,21 +287,21 @@ pub enum MatchCheckErr { | |||
286 | /// | 287 | /// |
287 | /// The `std::result::Result` type is used here rather than a custom enum | 288 | /// The `std::result::Result` type is used here rather than a custom enum |
288 | /// to allow the use of `?`. | 289 | /// to allow the use of `?`. |
289 | pub type MatchCheckResult<T> = Result<T, MatchCheckErr>; | 290 | pub(super) type MatchCheckResult<T> = Result<T, MatchCheckErr>; |
290 | 291 | ||
291 | #[derive(Debug)] | 292 | #[derive(Debug)] |
292 | /// A row in a Matrix. | 293 | /// A row in a Matrix. |
293 | /// | 294 | /// |
294 | /// This type is modeled from the struct of the same name in `rustc`. | 295 | /// This type is modeled from the struct of the same name in `rustc`. |
295 | pub(crate) struct PatStack(PatStackInner); | 296 | pub(super) struct PatStack(PatStackInner); |
296 | type PatStackInner = SmallVec<[PatIdOrWild; 2]>; | 297 | type PatStackInner = SmallVec<[PatIdOrWild; 2]>; |
297 | 298 | ||
298 | impl PatStack { | 299 | impl PatStack { |
299 | pub(crate) fn from_pattern(pat_id: PatId) -> PatStack { | 300 | pub(super) fn from_pattern(pat_id: PatId) -> PatStack { |
300 | Self(smallvec!(pat_id.into())) | 301 | Self(smallvec!(pat_id.into())) |
301 | } | 302 | } |
302 | 303 | ||
303 | pub(crate) fn from_wild() -> PatStack { | 304 | pub(super) fn from_wild() -> PatStack { |
304 | Self(smallvec!(PatIdOrWild::Wild)) | 305 | Self(smallvec!(PatIdOrWild::Wild)) |
305 | } | 306 | } |
306 | 307 | ||
@@ -509,14 +510,14 @@ impl PatStack { | |||
509 | /// A collection of PatStack. | 510 | /// A collection of PatStack. |
510 | /// | 511 | /// |
511 | /// This type is modeled from the struct of the same name in `rustc`. | 512 | /// This type is modeled from the struct of the same name in `rustc`. |
512 | pub(crate) struct Matrix(Vec<PatStack>); | 513 | pub(super) struct Matrix(Vec<PatStack>); |
513 | 514 | ||
514 | impl Matrix { | 515 | impl Matrix { |
515 | pub(crate) fn empty() -> Self { | 516 | pub(super) fn empty() -> Self { |
516 | Self(vec![]) | 517 | Self(vec![]) |
517 | } | 518 | } |
518 | 519 | ||
519 | pub(crate) fn push(&mut self, cx: &MatchCheckCtx, row: PatStack) { | 520 | pub(super) fn push(&mut self, cx: &MatchCheckCtx, row: PatStack) { |
520 | if let Some(Pat::Or(pat_ids)) = row.get_head().map(|pat_id| pat_id.as_pat(cx)) { | 521 | if let Some(Pat::Or(pat_ids)) = row.get_head().map(|pat_id| pat_id.as_pat(cx)) { |
521 | // Or patterns are expanded here | 522 | // Or patterns are expanded here |
522 | for pat_id in pat_ids { | 523 | for pat_id in pat_ids { |
@@ -578,16 +579,16 @@ impl Matrix { | |||
578 | /// not matched by an prior match arms. | 579 | /// not matched by an prior match arms. |
579 | /// | 580 | /// |
580 | /// We may eventually need an `Unknown` variant here. | 581 | /// We may eventually need an `Unknown` variant here. |
581 | pub enum Usefulness { | 582 | pub(super) enum Usefulness { |
582 | Useful, | 583 | Useful, |
583 | NotUseful, | 584 | NotUseful, |
584 | } | 585 | } |
585 | 586 | ||
586 | pub struct MatchCheckCtx<'a> { | 587 | pub(super) struct MatchCheckCtx<'a> { |
587 | pub match_expr: Idx<Expr>, | 588 | pub(super) match_expr: Idx<Expr>, |
588 | pub body: Arc<Body>, | 589 | pub(super) body: Arc<Body>, |
589 | pub infer: Arc<InferenceResult>, | 590 | pub(super) infer: Arc<InferenceResult>, |
590 | pub db: &'a dyn HirDatabase, | 591 | pub(super) db: &'a dyn HirDatabase, |
591 | } | 592 | } |
592 | 593 | ||
593 | /// Given a set of patterns `matrix`, and pattern to consider `v`, determines | 594 | /// Given a set of patterns `matrix`, and pattern to consider `v`, determines |
@@ -598,7 +599,7 @@ pub struct MatchCheckCtx<'a> { | |||
598 | /// expected that you have already type checked the match arms. All patterns in | 599 | /// expected that you have already type checked the match arms. All patterns in |
599 | /// matrix should be the same type as v, as well as they should all be the same | 600 | /// matrix should be the same type as v, as well as they should all be the same |
600 | /// type as the match expression. | 601 | /// type as the match expression. |
601 | pub(crate) fn is_useful( | 602 | pub(super) fn is_useful( |
602 | cx: &MatchCheckCtx, | 603 | cx: &MatchCheckCtx, |
603 | matrix: &Matrix, | 604 | matrix: &Matrix, |
604 | v: &PatStack, | 605 | v: &PatStack, |
@@ -836,685 +837,251 @@ fn enum_variant_matches(cx: &MatchCheckCtx, pat_id: PatId, enum_variant_id: Enum | |||
836 | 837 | ||
837 | #[cfg(test)] | 838 | #[cfg(test)] |
838 | mod tests { | 839 | mod tests { |
839 | pub(super) use insta::assert_snapshot; | 840 | use crate::diagnostics::tests::check_diagnostics; |
840 | pub(super) use ra_db::fixture::WithFixture; | ||
841 | |||
842 | pub(super) use crate::{diagnostics::MissingMatchArms, test_db::TestDB}; | ||
843 | |||
844 | pub(super) fn check_diagnostic_message(ra_fixture: &str) -> String { | ||
845 | TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().0 | ||
846 | } | ||
847 | |||
848 | pub(super) fn check_diagnostic(ra_fixture: &str) { | ||
849 | let diagnostic_count = | ||
850 | TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().1; | ||
851 | |||
852 | assert_eq!(1, diagnostic_count, "no diagnostic reported"); | ||
853 | } | ||
854 | |||
855 | pub(super) fn check_no_diagnostic(ra_fixture: &str) { | ||
856 | let (s, diagnostic_count) = | ||
857 | TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>(); | ||
858 | |||
859 | assert_eq!(0, diagnostic_count, "expected no diagnostic, found one: {}", s); | ||
860 | } | ||
861 | |||
862 | #[test] | ||
863 | fn empty_tuple_no_arms_diagnostic_message() { | ||
864 | assert_snapshot!( | ||
865 | check_diagnostic_message(r" | ||
866 | fn test_fn() { | ||
867 | match () { | ||
868 | } | ||
869 | } | ||
870 | "), | ||
871 | @"\"()\": Missing match arm\n" | ||
872 | ); | ||
873 | } | ||
874 | |||
875 | #[test] | ||
876 | fn empty_tuple_no_arms() { | ||
877 | check_diagnostic( | ||
878 | r" | ||
879 | fn test_fn() { | ||
880 | match () { | ||
881 | } | ||
882 | } | ||
883 | ", | ||
884 | ); | ||
885 | } | ||
886 | |||
887 | #[test] | ||
888 | fn empty_tuple_wild() { | ||
889 | check_no_diagnostic( | ||
890 | r" | ||
891 | fn test_fn() { | ||
892 | match () { | ||
893 | _ => {} | ||
894 | } | ||
895 | } | ||
896 | ", | ||
897 | ); | ||
898 | } | ||
899 | |||
900 | #[test] | ||
901 | fn empty_tuple_no_diagnostic() { | ||
902 | check_no_diagnostic( | ||
903 | r" | ||
904 | fn test_fn() { | ||
905 | match () { | ||
906 | () => {} | ||
907 | } | ||
908 | } | ||
909 | ", | ||
910 | ); | ||
911 | } | ||
912 | |||
913 | #[test] | ||
914 | fn tuple_of_empty_tuple_no_arms() { | ||
915 | check_diagnostic( | ||
916 | r" | ||
917 | fn test_fn() { | ||
918 | match (()) { | ||
919 | } | ||
920 | } | ||
921 | ", | ||
922 | ); | ||
923 | } | ||
924 | |||
925 | #[test] | ||
926 | fn tuple_of_empty_tuple_no_diagnostic() { | ||
927 | check_no_diagnostic( | ||
928 | r" | ||
929 | fn test_fn() { | ||
930 | match (()) { | ||
931 | (()) => {} | ||
932 | } | ||
933 | } | ||
934 | ", | ||
935 | ); | ||
936 | } | ||
937 | 841 | ||
938 | #[test] | 842 | #[test] |
939 | fn tuple_of_two_empty_tuple_no_arms() { | 843 | fn empty_tuple() { |
940 | check_diagnostic( | 844 | check_diagnostics( |
941 | r" | 845 | r#" |
942 | fn test_fn() { | 846 | fn main() { |
943 | match ((), ()) { | 847 | match () { } |
944 | } | 848 | //^^ Missing match arm |
945 | } | 849 | match (()) { } |
946 | ", | 850 | //^^^^ Missing match arm |
947 | ); | ||
948 | } | ||
949 | 851 | ||
950 | #[test] | 852 | match () { _ => (), } |
951 | fn tuple_of_two_empty_tuple_no_diagnostic() { | 853 | match () { () => (), } |
952 | check_no_diagnostic( | 854 | match (()) { (()) => (), } |
953 | r" | 855 | } |
954 | fn test_fn() { | 856 | "#, |
955 | match ((), ()) { | ||
956 | ((), ()) => {} | ||
957 | } | ||
958 | } | ||
959 | ", | ||
960 | ); | ||
961 | } | ||
962 | |||
963 | #[test] | ||
964 | fn bool_no_arms() { | ||
965 | check_diagnostic( | ||
966 | r" | ||
967 | fn test_fn() { | ||
968 | match false { | ||
969 | } | ||
970 | } | ||
971 | ", | ||
972 | ); | ||
973 | } | ||
974 | |||
975 | #[test] | ||
976 | fn bool_missing_arm() { | ||
977 | check_diagnostic( | ||
978 | r" | ||
979 | fn test_fn() { | ||
980 | match false { | ||
981 | true => {} | ||
982 | } | ||
983 | } | ||
984 | ", | ||
985 | ); | ||
986 | } | ||
987 | |||
988 | #[test] | ||
989 | fn bool_no_diagnostic() { | ||
990 | check_no_diagnostic( | ||
991 | r" | ||
992 | fn test_fn() { | ||
993 | match false { | ||
994 | true => {} | ||
995 | false => {} | ||
996 | } | ||
997 | } | ||
998 | ", | ||
999 | ); | ||
1000 | } | ||
1001 | |||
1002 | #[test] | ||
1003 | fn tuple_of_bools_no_arms() { | ||
1004 | check_diagnostic( | ||
1005 | r" | ||
1006 | fn test_fn() { | ||
1007 | match (false, true) { | ||
1008 | } | ||
1009 | } | ||
1010 | ", | ||
1011 | ); | ||
1012 | } | ||
1013 | |||
1014 | #[test] | ||
1015 | fn tuple_of_bools_missing_arms() { | ||
1016 | check_diagnostic( | ||
1017 | r" | ||
1018 | fn test_fn() { | ||
1019 | match (false, true) { | ||
1020 | (true, true) => {}, | ||
1021 | } | ||
1022 | } | ||
1023 | ", | ||
1024 | ); | ||
1025 | } | ||
1026 | |||
1027 | #[test] | ||
1028 | fn tuple_of_bools_missing_arm() { | ||
1029 | check_diagnostic( | ||
1030 | r" | ||
1031 | fn test_fn() { | ||
1032 | match (false, true) { | ||
1033 | (false, true) => {}, | ||
1034 | (false, false) => {}, | ||
1035 | (true, false) => {}, | ||
1036 | } | ||
1037 | } | ||
1038 | ", | ||
1039 | ); | ||
1040 | } | ||
1041 | |||
1042 | #[test] | ||
1043 | fn tuple_of_bools_with_wilds() { | ||
1044 | check_no_diagnostic( | ||
1045 | r" | ||
1046 | fn test_fn() { | ||
1047 | match (false, true) { | ||
1048 | (false, _) => {}, | ||
1049 | (true, false) => {}, | ||
1050 | (_, true) => {}, | ||
1051 | } | ||
1052 | } | ||
1053 | ", | ||
1054 | ); | 857 | ); |
1055 | } | 858 | } |
1056 | 859 | ||
1057 | #[test] | 860 | #[test] |
1058 | fn tuple_of_bools_no_diagnostic() { | 861 | fn tuple_of_two_empty_tuple() { |
1059 | check_no_diagnostic( | 862 | check_diagnostics( |
1060 | r" | 863 | r#" |
1061 | fn test_fn() { | 864 | fn main() { |
1062 | match (false, true) { | 865 | match ((), ()) { } |
1063 | (true, true) => {}, | 866 | //^^^^^^^^ Missing match arm |
1064 | (true, false) => {}, | ||
1065 | (false, true) => {}, | ||
1066 | (false, false) => {}, | ||
1067 | } | ||
1068 | } | ||
1069 | ", | ||
1070 | ); | ||
1071 | } | ||
1072 | 867 | ||
1073 | #[test] | 868 | match ((), ()) { ((), ()) => (), } |
1074 | fn tuple_of_bools_binding_missing_arms() { | 869 | } |
1075 | check_diagnostic( | 870 | "#, |
1076 | r" | 871 | ); |
1077 | fn test_fn() { | 872 | } |
1078 | match (false, true) { | 873 | |
1079 | (true, _x) => {}, | 874 | #[test] |
1080 | } | 875 | fn boolean() { |
1081 | } | 876 | check_diagnostics( |
1082 | ", | 877 | r#" |
1083 | ); | 878 | fn test_main() { |
1084 | } | 879 | match false { } |
1085 | 880 | //^^^^^ Missing match arm | |
1086 | #[test] | 881 | match false { true => (), } |
1087 | fn tuple_of_bools_binding_no_diagnostic() { | 882 | //^^^^^ Missing match arm |
1088 | check_no_diagnostic( | 883 | match (false, true) {} |
1089 | r" | 884 | //^^^^^^^^^^^^^ Missing match arm |
1090 | fn test_fn() { | 885 | match (false, true) { (true, true) => (), } |
1091 | match (false, true) { | 886 | //^^^^^^^^^^^^^ Missing match arm |
1092 | (true, _x) => {}, | 887 | match (false, true) { |
1093 | (false, true) => {}, | 888 | //^^^^^^^^^^^^^ Missing match arm |
1094 | (false, false) => {}, | 889 | (false, true) => (), |
1095 | } | 890 | (false, false) => (), |
1096 | } | 891 | (true, false) => (), |
1097 | ", | 892 | } |
893 | match (false, true) { (true, _x) => (), } | ||
894 | //^^^^^^^^^^^^^ Missing match arm | ||
895 | |||
896 | match false { true => (), false => (), } | ||
897 | match (false, true) { | ||
898 | (false, _) => (), | ||
899 | (true, false) => (), | ||
900 | (_, true) => (), | ||
901 | } | ||
902 | match (false, true) { | ||
903 | (true, true) => (), | ||
904 | (true, false) => (), | ||
905 | (false, true) => (), | ||
906 | (false, false) => (), | ||
907 | } | ||
908 | match (false, true) { | ||
909 | (true, _x) => (), | ||
910 | (false, true) => (), | ||
911 | (false, false) => (), | ||
912 | } | ||
913 | match (false, true, false) { | ||
914 | (false, ..) => (), | ||
915 | (true, ..) => (), | ||
916 | } | ||
917 | match (false, true, false) { | ||
918 | (.., false) => (), | ||
919 | (.., true) => (), | ||
920 | } | ||
921 | match (false, true, false) { (..) => (), } | ||
922 | } | ||
923 | "#, | ||
1098 | ); | 924 | ); |
1099 | } | 925 | } |
1100 | 926 | ||
1101 | #[test] | 927 | #[test] |
1102 | fn tuple_of_bools_with_ellipsis_at_end_no_diagnostic() { | 928 | fn tuple_of_tuple_and_bools() { |
1103 | check_no_diagnostic( | 929 | check_diagnostics( |
1104 | r" | 930 | r#" |
1105 | fn test_fn() { | 931 | fn main() { |
1106 | match (false, true, false) { | 932 | match (false, ((), false)) {} |
1107 | (false, ..) => {}, | 933 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm |
1108 | (true, ..) => {}, | 934 | match (false, ((), false)) { (true, ((), true)) => (), } |
1109 | } | 935 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm |
1110 | } | 936 | match (false, ((), false)) { (true, _) => (), } |
1111 | ", | 937 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm |
1112 | ); | ||
1113 | } | ||
1114 | 938 | ||
1115 | #[test] | 939 | match (false, ((), false)) { |
1116 | fn tuple_of_bools_with_ellipsis_at_beginning_no_diagnostic() { | 940 | (true, ((), true)) => (), |
1117 | check_no_diagnostic( | 941 | (true, ((), false)) => (), |
1118 | r" | 942 | (false, ((), true)) => (), |
1119 | fn test_fn() { | 943 | (false, ((), false)) => (), |
1120 | match (false, true, false) { | ||
1121 | (.., false) => {}, | ||
1122 | (.., true) => {}, | ||
1123 | } | ||
1124 | } | ||
1125 | ", | ||
1126 | ); | ||
1127 | } | 944 | } |
1128 | 945 | match (false, ((), false)) { | |
1129 | #[test] | 946 | (true, ((), true)) => (), |
1130 | fn tuple_of_bools_with_ellipsis_no_diagnostic() { | 947 | (true, ((), false)) => (), |
1131 | check_no_diagnostic( | 948 | (false, _) => (), |
1132 | r" | ||
1133 | fn test_fn() { | ||
1134 | match (false, true, false) { | ||
1135 | (..) => {}, | ||
1136 | } | ||
1137 | } | ||
1138 | ", | ||
1139 | ); | ||
1140 | } | 949 | } |
1141 | 950 | } | |
1142 | #[test] | 951 | "#, |
1143 | fn tuple_of_tuple_and_bools_no_arms() { | ||
1144 | check_diagnostic( | ||
1145 | r" | ||
1146 | fn test_fn() { | ||
1147 | match (false, ((), false)) { | ||
1148 | } | ||
1149 | } | ||
1150 | ", | ||
1151 | ); | 952 | ); |
1152 | } | 953 | } |
1153 | 954 | ||
1154 | #[test] | 955 | #[test] |
1155 | fn tuple_of_tuple_and_bools_missing_arms() { | 956 | fn enums() { |
1156 | check_diagnostic( | 957 | check_diagnostics( |
1157 | r" | 958 | r#" |
1158 | fn test_fn() { | 959 | enum Either { A, B, } |
1159 | match (false, ((), false)) { | ||
1160 | (true, ((), true)) => {}, | ||
1161 | } | ||
1162 | } | ||
1163 | ", | ||
1164 | ); | ||
1165 | } | ||
1166 | 960 | ||
1167 | #[test] | 961 | fn main() { |
1168 | fn tuple_of_tuple_and_bools_no_diagnostic() { | 962 | match Either::A { } |
1169 | check_no_diagnostic( | 963 | //^^^^^^^^^ Missing match arm |
1170 | r" | 964 | match Either::B { Either::A => (), } |
1171 | fn test_fn() { | 965 | //^^^^^^^^^ Missing match arm |
1172 | match (false, ((), false)) { | ||
1173 | (true, ((), true)) => {}, | ||
1174 | (true, ((), false)) => {}, | ||
1175 | (false, ((), true)) => {}, | ||
1176 | (false, ((), false)) => {}, | ||
1177 | } | ||
1178 | } | ||
1179 | ", | ||
1180 | ); | ||
1181 | } | ||
1182 | 966 | ||
1183 | #[test] | 967 | match &Either::B { |
1184 | fn tuple_of_tuple_and_bools_wildcard_missing_arms() { | 968 | //^^^^^^^^^^ Missing match arm |
1185 | check_diagnostic( | 969 | Either::A => (), |
1186 | r" | ||
1187 | fn test_fn() { | ||
1188 | match (false, ((), false)) { | ||
1189 | (true, _) => {}, | ||
1190 | } | ||
1191 | } | ||
1192 | ", | ||
1193 | ); | ||
1194 | } | 970 | } |
1195 | 971 | ||
1196 | #[test] | 972 | match Either::B { |
1197 | fn tuple_of_tuple_and_bools_wildcard_no_diagnostic() { | 973 | Either::A => (), Either::B => (), |
1198 | check_no_diagnostic( | ||
1199 | r" | ||
1200 | fn test_fn() { | ||
1201 | match (false, ((), false)) { | ||
1202 | (true, ((), true)) => {}, | ||
1203 | (true, ((), false)) => {}, | ||
1204 | (false, _) => {}, | ||
1205 | } | ||
1206 | } | ||
1207 | ", | ||
1208 | ); | ||
1209 | } | 974 | } |
1210 | 975 | match &Either::B { | |
1211 | #[test] | 976 | Either::A => (), Either::B => (), |
1212 | fn enum_no_arms() { | ||
1213 | check_diagnostic( | ||
1214 | r" | ||
1215 | enum Either { | ||
1216 | A, | ||
1217 | B, | ||
1218 | } | ||
1219 | fn test_fn() { | ||
1220 | match Either::A { | ||
1221 | } | ||
1222 | } | ||
1223 | ", | ||
1224 | ); | ||
1225 | } | 977 | } |
1226 | 978 | } | |
1227 | #[test] | 979 | "#, |
1228 | fn enum_missing_arms() { | ||
1229 | check_diagnostic( | ||
1230 | r" | ||
1231 | enum Either { | ||
1232 | A, | ||
1233 | B, | ||
1234 | } | ||
1235 | fn test_fn() { | ||
1236 | match Either::B { | ||
1237 | Either::A => {}, | ||
1238 | } | ||
1239 | } | ||
1240 | ", | ||
1241 | ); | 980 | ); |
1242 | } | 981 | } |
1243 | 982 | ||
1244 | #[test] | 983 | #[test] |
1245 | fn enum_no_diagnostic() { | 984 | fn enum_containing_bool() { |
1246 | check_no_diagnostic( | 985 | check_diagnostics( |
1247 | r" | 986 | r#" |
1248 | enum Either { | 987 | enum Either { A(bool), B } |
1249 | A, | ||
1250 | B, | ||
1251 | } | ||
1252 | fn test_fn() { | ||
1253 | match Either::B { | ||
1254 | Either::A => {}, | ||
1255 | Either::B => {}, | ||
1256 | } | ||
1257 | } | ||
1258 | ", | ||
1259 | ); | ||
1260 | } | ||
1261 | 988 | ||
1262 | #[test] | 989 | fn main() { |
1263 | fn enum_ref_missing_arms() { | 990 | match Either::B { } |
1264 | check_diagnostic( | 991 | //^^^^^^^^^ Missing match arm |
1265 | r" | 992 | match Either::B { |
1266 | enum Either { | 993 | //^^^^^^^^^ Missing match arm |
1267 | A, | 994 | Either::A(true) => (), Either::B => () |
1268 | B, | ||
1269 | } | ||
1270 | fn test_fn() { | ||
1271 | match &Either::B { | ||
1272 | Either::A => {}, | ||
1273 | } | ||
1274 | } | ||
1275 | ", | ||
1276 | ); | ||
1277 | } | 995 | } |
1278 | 996 | ||
1279 | #[test] | 997 | match Either::B { |
1280 | fn enum_ref_no_diagnostic() { | 998 | Either::A(true) => (), |
1281 | check_no_diagnostic( | 999 | Either::A(false) => (), |
1282 | r" | 1000 | Either::B => (), |
1283 | enum Either { | ||
1284 | A, | ||
1285 | B, | ||
1286 | } | ||
1287 | fn test_fn() { | ||
1288 | match &Either::B { | ||
1289 | Either::A => {}, | ||
1290 | Either::B => {}, | ||
1291 | } | ||
1292 | } | ||
1293 | ", | ||
1294 | ); | ||
1295 | } | 1001 | } |
1296 | 1002 | match Either::B { | |
1297 | #[test] | 1003 | Either::B => (), |
1298 | fn enum_containing_bool_no_arms() { | 1004 | _ => (), |
1299 | check_diagnostic( | ||
1300 | r" | ||
1301 | enum Either { | ||
1302 | A(bool), | ||
1303 | B, | ||
1304 | } | ||
1305 | fn test_fn() { | ||
1306 | match Either::B { | ||
1307 | } | ||
1308 | } | ||
1309 | ", | ||
1310 | ); | ||
1311 | } | 1005 | } |
1312 | 1006 | match Either::B { | |
1313 | #[test] | 1007 | Either::A(_) => (), |
1314 | fn enum_containing_bool_missing_arms() { | 1008 | Either::B => (), |
1315 | check_diagnostic( | ||
1316 | r" | ||
1317 | enum Either { | ||
1318 | A(bool), | ||
1319 | B, | ||
1320 | } | ||
1321 | fn test_fn() { | ||
1322 | match Either::B { | ||
1323 | Either::A(true) => (), | ||
1324 | Either::B => (), | ||
1325 | } | ||
1326 | } | ||
1327 | ", | ||
1328 | ); | ||
1329 | } | 1009 | } |
1330 | 1010 | ||
1331 | #[test] | 1011 | } |
1332 | fn enum_containing_bool_no_diagnostic() { | 1012 | "#, |
1333 | check_no_diagnostic( | ||
1334 | r" | ||
1335 | enum Either { | ||
1336 | A(bool), | ||
1337 | B, | ||
1338 | } | ||
1339 | fn test_fn() { | ||
1340 | match Either::B { | ||
1341 | Either::A(true) => (), | ||
1342 | Either::A(false) => (), | ||
1343 | Either::B => (), | ||
1344 | } | ||
1345 | } | ||
1346 | ", | ||
1347 | ); | 1013 | ); |
1348 | } | 1014 | } |
1349 | 1015 | ||
1350 | #[test] | 1016 | #[test] |
1351 | fn enum_containing_bool_with_wild_no_diagnostic() { | 1017 | fn enum_different_sizes() { |
1352 | check_no_diagnostic( | 1018 | check_diagnostics( |
1353 | r" | 1019 | r#" |
1354 | enum Either { | 1020 | enum Either { A(bool), B(bool, bool) } |
1355 | A(bool), | ||
1356 | B, | ||
1357 | } | ||
1358 | fn test_fn() { | ||
1359 | match Either::B { | ||
1360 | Either::B => (), | ||
1361 | _ => (), | ||
1362 | } | ||
1363 | } | ||
1364 | ", | ||
1365 | ); | ||
1366 | } | ||
1367 | 1021 | ||
1368 | #[test] | 1022 | fn main() { |
1369 | fn enum_containing_bool_with_wild_2_no_diagnostic() { | 1023 | match Either::A(false) { |
1370 | check_no_diagnostic( | 1024 | //^^^^^^^^^^^^^^^^ Missing match arm |
1371 | r" | 1025 | Either::A(_) => (), |
1372 | enum Either { | 1026 | Either::B(false, _) => (), |
1373 | A(bool), | ||
1374 | B, | ||
1375 | } | ||
1376 | fn test_fn() { | ||
1377 | match Either::B { | ||
1378 | Either::A(_) => (), | ||
1379 | Either::B => (), | ||
1380 | } | ||
1381 | } | ||
1382 | ", | ||
1383 | ); | ||
1384 | } | 1027 | } |
1385 | 1028 | ||
1386 | #[test] | 1029 | match Either::A(false) { |
1387 | fn enum_different_sizes_missing_arms() { | 1030 | Either::A(_) => (), |
1388 | check_diagnostic( | 1031 | Either::B(true, _) => (), |
1389 | r" | 1032 | Either::B(false, _) => (), |
1390 | enum Either { | ||
1391 | A(bool), | ||
1392 | B(bool, bool), | ||
1393 | } | ||
1394 | fn test_fn() { | ||
1395 | match Either::A(false) { | ||
1396 | Either::A(_) => (), | ||
1397 | Either::B(false, _) => (), | ||
1398 | } | ||
1399 | } | ||
1400 | ", | ||
1401 | ); | ||
1402 | } | 1033 | } |
1403 | 1034 | match Either::A(false) { | |
1404 | #[test] | 1035 | Either::A(true) | Either::A(false) => (), |
1405 | fn enum_different_sizes_no_diagnostic() { | 1036 | Either::B(true, _) => (), |
1406 | check_no_diagnostic( | 1037 | Either::B(false, _) => (), |
1407 | r" | ||
1408 | enum Either { | ||
1409 | A(bool), | ||
1410 | B(bool, bool), | ||
1411 | } | ||
1412 | fn test_fn() { | ||
1413 | match Either::A(false) { | ||
1414 | Either::A(_) => (), | ||
1415 | Either::B(true, _) => (), | ||
1416 | Either::B(false, _) => (), | ||
1417 | } | ||
1418 | } | ||
1419 | ", | ||
1420 | ); | ||
1421 | } | 1038 | } |
1422 | 1039 | } | |
1423 | #[test] | 1040 | "#, |
1424 | fn or_no_diagnostic() { | ||
1425 | check_no_diagnostic( | ||
1426 | r" | ||
1427 | enum Either { | ||
1428 | A(bool), | ||
1429 | B(bool, bool), | ||
1430 | } | ||
1431 | fn test_fn() { | ||
1432 | match Either::A(false) { | ||
1433 | Either::A(true) | Either::A(false) => (), | ||
1434 | Either::B(true, _) => (), | ||
1435 | Either::B(false, _) => (), | ||
1436 | } | ||
1437 | } | ||
1438 | ", | ||
1439 | ); | 1041 | ); |
1440 | } | 1042 | } |
1441 | 1043 | ||
1442 | #[test] | 1044 | #[test] |
1443 | fn tuple_of_enum_no_diagnostic() { | 1045 | fn tuple_of_enum_no_diagnostic() { |
1444 | check_no_diagnostic( | 1046 | check_diagnostics( |
1445 | r" | 1047 | r#" |
1446 | enum Either { | 1048 | enum Either { A(bool), B(bool, bool) } |
1447 | A(bool), | 1049 | enum Either2 { C, D } |
1448 | B(bool, bool), | 1050 | |
1449 | } | 1051 | fn main() { |
1450 | enum Either2 { | 1052 | match (Either::A(false), Either2::C) { |
1451 | C, | 1053 | (Either::A(true), _) | (Either::A(false), _) => (), |
1452 | D, | 1054 | (Either::B(true, _), Either2::C) => (), |
1453 | } | 1055 | (Either::B(false, _), Either2::C) => (), |
1454 | fn test_fn() { | 1056 | (Either::B(_, _), Either2::D) => (), |
1455 | match (Either::A(false), Either2::C) { | ||
1456 | (Either::A(true), _) | (Either::A(false), _) => (), | ||
1457 | (Either::B(true, _), Either2::C) => (), | ||
1458 | (Either::B(false, _), Either2::C) => (), | ||
1459 | (Either::B(_, _), Either2::D) => (), | ||
1460 | } | ||
1461 | } | ||
1462 | ", | ||
1463 | ); | ||
1464 | } | ||
1465 | |||
1466 | #[test] | ||
1467 | fn mismatched_types() { | ||
1468 | // Match statements with arms that don't match the | ||
1469 | // expression pattern do not fire this diagnostic. | ||
1470 | check_no_diagnostic( | ||
1471 | r" | ||
1472 | enum Either { | ||
1473 | A, | ||
1474 | B, | ||
1475 | } | ||
1476 | enum Either2 { | ||
1477 | C, | ||
1478 | D, | ||
1479 | } | ||
1480 | fn test_fn() { | ||
1481 | match Either::A { | ||
1482 | Either2::C => (), | ||
1483 | Either2::D => (), | ||
1484 | } | ||
1485 | } | ||
1486 | ", | ||
1487 | ); | ||
1488 | } | 1057 | } |
1489 | 1058 | } | |
1490 | #[test] | 1059 | "#, |
1491 | fn mismatched_types_with_different_arity() { | ||
1492 | // Match statements with arms that don't match the | ||
1493 | // expression pattern do not fire this diagnostic. | ||
1494 | check_no_diagnostic( | ||
1495 | r" | ||
1496 | fn test_fn() { | ||
1497 | match (true, false) { | ||
1498 | (true, false, true) => (), | ||
1499 | (true) => (), | ||
1500 | } | ||
1501 | } | ||
1502 | ", | ||
1503 | ); | 1060 | ); |
1504 | } | 1061 | } |
1505 | 1062 | ||
1506 | #[test] | 1063 | #[test] |
1507 | fn malformed_match_arm_tuple_missing_pattern() { | 1064 | fn mismatched_types() { |
1508 | // Match statements with arms that don't match the | 1065 | // Match statements with arms that don't match the |
1509 | // expression pattern do not fire this diagnostic. | 1066 | // expression pattern do not fire this diagnostic. |
1510 | check_no_diagnostic( | 1067 | check_diagnostics( |
1511 | r" | 1068 | r#" |
1512 | fn test_fn() { | 1069 | enum Either { A, B } |
1513 | match (0) { | 1070 | enum Either2 { C, D } |
1514 | () => (), | 1071 | |
1515 | } | 1072 | fn main() { |
1516 | } | 1073 | match Either::A { |
1517 | ", | 1074 | Either2::C => (), |
1075 | Either2::D => (), | ||
1076 | } | ||
1077 | match (true, false) { | ||
1078 | (true, false, true) => (), | ||
1079 | (true) => (), | ||
1080 | } | ||
1081 | match (0) { () => () } | ||
1082 | match Unresolved::Bar { Unresolved::Baz => () } | ||
1083 | } | ||
1084 | "#, | ||
1518 | ); | 1085 | ); |
1519 | } | 1086 | } |
1520 | 1087 | ||
@@ -1522,636 +1089,333 @@ mod tests { | |||
1522 | fn malformed_match_arm_tuple_enum_missing_pattern() { | 1089 | fn malformed_match_arm_tuple_enum_missing_pattern() { |
1523 | // We are testing to be sure we don't panic here when the match | 1090 | // We are testing to be sure we don't panic here when the match |
1524 | // arm `Either::B` is missing its pattern. | 1091 | // arm `Either::B` is missing its pattern. |
1525 | check_no_diagnostic( | 1092 | check_diagnostics( |
1526 | r" | 1093 | r#" |
1527 | enum Either { | 1094 | enum Either { A, B(u32) } |
1528 | A, | ||
1529 | B(u32), | ||
1530 | } | ||
1531 | fn test_fn() { | ||
1532 | match Either::A { | ||
1533 | Either::A => (), | ||
1534 | Either::B() => (), | ||
1535 | } | ||
1536 | } | ||
1537 | ", | ||
1538 | ); | ||
1539 | } | ||
1540 | 1095 | ||
1541 | #[test] | 1096 | fn main() { |
1542 | fn enum_not_in_scope() { | 1097 | match Either::A { |
1543 | // The enum is not in scope so we don't perform exhaustiveness | 1098 | Either::A => (), |
1544 | // checking, but we want to be sure we don't panic here (and | 1099 | Either::B() => (), |
1545 | // we don't create a diagnostic). | 1100 | } |
1546 | check_no_diagnostic( | 1101 | } |
1547 | r" | 1102 | "#, |
1548 | fn test_fn() { | ||
1549 | match Foo::Bar { | ||
1550 | Foo::Baz => (), | ||
1551 | } | ||
1552 | } | ||
1553 | ", | ||
1554 | ); | 1103 | ); |
1555 | } | 1104 | } |
1556 | 1105 | ||
1557 | #[test] | 1106 | #[test] |
1558 | fn expr_diverges() { | 1107 | fn expr_diverges() { |
1559 | check_no_diagnostic( | 1108 | check_diagnostics( |
1560 | r" | 1109 | r#" |
1561 | enum Either { | 1110 | enum Either { A, B } |
1562 | A, | ||
1563 | B, | ||
1564 | } | ||
1565 | fn test_fn() { | ||
1566 | match loop {} { | ||
1567 | Either::A => (), | ||
1568 | Either::B => (), | ||
1569 | } | ||
1570 | } | ||
1571 | ", | ||
1572 | ); | ||
1573 | } | ||
1574 | 1111 | ||
1575 | #[test] | 1112 | fn main() { |
1576 | fn expr_loop_with_break() { | 1113 | match loop {} { |
1577 | check_no_diagnostic( | 1114 | Either::A => (), |
1578 | r" | 1115 | Either::B => (), |
1579 | enum Either { | 1116 | } |
1580 | A, | 1117 | match loop {} { |
1581 | B, | 1118 | Either::A => (), |
1582 | } | 1119 | } |
1583 | fn test_fn() { | 1120 | match loop { break Foo::A } { |
1584 | match loop { break Foo::A } { | 1121 | //^^^^^^^^^^^^^^^^^^^^^ Missing match arm |
1585 | Either::A => (), | 1122 | Either::A => (), |
1586 | Either::B => (), | 1123 | } |
1587 | } | 1124 | match loop { break Foo::A } { |
1588 | } | 1125 | Either::A => (), |
1589 | ", | 1126 | Either::B => (), |
1127 | } | ||
1128 | } | ||
1129 | "#, | ||
1590 | ); | 1130 | ); |
1591 | } | 1131 | } |
1592 | 1132 | ||
1593 | #[test] | 1133 | #[test] |
1594 | fn expr_partially_diverges() { | 1134 | fn expr_partially_diverges() { |
1595 | check_no_diagnostic( | 1135 | check_diagnostics( |
1596 | r" | 1136 | r#" |
1597 | enum Either<T> { | 1137 | enum Either<T> { A(T), B } |
1598 | A(T), | ||
1599 | B, | ||
1600 | } | ||
1601 | fn foo() -> Either<!> { | ||
1602 | Either::B | ||
1603 | } | ||
1604 | fn test_fn() -> u32 { | ||
1605 | match foo() { | ||
1606 | Either::A(val) => val, | ||
1607 | Either::B => 0, | ||
1608 | } | ||
1609 | } | ||
1610 | ", | ||
1611 | ); | ||
1612 | } | ||
1613 | 1138 | ||
1614 | #[test] | 1139 | fn foo() -> Either<!> { Either::B } |
1615 | fn enum_record_no_arms() { | 1140 | fn main() -> u32 { |
1616 | check_diagnostic( | 1141 | match foo() { |
1617 | r" | 1142 | Either::A(val) => val, |
1618 | enum Either { | 1143 | Either::B => 0, |
1619 | A { foo: bool }, | ||
1620 | B, | ||
1621 | } | ||
1622 | fn test_fn() { | ||
1623 | let a = Either::A { foo: true }; | ||
1624 | match a { | ||
1625 | } | ||
1626 | } | ||
1627 | ", | ||
1628 | ); | ||
1629 | } | 1144 | } |
1630 | 1145 | } | |
1631 | #[test] | 1146 | "#, |
1632 | fn enum_record_missing_arms() { | ||
1633 | check_diagnostic( | ||
1634 | r" | ||
1635 | enum Either { | ||
1636 | A { foo: bool }, | ||
1637 | B, | ||
1638 | } | ||
1639 | fn test_fn() { | ||
1640 | let a = Either::A { foo: true }; | ||
1641 | match a { | ||
1642 | Either::A { foo: true } => (), | ||
1643 | } | ||
1644 | } | ||
1645 | ", | ||
1646 | ); | 1147 | ); |
1647 | } | 1148 | } |
1648 | 1149 | ||
1649 | #[test] | 1150 | #[test] |
1650 | fn enum_record_no_diagnostic() { | 1151 | fn enum_record() { |
1651 | check_no_diagnostic( | 1152 | check_diagnostics( |
1652 | r" | 1153 | r#" |
1653 | enum Either { | 1154 | enum Either { A { foo: bool }, B } |
1654 | A { foo: bool }, | ||
1655 | B, | ||
1656 | } | ||
1657 | fn test_fn() { | ||
1658 | let a = Either::A { foo: true }; | ||
1659 | match a { | ||
1660 | Either::A { foo: true } => (), | ||
1661 | Either::A { foo: false } => (), | ||
1662 | Either::B => (), | ||
1663 | } | ||
1664 | } | ||
1665 | ", | ||
1666 | ); | ||
1667 | } | ||
1668 | 1155 | ||
1669 | #[test] | 1156 | fn main() { |
1670 | fn enum_record_missing_field_no_diagnostic() { | 1157 | let a = Either::A { foo: true }; |
1671 | // When `Either::A` is missing a struct member, we don't want | 1158 | match a { } |
1672 | // to fire the missing match arm diagnostic. This should fire | 1159 | //^ Missing match arm |
1673 | // some other diagnostic. | 1160 | match a { Either::A { foo: true } => () } |
1674 | check_no_diagnostic( | 1161 | //^ Missing match arm |
1675 | r" | 1162 | match a { |
1676 | enum Either { | 1163 | Either::A { } => (), |
1677 | A { foo: bool }, | 1164 | //^^^ Missing structure fields: |
1678 | B, | 1165 | // | - foo |
1679 | } | 1166 | Either::B => (), |
1680 | fn test_fn() { | ||
1681 | let a = Either::B; | ||
1682 | match a { | ||
1683 | Either::A { } => (), | ||
1684 | Either::B => (), | ||
1685 | } | ||
1686 | } | ||
1687 | ", | ||
1688 | ); | ||
1689 | } | 1167 | } |
1168 | match a { | ||
1169 | //^ Missing match arm | ||
1170 | Either::A { } => (), | ||
1171 | } //^^^ Missing structure fields: | ||
1172 | // | - foo | ||
1690 | 1173 | ||
1691 | #[test] | 1174 | match a { |
1692 | fn enum_record_missing_field_missing_match_arm() { | 1175 | Either::A { foo: true } => (), |
1693 | // Even though `Either::A` is missing fields, we still want to fire | 1176 | Either::A { foo: false } => (), |
1694 | // the missing arm diagnostic here, since we know `Either::B` is missing. | 1177 | Either::B => (), |
1695 | check_diagnostic( | ||
1696 | r" | ||
1697 | enum Either { | ||
1698 | A { foo: bool }, | ||
1699 | B, | ||
1700 | } | ||
1701 | fn test_fn() { | ||
1702 | let a = Either::B; | ||
1703 | match a { | ||
1704 | Either::A { } => (), | ||
1705 | } | ||
1706 | } | ||
1707 | ", | ||
1708 | ); | ||
1709 | } | 1178 | } |
1710 | 1179 | match a { | |
1711 | #[test] | 1180 | Either::A { foo: _ } => (), |
1712 | fn enum_record_no_diagnostic_wild() { | 1181 | Either::B => (), |
1713 | check_no_diagnostic( | ||
1714 | r" | ||
1715 | enum Either { | ||
1716 | A { foo: bool }, | ||
1717 | B, | ||
1718 | } | ||
1719 | fn test_fn() { | ||
1720 | let a = Either::A { foo: true }; | ||
1721 | match a { | ||
1722 | Either::A { foo: _ } => (), | ||
1723 | Either::B => (), | ||
1724 | } | ||
1725 | } | ||
1726 | ", | ||
1727 | ); | ||
1728 | } | 1182 | } |
1729 | 1183 | } | |
1730 | #[test] | 1184 | "#, |
1731 | fn enum_record_fields_out_of_order_missing_arm() { | ||
1732 | check_diagnostic( | ||
1733 | r" | ||
1734 | enum Either { | ||
1735 | A { foo: bool, bar: () }, | ||
1736 | B, | ||
1737 | } | ||
1738 | fn test_fn() { | ||
1739 | let a = Either::A { foo: true }; | ||
1740 | match a { | ||
1741 | Either::A { bar: (), foo: false } => (), | ||
1742 | Either::A { foo: true, bar: () } => (), | ||
1743 | } | ||
1744 | } | ||
1745 | ", | ||
1746 | ); | 1185 | ); |
1747 | } | 1186 | } |
1748 | 1187 | ||
1749 | #[test] | 1188 | #[test] |
1750 | fn enum_record_fields_out_of_order_no_diagnostic() { | 1189 | fn enum_record_fields_out_of_order() { |
1751 | check_no_diagnostic( | 1190 | check_diagnostics( |
1752 | r" | 1191 | r#" |
1753 | enum Either { | 1192 | enum Either { |
1754 | A { foo: bool, bar: () }, | 1193 | A { foo: bool, bar: () }, |
1755 | B, | 1194 | B, |
1756 | } | 1195 | } |
1757 | fn test_fn() { | ||
1758 | let a = Either::A { foo: true }; | ||
1759 | match a { | ||
1760 | Either::A { bar: (), foo: false } => (), | ||
1761 | Either::A { foo: true, bar: () } => (), | ||
1762 | Either::B => (), | ||
1763 | } | ||
1764 | } | ||
1765 | ", | ||
1766 | ); | ||
1767 | } | ||
1768 | 1196 | ||
1769 | #[test] | 1197 | fn main() { |
1770 | fn enum_record_ellipsis_missing_arm() { | 1198 | let a = Either::A { foo: true, bar: () }; |
1771 | check_diagnostic( | 1199 | match a { |
1772 | r" | 1200 | //^ Missing match arm |
1773 | enum Either { | 1201 | Either::A { bar: (), foo: false } => (), |
1774 | A { foo: bool, bar: bool }, | 1202 | Either::A { foo: true, bar: () } => (), |
1775 | B, | ||
1776 | } | ||
1777 | fn test_fn() { | ||
1778 | match Either::B { | ||
1779 | Either::A { foo: true, .. } => (), | ||
1780 | Either::B => (), | ||
1781 | } | ||
1782 | } | ||
1783 | ", | ||
1784 | ); | ||
1785 | } | 1203 | } |
1786 | 1204 | ||
1787 | #[test] | 1205 | match a { |
1788 | fn enum_record_ellipsis_no_diagnostic() { | 1206 | Either::A { bar: (), foo: false } => (), |
1789 | check_no_diagnostic( | 1207 | Either::A { foo: true, bar: () } => (), |
1790 | r" | 1208 | Either::B => (), |
1791 | enum Either { | ||
1792 | A { foo: bool, bar: bool }, | ||
1793 | B, | ||
1794 | } | ||
1795 | fn test_fn() { | ||
1796 | let a = Either::A { foo: true }; | ||
1797 | match a { | ||
1798 | Either::A { foo: true, .. } => (), | ||
1799 | Either::A { foo: false, .. } => (), | ||
1800 | Either::B => (), | ||
1801 | } | ||
1802 | } | ||
1803 | ", | ||
1804 | ); | ||
1805 | } | 1209 | } |
1806 | 1210 | } | |
1807 | #[test] | 1211 | "#, |
1808 | fn enum_record_ellipsis_all_fields_missing_arm() { | ||
1809 | check_diagnostic( | ||
1810 | r" | ||
1811 | enum Either { | ||
1812 | A { foo: bool, bar: bool }, | ||
1813 | B, | ||
1814 | } | ||
1815 | fn test_fn() { | ||
1816 | let a = Either::B; | ||
1817 | match a { | ||
1818 | Either::A { .. } => (), | ||
1819 | } | ||
1820 | } | ||
1821 | ", | ||
1822 | ); | 1212 | ); |
1823 | } | 1213 | } |
1824 | 1214 | ||
1825 | #[test] | 1215 | #[test] |
1826 | fn enum_record_ellipsis_all_fields_no_diagnostic() { | 1216 | fn enum_record_ellipsis() { |
1827 | check_no_diagnostic( | 1217 | check_diagnostics( |
1828 | r" | 1218 | r#" |
1829 | enum Either { | 1219 | enum Either { |
1830 | A { foo: bool, bar: bool }, | 1220 | A { foo: bool, bar: bool }, |
1831 | B, | 1221 | B, |
1832 | } | 1222 | } |
1833 | fn test_fn() { | ||
1834 | let a = Either::B; | ||
1835 | match a { | ||
1836 | Either::A { .. } => (), | ||
1837 | Either::B => (), | ||
1838 | } | ||
1839 | } | ||
1840 | ", | ||
1841 | ); | ||
1842 | } | ||
1843 | 1223 | ||
1844 | #[test] | 1224 | fn main() { |
1845 | fn enum_tuple_partial_ellipsis_no_diagnostic() { | 1225 | let a = Either::B; |
1846 | check_no_diagnostic( | 1226 | match a { |
1847 | r" | 1227 | //^ Missing match arm |
1848 | enum Either { | 1228 | Either::A { foo: true, .. } => (), |
1849 | A(bool, bool, bool, bool), | 1229 | Either::B => (), |
1850 | B, | ||
1851 | } | ||
1852 | fn test_fn() { | ||
1853 | match Either::B { | ||
1854 | Either::A(true, .., true) => {}, | ||
1855 | Either::A(true, .., false) => {}, | ||
1856 | Either::A(false, .., true) => {}, | ||
1857 | Either::A(false, .., false) => {}, | ||
1858 | Either::B => {}, | ||
1859 | } | ||
1860 | } | ||
1861 | ", | ||
1862 | ); | ||
1863 | } | 1230 | } |
1864 | 1231 | match a { | |
1865 | #[test] | 1232 | //^ Missing match arm |
1866 | fn enum_tuple_partial_ellipsis_2_no_diagnostic() { | 1233 | Either::A { .. } => (), |
1867 | check_no_diagnostic( | ||
1868 | r" | ||
1869 | enum Either { | ||
1870 | A(bool, bool, bool, bool), | ||
1871 | B, | ||
1872 | } | ||
1873 | fn test_fn() { | ||
1874 | match Either::B { | ||
1875 | Either::A(true, .., true) => {}, | ||
1876 | Either::A(true, .., false) => {}, | ||
1877 | Either::A(.., true) => {}, | ||
1878 | Either::A(.., false) => {}, | ||
1879 | Either::B => {}, | ||
1880 | } | ||
1881 | } | ||
1882 | ", | ||
1883 | ); | ||
1884 | } | 1234 | } |
1885 | 1235 | ||
1886 | #[test] | 1236 | match a { |
1887 | fn enum_tuple_partial_ellipsis_missing_arm() { | 1237 | Either::A { foo: true, .. } => (), |
1888 | check_diagnostic( | 1238 | Either::A { foo: false, .. } => (), |
1889 | r" | 1239 | Either::B => (), |
1890 | enum Either { | ||
1891 | A(bool, bool, bool, bool), | ||
1892 | B, | ||
1893 | } | ||
1894 | fn test_fn() { | ||
1895 | match Either::B { | ||
1896 | Either::A(true, .., true) => {}, | ||
1897 | Either::A(true, .., false) => {}, | ||
1898 | Either::A(false, .., false) => {}, | ||
1899 | Either::B => {}, | ||
1900 | } | ||
1901 | } | ||
1902 | ", | ||
1903 | ); | ||
1904 | } | 1240 | } |
1905 | 1241 | ||
1906 | #[test] | 1242 | match a { |
1907 | fn enum_tuple_partial_ellipsis_2_missing_arm() { | 1243 | Either::A { .. } => (), |
1908 | check_diagnostic( | 1244 | Either::B => (), |
1909 | r" | ||
1910 | enum Either { | ||
1911 | A(bool, bool, bool, bool), | ||
1912 | B, | ||
1913 | } | ||
1914 | fn test_fn() { | ||
1915 | match Either::B { | ||
1916 | Either::A(true, .., true) => {}, | ||
1917 | Either::A(true, .., false) => {}, | ||
1918 | Either::A(.., true) => {}, | ||
1919 | Either::B => {}, | ||
1920 | } | ||
1921 | } | ||
1922 | ", | ||
1923 | ); | ||
1924 | } | 1245 | } |
1925 | 1246 | } | |
1926 | #[test] | 1247 | "#, |
1927 | fn enum_tuple_ellipsis_no_diagnostic() { | ||
1928 | check_no_diagnostic( | ||
1929 | r" | ||
1930 | enum Either { | ||
1931 | A(bool, bool, bool, bool), | ||
1932 | B, | ||
1933 | } | ||
1934 | fn test_fn() { | ||
1935 | match Either::B { | ||
1936 | Either::A(..) => {}, | ||
1937 | Either::B => {}, | ||
1938 | } | ||
1939 | } | ||
1940 | ", | ||
1941 | ); | 1248 | ); |
1942 | } | 1249 | } |
1943 | 1250 | ||
1944 | #[test] | 1251 | #[test] |
1945 | fn enum_never() { | 1252 | fn enum_tuple_partial_ellipsis() { |
1946 | check_no_diagnostic( | 1253 | check_diagnostics( |
1947 | r" | 1254 | r#" |
1948 | enum Never {} | 1255 | enum Either { |
1256 | A(bool, bool, bool, bool), | ||
1257 | B, | ||
1258 | } | ||
1949 | 1259 | ||
1950 | fn test_fn(never: Never) { | 1260 | fn main() { |
1951 | match never {} | 1261 | match Either::B { |
1952 | } | 1262 | //^^^^^^^^^ Missing match arm |
1953 | ", | 1263 | Either::A(true, .., true) => (), |
1954 | ); | 1264 | Either::A(true, .., false) => (), |
1265 | Either::A(false, .., false) => (), | ||
1266 | Either::B => (), | ||
1267 | } | ||
1268 | match Either::B { | ||
1269 | //^^^^^^^^^ Missing match arm | ||
1270 | Either::A(true, .., true) => (), | ||
1271 | Either::A(true, .., false) => (), | ||
1272 | Either::A(.., true) => (), | ||
1273 | Either::B => (), | ||
1274 | } | ||
1275 | |||
1276 | match Either::B { | ||
1277 | Either::A(true, .., true) => (), | ||
1278 | Either::A(true, .., false) => (), | ||
1279 | Either::A(false, .., true) => (), | ||
1280 | Either::A(false, .., false) => (), | ||
1281 | Either::B => (), | ||
1282 | } | ||
1283 | match Either::B { | ||
1284 | Either::A(true, .., true) => (), | ||
1285 | Either::A(true, .., false) => (), | ||
1286 | Either::A(.., true) => (), | ||
1287 | Either::A(.., false) => (), | ||
1288 | Either::B => (), | ||
1955 | } | 1289 | } |
1956 | 1290 | } | |
1957 | #[test] | 1291 | "#, |
1958 | fn type_never() { | ||
1959 | check_no_diagnostic( | ||
1960 | r" | ||
1961 | fn test_fn(never: !) { | ||
1962 | match never {} | ||
1963 | } | ||
1964 | ", | ||
1965 | ); | 1292 | ); |
1966 | } | 1293 | } |
1967 | 1294 | ||
1968 | #[test] | 1295 | #[test] |
1969 | fn enum_never_ref() { | 1296 | fn never() { |
1970 | check_no_diagnostic( | 1297 | check_diagnostics( |
1971 | r" | 1298 | r#" |
1972 | enum Never {} | 1299 | enum Never {} |
1973 | 1300 | ||
1974 | fn test_fn(never: &Never) { | 1301 | fn enum_(never: Never) { |
1975 | match never {} | 1302 | match never {} |
1976 | } | 1303 | } |
1977 | ", | 1304 | fn enum_ref(never: &Never) { |
1978 | ); | 1305 | match never {} |
1979 | } | 1306 | } |
1980 | 1307 | fn bang(never: !) { | |
1981 | #[test] | 1308 | match never {} |
1982 | fn expr_diverges_missing_arm() { | 1309 | } |
1983 | check_no_diagnostic( | 1310 | "#, |
1984 | r" | ||
1985 | enum Either { | ||
1986 | A, | ||
1987 | B, | ||
1988 | } | ||
1989 | fn test_fn() { | ||
1990 | match loop {} { | ||
1991 | Either::A => (), | ||
1992 | } | ||
1993 | } | ||
1994 | ", | ||
1995 | ); | 1311 | ); |
1996 | } | 1312 | } |
1997 | 1313 | ||
1998 | #[test] | 1314 | #[test] |
1999 | fn or_pattern_panic() { | 1315 | fn or_pattern_panic() { |
2000 | check_no_diagnostic( | 1316 | check_diagnostics( |
2001 | r" | 1317 | r#" |
2002 | pub enum Category { | 1318 | pub enum Category { Infinity, Zero } |
2003 | Infinity, | ||
2004 | Zero, | ||
2005 | } | ||
2006 | 1319 | ||
2007 | fn panic(a: Category, b: Category) { | 1320 | fn panic(a: Category, b: Category) { |
2008 | match (a, b) { | 1321 | match (a, b) { |
2009 | (Category::Zero | Category::Infinity, _) => {} | 1322 | (Category::Zero | Category::Infinity, _) => (), |
2010 | (_, Category::Zero | Category::Infinity) => {} | 1323 | (_, Category::Zero | Category::Infinity) => (), |
2011 | } | ||
2012 | } | ||
2013 | ", | ||
2014 | ); | ||
2015 | } | 1324 | } |
2016 | 1325 | ||
2017 | #[test] | 1326 | // FIXME: This is a false positive, but the code used to cause a panic in the match checker, |
2018 | fn or_pattern_panic_2() { | 1327 | // so this acts as a regression test for that. |
2019 | // FIXME: This is a false positive, but the code used to cause a panic in the match checker, | 1328 | match (a, b) { |
2020 | // so this acts as a regression test for that. | 1329 | //^^^^^^ Missing match arm |
2021 | check_diagnostic( | 1330 | (Category::Infinity, Category::Infinity) | (Category::Zero, Category::Zero) => (), |
2022 | r" | 1331 | (Category::Infinity | Category::Zero, _) => (), |
2023 | pub enum Category { | ||
2024 | Infinity, | ||
2025 | Zero, | ||
2026 | } | ||
2027 | |||
2028 | fn panic(a: Category, b: Category) { | ||
2029 | match (a, b) { | ||
2030 | (Category::Infinity, Category::Infinity) | (Category::Zero, Category::Zero) => {} | ||
2031 | |||
2032 | (Category::Infinity | Category::Zero, _) => {} | ||
2033 | } | ||
2034 | } | ||
2035 | ", | ||
2036 | ); | ||
2037 | } | 1332 | } |
2038 | } | 1333 | } |
2039 | 1334 | "#, | |
2040 | #[cfg(test)] | 1335 | ); |
2041 | mod false_negatives { | 1336 | } |
2042 | //! The implementation of match checking here is a work in progress. As we roll this out, we | 1337 | |
2043 | //! prefer false negatives to false positives (ideally there would be no false positives). This | 1338 | mod false_negatives { |
2044 | //! test module should document known false negatives. Eventually we will have a complete | 1339 | //! The implementation of match checking here is a work in progress. As we roll this out, we |
2045 | //! implementation of match checking and this module will be empty. | 1340 | //! prefer false negatives to false positives (ideally there would be no false positives). This |
2046 | //! | 1341 | //! test module should document known false negatives. Eventually we will have a complete |
2047 | //! The reasons for documenting known false negatives: | 1342 | //! implementation of match checking and this module will be empty. |
2048 | //! | 1343 | //! |
2049 | //! 1. It acts as a backlog of work that can be done to improve the behavior of the system. | 1344 | //! The reasons for documenting known false negatives: |
2050 | //! 2. It ensures the code doesn't panic when handling these cases. | 1345 | //! |
2051 | 1346 | //! 1. It acts as a backlog of work that can be done to improve the behavior of the system. | |
2052 | use super::tests::*; | 1347 | //! 2. It ensures the code doesn't panic when handling these cases. |
2053 | 1348 | use super::*; | |
2054 | #[test] | 1349 | |
2055 | fn integers() { | 1350 | #[test] |
2056 | // This is a false negative. | 1351 | fn integers() { |
2057 | // We don't currently check integer exhaustiveness. | 1352 | // We don't currently check integer exhaustiveness. |
2058 | check_no_diagnostic( | 1353 | check_diagnostics( |
2059 | r" | 1354 | r#" |
2060 | fn test_fn() { | 1355 | fn main() { |
2061 | match 5 { | 1356 | match 5 { |
2062 | 10 => (), | 1357 | 10 => (), |
2063 | 11..20 => (), | 1358 | 11..20 => (), |
2064 | } | ||
2065 | } | ||
2066 | ", | ||
2067 | ); | ||
2068 | } | ||
2069 | |||
2070 | #[test] | ||
2071 | fn internal_or() { | ||
2072 | // This is a false negative. | ||
2073 | // We do not currently handle patterns with internal `or`s. | ||
2074 | check_no_diagnostic( | ||
2075 | r" | ||
2076 | fn test_fn() { | ||
2077 | enum Either { | ||
2078 | A(bool), | ||
2079 | B, | ||
2080 | } | ||
2081 | match Either::B { | ||
2082 | Either::A(true | false) => (), | ||
2083 | } | ||
2084 | } | ||
2085 | ", | ||
2086 | ); | ||
2087 | } | 1359 | } |
1360 | } | ||
1361 | "#, | ||
1362 | ); | ||
1363 | } | ||
2088 | 1364 | ||
2089 | #[test] | 1365 | #[test] |
2090 | fn expr_loop_missing_arm() { | 1366 | fn internal_or() { |
2091 | // This is a false negative. | 1367 | // We do not currently handle patterns with internal `or`s. |
2092 | // We currently infer the type of `loop { break Foo::A }` to `!`, which | 1368 | check_diagnostics( |
2093 | // causes us to skip the diagnostic since `Either::A` doesn't type check | 1369 | r#" |
2094 | // with `!`. | 1370 | fn main() { |
2095 | check_diagnostic( | 1371 | enum Either { A(bool), B } |
2096 | r" | 1372 | match Either::B { |
2097 | enum Either { | 1373 | Either::A(true | false) => (), |
2098 | A, | ||
2099 | B, | ||
2100 | } | ||
2101 | fn test_fn() { | ||
2102 | match loop { break Foo::A } { | ||
2103 | Either::A => (), | ||
2104 | } | ||
2105 | } | ||
2106 | ", | ||
2107 | ); | ||
2108 | } | 1374 | } |
1375 | } | ||
1376 | "#, | ||
1377 | ); | ||
1378 | } | ||
2109 | 1379 | ||
2110 | #[test] | 1380 | #[test] |
2111 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { | 1381 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { |
2112 | // This is a false negative. | 1382 | // We don't currently handle tuple patterns with ellipsis. |
2113 | // We don't currently handle tuple patterns with ellipsis. | 1383 | check_diagnostics( |
2114 | check_no_diagnostic( | 1384 | r#" |
2115 | r" | 1385 | fn main() { |
2116 | fn test_fn() { | 1386 | match (false, true, false) { |
2117 | match (false, true, false) { | 1387 | (false, ..) => (), |
2118 | (false, ..) => {}, | ||
2119 | } | ||
2120 | } | ||
2121 | ", | ||
2122 | ); | ||
2123 | } | 1388 | } |
1389 | } | ||
1390 | "#, | ||
1391 | ); | ||
1392 | } | ||
2124 | 1393 | ||
2125 | #[test] | 1394 | #[test] |
2126 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { | 1395 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { |
2127 | // This is a false negative. | 1396 | // We don't currently handle tuple patterns with ellipsis. |
2128 | // We don't currently handle tuple patterns with ellipsis. | 1397 | check_diagnostics( |
2129 | check_no_diagnostic( | 1398 | r#" |
2130 | r" | 1399 | fn main() { |
2131 | fn test_fn() { | 1400 | match (false, true, false) { |
2132 | match (false, true, false) { | 1401 | (.., false) => (), |
2133 | (.., false) => {}, | ||
2134 | } | ||
2135 | } | ||
2136 | ", | ||
2137 | ); | ||
2138 | } | 1402 | } |
1403 | } | ||
1404 | "#, | ||
1405 | ); | ||
1406 | } | ||
2139 | 1407 | ||
2140 | #[test] | 1408 | #[test] |
2141 | fn struct_missing_arm() { | 1409 | fn struct_missing_arm() { |
2142 | // This is a false negative. | 1410 | // We don't currently handle structs. |
2143 | // We don't currently handle structs. | 1411 | check_diagnostics( |
2144 | check_no_diagnostic( | 1412 | r#" |
2145 | r" | 1413 | struct Foo { a: bool } |
2146 | struct Foo { | 1414 | fn main(f: Foo) { |
2147 | a: bool, | 1415 | match f { Foo { a: true } => () } |
2148 | } | 1416 | } |
2149 | fn test_fn(f: Foo) { | 1417 | "#, |
2150 | match f { | 1418 | ); |
2151 | Foo { a: true } => {}, | 1419 | } |
2152 | } | ||
2153 | } | ||
2154 | ", | ||
2155 | ); | ||
2156 | } | 1420 | } |
2157 | } | 1421 | } |
diff --git a/crates/ra_hir_ty/src/unsafe_validation.rs b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs index c512c4f8e..9e4ed9a8b 100644 --- a/crates/ra_hir_ty/src/unsafe_validation.rs +++ b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs | |||
@@ -6,7 +6,7 @@ use std::sync::Arc; | |||
6 | use hir_def::{ | 6 | use hir_def::{ |
7 | body::Body, | 7 | body::Body, |
8 | expr::{Expr, ExprId, UnaryOp}, | 8 | expr::{Expr, ExprId, UnaryOp}, |
9 | DefWithBodyId, FunctionId, | 9 | DefWithBodyId, |
10 | }; | 10 | }; |
11 | use hir_expand::diagnostics::DiagnosticSink; | 11 | use hir_expand::diagnostics::DiagnosticSink; |
12 | 12 | ||
@@ -15,26 +15,29 @@ use crate::{ | |||
15 | InferenceResult, Ty, TypeCtor, | 15 | InferenceResult, Ty, TypeCtor, |
16 | }; | 16 | }; |
17 | 17 | ||
18 | pub struct UnsafeValidator<'a, 'b: 'a> { | 18 | pub(super) struct UnsafeValidator<'a, 'b: 'a> { |
19 | func: FunctionId, | 19 | owner: DefWithBodyId, |
20 | infer: Arc<InferenceResult>, | 20 | infer: Arc<InferenceResult>, |
21 | sink: &'a mut DiagnosticSink<'b>, | 21 | sink: &'a mut DiagnosticSink<'b>, |
22 | } | 22 | } |
23 | 23 | ||
24 | impl<'a, 'b> UnsafeValidator<'a, 'b> { | 24 | impl<'a, 'b> UnsafeValidator<'a, 'b> { |
25 | pub fn new( | 25 | pub(super) fn new( |
26 | func: FunctionId, | 26 | owner: DefWithBodyId, |
27 | infer: Arc<InferenceResult>, | 27 | infer: Arc<InferenceResult>, |
28 | sink: &'a mut DiagnosticSink<'b>, | 28 | sink: &'a mut DiagnosticSink<'b>, |
29 | ) -> UnsafeValidator<'a, 'b> { | 29 | ) -> UnsafeValidator<'a, 'b> { |
30 | UnsafeValidator { func, infer, sink } | 30 | UnsafeValidator { owner, infer, sink } |
31 | } | 31 | } |
32 | 32 | ||
33 | pub fn validate_body(&mut self, db: &dyn HirDatabase) { | 33 | pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { |
34 | let def = self.func.into(); | 34 | let def = self.owner.into(); |
35 | let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def); | 35 | let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def); |
36 | let func_data = db.function_data(self.func); | 36 | let is_unsafe = match self.owner { |
37 | if func_data.is_unsafe | 37 | DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe, |
38 | DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false, | ||
39 | }; | ||
40 | if is_unsafe | ||
38 | || unsafe_expressions | 41 | || unsafe_expressions |
39 | .iter() | 42 | .iter() |
40 | .filter(|unsafe_expr| !unsafe_expr.inside_unsafe_block) | 43 | .filter(|unsafe_expr| !unsafe_expr.inside_unsafe_block) |
@@ -118,3 +121,53 @@ fn walk_unsafe( | |||
118 | walk_unsafe(unsafe_exprs, db, infer, body, child, inside_unsafe_block); | 121 | walk_unsafe(unsafe_exprs, db, infer, body, child, inside_unsafe_block); |
119 | }); | 122 | }); |
120 | } | 123 | } |
124 | |||
125 | #[cfg(test)] | ||
126 | mod tests { | ||
127 | use crate::diagnostics::tests::check_diagnostics; | ||
128 | |||
129 | #[test] | ||
130 | fn missing_unsafe_diagnostic_with_raw_ptr() { | ||
131 | check_diagnostics( | ||
132 | r#" | ||
133 | fn main() { | ||
134 | let x = &5 as *const usize; | ||
135 | unsafe { let y = *x; } | ||
136 | let z = *x; | ||
137 | } //^^ This operation is unsafe and requires an unsafe function or block | ||
138 | "#, | ||
139 | ) | ||
140 | } | ||
141 | |||
142 | #[test] | ||
143 | fn missing_unsafe_diagnostic_with_unsafe_call() { | ||
144 | check_diagnostics( | ||
145 | r#" | ||
146 | struct HasUnsafe; | ||
147 | |||
148 | impl HasUnsafe { | ||
149 | unsafe fn unsafe_fn(&self) { | ||
150 | let x = &5 as *const usize; | ||
151 | let y = *x; | ||
152 | } | ||
153 | } | ||
154 | |||
155 | unsafe fn unsafe_fn() { | ||
156 | let x = &5 as *const usize; | ||
157 | let y = *x; | ||
158 | } | ||
159 | |||
160 | fn main() { | ||
161 | unsafe_fn(); | ||
162 | //^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block | ||
163 | HasUnsafe.unsafe_fn(); | ||
164 | //^^^^^^^^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block | ||
165 | unsafe { | ||
166 | unsafe_fn(); | ||
167 | HasUnsafe.unsafe_fn(); | ||
168 | } | ||
169 | } | ||
170 | "#, | ||
171 | ); | ||
172 | } | ||
173 | } | ||
diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs index ac68c5661..c860c254c 100644 --- a/crates/ra_hir_ty/src/display.rs +++ b/crates/ra_hir_ty/src/display.rs | |||
@@ -243,10 +243,17 @@ impl HirDisplay for ApplicationTy { | |||
243 | write!(f, ")")?; | 243 | write!(f, ")")?; |
244 | } | 244 | } |
245 | } | 245 | } |
246 | TypeCtor::FnPtr { .. } => { | 246 | TypeCtor::FnPtr { is_varargs, .. } => { |
247 | let sig = FnSig::from_fn_ptr_substs(&self.parameters); | 247 | let sig = FnSig::from_fn_ptr_substs(&self.parameters, is_varargs); |
248 | write!(f, "fn(")?; | 248 | write!(f, "fn(")?; |
249 | f.write_joined(sig.params(), ", ")?; | 249 | f.write_joined(sig.params(), ", ")?; |
250 | if is_varargs { | ||
251 | if sig.params().is_empty() { | ||
252 | write!(f, "...")?; | ||
253 | } else { | ||
254 | write!(f, ", ...")?; | ||
255 | } | ||
256 | } | ||
250 | write!(f, ")")?; | 257 | write!(f, ")")?; |
251 | let ret = sig.ret(); | 258 | let ret = sig.ret(); |
252 | if *ret != Ty::unit() { | 259 | if *ret != Ty::unit() { |
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 2ce4f65cc..28f32a0a4 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs | |||
@@ -168,7 +168,7 @@ impl InferenceResult { | |||
168 | pub fn add_diagnostics( | 168 | pub fn add_diagnostics( |
169 | &self, | 169 | &self, |
170 | db: &dyn HirDatabase, | 170 | db: &dyn HirDatabase, |
171 | owner: FunctionId, | 171 | owner: DefWithBodyId, |
172 | sink: &mut DiagnosticSink, | 172 | sink: &mut DiagnosticSink, |
173 | ) { | 173 | ) { |
174 | self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink)) | 174 | self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink)) |
@@ -760,7 +760,7 @@ impl std::ops::BitOrAssign for Diverges { | |||
760 | } | 760 | } |
761 | 761 | ||
762 | mod diagnostics { | 762 | mod diagnostics { |
763 | use hir_def::{expr::ExprId, FunctionId}; | 763 | use hir_def::{expr::ExprId, DefWithBodyId}; |
764 | use hir_expand::diagnostics::DiagnosticSink; | 764 | use hir_expand::diagnostics::DiagnosticSink; |
765 | 765 | ||
766 | use crate::{ | 766 | use crate::{ |
@@ -778,17 +778,17 @@ mod diagnostics { | |||
778 | pub(super) fn add_to( | 778 | pub(super) fn add_to( |
779 | &self, | 779 | &self, |
780 | db: &dyn HirDatabase, | 780 | db: &dyn HirDatabase, |
781 | owner: FunctionId, | 781 | owner: DefWithBodyId, |
782 | sink: &mut DiagnosticSink, | 782 | sink: &mut DiagnosticSink, |
783 | ) { | 783 | ) { |
784 | match self { | 784 | match self { |
785 | InferenceDiagnostic::NoSuchField { expr, field } => { | 785 | InferenceDiagnostic::NoSuchField { expr, field } => { |
786 | let (_, source_map) = db.body_with_source_map(owner.into()); | 786 | let (_, source_map) = db.body_with_source_map(owner); |
787 | let field = source_map.field_syntax(*expr, *field); | 787 | let field = source_map.field_syntax(*expr, *field); |
788 | sink.push(NoSuchField { file: field.file_id, field: field.value }) | 788 | sink.push(NoSuchField { file: field.file_id, field: field.value }) |
789 | } | 789 | } |
790 | InferenceDiagnostic::BreakOutsideOfLoop { expr } => { | 790 | InferenceDiagnostic::BreakOutsideOfLoop { expr } => { |
791 | let (_, source_map) = db.body_with_source_map(owner.into()); | 791 | let (_, source_map) = db.body_with_source_map(owner); |
792 | let ptr = source_map | 792 | let ptr = source_map |
793 | .expr_syntax(*expr) | 793 | .expr_syntax(*expr) |
794 | .expect("break outside of loop in synthetic syntax"); | 794 | .expect("break outside of loop in synthetic syntax"); |
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index bd9a387f5..ab586b018 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs | |||
@@ -220,7 +220,7 @@ impl<'a> InferenceContext<'a> { | |||
220 | }; | 220 | }; |
221 | sig_tys.push(ret_ty.clone()); | 221 | sig_tys.push(ret_ty.clone()); |
222 | let sig_ty = Ty::apply( | 222 | let sig_ty = Ty::apply( |
223 | TypeCtor::FnPtr { num_args: sig_tys.len() as u16 - 1 }, | 223 | TypeCtor::FnPtr { num_args: sig_tys.len() as u16 - 1, is_varargs: false }, |
224 | Substs(sig_tys.clone().into()), | 224 | Substs(sig_tys.clone().into()), |
225 | ); | 225 | ); |
226 | let closure_ty = | 226 | let closure_ty = |
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 2652d200f..c4c24a83b 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -12,15 +12,12 @@ pub mod traits; | |||
12 | pub mod method_resolution; | 12 | pub mod method_resolution; |
13 | mod op; | 13 | mod op; |
14 | mod lower; | 14 | mod lower; |
15 | mod match_checking; | ||
16 | pub(crate) mod infer; | 15 | pub(crate) mod infer; |
17 | pub(crate) mod utils; | 16 | pub(crate) mod utils; |
18 | 17 | ||
19 | pub mod display; | 18 | pub mod display; |
20 | pub mod db; | 19 | pub mod db; |
21 | pub mod diagnostics; | 20 | pub mod diagnostics; |
22 | pub mod expr; | ||
23 | pub mod unsafe_validation; | ||
24 | 21 | ||
25 | #[cfg(test)] | 22 | #[cfg(test)] |
26 | mod tests; | 23 | mod tests; |
@@ -115,7 +112,7 @@ pub enum TypeCtor { | |||
115 | /// fn foo() -> i32 { 1 } | 112 | /// fn foo() -> i32 { 1 } |
116 | /// let bar: fn() -> i32 = foo; | 113 | /// let bar: fn() -> i32 = foo; |
117 | /// ``` | 114 | /// ``` |
118 | FnPtr { num_args: u16 }, | 115 | FnPtr { num_args: u16, is_varargs: bool }, |
119 | 116 | ||
120 | /// The never type `!`. | 117 | /// The never type `!`. |
121 | Never, | 118 | Never, |
@@ -190,7 +187,7 @@ impl TypeCtor { | |||
190 | } | 187 | } |
191 | } | 188 | } |
192 | } | 189 | } |
193 | TypeCtor::FnPtr { num_args } => num_args as usize + 1, | 190 | TypeCtor::FnPtr { num_args, is_varargs: _ } => num_args as usize + 1, |
194 | TypeCtor::Tuple { cardinality } => cardinality as usize, | 191 | TypeCtor::Tuple { cardinality } => cardinality as usize, |
195 | } | 192 | } |
196 | } | 193 | } |
@@ -670,19 +667,20 @@ pub enum TyKind { | |||
670 | #[derive(Clone, PartialEq, Eq, Debug)] | 667 | #[derive(Clone, PartialEq, Eq, Debug)] |
671 | pub struct FnSig { | 668 | pub struct FnSig { |
672 | params_and_return: Arc<[Ty]>, | 669 | params_and_return: Arc<[Ty]>, |
670 | is_varargs: bool, | ||
673 | } | 671 | } |
674 | 672 | ||
675 | /// A polymorphic function signature. | 673 | /// A polymorphic function signature. |
676 | pub type PolyFnSig = Binders<FnSig>; | 674 | pub type PolyFnSig = Binders<FnSig>; |
677 | 675 | ||
678 | impl FnSig { | 676 | impl FnSig { |
679 | pub fn from_params_and_return(mut params: Vec<Ty>, ret: Ty) -> FnSig { | 677 | pub fn from_params_and_return(mut params: Vec<Ty>, ret: Ty, is_varargs: bool) -> FnSig { |
680 | params.push(ret); | 678 | params.push(ret); |
681 | FnSig { params_and_return: params.into() } | 679 | FnSig { params_and_return: params.into(), is_varargs } |
682 | } | 680 | } |
683 | 681 | ||
684 | pub fn from_fn_ptr_substs(substs: &Substs) -> FnSig { | 682 | pub fn from_fn_ptr_substs(substs: &Substs, is_varargs: bool) -> FnSig { |
685 | FnSig { params_and_return: Arc::clone(&substs.0) } | 683 | FnSig { params_and_return: Arc::clone(&substs.0), is_varargs } |
686 | } | 684 | } |
687 | 685 | ||
688 | pub fn params(&self) -> &[Ty] { | 686 | pub fn params(&self) -> &[Ty] { |
@@ -727,7 +725,7 @@ impl Ty { | |||
727 | } | 725 | } |
728 | pub fn fn_ptr(sig: FnSig) -> Self { | 726 | pub fn fn_ptr(sig: FnSig) -> Self { |
729 | Ty::apply( | 727 | Ty::apply( |
730 | TypeCtor::FnPtr { num_args: sig.params().len() as u16 }, | 728 | TypeCtor::FnPtr { num_args: sig.params().len() as u16, is_varargs: sig.is_varargs }, |
731 | Substs(sig.params_and_return), | 729 | Substs(sig.params_and_return), |
732 | ) | 730 | ) |
733 | } | 731 | } |
@@ -824,7 +822,9 @@ impl Ty { | |||
824 | fn callable_sig(&self, db: &dyn HirDatabase) -> Option<FnSig> { | 822 | fn callable_sig(&self, db: &dyn HirDatabase) -> Option<FnSig> { |
825 | match self { | 823 | match self { |
826 | Ty::Apply(a_ty) => match a_ty.ctor { | 824 | Ty::Apply(a_ty) => match a_ty.ctor { |
827 | TypeCtor::FnPtr { .. } => Some(FnSig::from_fn_ptr_substs(&a_ty.parameters)), | 825 | TypeCtor::FnPtr { is_varargs, .. } => { |
826 | Some(FnSig::from_fn_ptr_substs(&a_ty.parameters, is_varargs)) | ||
827 | } | ||
828 | TypeCtor::FnDef(def) => { | 828 | TypeCtor::FnDef(def) => { |
829 | let sig = db.callable_item_signature(def); | 829 | let sig = db.callable_item_signature(def); |
830 | Some(sig.subst(&a_ty.parameters)) | 830 | Some(sig.subst(&a_ty.parameters)) |
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index 101b8aebe..6f4398e84 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs | |||
@@ -176,9 +176,12 @@ impl Ty { | |||
176 | Ty::apply_one(TypeCtor::Ref(*mutability), inner_ty) | 176 | Ty::apply_one(TypeCtor::Ref(*mutability), inner_ty) |
177 | } | 177 | } |
178 | TypeRef::Placeholder => Ty::Unknown, | 178 | TypeRef::Placeholder => Ty::Unknown, |
179 | TypeRef::Fn(params) => { | 179 | TypeRef::Fn(params, is_varargs) => { |
180 | let sig = Substs(params.iter().map(|tr| Ty::from_hir(ctx, tr)).collect()); | 180 | let sig = Substs(params.iter().map(|tr| Ty::from_hir(ctx, tr)).collect()); |
181 | Ty::apply(TypeCtor::FnPtr { num_args: sig.len() as u16 - 1 }, sig) | 181 | Ty::apply( |
182 | TypeCtor::FnPtr { num_args: sig.len() as u16 - 1, is_varargs: *is_varargs }, | ||
183 | sig, | ||
184 | ) | ||
182 | } | 185 | } |
183 | TypeRef::DynTrait(bounds) => { | 186 | TypeRef::DynTrait(bounds) => { |
184 | let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); | 187 | let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); |
@@ -996,7 +999,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { | |||
996 | let ret = Ty::from_hir(&ctx_ret, &data.ret_type); | 999 | let ret = Ty::from_hir(&ctx_ret, &data.ret_type); |
997 | let generics = generics(db.upcast(), def.into()); | 1000 | let generics = generics(db.upcast(), def.into()); |
998 | let num_binders = generics.len(); | 1001 | let num_binders = generics.len(); |
999 | Binders::new(num_binders, FnSig::from_params_and_return(params, ret)) | 1002 | Binders::new(num_binders, FnSig::from_params_and_return(params, ret, data.is_varargs)) |
1000 | } | 1003 | } |
1001 | 1004 | ||
1002 | /// Build the declared type of a function. This should not need to look at the | 1005 | /// Build the declared type of a function. This should not need to look at the |
@@ -1047,7 +1050,7 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS | |||
1047 | let params = | 1050 | let params = |
1048 | fields.iter().map(|(_, field)| Ty::from_hir(&ctx, &field.type_ref)).collect::<Vec<_>>(); | 1051 | fields.iter().map(|(_, field)| Ty::from_hir(&ctx, &field.type_ref)).collect::<Vec<_>>(); |
1049 | let ret = type_for_adt(db, def.into()); | 1052 | let ret = type_for_adt(db, def.into()); |
1050 | Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value)) | 1053 | Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value, false)) |
1051 | } | 1054 | } |
1052 | 1055 | ||
1053 | /// Build the type of a tuple struct constructor. | 1056 | /// Build the type of a tuple struct constructor. |
@@ -1071,7 +1074,7 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) | |||
1071 | let params = | 1074 | let params = |
1072 | fields.iter().map(|(_, field)| Ty::from_hir(&ctx, &field.type_ref)).collect::<Vec<_>>(); | 1075 | fields.iter().map(|(_, field)| Ty::from_hir(&ctx, &field.type_ref)).collect::<Vec<_>>(); |
1073 | let ret = type_for_adt(db, def.parent.into()); | 1076 | let ret = type_for_adt(db, def.parent.into()); |
1074 | Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value)) | 1077 | Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value, false)) |
1075 | } | 1078 | } |
1076 | 1079 | ||
1077 | /// Build the type of a tuple enum variant constructor. | 1080 | /// Build the type of a tuple enum variant constructor. |
diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs index dc447955f..a1714ff0f 100644 --- a/crates/ra_hir_ty/src/test_db.rs +++ b/crates/ra_hir_ty/src/test_db.rs | |||
@@ -5,19 +5,13 @@ use std::{ | |||
5 | sync::{Arc, Mutex}, | 5 | sync::{Arc, Mutex}, |
6 | }; | 6 | }; |
7 | 7 | ||
8 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId}; | 8 | use hir_def::{db::DefDatabase, ModuleId}; |
9 | use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink}; | 9 | use hir_expand::db::AstDatabase; |
10 | use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast}; | 10 | use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast}; |
11 | use ra_syntax::TextRange; | 11 | use ra_syntax::TextRange; |
12 | use rustc_hash::{FxHashMap, FxHashSet}; | 12 | use rustc_hash::{FxHashMap, FxHashSet}; |
13 | use stdx::format_to; | ||
14 | use test_utils::extract_annotations; | 13 | use test_utils::extract_annotations; |
15 | 14 | ||
16 | use crate::{ | ||
17 | db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator, | ||
18 | unsafe_validation::UnsafeValidator, | ||
19 | }; | ||
20 | |||
21 | #[salsa::database( | 15 | #[salsa::database( |
22 | ra_db::SourceDatabaseExtStorage, | 16 | ra_db::SourceDatabaseExtStorage, |
23 | ra_db::SourceDatabaseStorage, | 17 | ra_db::SourceDatabaseStorage, |
@@ -82,7 +76,7 @@ impl FileLoader for TestDB { | |||
82 | } | 76 | } |
83 | 77 | ||
84 | impl TestDB { | 78 | impl TestDB { |
85 | pub fn module_for_file(&self, file_id: FileId) -> ModuleId { | 79 | pub(crate) fn module_for_file(&self, file_id: FileId) -> ModuleId { |
86 | for &krate in self.relevant_crates(file_id).iter() { | 80 | for &krate in self.relevant_crates(file_id).iter() { |
87 | let crate_def_map = self.crate_def_map(krate); | 81 | let crate_def_map = self.crate_def_map(krate); |
88 | for (local_id, data) in crate_def_map.modules.iter() { | 82 | for (local_id, data) in crate_def_map.modules.iter() { |
@@ -94,67 +88,7 @@ impl TestDB { | |||
94 | panic!("Can't find module for file") | 88 | panic!("Can't find module for file") |
95 | } | 89 | } |
96 | 90 | ||
97 | fn diag<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) { | 91 | pub(crate) fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> { |
98 | let crate_graph = self.crate_graph(); | ||
99 | for krate in crate_graph.iter() { | ||
100 | let crate_def_map = self.crate_def_map(krate); | ||
101 | |||
102 | let mut fns = Vec::new(); | ||
103 | for (module_id, _) in crate_def_map.modules.iter() { | ||
104 | for decl in crate_def_map[module_id].scope.declarations() { | ||
105 | if let ModuleDefId::FunctionId(f) = decl { | ||
106 | fns.push(f) | ||
107 | } | ||
108 | } | ||
109 | |||
110 | for impl_id in crate_def_map[module_id].scope.impls() { | ||
111 | let impl_data = self.impl_data(impl_id); | ||
112 | for item in impl_data.items.iter() { | ||
113 | if let AssocItemId::FunctionId(f) = item { | ||
114 | fns.push(*f) | ||
115 | } | ||
116 | } | ||
117 | } | ||
118 | } | ||
119 | |||
120 | for f in fns { | ||
121 | let infer = self.infer(f.into()); | ||
122 | let mut sink = DiagnosticSink::new(&mut cb); | ||
123 | infer.add_diagnostics(self, f, &mut sink); | ||
124 | let mut validator = ExprValidator::new(f, infer.clone(), &mut sink); | ||
125 | validator.validate_body(self); | ||
126 | let mut validator = UnsafeValidator::new(f, infer, &mut sink); | ||
127 | validator.validate_body(self); | ||
128 | } | ||
129 | } | ||
130 | } | ||
131 | |||
132 | pub fn diagnostics(&self) -> (String, u32) { | ||
133 | let mut buf = String::new(); | ||
134 | let mut count = 0; | ||
135 | self.diag(|d| { | ||
136 | format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message()); | ||
137 | count += 1; | ||
138 | }); | ||
139 | (buf, count) | ||
140 | } | ||
141 | |||
142 | /// Like `diagnostics`, but filtered for a single diagnostic. | ||
143 | pub fn diagnostic<D: Diagnostic>(&self) -> (String, u32) { | ||
144 | let mut buf = String::new(); | ||
145 | let mut count = 0; | ||
146 | self.diag(|d| { | ||
147 | // We want to filter diagnostics by the particular one we are testing for, to | ||
148 | // avoid surprising results in tests. | ||
149 | if d.downcast_ref::<D>().is_some() { | ||
150 | format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message()); | ||
151 | count += 1; | ||
152 | }; | ||
153 | }); | ||
154 | (buf, count) | ||
155 | } | ||
156 | |||
157 | pub fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> { | ||
158 | let mut files = Vec::new(); | 92 | let mut files = Vec::new(); |
159 | let crate_graph = self.crate_graph(); | 93 | let crate_graph = self.crate_graph(); |
160 | for krate in crate_graph.iter() { | 94 | for krate in crate_graph.iter() { |
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs index 27f5a60bf..d57b3f288 100644 --- a/crates/ra_hir_ty/src/tests.rs +++ b/crates/ra_hir_ty/src/tests.rs | |||
@@ -20,7 +20,6 @@ use hir_def::{ | |||
20 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, | 20 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, |
21 | }; | 21 | }; |
22 | use hir_expand::{db::AstDatabase, InFile}; | 22 | use hir_expand::{db::AstDatabase, InFile}; |
23 | use insta::assert_snapshot; | ||
24 | use ra_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; | 23 | use ra_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; |
25 | use ra_syntax::{ | 24 | use ra_syntax::{ |
26 | algo, | 25 | algo, |
@@ -341,410 +340,3 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() { | |||
341 | assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events) | 340 | assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events) |
342 | } | 341 | } |
343 | } | 342 | } |
344 | |||
345 | #[test] | ||
346 | fn no_such_field_diagnostics() { | ||
347 | let diagnostics = TestDB::with_files( | ||
348 | r" | ||
349 | //- /lib.rs | ||
350 | struct S { foo: i32, bar: () } | ||
351 | impl S { | ||
352 | fn new() -> S { | ||
353 | S { | ||
354 | foo: 92, | ||
355 | baz: 62, | ||
356 | } | ||
357 | } | ||
358 | } | ||
359 | ", | ||
360 | ) | ||
361 | .diagnostics() | ||
362 | .0; | ||
363 | |||
364 | assert_snapshot!(diagnostics, @r###" | ||
365 | "baz: 62": no such field | ||
366 | "{\n foo: 92,\n baz: 62,\n }": Missing structure fields: | ||
367 | - bar | ||
368 | "### | ||
369 | ); | ||
370 | } | ||
371 | |||
372 | #[test] | ||
373 | fn no_such_field_with_feature_flag_diagnostics() { | ||
374 | let diagnostics = TestDB::with_files( | ||
375 | r#" | ||
376 | //- /lib.rs crate:foo cfg:feature=foo | ||
377 | struct MyStruct { | ||
378 | my_val: usize, | ||
379 | #[cfg(feature = "foo")] | ||
380 | bar: bool, | ||
381 | } | ||
382 | |||
383 | impl MyStruct { | ||
384 | #[cfg(feature = "foo")] | ||
385 | pub(crate) fn new(my_val: usize, bar: bool) -> Self { | ||
386 | Self { my_val, bar } | ||
387 | } | ||
388 | |||
389 | #[cfg(not(feature = "foo"))] | ||
390 | pub(crate) fn new(my_val: usize, _bar: bool) -> Self { | ||
391 | Self { my_val } | ||
392 | } | ||
393 | } | ||
394 | "#, | ||
395 | ) | ||
396 | .diagnostics() | ||
397 | .0; | ||
398 | |||
399 | assert_snapshot!(diagnostics, @r###""###); | ||
400 | } | ||
401 | |||
402 | #[test] | ||
403 | fn no_such_field_enum_with_feature_flag_diagnostics() { | ||
404 | let diagnostics = TestDB::with_files( | ||
405 | r#" | ||
406 | //- /lib.rs crate:foo cfg:feature=foo | ||
407 | enum Foo { | ||
408 | #[cfg(not(feature = "foo"))] | ||
409 | Buz, | ||
410 | #[cfg(feature = "foo")] | ||
411 | Bar, | ||
412 | Baz | ||
413 | } | ||
414 | |||
415 | fn test_fn(f: Foo) { | ||
416 | match f { | ||
417 | Foo::Bar => {}, | ||
418 | Foo::Baz => {}, | ||
419 | } | ||
420 | } | ||
421 | "#, | ||
422 | ) | ||
423 | .diagnostics() | ||
424 | .0; | ||
425 | |||
426 | assert_snapshot!(diagnostics, @r###""###); | ||
427 | } | ||
428 | |||
429 | #[test] | ||
430 | fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() { | ||
431 | let diagnostics = TestDB::with_files( | ||
432 | r#" | ||
433 | //- /lib.rs crate:foo cfg:feature=foo | ||
434 | struct S { | ||
435 | #[cfg(feature = "foo")] | ||
436 | foo: u32, | ||
437 | #[cfg(not(feature = "foo"))] | ||
438 | bar: u32, | ||
439 | } | ||
440 | |||
441 | impl S { | ||
442 | #[cfg(feature = "foo")] | ||
443 | fn new(foo: u32) -> Self { | ||
444 | Self { foo } | ||
445 | } | ||
446 | #[cfg(not(feature = "foo"))] | ||
447 | fn new(bar: u32) -> Self { | ||
448 | Self { bar } | ||
449 | } | ||
450 | } | ||
451 | "#, | ||
452 | ) | ||
453 | .diagnostics() | ||
454 | .0; | ||
455 | |||
456 | assert_snapshot!(diagnostics, @r###""###); | ||
457 | } | ||
458 | |||
459 | #[test] | ||
460 | fn no_such_field_with_feature_flag_diagnostics_on_block_expr() { | ||
461 | let diagnostics = TestDB::with_files( | ||
462 | r#" | ||
463 | //- /lib.rs crate:foo cfg:feature=foo | ||
464 | struct S { | ||
465 | #[cfg(feature = "foo")] | ||
466 | foo: u32, | ||
467 | #[cfg(not(feature = "foo"))] | ||
468 | bar: u32, | ||
469 | } | ||
470 | |||
471 | impl S { | ||
472 | fn new(bar: u32) -> Self { | ||
473 | #[cfg(feature = "foo")] | ||
474 | { | ||
475 | Self { foo: bar } | ||
476 | } | ||
477 | #[cfg(not(feature = "foo"))] | ||
478 | { | ||
479 | Self { bar } | ||
480 | } | ||
481 | } | ||
482 | } | ||
483 | "#, | ||
484 | ) | ||
485 | .diagnostics() | ||
486 | .0; | ||
487 | |||
488 | assert_snapshot!(diagnostics, @r###""###); | ||
489 | } | ||
490 | |||
491 | #[test] | ||
492 | fn no_such_field_with_feature_flag_diagnostics_on_struct_fields() { | ||
493 | let diagnostics = TestDB::with_files( | ||
494 | r#" | ||
495 | //- /lib.rs crate:foo cfg:feature=foo | ||
496 | struct S { | ||
497 | #[cfg(feature = "foo")] | ||
498 | foo: u32, | ||
499 | #[cfg(not(feature = "foo"))] | ||
500 | bar: u32, | ||
501 | } | ||
502 | |||
503 | impl S { | ||
504 | fn new(val: u32) -> Self { | ||
505 | Self { | ||
506 | #[cfg(feature = "foo")] | ||
507 | foo: val, | ||
508 | #[cfg(not(feature = "foo"))] | ||
509 | bar: val, | ||
510 | } | ||
511 | } | ||
512 | } | ||
513 | "#, | ||
514 | ) | ||
515 | .diagnostics() | ||
516 | .0; | ||
517 | |||
518 | assert_snapshot!(diagnostics, @r###""###); | ||
519 | } | ||
520 | |||
521 | #[test] | ||
522 | fn no_such_field_with_type_macro() { | ||
523 | let diagnostics = TestDB::with_files( | ||
524 | r" | ||
525 | macro_rules! Type { | ||
526 | () => { u32 }; | ||
527 | } | ||
528 | |||
529 | struct Foo { | ||
530 | bar: Type![], | ||
531 | } | ||
532 | impl Foo { | ||
533 | fn new() -> Self { | ||
534 | Foo { bar: 0 } | ||
535 | } | ||
536 | } | ||
537 | ", | ||
538 | ) | ||
539 | .diagnostics() | ||
540 | .0; | ||
541 | |||
542 | assert_snapshot!(diagnostics, @r###""###); | ||
543 | } | ||
544 | |||
545 | #[test] | ||
546 | fn missing_record_pat_field_diagnostic() { | ||
547 | let diagnostics = TestDB::with_files( | ||
548 | r" | ||
549 | //- /lib.rs | ||
550 | struct S { foo: i32, bar: () } | ||
551 | fn baz(s: S) { | ||
552 | let S { foo: _ } = s; | ||
553 | } | ||
554 | ", | ||
555 | ) | ||
556 | .diagnostics() | ||
557 | .0; | ||
558 | |||
559 | assert_snapshot!(diagnostics, @r###" | ||
560 | "{ foo: _ }": Missing structure fields: | ||
561 | - bar | ||
562 | "### | ||
563 | ); | ||
564 | } | ||
565 | |||
566 | #[test] | ||
567 | fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() { | ||
568 | let diagnostics = TestDB::with_files( | ||
569 | r" | ||
570 | //- /lib.rs | ||
571 | struct S { foo: i32, bar: () } | ||
572 | fn baz(s: S) -> i32 { | ||
573 | match s { | ||
574 | S { foo, .. } => foo, | ||
575 | } | ||
576 | } | ||
577 | ", | ||
578 | ) | ||
579 | .diagnostics() | ||
580 | .0; | ||
581 | |||
582 | assert_snapshot!(diagnostics, @""); | ||
583 | } | ||
584 | |||
585 | #[test] | ||
586 | fn missing_unsafe_diagnostic_with_raw_ptr() { | ||
587 | let diagnostics = TestDB::with_files( | ||
588 | r" | ||
589 | //- /lib.rs | ||
590 | fn missing_unsafe() { | ||
591 | let x = &5 as *const usize; | ||
592 | let y = *x; | ||
593 | } | ||
594 | ", | ||
595 | ) | ||
596 | .diagnostics() | ||
597 | .0; | ||
598 | |||
599 | assert_snapshot!(diagnostics, @r#""*x": This operation is unsafe and requires an unsafe function or block"#); | ||
600 | } | ||
601 | |||
602 | #[test] | ||
603 | fn missing_unsafe_diagnostic_with_unsafe_call() { | ||
604 | let diagnostics = TestDB::with_files( | ||
605 | r" | ||
606 | //- /lib.rs | ||
607 | unsafe fn unsafe_fn() { | ||
608 | let x = &5 as *const usize; | ||
609 | let y = *x; | ||
610 | } | ||
611 | |||
612 | fn missing_unsafe() { | ||
613 | unsafe_fn(); | ||
614 | } | ||
615 | ", | ||
616 | ) | ||
617 | .diagnostics() | ||
618 | .0; | ||
619 | |||
620 | assert_snapshot!(diagnostics, @r#""unsafe_fn()": This operation is unsafe and requires an unsafe function or block"#); | ||
621 | } | ||
622 | |||
623 | #[test] | ||
624 | fn missing_unsafe_diagnostic_with_unsafe_method_call() { | ||
625 | let diagnostics = TestDB::with_files( | ||
626 | r" | ||
627 | struct HasUnsafe; | ||
628 | |||
629 | impl HasUnsafe { | ||
630 | unsafe fn unsafe_fn(&self) { | ||
631 | let x = &5 as *const usize; | ||
632 | let y = *x; | ||
633 | } | ||
634 | } | ||
635 | |||
636 | fn missing_unsafe() { | ||
637 | HasUnsafe.unsafe_fn(); | ||
638 | } | ||
639 | |||
640 | ", | ||
641 | ) | ||
642 | .diagnostics() | ||
643 | .0; | ||
644 | |||
645 | assert_snapshot!(diagnostics, @r#""HasUnsafe.unsafe_fn()": This operation is unsafe and requires an unsafe function or block"#); | ||
646 | } | ||
647 | |||
648 | #[test] | ||
649 | fn no_missing_unsafe_diagnostic_with_raw_ptr_in_unsafe_block() { | ||
650 | let diagnostics = TestDB::with_files( | ||
651 | r" | ||
652 | fn nothing_to_see_move_along() { | ||
653 | let x = &5 as *const usize; | ||
654 | unsafe { | ||
655 | let y = *x; | ||
656 | } | ||
657 | } | ||
658 | ", | ||
659 | ) | ||
660 | .diagnostics() | ||
661 | .0; | ||
662 | |||
663 | assert_snapshot!(diagnostics, @""); | ||
664 | } | ||
665 | |||
666 | #[test] | ||
667 | fn missing_unsafe_diagnostic_with_raw_ptr_outside_unsafe_block() { | ||
668 | let diagnostics = TestDB::with_files( | ||
669 | r" | ||
670 | fn nothing_to_see_move_along() { | ||
671 | let x = &5 as *const usize; | ||
672 | unsafe { | ||
673 | let y = *x; | ||
674 | } | ||
675 | let z = *x; | ||
676 | } | ||
677 | ", | ||
678 | ) | ||
679 | .diagnostics() | ||
680 | .0; | ||
681 | |||
682 | assert_snapshot!(diagnostics, @r#""*x": This operation is unsafe and requires an unsafe function or block"#); | ||
683 | } | ||
684 | |||
685 | #[test] | ||
686 | fn no_missing_unsafe_diagnostic_with_unsafe_call_in_unsafe_block() { | ||
687 | let diagnostics = TestDB::with_files( | ||
688 | r" | ||
689 | unsafe fn unsafe_fn() { | ||
690 | let x = &5 as *const usize; | ||
691 | let y = *x; | ||
692 | } | ||
693 | |||
694 | fn nothing_to_see_move_along() { | ||
695 | unsafe { | ||
696 | unsafe_fn(); | ||
697 | } | ||
698 | } | ||
699 | ", | ||
700 | ) | ||
701 | .diagnostics() | ||
702 | .0; | ||
703 | |||
704 | assert_snapshot!(diagnostics, @""); | ||
705 | } | ||
706 | |||
707 | #[test] | ||
708 | fn no_missing_unsafe_diagnostic_with_unsafe_method_call_in_unsafe_block() { | ||
709 | let diagnostics = TestDB::with_files( | ||
710 | r" | ||
711 | struct HasUnsafe; | ||
712 | |||
713 | impl HasUnsafe { | ||
714 | unsafe fn unsafe_fn() { | ||
715 | let x = &5 as *const usize; | ||
716 | let y = *x; | ||
717 | } | ||
718 | } | ||
719 | |||
720 | fn nothing_to_see_move_along() { | ||
721 | unsafe { | ||
722 | HasUnsafe.unsafe_fn(); | ||
723 | } | ||
724 | } | ||
725 | |||
726 | ", | ||
727 | ) | ||
728 | .diagnostics() | ||
729 | .0; | ||
730 | |||
731 | assert_snapshot!(diagnostics, @""); | ||
732 | } | ||
733 | |||
734 | #[test] | ||
735 | fn break_outside_of_loop() { | ||
736 | let diagnostics = TestDB::with_files( | ||
737 | r" | ||
738 | //- /lib.rs | ||
739 | fn foo() { | ||
740 | break; | ||
741 | } | ||
742 | ", | ||
743 | ) | ||
744 | .diagnostics() | ||
745 | .0; | ||
746 | |||
747 | assert_snapshot!(diagnostics, @r###""break": break outside of loop | ||
748 | "### | ||
749 | ); | ||
750 | } | ||
diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs index 86e22e459..60cc9a9f5 100644 --- a/crates/ra_hir_ty/src/traits/builtin.rs +++ b/crates/ra_hir_ty/src/traits/builtin.rs | |||
@@ -121,7 +121,7 @@ fn closure_fn_trait_impl_datum( | |||
121 | .build(), | 121 | .build(), |
122 | ); | 122 | ); |
123 | let sig_ty = Ty::apply( | 123 | let sig_ty = Ty::apply( |
124 | TypeCtor::FnPtr { num_args }, | 124 | TypeCtor::FnPtr { num_args, is_varargs: false }, |
125 | Substs::builder(num_args as usize + 1) | 125 | Substs::builder(num_args as usize + 1) |
126 | .fill_with_bound_vars(DebruijnIndex::INNERMOST, 0) | 126 | .fill_with_bound_vars(DebruijnIndex::INNERMOST, 0) |
127 | .build(), | 127 | .build(), |
diff --git a/crates/ra_hir_ty/src/traits/chalk/mapping.rs b/crates/ra_hir_ty/src/traits/chalk/mapping.rs index 06453ef82..3ebb55f77 100644 --- a/crates/ra_hir_ty/src/traits/chalk/mapping.rs +++ b/crates/ra_hir_ty/src/traits/chalk/mapping.rs | |||
@@ -30,7 +30,8 @@ impl ToChalk for Ty { | |||
30 | Ty::Apply(apply_ty) => match apply_ty.ctor { | 30 | Ty::Apply(apply_ty) => match apply_ty.ctor { |
31 | TypeCtor::Ref(m) => ref_to_chalk(db, m, apply_ty.parameters), | 31 | TypeCtor::Ref(m) => ref_to_chalk(db, m, apply_ty.parameters), |
32 | TypeCtor::Array => array_to_chalk(db, apply_ty.parameters), | 32 | TypeCtor::Array => array_to_chalk(db, apply_ty.parameters), |
33 | TypeCtor::FnPtr { num_args: _ } => { | 33 | TypeCtor::FnPtr { num_args: _, is_varargs: _ } => { |
34 | // FIXME: handle is_varargs | ||
34 | let substitution = apply_ty.parameters.to_chalk(db).shifted_in(&Interner); | 35 | let substitution = apply_ty.parameters.to_chalk(db).shifted_in(&Interner); |
35 | chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: 0, substitution }) | 36 | chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: 0, substitution }) |
36 | .intern(&Interner) | 37 | .intern(&Interner) |
@@ -124,7 +125,10 @@ impl ToChalk for Ty { | |||
124 | substitution.shifted_out(&Interner).expect("fn ptr should have no binders"), | 125 | substitution.shifted_out(&Interner).expect("fn ptr should have no binders"), |
125 | ); | 126 | ); |
126 | Ty::Apply(ApplicationTy { | 127 | Ty::Apply(ApplicationTy { |
127 | ctor: TypeCtor::FnPtr { num_args: (parameters.len() - 1) as u16 }, | 128 | ctor: TypeCtor::FnPtr { |
129 | num_args: (parameters.len() - 1) as u16, | ||
130 | is_varargs: false, | ||
131 | }, | ||
128 | parameters, | 132 | parameters, |
129 | }) | 133 | }) |
130 | } | 134 | } |
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml index df2fad520..6f8107491 100644 --- a/crates/ra_ide/Cargo.toml +++ b/crates/ra_ide/Cargo.toml | |||
@@ -3,6 +3,7 @@ edition = "2018" | |||
3 | name = "ra_ide" | 3 | name = "ra_ide" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs index a6bdf1c9d..e1d6efb2a 100644 --- a/crates/ra_ide/src/call_info.rs +++ b/crates/ra_ide/src/call_info.rs | |||
@@ -93,7 +93,7 @@ fn call_info_for_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Op | |||
93 | arg_list | 93 | arg_list |
94 | .args() | 94 | .args() |
95 | .take_while(|arg| { | 95 | .take_while(|arg| { |
96 | arg.syntax().text_range().end() < token.text_range().start() | 96 | arg.syntax().text_range().end() <= token.text_range().start() |
97 | }) | 97 | }) |
98 | .count(), | 98 | .count(), |
99 | ); | 99 | ); |
@@ -213,169 +213,187 @@ impl CallInfo { | |||
213 | 213 | ||
214 | #[cfg(test)] | 214 | #[cfg(test)] |
215 | mod tests { | 215 | mod tests { |
216 | use expect::{expect, Expect}; | ||
216 | use test_utils::mark; | 217 | use test_utils::mark; |
217 | 218 | ||
218 | use crate::mock_analysis::analysis_and_position; | 219 | use crate::mock_analysis::analysis_and_position; |
219 | 220 | ||
220 | use super::*; | 221 | fn check(ra_fixture: &str, expect: Expect) { |
221 | 222 | let (analysis, position) = analysis_and_position(ra_fixture); | |
222 | // These are only used when testing | 223 | let call_info = analysis.call_info(position).unwrap(); |
223 | impl CallInfo { | 224 | let actual = match call_info { |
224 | fn doc(&self) -> Option<hir::Documentation> { | 225 | Some(call_info) => { |
225 | self.signature.doc.clone() | 226 | let docs = match &call_info.signature.doc { |
226 | } | 227 | None => "".to_string(), |
227 | 228 | Some(docs) => format!("{}\n------\n", docs.as_str()), | |
228 | fn label(&self) -> String { | 229 | }; |
229 | self.signature.to_string() | 230 | let params = call_info |
230 | } | 231 | .parameters() |
231 | } | 232 | .iter() |
232 | 233 | .enumerate() | |
233 | fn call_info_helper(text: &str) -> Option<CallInfo> { | 234 | .map(|(i, param)| { |
234 | let (analysis, position) = analysis_and_position(text); | 235 | if Some(i) == call_info.active_parameter { |
235 | analysis.call_info(position).unwrap() | 236 | format!("<{}>", param) |
236 | } | 237 | } else { |
237 | 238 | param.clone() | |
238 | fn call_info(text: &str) -> CallInfo { | 239 | } |
239 | let info = call_info_helper(text); | 240 | }) |
240 | assert!(info.is_some()); | 241 | .collect::<Vec<_>>() |
241 | info.unwrap() | 242 | .join(", "); |
242 | } | 243 | format!("{}{}\n({})\n", docs, call_info.signature, params) |
243 | 244 | } | |
244 | fn no_call_info(text: &str) { | 245 | None => String::new(), |
245 | let info = call_info_helper(text); | 246 | }; |
246 | assert!(info.is_none()); | 247 | expect.assert_eq(&actual); |
247 | } | 248 | } |
248 | 249 | ||
249 | #[test] | 250 | #[test] |
250 | fn test_fn_signature_two_args_firstx() { | 251 | fn test_fn_signature_two_args() { |
251 | let info = call_info( | 252 | check( |
252 | r#"fn foo(x: u32, y: u32) -> u32 {x + y} | 253 | r#" |
253 | fn bar() { foo(<|>3, ); }"#, | 254 | fn foo(x: u32, y: u32) -> u32 {x + y} |
255 | fn bar() { foo(<|>3, ); } | ||
256 | "#, | ||
257 | expect![[r#" | ||
258 | fn foo(x: u32, y: u32) -> u32 | ||
259 | (<x: u32>, y: u32) | ||
260 | "#]], | ||
254 | ); | 261 | ); |
255 | 262 | check( | |
256 | assert_eq!(info.parameters(), ["x: u32", "y: u32"]); | 263 | r#" |
257 | assert_eq!(info.active_parameter, Some(0)); | 264 | fn foo(x: u32, y: u32) -> u32 {x + y} |
258 | } | 265 | fn bar() { foo(3<|>, ); } |
259 | 266 | "#, | |
260 | #[test] | 267 | expect![[r#" |
261 | fn test_fn_signature_two_args_second() { | 268 | fn foo(x: u32, y: u32) -> u32 |
262 | let info = call_info( | 269 | (<x: u32>, y: u32) |
263 | r#"fn foo(x: u32, y: u32) -> u32 {x + y} | 270 | "#]], |
264 | fn bar() { foo(3, <|>); }"#, | 271 | ); |
272 | check( | ||
273 | r#" | ||
274 | fn foo(x: u32, y: u32) -> u32 {x + y} | ||
275 | fn bar() { foo(3,<|> ); } | ||
276 | "#, | ||
277 | expect![[r#" | ||
278 | fn foo(x: u32, y: u32) -> u32 | ||
279 | (x: u32, <y: u32>) | ||
280 | "#]], | ||
281 | ); | ||
282 | check( | ||
283 | r#" | ||
284 | fn foo(x: u32, y: u32) -> u32 {x + y} | ||
285 | fn bar() { foo(3, <|>); } | ||
286 | "#, | ||
287 | expect![[r#" | ||
288 | fn foo(x: u32, y: u32) -> u32 | ||
289 | (x: u32, <y: u32>) | ||
290 | "#]], | ||
265 | ); | 291 | ); |
266 | |||
267 | assert_eq!(info.parameters(), ["x: u32", "y: u32"]); | ||
268 | assert_eq!(info.active_parameter, Some(1)); | ||
269 | } | 292 | } |
270 | 293 | ||
271 | #[test] | 294 | #[test] |
272 | fn test_fn_signature_two_args_empty() { | 295 | fn test_fn_signature_two_args_empty() { |
273 | let info = call_info( | 296 | check( |
274 | r#"fn foo(x: u32, y: u32) -> u32 {x + y} | 297 | r#" |
275 | fn bar() { foo(<|>); }"#, | 298 | fn foo(x: u32, y: u32) -> u32 {x + y} |
299 | fn bar() { foo(<|>); } | ||
300 | "#, | ||
301 | expect![[r#" | ||
302 | fn foo(x: u32, y: u32) -> u32 | ||
303 | (<x: u32>, y: u32) | ||
304 | "#]], | ||
276 | ); | 305 | ); |
277 | |||
278 | assert_eq!(info.parameters(), ["x: u32", "y: u32"]); | ||
279 | assert_eq!(info.active_parameter, Some(0)); | ||
280 | } | 306 | } |
281 | 307 | ||
282 | #[test] | 308 | #[test] |
283 | fn test_fn_signature_two_args_first_generics() { | 309 | fn test_fn_signature_two_args_first_generics() { |
284 | let info = call_info( | 310 | check( |
285 | r#"fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 where T: Copy + Display, U: Debug {x + y} | ||
286 | fn bar() { foo(<|>3, ); }"#, | ||
287 | ); | ||
288 | |||
289 | assert_eq!(info.parameters(), ["x: T", "y: U"]); | ||
290 | assert_eq!( | ||
291 | info.label(), | ||
292 | r#" | 311 | r#" |
293 | fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 | 312 | fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 |
294 | where T: Copy + Display, | 313 | where T: Copy + Display, U: Debug |
295 | U: Debug | 314 | { x + y } |
296 | "# | 315 | |
297 | .trim() | 316 | fn bar() { foo(<|>3, ); } |
317 | "#, | ||
318 | expect![[r#" | ||
319 | fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 | ||
320 | where T: Copy + Display, | ||
321 | U: Debug | ||
322 | (<x: T>, y: U) | ||
323 | "#]], | ||
298 | ); | 324 | ); |
299 | assert_eq!(info.active_parameter, Some(0)); | ||
300 | } | 325 | } |
301 | 326 | ||
302 | #[test] | 327 | #[test] |
303 | fn test_fn_signature_no_params() { | 328 | fn test_fn_signature_no_params() { |
304 | let info = call_info( | 329 | check( |
305 | r#"fn foo<T>() -> T where T: Copy + Display {} | ||
306 | fn bar() { foo(<|>); }"#, | ||
307 | ); | ||
308 | |||
309 | assert!(info.parameters().is_empty()); | ||
310 | assert_eq!( | ||
311 | info.label(), | ||
312 | r#" | 330 | r#" |
313 | fn foo<T>() -> T | 331 | fn foo<T>() -> T where T: Copy + Display {} |
314 | where T: Copy + Display | 332 | fn bar() { foo(<|>); } |
315 | "# | 333 | "#, |
316 | .trim() | 334 | expect![[r#" |
335 | fn foo<T>() -> T | ||
336 | where T: Copy + Display | ||
337 | () | ||
338 | "#]], | ||
317 | ); | 339 | ); |
318 | assert!(info.active_parameter.is_none()); | ||
319 | } | 340 | } |
320 | 341 | ||
321 | #[test] | 342 | #[test] |
322 | fn test_fn_signature_for_impl() { | 343 | fn test_fn_signature_for_impl() { |
323 | let info = call_info( | 344 | check( |
324 | r#"struct F; impl F { pub fn new() { F{}} } | 345 | r#" |
325 | fn bar() {let _ : F = F::new(<|>);}"#, | 346 | struct F; impl F { pub fn new() { F{}} } |
347 | fn bar() {let _ : F = F::new(<|>);} | ||
348 | "#, | ||
349 | expect![[r#" | ||
350 | pub fn new() | ||
351 | () | ||
352 | "#]], | ||
326 | ); | 353 | ); |
327 | |||
328 | assert!(info.parameters().is_empty()); | ||
329 | assert_eq!(info.active_parameter, None); | ||
330 | } | 354 | } |
331 | 355 | ||
332 | #[test] | 356 | #[test] |
333 | fn test_fn_signature_for_method_self() { | 357 | fn test_fn_signature_for_method_self() { |
334 | let info = call_info( | 358 | check( |
335 | r#"struct F; | 359 | r#" |
336 | impl F { | 360 | struct S; |
337 | pub fn new() -> F{ | 361 | impl S { pub fn do_it(&self) {} } |
338 | F{} | ||
339 | } | ||
340 | |||
341 | pub fn do_it(&self) {} | ||
342 | } | ||
343 | 362 | ||
344 | fn bar() { | 363 | fn bar() { |
345 | let f : F = F::new(); | 364 | let s: S = S; |
346 | f.do_it(<|>); | 365 | s.do_it(<|>); |
347 | }"#, | 366 | } |
367 | "#, | ||
368 | expect![[r#" | ||
369 | pub fn do_it(&self) | ||
370 | (&self) | ||
371 | "#]], | ||
348 | ); | 372 | ); |
349 | |||
350 | assert_eq!(info.parameters(), ["&self"]); | ||
351 | assert_eq!(info.active_parameter, None); | ||
352 | } | 373 | } |
353 | 374 | ||
354 | #[test] | 375 | #[test] |
355 | fn test_fn_signature_for_method_with_arg() { | 376 | fn test_fn_signature_for_method_with_arg() { |
356 | let info = call_info( | 377 | check( |
357 | r#"struct F; | 378 | r#" |
358 | impl F { | 379 | struct S; |
359 | pub fn new() -> F{ | 380 | impl S { pub fn do_it(&self, x: i32) {} } |
360 | F{} | ||
361 | } | ||
362 | |||
363 | pub fn do_it(&self, x: i32) {} | ||
364 | } | ||
365 | 381 | ||
366 | fn bar() { | 382 | fn bar() { |
367 | let f : F = F::new(); | 383 | let s: S = S; |
368 | f.do_it(<|>); | 384 | s.do_it(<|>); |
369 | }"#, | 385 | } |
386 | "#, | ||
387 | expect![[r#" | ||
388 | pub fn do_it(&self, x: i32) | ||
389 | (&self, <x: i32>) | ||
390 | "#]], | ||
370 | ); | 391 | ); |
371 | |||
372 | assert_eq!(info.parameters(), ["&self", "x: i32"]); | ||
373 | assert_eq!(info.active_parameter, Some(1)); | ||
374 | } | 392 | } |
375 | 393 | ||
376 | #[test] | 394 | #[test] |
377 | fn test_fn_signature_with_docs_simple() { | 395 | fn test_fn_signature_with_docs_simple() { |
378 | let info = call_info( | 396 | check( |
379 | r#" | 397 | r#" |
380 | /// test | 398 | /// test |
381 | // non-doc-comment | 399 | // non-doc-comment |
@@ -387,17 +405,18 @@ fn bar() { | |||
387 | let _ = foo(<|>); | 405 | let _ = foo(<|>); |
388 | } | 406 | } |
389 | "#, | 407 | "#, |
408 | expect![[r#" | ||
409 | test | ||
410 | ------ | ||
411 | fn foo(j: u32) -> u32 | ||
412 | (<j: u32>) | ||
413 | "#]], | ||
390 | ); | 414 | ); |
391 | |||
392 | assert_eq!(info.parameters(), ["j: u32"]); | ||
393 | assert_eq!(info.active_parameter, Some(0)); | ||
394 | assert_eq!(info.label(), "fn foo(j: u32) -> u32"); | ||
395 | assert_eq!(info.doc().map(|it| it.into()), Some("test".to_string())); | ||
396 | } | 415 | } |
397 | 416 | ||
398 | #[test] | 417 | #[test] |
399 | fn test_fn_signature_with_docs() { | 418 | fn test_fn_signature_with_docs() { |
400 | let info = call_info( | 419 | check( |
401 | r#" | 420 | r#" |
402 | /// Adds one to the number given. | 421 | /// Adds one to the number given. |
403 | /// | 422 | /// |
@@ -415,31 +434,26 @@ pub fn add_one(x: i32) -> i32 { | |||
415 | pub fn do() { | 434 | pub fn do() { |
416 | add_one(<|> | 435 | add_one(<|> |
417 | }"#, | 436 | }"#, |
418 | ); | 437 | expect![[r##" |
419 | 438 | Adds one to the number given. | |
420 | assert_eq!(info.parameters(), ["x: i32"]); | ||
421 | assert_eq!(info.active_parameter, Some(0)); | ||
422 | assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32"); | ||
423 | assert_eq!( | ||
424 | info.doc().map(|it| it.into()), | ||
425 | Some( | ||
426 | r#"Adds one to the number given. | ||
427 | 439 | ||
428 | # Examples | 440 | # Examples |
429 | 441 | ||
430 | ``` | 442 | ``` |
431 | let five = 5; | 443 | let five = 5; |
432 | 444 | ||
433 | assert_eq!(6, my_crate::add_one(5)); | 445 | assert_eq!(6, my_crate::add_one(5)); |
434 | ```"# | 446 | ``` |
435 | .to_string() | 447 | ------ |
436 | ) | 448 | pub fn add_one(x: i32) -> i32 |
449 | (<x: i32>) | ||
450 | "##]], | ||
437 | ); | 451 | ); |
438 | } | 452 | } |
439 | 453 | ||
440 | #[test] | 454 | #[test] |
441 | fn test_fn_signature_with_docs_impl() { | 455 | fn test_fn_signature_with_docs_impl() { |
442 | let info = call_info( | 456 | check( |
443 | r#" | 457 | r#" |
444 | struct addr; | 458 | struct addr; |
445 | impl addr { | 459 | impl addr { |
@@ -460,32 +474,28 @@ impl addr { | |||
460 | pub fn do_it() { | 474 | pub fn do_it() { |
461 | addr {}; | 475 | addr {}; |
462 | addr::add_one(<|>); | 476 | addr::add_one(<|>); |
463 | }"#, | 477 | } |
464 | ); | 478 | "#, |
465 | 479 | expect![[r##" | |
466 | assert_eq!(info.parameters(), ["x: i32"]); | 480 | Adds one to the number given. |
467 | assert_eq!(info.active_parameter, Some(0)); | ||
468 | assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32"); | ||
469 | assert_eq!( | ||
470 | info.doc().map(|it| it.into()), | ||
471 | Some( | ||
472 | r#"Adds one to the number given. | ||
473 | 481 | ||
474 | # Examples | 482 | # Examples |
475 | 483 | ||
476 | ``` | 484 | ``` |
477 | let five = 5; | 485 | let five = 5; |
478 | 486 | ||
479 | assert_eq!(6, my_crate::add_one(5)); | 487 | assert_eq!(6, my_crate::add_one(5)); |
480 | ```"# | 488 | ``` |
481 | .to_string() | 489 | ------ |
482 | ) | 490 | pub fn add_one(x: i32) -> i32 |
491 | (<x: i32>) | ||
492 | "##]], | ||
483 | ); | 493 | ); |
484 | } | 494 | } |
485 | 495 | ||
486 | #[test] | 496 | #[test] |
487 | fn test_fn_signature_with_docs_from_actix() { | 497 | fn test_fn_signature_with_docs_from_actix() { |
488 | let info = call_info( | 498 | check( |
489 | r#" | 499 | r#" |
490 | struct WriteHandler<E>; | 500 | struct WriteHandler<E>; |
491 | 501 | ||
@@ -509,101 +519,102 @@ impl<E> WriteHandler<E> { | |||
509 | pub fn foo(mut r: WriteHandler<()>) { | 519 | pub fn foo(mut r: WriteHandler<()>) { |
510 | r.finished(<|>); | 520 | r.finished(<|>); |
511 | } | 521 | } |
512 | |||
513 | "#, | 522 | "#, |
514 | ); | 523 | expect![[r#" |
515 | 524 | Method is called when writer finishes. | |
516 | assert_eq!(info.label(), "fn finished(&mut self, ctx: &mut Self::Context)".to_string()); | 525 | |
517 | assert_eq!(info.parameters(), ["&mut self", "ctx: &mut Self::Context"]); | 526 | By default this method stops actor's `Context`. |
518 | assert_eq!(info.active_parameter, Some(1)); | 527 | ------ |
519 | assert_eq!( | 528 | fn finished(&mut self, ctx: &mut Self::Context) |
520 | info.doc().map(|it| it.into()), | 529 | (&mut self, <ctx: &mut Self::Context>) |
521 | Some( | 530 | "#]], |
522 | r#"Method is called when writer finishes. | ||
523 | |||
524 | By default this method stops actor's `Context`."# | ||
525 | .to_string() | ||
526 | ) | ||
527 | ); | 531 | ); |
528 | } | 532 | } |
529 | 533 | ||
530 | #[test] | 534 | #[test] |
531 | fn call_info_bad_offset() { | 535 | fn call_info_bad_offset() { |
532 | mark::check!(call_info_bad_offset); | 536 | mark::check!(call_info_bad_offset); |
533 | let (analysis, position) = analysis_and_position( | 537 | check( |
534 | r#"fn foo(x: u32, y: u32) -> u32 {x + y} | 538 | r#" |
535 | fn bar() { foo <|> (3, ); }"#, | 539 | fn foo(x: u32, y: u32) -> u32 {x + y} |
540 | fn bar() { foo <|> (3, ); } | ||
541 | "#, | ||
542 | expect![[""]], | ||
536 | ); | 543 | ); |
537 | let call_info = analysis.call_info(position).unwrap(); | ||
538 | assert!(call_info.is_none()); | ||
539 | } | 544 | } |
540 | 545 | ||
541 | #[test] | 546 | #[test] |
542 | fn test_nested_method_in_lamba() { | 547 | fn test_nested_method_in_lambda() { |
543 | let info = call_info( | 548 | check( |
544 | r#"struct Foo; | 549 | r#" |
545 | 550 | struct Foo; | |
546 | impl Foo { | 551 | impl Foo { fn bar(&self, _: u32) { } } |
547 | fn bar(&self, _: u32) { } | ||
548 | } | ||
549 | 552 | ||
550 | fn bar(_: u32) { } | 553 | fn bar(_: u32) { } |
551 | 554 | ||
552 | fn main() { | 555 | fn main() { |
553 | let foo = Foo; | 556 | let foo = Foo; |
554 | std::thread::spawn(move || foo.bar(<|>)); | 557 | std::thread::spawn(move || foo.bar(<|>)); |
555 | }"#, | 558 | } |
559 | "#, | ||
560 | expect![[r#" | ||
561 | fn bar(&self, _: u32) | ||
562 | (&self, <_: u32>) | ||
563 | "#]], | ||
556 | ); | 564 | ); |
557 | |||
558 | assert_eq!(info.parameters(), ["&self", "_: u32"]); | ||
559 | assert_eq!(info.active_parameter, Some(1)); | ||
560 | assert_eq!(info.label(), "fn bar(&self, _: u32)"); | ||
561 | } | 565 | } |
562 | 566 | ||
563 | #[test] | 567 | #[test] |
564 | fn works_for_tuple_structs() { | 568 | fn works_for_tuple_structs() { |
565 | let info = call_info( | 569 | check( |
566 | r#" | 570 | r#" |
567 | /// A cool tuple struct | 571 | /// A cool tuple struct |
568 | struct TS(u32, i32); | 572 | struct TS(u32, i32); |
569 | fn main() { | 573 | fn main() { |
570 | let s = TS(0, <|>); | 574 | let s = TS(0, <|>); |
571 | }"#, | 575 | } |
576 | "#, | ||
577 | expect![[r#" | ||
578 | A cool tuple struct | ||
579 | ------ | ||
580 | struct TS(u32, i32) -> TS | ||
581 | (u32, <i32>) | ||
582 | "#]], | ||
572 | ); | 583 | ); |
573 | |||
574 | assert_eq!(info.label(), "struct TS(u32, i32) -> TS"); | ||
575 | assert_eq!(info.doc().map(|it| it.into()), Some("A cool tuple struct".to_string())); | ||
576 | assert_eq!(info.active_parameter, Some(1)); | ||
577 | } | 584 | } |
578 | 585 | ||
579 | #[test] | 586 | #[test] |
580 | fn generic_struct() { | 587 | fn generic_struct() { |
581 | let info = call_info( | 588 | check( |
582 | r#" | 589 | r#" |
583 | struct TS<T>(T); | 590 | struct TS<T>(T); |
584 | fn main() { | 591 | fn main() { |
585 | let s = TS(<|>); | 592 | let s = TS(<|>); |
586 | }"#, | 593 | } |
594 | "#, | ||
595 | expect![[r#" | ||
596 | struct TS<T>(T) -> TS | ||
597 | (<T>) | ||
598 | "#]], | ||
587 | ); | 599 | ); |
588 | |||
589 | assert_eq!(info.label(), "struct TS<T>(T) -> TS"); | ||
590 | assert_eq!(info.active_parameter, Some(0)); | ||
591 | } | 600 | } |
592 | 601 | ||
593 | #[test] | 602 | #[test] |
594 | fn cant_call_named_structs() { | 603 | fn cant_call_named_structs() { |
595 | no_call_info( | 604 | check( |
596 | r#" | 605 | r#" |
597 | struct TS { x: u32, y: i32 } | 606 | struct TS { x: u32, y: i32 } |
598 | fn main() { | 607 | fn main() { |
599 | let s = TS(<|>); | 608 | let s = TS(<|>); |
600 | }"#, | 609 | } |
610 | "#, | ||
611 | expect![[""]], | ||
601 | ); | 612 | ); |
602 | } | 613 | } |
603 | 614 | ||
604 | #[test] | 615 | #[test] |
605 | fn works_for_enum_variants() { | 616 | fn works_for_enum_variants() { |
606 | let info = call_info( | 617 | check( |
607 | r#" | 618 | r#" |
608 | enum E { | 619 | enum E { |
609 | /// A Variant | 620 | /// A Variant |
@@ -617,17 +628,19 @@ enum E { | |||
617 | fn main() { | 628 | fn main() { |
618 | let a = E::A(<|>); | 629 | let a = E::A(<|>); |
619 | } | 630 | } |
620 | "#, | 631 | "#, |
632 | expect![[r#" | ||
633 | A Variant | ||
634 | ------ | ||
635 | E::A(0: i32) | ||
636 | (<0: i32>) | ||
637 | "#]], | ||
621 | ); | 638 | ); |
622 | |||
623 | assert_eq!(info.label(), "E::A(0: i32)"); | ||
624 | assert_eq!(info.doc().map(|it| it.into()), Some("A Variant".to_string())); | ||
625 | assert_eq!(info.active_parameter, Some(0)); | ||
626 | } | 639 | } |
627 | 640 | ||
628 | #[test] | 641 | #[test] |
629 | fn cant_call_enum_records() { | 642 | fn cant_call_enum_records() { |
630 | no_call_info( | 643 | check( |
631 | r#" | 644 | r#" |
632 | enum E { | 645 | enum E { |
633 | /// A Variant | 646 | /// A Variant |
@@ -641,13 +654,14 @@ enum E { | |||
641 | fn main() { | 654 | fn main() { |
642 | let a = E::C(<|>); | 655 | let a = E::C(<|>); |
643 | } | 656 | } |
644 | "#, | 657 | "#, |
658 | expect![[""]], | ||
645 | ); | 659 | ); |
646 | } | 660 | } |
647 | 661 | ||
648 | #[test] | 662 | #[test] |
649 | fn fn_signature_for_macro() { | 663 | fn fn_signature_for_macro() { |
650 | let info = call_info( | 664 | check( |
651 | r#" | 665 | r#" |
652 | /// empty macro | 666 | /// empty macro |
653 | macro_rules! foo { | 667 | macro_rules! foo { |
@@ -657,31 +671,30 @@ macro_rules! foo { | |||
657 | fn f() { | 671 | fn f() { |
658 | foo!(<|>); | 672 | foo!(<|>); |
659 | } | 673 | } |
660 | "#, | 674 | "#, |
675 | expect![[r#" | ||
676 | empty macro | ||
677 | ------ | ||
678 | foo!() | ||
679 | () | ||
680 | "#]], | ||
661 | ); | 681 | ); |
662 | |||
663 | assert_eq!(info.label(), "foo!()"); | ||
664 | assert_eq!(info.doc().map(|it| it.into()), Some("empty macro".to_string())); | ||
665 | } | 682 | } |
666 | 683 | ||
667 | #[test] | 684 | #[test] |
668 | fn fn_signature_for_call_in_macro() { | 685 | fn fn_signature_for_call_in_macro() { |
669 | let info = call_info( | 686 | check( |
670 | r#" | 687 | r#" |
671 | macro_rules! id { | 688 | macro_rules! id { ($($tt:tt)*) => { $($tt)* } } |
672 | ($($tt:tt)*) => { $($tt)* } | 689 | fn foo() { } |
673 | } | 690 | id! { |
674 | fn foo() { | 691 | fn bar() { foo(<|>); } |
675 | 692 | } | |
676 | } | 693 | "#, |
677 | id! { | 694 | expect![[r#" |
678 | fn bar() { | 695 | fn foo() |
679 | foo(<|>); | 696 | () |
680 | } | 697 | "#]], |
681 | } | ||
682 | "#, | ||
683 | ); | 698 | ); |
684 | |||
685 | assert_eq!(info.label(), "fn foo()"); | ||
686 | } | 699 | } |
687 | } | 700 | } |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 3d93f7067..9e82d6854 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -63,6 +63,8 @@ pub(crate) struct CompletionContext<'a> { | |||
63 | pub(super) dot_receiver_is_ambiguous_float_literal: bool, | 63 | pub(super) dot_receiver_is_ambiguous_float_literal: bool, |
64 | /// If this is a call (method or function) in particular, i.e. the () are already there. | 64 | /// If this is a call (method or function) in particular, i.e. the () are already there. |
65 | pub(super) is_call: bool, | 65 | pub(super) is_call: bool, |
66 | /// Like `is_call`, but for tuple patterns. | ||
67 | pub(super) is_pattern_call: bool, | ||
66 | /// If this is a macro call, i.e. the () are already there. | 68 | /// If this is a macro call, i.e. the () are already there. |
67 | pub(super) is_macro_call: bool, | 69 | pub(super) is_macro_call: bool, |
68 | pub(super) is_path_type: bool, | 70 | pub(super) is_path_type: bool, |
@@ -136,6 +138,7 @@ impl<'a> CompletionContext<'a> { | |||
136 | is_new_item: false, | 138 | is_new_item: false, |
137 | dot_receiver: None, | 139 | dot_receiver: None, |
138 | is_call: false, | 140 | is_call: false, |
141 | is_pattern_call: false, | ||
139 | is_macro_call: false, | 142 | is_macro_call: false, |
140 | is_path_type: false, | 143 | is_path_type: false, |
141 | has_type_args: false, | 144 | has_type_args: false, |
@@ -370,6 +373,8 @@ impl<'a> CompletionContext<'a> { | |||
370 | .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) | 373 | .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) |
371 | .is_some(); | 374 | .is_some(); |
372 | self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); | 375 | self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); |
376 | self.is_pattern_call = | ||
377 | path.syntax().parent().and_then(ast::TupleStructPat::cast).is_some(); | ||
373 | 378 | ||
374 | self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); | 379 | self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); |
375 | self.has_type_args = segment.type_arg_list().is_some(); | 380 | self.has_type_args = segment.type_arg_list().is_some(); |
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 48afee5fb..64349dcb8 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs | |||
@@ -315,6 +315,7 @@ impl Completions { | |||
315 | } | 315 | } |
316 | 316 | ||
317 | if variant_kind == StructKind::Tuple { | 317 | if variant_kind == StructKind::Tuple { |
318 | mark::hit!(inserts_parens_for_tuple_enums); | ||
318 | let params = Params::Anonymous(variant.fields(ctx.db).len()); | 319 | let params = Params::Anonymous(variant.fields(ctx.db).len()); |
319 | res = res.add_call_parens(ctx, qualified_name, params) | 320 | res = res.add_call_parens(ctx, qualified_name, params) |
320 | } | 321 | } |
@@ -383,10 +384,17 @@ impl Builder { | |||
383 | if !ctx.config.add_call_parenthesis { | 384 | if !ctx.config.add_call_parenthesis { |
384 | return self; | 385 | return self; |
385 | } | 386 | } |
386 | if ctx.use_item_syntax.is_some() || ctx.is_call { | 387 | if ctx.use_item_syntax.is_some() { |
387 | mark::hit!(no_parens_in_use_item); | 388 | mark::hit!(no_parens_in_use_item); |
388 | return self; | 389 | return self; |
389 | } | 390 | } |
391 | if ctx.is_pattern_call { | ||
392 | mark::hit!(dont_duplicate_pattern_parens); | ||
393 | return self; | ||
394 | } | ||
395 | if ctx.is_call { | ||
396 | return self; | ||
397 | } | ||
390 | 398 | ||
391 | // Don't add parentheses if the expected type is some function reference. | 399 | // Don't add parentheses if the expected type is some function reference. |
392 | if let Some(ty) = &ctx.expected_type { | 400 | if let Some(ty) = &ctx.expected_type { |
@@ -865,6 +873,7 @@ fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 } | |||
865 | 873 | ||
866 | #[test] | 874 | #[test] |
867 | fn inserts_parens_for_tuple_enums() { | 875 | fn inserts_parens_for_tuple_enums() { |
876 | mark::check!(inserts_parens_for_tuple_enums); | ||
868 | check_edit( | 877 | check_edit( |
869 | "Some", | 878 | "Some", |
870 | r#" | 879 | r#" |
@@ -906,6 +915,30 @@ fn main(value: Option<i32>) { | |||
906 | } | 915 | } |
907 | 916 | ||
908 | #[test] | 917 | #[test] |
918 | fn dont_duplicate_pattern_parens() { | ||
919 | mark::check!(dont_duplicate_pattern_parens); | ||
920 | check_edit( | ||
921 | "Var", | ||
922 | r#" | ||
923 | enum E { Var(i32) } | ||
924 | fn main() { | ||
925 | match E::Var(92) { | ||
926 | E::<|>(92) => (), | ||
927 | } | ||
928 | } | ||
929 | "#, | ||
930 | r#" | ||
931 | enum E { Var(i32) } | ||
932 | fn main() { | ||
933 | match E::Var(92) { | ||
934 | E::Var(92) => (), | ||
935 | } | ||
936 | } | ||
937 | "#, | ||
938 | ); | ||
939 | } | ||
940 | |||
941 | #[test] | ||
909 | fn no_call_parens_if_fn_ptr_needed() { | 942 | fn no_call_parens_if_fn_ptr_needed() { |
910 | mark::check!(no_call_parens_if_fn_ptr_needed); | 943 | mark::check!(no_call_parens_if_fn_ptr_needed); |
911 | check_edit( | 944 | check_edit( |
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index f575d738f..c30b20611 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs | |||
@@ -866,4 +866,22 @@ type Alias<T> = T<|>; | |||
866 | "#, | 866 | "#, |
867 | ) | 867 | ) |
868 | } | 868 | } |
869 | |||
870 | #[test] | ||
871 | fn goto_def_for_macro_container() { | ||
872 | check( | ||
873 | r#" | ||
874 | //- /lib.rs | ||
875 | foo::module<|>::mac!(); | ||
876 | |||
877 | //- /foo/lib.rs | ||
878 | pub mod module { | ||
879 | //^^^^^^ | ||
880 | #[macro_export] | ||
881 | macro_rules! _mac { () => { () } } | ||
882 | pub use crate::_mac as mac; | ||
883 | } | ||
884 | "#, | ||
885 | ); | ||
886 | } | ||
869 | } | 887 | } |
diff --git a/crates/ra_ide_db/Cargo.toml b/crates/ra_ide_db/Cargo.toml index c3921bd40..bcddad60c 100644 --- a/crates/ra_ide_db/Cargo.toml +++ b/crates/ra_ide_db/Cargo.toml | |||
@@ -3,6 +3,7 @@ edition = "2018" | |||
3 | name = "ra_ide_db" | 3 | name = "ra_ide_db" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs index 3ef5e74b6..bcaabca92 100644 --- a/crates/ra_ide_db/src/defs.rs +++ b/crates/ra_ide_db/src/defs.rs | |||
@@ -255,8 +255,14 @@ pub fn classify_name_ref( | |||
255 | } | 255 | } |
256 | 256 | ||
257 | if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { | 257 | if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { |
258 | if let Some(macro_def) = sema.resolve_macro_call(¯o_call) { | 258 | if let Some(path) = macro_call.path() { |
259 | return Some(NameRefClass::Definition(Definition::Macro(macro_def))); | 259 | if path.qualifier().is_none() { |
260 | // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment | ||
261 | // paths are handled below (allowing `log<|>::info!` to resolve to the log crate). | ||
262 | if let Some(macro_def) = sema.resolve_macro_call(¯o_call) { | ||
263 | return Some(NameRefClass::Definition(Definition::Macro(macro_def))); | ||
264 | } | ||
265 | } | ||
260 | } | 266 | } |
261 | } | 267 | } |
262 | 268 | ||
diff --git a/crates/ra_mbe/Cargo.toml b/crates/ra_mbe/Cargo.toml index 4dec24914..a26746a19 100644 --- a/crates/ra_mbe/Cargo.toml +++ b/crates/ra_mbe/Cargo.toml | |||
@@ -3,6 +3,7 @@ edition = "2018" | |||
3 | name = "ra_mbe" | 3 | name = "ra_mbe" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/ra_parser/Cargo.toml b/crates/ra_parser/Cargo.toml index 0da581fd5..72ec3e4d9 100644 --- a/crates/ra_parser/Cargo.toml +++ b/crates/ra_parser/Cargo.toml | |||
@@ -4,6 +4,7 @@ name = "ra_parser" | |||
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | publish = false | 6 | publish = false |
7 | license = "MIT OR Apache-2.0" | ||
7 | 8 | ||
8 | [lib] | 9 | [lib] |
9 | doctest = false | 10 | doctest = false |
diff --git a/crates/ra_proc_macro/Cargo.toml b/crates/ra_proc_macro/Cargo.toml index d009ceb82..c4b6e9e7b 100644 --- a/crates/ra_proc_macro/Cargo.toml +++ b/crates/ra_proc_macro/Cargo.toml | |||
@@ -4,6 +4,7 @@ name = "ra_proc_macro" | |||
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | publish = false | 6 | publish = false |
7 | license = "MIT OR Apache-2.0" | ||
7 | 8 | ||
8 | [lib] | 9 | [lib] |
9 | doctest = false | 10 | doctest = false |
diff --git a/crates/ra_proc_macro_srv/Cargo.toml b/crates/ra_proc_macro_srv/Cargo.toml index 582102945..12ce497f8 100644 --- a/crates/ra_proc_macro_srv/Cargo.toml +++ b/crates/ra_proc_macro_srv/Cargo.toml | |||
@@ -4,6 +4,7 @@ name = "ra_proc_macro_srv" | |||
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | publish = false | 6 | publish = false |
7 | license = "MIT OR Apache-2.0" | ||
7 | 8 | ||
8 | [lib] | 9 | [lib] |
9 | doctest = false | 10 | doctest = false |
diff --git a/crates/ra_prof/Cargo.toml b/crates/ra_prof/Cargo.toml index 3cd8481ea..b3d52985a 100644 --- a/crates/ra_prof/Cargo.toml +++ b/crates/ra_prof/Cargo.toml | |||
@@ -4,6 +4,7 @@ name = "ra_prof" | |||
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | publish = false | 6 | publish = false |
7 | license = "MIT OR Apache-2.0" | ||
7 | 8 | ||
8 | [lib] | 9 | [lib] |
9 | doctest = false | 10 | doctest = false |
diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml index b1b44dcf7..827eb7e28 100644 --- a/crates/ra_project_model/Cargo.toml +++ b/crates/ra_project_model/Cargo.toml | |||
@@ -3,6 +3,7 @@ edition = "2018" | |||
3 | name = "ra_project_model" | 3 | name = "ra_project_model" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 940c30c7f..abc7a646c 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs | |||
@@ -189,6 +189,21 @@ impl ast::RecordFieldList { | |||
189 | } | 189 | } |
190 | } | 190 | } |
191 | 191 | ||
192 | impl ast::TypeAliasDef { | ||
193 | #[must_use] | ||
194 | pub fn remove_bounds(&self) -> ast::TypeAliasDef { | ||
195 | let colon = match self.colon_token() { | ||
196 | Some(it) => it, | ||
197 | None => return self.clone(), | ||
198 | }; | ||
199 | let end = match self.type_bound_list() { | ||
200 | Some(it) => it.syntax().clone().into(), | ||
201 | None => colon.clone().into(), | ||
202 | }; | ||
203 | self.replace_children(colon.into()..=end, iter::empty()) | ||
204 | } | ||
205 | } | ||
206 | |||
192 | impl ast::TypeParam { | 207 | impl ast::TypeParam { |
193 | #[must_use] | 208 | #[must_use] |
194 | pub fn remove_bounds(&self) -> ast::TypeParam { | 209 | pub fn remove_bounds(&self) -> ast::TypeParam { |
diff --git a/crates/ra_text_edit/Cargo.toml b/crates/ra_text_edit/Cargo.toml index 46a2ab68f..dbb223350 100644 --- a/crates/ra_text_edit/Cargo.toml +++ b/crates/ra_text_edit/Cargo.toml | |||
@@ -4,6 +4,7 @@ name = "ra_text_edit" | |||
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | publish = false | 6 | publish = false |
7 | license = "MIT OR Apache-2.0" | ||
7 | 8 | ||
8 | [lib] | 9 | [lib] |
9 | doctest = false | 10 | doctest = false |
diff --git a/crates/ra_toolchain/Cargo.toml b/crates/ra_toolchain/Cargo.toml index 038c29ded..84b748c0a 100644 --- a/crates/ra_toolchain/Cargo.toml +++ b/crates/ra_toolchain/Cargo.toml | |||
@@ -3,6 +3,7 @@ edition = "2018" | |||
3 | name = "ra_toolchain" | 3 | name = "ra_toolchain" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/ra_tt/Cargo.toml b/crates/ra_tt/Cargo.toml index b5b12e3af..3c45248c3 100644 --- a/crates/ra_tt/Cargo.toml +++ b/crates/ra_tt/Cargo.toml | |||
@@ -3,6 +3,7 @@ edition = "2018" | |||
3 | name = "ra_tt" | 3 | name = "ra_tt" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 5074740cc..57724bcbc 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -4,6 +4,7 @@ name = "rust-analyzer" | |||
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | autobins = false | 6 | autobins = false |
7 | license = "MIT OR Apache-2.0" | ||
7 | 8 | ||
8 | [lib] | 9 | [lib] |
9 | doctest = false | 10 | doctest = false |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 702f25a19..5afcc2d87 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -122,29 +122,33 @@ impl GlobalState { | |||
122 | ); | 122 | ); |
123 | }; | 123 | }; |
124 | 124 | ||
125 | let registration_options = lsp_types::TextDocumentRegistrationOptions { | 125 | let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions { |
126 | document_selector: Some(vec![ | 126 | include_text: Some(false), |
127 | lsp_types::DocumentFilter { | 127 | text_document_registration_options: lsp_types::TextDocumentRegistrationOptions { |
128 | language: None, | 128 | document_selector: Some(vec![ |
129 | scheme: None, | 129 | lsp_types::DocumentFilter { |
130 | pattern: Some("**/*.rs".into()), | 130 | language: None, |
131 | }, | 131 | scheme: None, |
132 | lsp_types::DocumentFilter { | 132 | pattern: Some("**/*.rs".into()), |
133 | language: None, | 133 | }, |
134 | scheme: None, | 134 | lsp_types::DocumentFilter { |
135 | pattern: Some("**/Cargo.toml".into()), | 135 | language: None, |
136 | }, | 136 | scheme: None, |
137 | lsp_types::DocumentFilter { | 137 | pattern: Some("**/Cargo.toml".into()), |
138 | language: None, | 138 | }, |
139 | scheme: None, | 139 | lsp_types::DocumentFilter { |
140 | pattern: Some("**/Cargo.lock".into()), | 140 | language: None, |
141 | }, | 141 | scheme: None, |
142 | ]), | 142 | pattern: Some("**/Cargo.lock".into()), |
143 | }, | ||
144 | ]), | ||
145 | }, | ||
143 | }; | 146 | }; |
147 | |||
144 | let registration = lsp_types::Registration { | 148 | let registration = lsp_types::Registration { |
145 | id: "textDocument/didSave".to_string(), | 149 | id: "textDocument/didSave".to_string(), |
146 | method: "textDocument/didSave".to_string(), | 150 | method: "textDocument/didSave".to_string(), |
147 | register_options: Some(serde_json::to_value(registration_options).unwrap()), | 151 | register_options: Some(serde_json::to_value(save_registration_options).unwrap()), |
148 | }; | 152 | }; |
149 | self.send_request::<lsp_types::request::RegisterCapability>( | 153 | self.send_request::<lsp_types::request::RegisterCapability>( |
150 | lsp_types::RegistrationParams { registrations: vec![registration] }, | 154 | lsp_types::RegistrationParams { registrations: vec![registration] }, |
diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml index f9e380c10..4c0b85861 100644 --- a/crates/stdx/Cargo.toml +++ b/crates/stdx/Cargo.toml | |||
@@ -3,6 +3,7 @@ name = "stdx" | |||
3 | version = "0.1.0" | 3 | version = "0.1.0" |
4 | authors = ["rust-analyzer developers"] | 4 | authors = ["rust-analyzer developers"] |
5 | edition = "2018" | 5 | edition = "2018" |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml index 6821db1e8..e719f4f7c 100644 --- a/crates/test_utils/Cargo.toml +++ b/crates/test_utils/Cargo.toml | |||
@@ -3,6 +3,7 @@ edition = "2018" | |||
3 | name = "test_utils" | 3 | name = "test_utils" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index e4aa894ac..ad586c882 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs | |||
@@ -179,31 +179,80 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> { | |||
179 | let mut res = Vec::new(); | 179 | let mut res = Vec::new(); |
180 | let mut prev_line_start: Option<TextSize> = None; | 180 | let mut prev_line_start: Option<TextSize> = None; |
181 | let mut line_start: TextSize = 0.into(); | 181 | let mut line_start: TextSize = 0.into(); |
182 | let mut prev_line_annotations: Vec<(TextSize, usize)> = Vec::new(); | ||
182 | for line in lines_with_ends(text) { | 183 | for line in lines_with_ends(text) { |
183 | if let Some(idx) = line.find("//^") { | 184 | let mut this_line_annotations = Vec::new(); |
184 | let offset = prev_line_start.unwrap() + TextSize::of(&line[..idx + "//".len()]); | 185 | if let Some(idx) = line.find("//") { |
185 | for (line_range, text) in extract_line_annotations(&line[idx + "//".len()..]) { | 186 | let annotation_offset = TextSize::of(&line[..idx + "//".len()]); |
186 | res.push((line_range + offset, text)) | 187 | for annotation in extract_line_annotations(&line[idx + "//".len()..]) { |
188 | match annotation { | ||
189 | LineAnnotation::Annotation { mut range, content } => { | ||
190 | range += annotation_offset; | ||
191 | this_line_annotations.push((range.end(), res.len())); | ||
192 | res.push((range + prev_line_start.unwrap(), content)) | ||
193 | } | ||
194 | LineAnnotation::Continuation { mut offset, content } => { | ||
195 | offset += annotation_offset; | ||
196 | let &(_, idx) = prev_line_annotations | ||
197 | .iter() | ||
198 | .find(|&&(off, _idx)| off == offset) | ||
199 | .unwrap(); | ||
200 | res[idx].1.push('\n'); | ||
201 | res[idx].1.push_str(&content); | ||
202 | res[idx].1.push('\n'); | ||
203 | } | ||
204 | } | ||
187 | } | 205 | } |
188 | } | 206 | } |
207 | |||
189 | prev_line_start = Some(line_start); | 208 | prev_line_start = Some(line_start); |
190 | line_start += TextSize::of(line); | 209 | line_start += TextSize::of(line); |
210 | |||
211 | prev_line_annotations = this_line_annotations; | ||
191 | } | 212 | } |
192 | res | 213 | res |
193 | } | 214 | } |
194 | 215 | ||
195 | fn extract_line_annotations(mut line: &str) -> Vec<(TextRange, String)> { | 216 | enum LineAnnotation { |
217 | Annotation { range: TextRange, content: String }, | ||
218 | Continuation { offset: TextSize, content: String }, | ||
219 | } | ||
220 | |||
221 | fn extract_line_annotations(mut line: &str) -> Vec<LineAnnotation> { | ||
196 | let mut res = Vec::new(); | 222 | let mut res = Vec::new(); |
197 | let mut offset: TextSize = 0.into(); | 223 | let mut offset: TextSize = 0.into(); |
198 | while !line.is_empty() { | 224 | let marker: fn(char) -> bool = if line.contains('^') { |c| c == '^' } else { |c| c == '|' }; |
199 | let len = line.chars().take_while(|&it| it == '^').count(); | 225 | loop { |
200 | assert!(len > 0); | 226 | match line.find(marker) { |
227 | Some(idx) => { | ||
228 | offset += TextSize::try_from(idx).unwrap(); | ||
229 | line = &line[idx..]; | ||
230 | } | ||
231 | None => break, | ||
232 | }; | ||
233 | |||
234 | let mut len = line.chars().take_while(|&it| it == '^').count(); | ||
235 | let mut continuation = false; | ||
236 | if len == 0 { | ||
237 | assert!(line.starts_with('|')); | ||
238 | continuation = true; | ||
239 | len = 1; | ||
240 | } | ||
201 | let range = TextRange::at(offset, len.try_into().unwrap()); | 241 | let range = TextRange::at(offset, len.try_into().unwrap()); |
202 | let next = line[len..].find('^').map_or(line.len(), |it| it + len); | 242 | let next = line[len..].find(marker).map_or(line.len(), |it| it + len); |
203 | res.push((range, line[len..][..next - len].trim().to_string())); | 243 | let content = line[len..][..next - len].trim().to_string(); |
244 | |||
245 | let annotation = if continuation { | ||
246 | LineAnnotation::Continuation { offset: range.end(), content } | ||
247 | } else { | ||
248 | LineAnnotation::Annotation { range, content } | ||
249 | }; | ||
250 | res.push(annotation); | ||
251 | |||
204 | line = &line[next..]; | 252 | line = &line[next..]; |
205 | offset += TextSize::try_from(next).unwrap(); | 253 | offset += TextSize::try_from(next).unwrap(); |
206 | } | 254 | } |
255 | |||
207 | res | 256 | res |
208 | } | 257 | } |
209 | 258 | ||
@@ -215,14 +264,18 @@ fn main() { | |||
215 | let (x, y) = (9, 2); | 264 | let (x, y) = (9, 2); |
216 | //^ def ^ def | 265 | //^ def ^ def |
217 | zoo + 1 | 266 | zoo + 1 |
218 | } //^^^ i32 | 267 | } //^^^ type: |
268 | // | i32 | ||
219 | "#, | 269 | "#, |
220 | ); | 270 | ); |
221 | let res = extract_annotations(&text) | 271 | let res = extract_annotations(&text) |
222 | .into_iter() | 272 | .into_iter() |
223 | .map(|(range, ann)| (&text[range], ann)) | 273 | .map(|(range, ann)| (&text[range], ann)) |
224 | .collect::<Vec<_>>(); | 274 | .collect::<Vec<_>>(); |
225 | assert_eq!(res, vec![("x", "def".into()), ("y", "def".into()), ("zoo", "i32".into()),]); | 275 | assert_eq!( |
276 | res, | ||
277 | vec![("x", "def".into()), ("y", "def".into()), ("zoo", "type:\ni32\n".into()),] | ||
278 | ); | ||
226 | } | 279 | } |
227 | 280 | ||
228 | // Comparison functionality borrowed from cargo: | 281 | // Comparison functionality borrowed from cargo: |
diff --git a/crates/test_utils/src/mark.rs b/crates/test_utils/src/mark.rs index 7c309a894..97f5a93ad 100644 --- a/crates/test_utils/src/mark.rs +++ b/crates/test_utils/src/mark.rs | |||
@@ -62,7 +62,7 @@ pub struct MarkChecker { | |||
62 | 62 | ||
63 | impl MarkChecker { | 63 | impl MarkChecker { |
64 | pub fn new(mark: &'static AtomicUsize) -> MarkChecker { | 64 | pub fn new(mark: &'static AtomicUsize) -> MarkChecker { |
65 | let value_on_entry = mark.load(Ordering::SeqCst); | 65 | let value_on_entry = mark.load(Ordering::Relaxed); |
66 | MarkChecker { mark, value_on_entry } | 66 | MarkChecker { mark, value_on_entry } |
67 | } | 67 | } |
68 | } | 68 | } |
@@ -72,7 +72,7 @@ impl Drop for MarkChecker { | |||
72 | if std::thread::panicking() { | 72 | if std::thread::panicking() { |
73 | return; | 73 | return; |
74 | } | 74 | } |
75 | let value_on_exit = self.mark.load(Ordering::SeqCst); | 75 | let value_on_exit = self.mark.load(Ordering::Relaxed); |
76 | assert!(value_on_exit > self.value_on_entry, "mark was not hit") | 76 | assert!(value_on_exit > self.value_on_entry, "mark was not hit") |
77 | } | 77 | } |
78 | } | 78 | } |
diff --git a/crates/vfs-notify/Cargo.toml b/crates/vfs-notify/Cargo.toml index eb1764c36..95c56ffa6 100644 --- a/crates/vfs-notify/Cargo.toml +++ b/crates/vfs-notify/Cargo.toml | |||
@@ -3,6 +3,7 @@ name = "vfs-notify" | |||
3 | version = "0.1.0" | 3 | version = "0.1.0" |
4 | authors = ["rust-analyzer developers"] | 4 | authors = ["rust-analyzer developers"] |
5 | edition = "2018" | 5 | edition = "2018" |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/vfs/Cargo.toml b/crates/vfs/Cargo.toml index db99707b3..b74cdb7ff 100644 --- a/crates/vfs/Cargo.toml +++ b/crates/vfs/Cargo.toml | |||
@@ -3,6 +3,7 @@ name = "vfs" | |||
3 | version = "0.1.0" | 3 | version = "0.1.0" |
4 | authors = ["rust-analyzer developers"] | 4 | authors = ["rust-analyzer developers"] |
5 | edition = "2018" | 5 | edition = "2018" |
6 | license = "MIT OR Apache-2.0" | ||
6 | 7 | ||
7 | [lib] | 8 | [lib] |
8 | doctest = false | 9 | doctest = false |
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs index 37c479306..e5e2ef530 100644 --- a/crates/vfs/src/file_set.rs +++ b/crates/vfs/src/file_set.rs | |||
@@ -62,7 +62,7 @@ impl FileSetConfig { | |||
62 | let mut res = vec![FileSet::default(); self.len()]; | 62 | let mut res = vec![FileSet::default(); self.len()]; |
63 | for (file_id, path) in vfs.iter() { | 63 | for (file_id, path) in vfs.iter() { |
64 | let root = self.classify(&path, &mut scratch_space); | 64 | let root = self.classify(&path, &mut scratch_space); |
65 | res[root].insert(file_id, path) | 65 | res[root].insert(file_id, path.clone()) |
66 | } | 66 | } |
67 | res | 67 | res |
68 | } | 68 | } |
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs index 3bfecd08f..cdf6f1fd0 100644 --- a/crates/vfs/src/lib.rs +++ b/crates/vfs/src/lib.rs | |||
@@ -90,12 +90,12 @@ impl Vfs { | |||
90 | pub fn file_contents(&self, file_id: FileId) -> &[u8] { | 90 | pub fn file_contents(&self, file_id: FileId) -> &[u8] { |
91 | self.get(file_id).as_deref().unwrap() | 91 | self.get(file_id).as_deref().unwrap() |
92 | } | 92 | } |
93 | pub fn iter(&self) -> impl Iterator<Item = (FileId, VfsPath)> + '_ { | 93 | pub fn iter(&self) -> impl Iterator<Item = (FileId, &VfsPath)> + '_ { |
94 | (0..self.data.len()) | 94 | (0..self.data.len()) |
95 | .map(|it| FileId(it as u32)) | 95 | .map(|it| FileId(it as u32)) |
96 | .filter(move |&file_id| self.get(file_id).is_some()) | 96 | .filter(move |&file_id| self.get(file_id).is_some()) |
97 | .map(move |file_id| { | 97 | .map(move |file_id| { |
98 | let path = self.interner.lookup(file_id).clone(); | 98 | let path = self.interner.lookup(file_id); |
99 | (file_id, path) | 99 | (file_id, path) |
100 | }) | 100 | }) |
101 | } | 101 | } |
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 8045a98ea..d1cfb5909 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml | |||
@@ -4,6 +4,7 @@ name = "xtask" | |||
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | publish = false | 6 | publish = false |
7 | license = "MIT OR Apache-2.0" | ||
7 | 8 | ||
8 | [lib] | 9 | [lib] |
9 | doctest = false | 10 | doctest = false |
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 747654c1f..94d451e23 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs | |||
@@ -20,7 +20,7 @@ use walkdir::{DirEntry, WalkDir}; | |||
20 | 20 | ||
21 | use crate::{ | 21 | use crate::{ |
22 | codegen::Mode, | 22 | codegen::Mode, |
23 | not_bash::{fs2, pushd, pushenv, rm_rf, run}, | 23 | not_bash::{fs2, pushd, pushenv, rm_rf}, |
24 | }; | 24 | }; |
25 | 25 | ||
26 | pub use anyhow::{bail, Context as _, Result}; | 26 | pub use anyhow::{bail, Context as _, Result}; |
diff --git a/xtask/src/not_bash.rs b/xtask/src/not_bash.rs index 8844fa216..0f3a56b25 100644 --- a/xtask/src/not_bash.rs +++ b/xtask/src/not_bash.rs | |||
@@ -54,7 +54,8 @@ pub mod fs2 { | |||
54 | } | 54 | } |
55 | } | 55 | } |
56 | 56 | ||
57 | macro_rules! _run { | 57 | #[macro_export] |
58 | macro_rules! run { | ||
58 | ($($expr:expr),*) => { | 59 | ($($expr:expr),*) => { |
59 | run!($($expr),*; echo = true) | 60 | run!($($expr),*; echo = true) |
60 | }; | 61 | }; |
@@ -65,7 +66,7 @@ macro_rules! _run { | |||
65 | $crate::not_bash::run_process(format!($($expr),*), false, Some($stdin)) | 66 | $crate::not_bash::run_process(format!($($expr),*), false, Some($stdin)) |
66 | }; | 67 | }; |
67 | } | 68 | } |
68 | pub(crate) use _run as run; | 69 | pub use crate::run; |
69 | 70 | ||
70 | pub struct Pushd { | 71 | pub struct Pushd { |
71 | _p: (), | 72 | _p: (), |
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs index fcfad609d..72088e414 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs | |||
@@ -5,7 +5,7 @@ use std::{ | |||
5 | 5 | ||
6 | use xtask::{ | 6 | use xtask::{ |
7 | codegen::{self, Mode}, | 7 | codegen::{self, Mode}, |
8 | not_bash::fs2, | 8 | not_bash::{fs2, run}, |
9 | project_root, run_rustfmt, rust_files, | 9 | project_root, run_rustfmt, rust_files, |
10 | }; | 10 | }; |
11 | 11 | ||
@@ -49,6 +49,45 @@ fn rust_files_are_tidy() { | |||
49 | tidy_docs.finish(); | 49 | tidy_docs.finish(); |
50 | } | 50 | } |
51 | 51 | ||
52 | #[test] | ||
53 | fn check_licenses() { | ||
54 | let expected = " | ||
55 | 0BSD OR MIT OR Apache-2.0 | ||
56 | Apache-2.0 | ||
57 | Apache-2.0 / MIT | ||
58 | Apache-2.0 OR BSL-1.0 | ||
59 | Apache-2.0 OR MIT | ||
60 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | ||
61 | Apache-2.0/MIT | ||
62 | BSD-2-Clause | ||
63 | BSD-3-Clause | ||
64 | CC0-1.0 | ||
65 | ISC | ||
66 | MIT | ||
67 | MIT / Apache-2.0 | ||
68 | MIT OR Apache-2.0 | ||
69 | MIT/Apache-2.0 | ||
70 | MIT/Apache-2.0 AND BSD-2-Clause | ||
71 | Unlicense OR MIT | ||
72 | Unlicense/MIT | ||
73 | Zlib | ||
74 | " | ||
75 | .lines() | ||
76 | .filter(|it| !it.is_empty()) | ||
77 | .collect::<Vec<_>>(); | ||
78 | |||
79 | let meta = run!("cargo metadata --format-version 1"; echo = false).unwrap(); | ||
80 | let mut licenses = meta | ||
81 | .split(|c| c == ',' || c == '{' || c == '}') | ||
82 | .filter(|it| it.contains(r#""license""#)) | ||
83 | .map(|it| it.trim()) | ||
84 | .map(|it| it[r#""license":"#.len()..].trim_matches('"')) | ||
85 | .collect::<Vec<_>>(); | ||
86 | licenses.sort(); | ||
87 | licenses.dedup(); | ||
88 | assert_eq!(licenses, expected); | ||
89 | } | ||
90 | |||
52 | fn check_todo(path: &Path, text: &str) { | 91 | fn check_todo(path: &Path, text: &str) { |
53 | let need_todo = &[ | 92 | let need_todo = &[ |
54 | // This file itself obviously needs to use todo (<- like this!). | 93 | // This file itself obviously needs to use todo (<- like this!). |