aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yaml52
-rw-r--r--Cargo.lock4
-rw-r--r--crates/expect/Cargo.toml1
-rw-r--r--crates/flycheck/Cargo.toml1
-rw-r--r--crates/paths/Cargo.toml1
-rw-r--r--crates/ra_arena/Cargo.toml1
-rw-r--r--crates/ra_assists/Cargo.toml1
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs25
-rw-r--r--crates/ra_cfg/Cargo.toml1
-rw-r--r--crates/ra_db/Cargo.toml1
-rw-r--r--crates/ra_fmt/Cargo.toml1
-rw-r--r--crates/ra_hir/Cargo.toml1
-rw-r--r--crates/ra_hir/src/code_model.rs15
-rw-r--r--crates/ra_hir/src/source_analyzer.rs2
-rw-r--r--crates/ra_hir_def/Cargo.toml1
-rw-r--r--crates/ra_hir_def/src/data.rs2
-rw-r--r--crates/ra_hir_def/src/item_tree.rs1
-rw-r--r--crates/ra_hir_def/src/item_tree/lower.rs22
-rw-r--r--crates/ra_hir_def/src/item_tree/tests.rs24
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs5
-rw-r--r--crates/ra_hir_def/src/nameres/tests/macros.rs24
-rw-r--r--crates/ra_hir_def/src/type_ref.rs13
-rw-r--r--crates/ra_hir_expand/Cargo.toml1
-rw-r--r--crates/ra_hir_ty/Cargo.toml1
-rw-r--r--crates/ra_hir_ty/src/diagnostics.rs253
-rw-r--r--crates/ra_hir_ty/src/diagnostics/expr.rs (renamed from crates/ra_hir_ty/src/expr.rs)252
-rw-r--r--crates/ra_hir_ty/src/diagnostics/match_check.rs (renamed from crates/ra_hir_ty/src/match_checking.rs)1678
-rw-r--r--crates/ra_hir_ty/src/diagnostics/unsafe_check.rs (renamed from crates/ra_hir_ty/src/unsafe_validation.rs)73
-rw-r--r--crates/ra_hir_ty/src/display.rs11
-rw-r--r--crates/ra_hir_ty/src/infer.rs10
-rw-r--r--crates/ra_hir_ty/src/infer/expr.rs2
-rw-r--r--crates/ra_hir_ty/src/lib.rs22
-rw-r--r--crates/ra_hir_ty/src/lower.rs13
-rw-r--r--crates/ra_hir_ty/src/test_db.rs74
-rw-r--r--crates/ra_hir_ty/src/tests.rs408
-rw-r--r--crates/ra_hir_ty/src/traits/builtin.rs2
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/mapping.rs8
-rw-r--r--crates/ra_ide/Cargo.toml1
-rw-r--r--crates/ra_ide/src/call_info.rs467
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs5
-rw-r--r--crates/ra_ide/src/completion/presentation.rs35
-rw-r--r--crates/ra_ide/src/goto_definition.rs18
-rw-r--r--crates/ra_ide_db/Cargo.toml1
-rw-r--r--crates/ra_ide_db/src/defs.rs10
-rw-r--r--crates/ra_mbe/Cargo.toml1
-rw-r--r--crates/ra_parser/Cargo.toml1
-rw-r--r--crates/ra_proc_macro/Cargo.toml1
-rw-r--r--crates/ra_proc_macro_srv/Cargo.toml1
-rw-r--r--crates/ra_prof/Cargo.toml1
-rw-r--r--crates/ra_project_model/Cargo.toml1
-rw-r--r--crates/ra_syntax/src/ast/edit.rs15
-rw-r--r--crates/ra_text_edit/Cargo.toml1
-rw-r--r--crates/ra_toolchain/Cargo.toml1
-rw-r--r--crates/ra_tt/Cargo.toml1
-rw-r--r--crates/rust-analyzer/Cargo.toml1
-rw-r--r--crates/rust-analyzer/src/main_loop.rs42
-rw-r--r--crates/stdx/Cargo.toml1
-rw-r--r--crates/test_utils/Cargo.toml1
-rw-r--r--crates/test_utils/src/lib.rs77
-rw-r--r--crates/test_utils/src/mark.rs4
-rw-r--r--crates/vfs-notify/Cargo.toml1
-rw-r--r--crates/vfs/Cargo.toml1
-rw-r--r--crates/vfs/src/file_set.rs2
-rw-r--r--crates/vfs/src/lib.rs4
-rw-r--r--xtask/Cargo.toml1
-rw-r--r--xtask/src/lib.rs2
-rw-r--r--xtask/src/not_bash.rs5
-rw-r--r--xtask/tests/tidy.rs41
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]]
732name = "lsp-types" 732name = "lsp-types"
733version = "0.76.0" 733version = "0.76.1"
734source = "registry+https://github.com/rust-lang/crates.io-index" 734source = "registry+https://github.com/rust-lang/crates.io-index"
735checksum = "af5586f0631c7f7826c3ea39377c326d7b4623138be7ab1204dab22e47717449" 735checksum = "55cfa1593e04985972e018890b2e1a9ed25c71efc800067fbf0990a9432421c8"
736dependencies = [ 736dependencies = [
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"
3version = "0.1.0" 3version = "0.1.0"
4authors = ["rust-analyzer developers"] 4authors = ["rust-analyzer developers"]
5edition = "2018" 5edition = "2018"
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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"
3name = "flycheck" 3name = "flycheck"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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"
3version = "0.1.0" 3version = "0.1.0"
4authors = ["rust-analyzer developers"] 4authors = ["rust-analyzer developers"]
5edition = "2018" 5edition = "2018"
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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"
3name = "ra_arena" 3name = "ra_arena"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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"
3name = "ra_assists" 3name = "ra_assists"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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#"
696trait Tr {
697 type Ty: Copy + 'static;
698}
699
700impl Tr for ()<|> {
701}"#,
702 r#"
703trait Tr {
704 type Ty: Copy + 'static;
705}
706
707impl 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"
3name = "ra_cfg" 3name = "ra_cfg"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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"
3name = "ra_db" 3name = "ra_db"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6publish = false 6publish = false
7license = "MIT OR Apache-2.0"
7 8
8[lib] 9[lib]
9doctest = false 10doctest = 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"
3name = "ra_hir" 3name = "ra_hir"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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::{
25use hir_ty::{ 25use 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};
34use ra_db::{CrateId, Edition, FileId}; 31use ra_db::{CrateId, Edition, FileId};
35use ra_prof::profile; 32use 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};
19use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; 19use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile};
20use hir_ty::{ 20use 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};
24use ra_syntax::{ 24use 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"
3name = "ra_hir_def" 3name = "ra_hir_def"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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]
665fn macro_expansion_overflow() {
666 mark::check!(macro_expansion_overflow);
667 compute_crate_def_map(
668 "
669macro_rules! a {
670 ($e:expr; $($t:tt)*) => {
671 b!($($t)*);
672 };
673 () => {};
674}
675
676macro_rules! b {
677 (static = $e:expr; $($t:tt)*) => {
678 a!($e; $($t)*);
679 };
680 () => {};
681}
682
683b! { 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"
3name = "ra_hir_expand" 3name = "ra_hir_expand"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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"
3name = "ra_hir_ty" 3name = "ra_hir_ty"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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
2mod expr;
3mod match_check;
4mod unsafe_check;
2 5
3use std::any::Any; 6use std::any::Any;
4 7
8use hir_def::DefWithBodyId;
9use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink};
5use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile}; 10use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile};
11use ra_prof::profile;
6use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; 12use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
7use stdx::format_to; 13use stdx::format_to;
8 14
9pub use hir_def::{diagnostics::UnresolvedModule, expr::MatchArm, path::Path}; 15use crate::db::HirDatabase;
10pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; 16
17pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields};
18
19pub 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)]
13pub struct NoSuchField { 30pub 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)]
249mod 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#"
322struct S { foo: i32, bar: () }
323impl 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
342struct MyStruct {
343 my_val: usize,
344 #[cfg(feature = "foo")]
345 bar: bool,
346}
347
348impl 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
367enum Foo {
368 #[cfg(not(feature = "foo"))]
369 Buz,
370 #[cfg(feature = "foo")]
371 Bar,
372 Baz
373}
374
375fn 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
390struct S {
391 #[cfg(feature = "foo")]
392 foo: u32,
393 #[cfg(not(feature = "foo"))]
394 bar: u32,
395}
396
397impl 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#"
429macro_rules! Type { () => { u32 }; }
430struct Foo { bar: Type![] }
431
432impl 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#"
445struct S { foo: i32, bar: () }
446fn 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"
459struct S { foo: i32, bar: () }
460fn 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#"
473fn 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
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use hir_def::{path::path, resolver::HasResolver, AdtId, FunctionId}; 5use hir_def::{path::path, resolver::HasResolver, AdtId, DefWithBodyId};
6use hir_expand::diagnostics::DiagnosticSink; 6use hir_expand::diagnostics::DiagnosticSink;
7use ra_syntax::{ast, AstPtr}; 7use ra_syntax::{ast, AstPtr};
8use rustc_hash::FxHashSet; 8use rustc_hash::FxHashSet;
@@ -10,9 +10,9 @@ use rustc_hash::FxHashSet;
10use crate::{ 10use 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
33pub struct ExprValidator<'a, 'b: 'a> { 33pub(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
39impl<'a, 'b> ExprValidator<'a, 'b> { 39impl<'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)]
378mod tests { 382mod 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() {} 389fn zero() {}
401 fn f() { zero(1); } 390fn 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() {} 397fn zero() {}
409 fn f() { zero(); } 398fn 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) {} 407fn one(arg: u8) {}
419 fn f() { one(); } 408fn 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) {} 415fn one(arg: u8) {}
427 fn f() { one(1); } 416fn 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; 425struct S;
437 impl S { 426impl S { fn method(&self) {} }
438 fn method(&self) {} 427
439 } 428fn 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; 436struct S;
451 impl S { 437impl S { fn method(&self) {} }
452 fn method(&self) {}
453 }
454 438
455 fn f() { 439fn 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; 451struct S;
468 impl S { 452impl 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; 462struct S;
482 impl S { 463impl S { fn method(&self, arg: u8) {} }
483 fn method(&self, arg: u8) {}
484 }
485 464
486 fn f() { 465fn 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); 477struct Tup(u8, u16);
499 fn f() { 478fn 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 { 489enum En { Variant(u8, u16), }
512 Variant(u8, u16), 490fn 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#"
501macro_rules! Type {
502 () => { u32 };
503}
504enum Foo {
505 Bar(Type![])
506}
507impl 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#"
524extern "C" {
525 fn fixed(fixed: u8);
526 fn varargs(fixed: u8, ...);
527 fn varargs2(...);
528}
529
530fn 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//! ```
219use std::sync::Arc; 219use std::sync::Arc;
220 220
221use smallvec::{smallvec, SmallVec}; 221use hir_def::{
222 222 adt::VariantData,
223use 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};
228use hir_def::{adt::VariantData, AdtId, EnumVariantId, VariantId};
229use ra_arena::Idx; 227use ra_arena::Idx;
228use smallvec::{smallvec, SmallVec};
229
230use 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)]
274pub enum MatchCheckErr { 275pub(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 `?`.
289pub type MatchCheckResult<T> = Result<T, MatchCheckErr>; 290pub(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`.
295pub(crate) struct PatStack(PatStackInner); 296pub(super) struct PatStack(PatStackInner);
296type PatStackInner = SmallVec<[PatIdOrWild; 2]>; 297type PatStackInner = SmallVec<[PatIdOrWild; 2]>;
297 298
298impl PatStack { 299impl 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`.
512pub(crate) struct Matrix(Vec<PatStack>); 513pub(super) struct Matrix(Vec<PatStack>);
513 514
514impl Matrix { 515impl 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.
581pub enum Usefulness { 582pub(super) enum Usefulness {
582 Useful, 583 Useful,
583 NotUseful, 584 NotUseful,
584} 585}
585 586
586pub struct MatchCheckCtx<'a> { 587pub(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.
601pub(crate) fn is_useful( 602pub(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)]
838mod tests { 839mod 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() { 846fn 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() { 864fn 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 ); 878fn 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() { 931fn 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() { 959enum Either { A, B, }
1159 match (false, ((), false)) {
1160 (true, ((), true)) => {},
1161 }
1162 }
1163 ",
1164 );
1165 }
1166 960
1167 #[test] 961fn 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 { 987enum 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] 989fn 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 { 1020enum 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] 1022fn 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 { 1048enum Either { A(bool), B(bool, bool) }
1447 A(bool), 1049enum Either2 { C, D }
1448 B(bool, bool), 1050
1449 } 1051fn 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() { 1069enum Either { A, B }
1513 match (0) { 1070enum Either2 { C, D }
1514 () => (), 1071
1515 } 1072fn 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 { 1094enum 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] 1096fn 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 { 1110enum 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] 1112fn 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> { 1137enum 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] 1139fn foo() -> Either<!> { Either::B }
1615 fn enum_record_no_arms() { 1140fn 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 { 1154enum 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] 1156fn 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 { 1192enum 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] 1197fn 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 { 1219enum 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] 1224fn 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 {} 1255enum Either {
1256 A(bool, bool, bool, bool),
1257 B,
1258}
1949 1259
1950 fn test_fn(never: Never) { 1260fn 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 {} 1299enum Never {}
1973 1300
1974 fn test_fn(never: &Never) { 1301fn enum_(never: Never) {
1975 match never {} 1302 match never {}
1976 } 1303}
1977 ", 1304fn enum_ref(never: &Never) {
1978 ); 1305 match never {}
1979 } 1306}
1980 1307fn 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 { 1318pub enum Category { Infinity, Zero }
2003 Infinity,
2004 Zero,
2005 }
2006 1319
2007 fn panic(a: Category, b: Category) { 1320fn 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 );
2041mod 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() { 1355fn 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 `!`. 1370fn 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" 1385fn 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" 1399fn 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" 1413struct Foo { a: bool }
2146 struct Foo { 1414fn 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;
6use hir_def::{ 6use 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};
11use hir_expand::diagnostics::DiagnosticSink; 11use hir_expand::diagnostics::DiagnosticSink;
12 12
@@ -15,26 +15,29 @@ use crate::{
15 InferenceResult, Ty, TypeCtor, 15 InferenceResult, Ty, TypeCtor,
16}; 16};
17 17
18pub struct UnsafeValidator<'a, 'b: 'a> { 18pub(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
24impl<'a, 'b> UnsafeValidator<'a, 'b> { 24impl<'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)]
126mod tests {
127 use crate::diagnostics::tests::check_diagnostics;
128
129 #[test]
130 fn missing_unsafe_diagnostic_with_raw_ptr() {
131 check_diagnostics(
132 r#"
133fn 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#"
146struct HasUnsafe;
147
148impl HasUnsafe {
149 unsafe fn unsafe_fn(&self) {
150 let x = &5 as *const usize;
151 let y = *x;
152 }
153}
154
155unsafe fn unsafe_fn() {
156 let x = &5 as *const usize;
157 let y = *x;
158}
159
160fn 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
762mod diagnostics { 762mod 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;
12pub mod method_resolution; 12pub mod method_resolution;
13mod op; 13mod op;
14mod lower; 14mod lower;
15mod match_checking;
16pub(crate) mod infer; 15pub(crate) mod infer;
17pub(crate) mod utils; 16pub(crate) mod utils;
18 17
19pub mod display; 18pub mod display;
20pub mod db; 19pub mod db;
21pub mod diagnostics; 20pub mod diagnostics;
22pub mod expr;
23pub mod unsafe_validation;
24 21
25#[cfg(test)] 22#[cfg(test)]
26mod tests; 23mod 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)]
671pub struct FnSig { 668pub 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.
676pub type PolyFnSig = Binders<FnSig>; 674pub type PolyFnSig = Binders<FnSig>;
677 675
678impl FnSig { 676impl 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
8use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId}; 8use hir_def::{db::DefDatabase, ModuleId};
9use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink}; 9use hir_expand::db::AstDatabase;
10use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast}; 10use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast};
11use ra_syntax::TextRange; 11use ra_syntax::TextRange;
12use rustc_hash::{FxHashMap, FxHashSet}; 12use rustc_hash::{FxHashMap, FxHashSet};
13use stdx::format_to;
14use test_utils::extract_annotations; 13use test_utils::extract_annotations;
15 14
16use 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
84impl TestDB { 78impl 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};
22use hir_expand::{db::AstDatabase, InFile}; 22use hir_expand::{db::AstDatabase, InFile};
23use insta::assert_snapshot;
24use ra_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; 23use ra_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt};
25use ra_syntax::{ 24use 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]
346fn 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]
373fn 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]
403fn 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]
430fn 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]
460fn 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]
492fn 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]
522fn 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]
546fn 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]
567fn 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]
586fn missing_unsafe_diagnostic_with_raw_ptr() {
587 let diagnostics = TestDB::with_files(
588 r"
589//- /lib.rs
590fn 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]
603fn missing_unsafe_diagnostic_with_unsafe_call() {
604 let diagnostics = TestDB::with_files(
605 r"
606//- /lib.rs
607unsafe fn unsafe_fn() {
608 let x = &5 as *const usize;
609 let y = *x;
610}
611
612fn 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]
624fn missing_unsafe_diagnostic_with_unsafe_method_call() {
625 let diagnostics = TestDB::with_files(
626 r"
627struct HasUnsafe;
628
629impl HasUnsafe {
630 unsafe fn unsafe_fn(&self) {
631 let x = &5 as *const usize;
632 let y = *x;
633 }
634}
635
636fn 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]
649fn no_missing_unsafe_diagnostic_with_raw_ptr_in_unsafe_block() {
650 let diagnostics = TestDB::with_files(
651 r"
652fn 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]
667fn missing_unsafe_diagnostic_with_raw_ptr_outside_unsafe_block() {
668 let diagnostics = TestDB::with_files(
669 r"
670fn 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]
686fn no_missing_unsafe_diagnostic_with_unsafe_call_in_unsafe_block() {
687 let diagnostics = TestDB::with_files(
688 r"
689unsafe fn unsafe_fn() {
690 let x = &5 as *const usize;
691 let y = *x;
692}
693
694fn 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]
708fn no_missing_unsafe_diagnostic_with_unsafe_method_call_in_unsafe_block() {
709 let diagnostics = TestDB::with_files(
710 r"
711struct HasUnsafe;
712
713impl HasUnsafe {
714 unsafe fn unsafe_fn() {
715 let x = &5 as *const usize;
716 let y = *x;
717 }
718}
719
720fn 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]
735fn 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"
3name = "ra_ide" 3name = "ra_ide"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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)]
215mod tests { 215mod 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#"
253fn bar() { foo(<|>3, ); }"#, 254fn foo(x: u32, y: u32) -> u32 {x + y}
255fn 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)); 264fn foo(x: u32, y: u32) -> u32 {x + y}
258 } 265fn 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 "#]],
264fn bar() { foo(3, <|>); }"#, 271 );
272 check(
273 r#"
274fn foo(x: u32, y: u32) -> u32 {x + y}
275fn 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#"
284fn foo(x: u32, y: u32) -> u32 {x + y}
285fn 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#"
275fn bar() { foo(<|>); }"#, 298fn foo(x: u32, y: u32) -> u32 {x + y}
299fn 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}
286fn bar() { foo(<|>3, ); }"#,
287 );
288
289 assert_eq!(info.parameters(), ["x: T", "y: U"]);
290 assert_eq!(
291 info.label(),
292 r#" 311 r#"
293fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 312fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
294where T: Copy + Display, 313 where T: Copy + Display, U: Debug
295 U: Debug 314{ x + y }
296 "# 315
297 .trim() 316fn 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 {}
306fn bar() { foo(<|>); }"#,
307 );
308
309 assert!(info.parameters().is_empty());
310 assert_eq!(
311 info.label(),
312 r#" 330 r#"
313fn foo<T>() -> T 331fn foo<T>() -> T where T: Copy + Display {}
314where T: Copy + Display 332fn 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#"
325fn bar() {let _ : F = F::new(<|>);}"#, 346struct F; impl F { pub fn new() { F{}} }
347fn 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#"
336impl F { 360struct S;
337 pub fn new() -> F{ 361impl S { pub fn do_it(&self) {} }
338 F{}
339 }
340
341 pub fn do_it(&self) {}
342}
343 362
344fn bar() { 363fn 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#"
358impl F { 379struct S;
359 pub fn new() -> F{ 380impl S { pub fn do_it(&self, x: i32) {} }
360 F{}
361 }
362
363 pub fn do_it(&self, x: i32) {}
364}
365 381
366fn bar() { 382fn 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 {
415pub fn do() { 434pub 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 ```
431let five = 5; 443 let five = 5;
432 444
433assert_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#"
444struct addr; 458struct addr;
445impl addr { 459impl addr {
@@ -460,32 +474,28 @@ impl addr {
460pub fn do_it() { 474pub 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 ```
477let five = 5; 485 let five = 5;
478 486
479assert_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#"
490struct WriteHandler<E>; 500struct WriteHandler<E>;
491 501
@@ -509,101 +519,102 @@ impl<E> WriteHandler<E> {
509pub fn foo(mut r: WriteHandler<()>) { 519pub 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
524By 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, ); }"#, 539fn foo(x: u32, y: u32) -> u32 {x + y}
540fn 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 550struct Foo;
546impl Foo { 551impl Foo { fn bar(&self, _: u32) { } }
547 fn bar(&self, _: u32) { }
548}
549 552
550fn bar(_: u32) { } 553fn bar(_: u32) { }
551 554
552fn main() { 555fn 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
568struct TS(u32, i32); 572struct TS(u32, i32);
569fn main() { 573fn 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#"
583struct TS<T>(T); 590struct TS<T>(T);
584fn main() { 591fn 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#"
597struct TS { x: u32, y: i32 } 606struct TS { x: u32, y: i32 }
598fn main() { 607fn 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#"
608enum E { 619enum E {
609 /// A Variant 620 /// A Variant
@@ -617,17 +628,19 @@ enum E {
617fn main() { 628fn 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#"
632enum E { 645enum E {
633 /// A Variant 646 /// A Variant
@@ -641,13 +654,14 @@ enum E {
641fn main() { 654fn 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
653macro_rules! foo { 667macro_rules! foo {
@@ -657,31 +671,30 @@ macro_rules! foo {
657fn f() { 671fn 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 { 688macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
672 ($($tt:tt)*) => { $($tt)* } 689fn foo() { }
673 } 690id! {
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#"
923enum E { Var(i32) }
924fn main() {
925 match E::Var(92) {
926 E::<|>(92) => (),
927 }
928}
929"#,
930 r#"
931enum E { Var(i32) }
932fn 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
875foo::module<|>::mac!();
876
877//- /foo/lib.rs
878pub 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"
3name = "ra_ide_db" 3name = "ra_ide_db"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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(&macro_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(&macro_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"
3name = "ra_mbe" 3name = "ra_mbe"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6publish = false 6publish = false
7license = "MIT OR Apache-2.0"
7 8
8[lib] 9[lib]
9doctest = false 10doctest = 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"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6publish = false 6publish = false
7license = "MIT OR Apache-2.0"
7 8
8[lib] 9[lib]
9doctest = false 10doctest = 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"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6publish = false 6publish = false
7license = "MIT OR Apache-2.0"
7 8
8[lib] 9[lib]
9doctest = false 10doctest = 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"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6publish = false 6publish = false
7license = "MIT OR Apache-2.0"
7 8
8[lib] 9[lib]
9doctest = false 10doctest = 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"
3name = "ra_project_model" 3name = "ra_project_model"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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
192impl 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
192impl ast::TypeParam { 207impl 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"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6publish = false 6publish = false
7license = "MIT OR Apache-2.0"
7 8
8[lib] 9[lib]
9doctest = false 10doctest = 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"
3name = "ra_toolchain" 3name = "ra_toolchain"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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"
3name = "ra_tt" 3name = "ra_tt"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6autobins = false 6autobins = false
7license = "MIT OR Apache-2.0"
7 8
8[lib] 9[lib]
9doctest = false 10doctest = 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"
3version = "0.1.0" 3version = "0.1.0"
4authors = ["rust-analyzer developers"] 4authors = ["rust-analyzer developers"]
5edition = "2018" 5edition = "2018"
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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"
3name = "test_utils" 3name = "test_utils"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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
195fn extract_line_annotations(mut line: &str) -> Vec<(TextRange, String)> { 216enum LineAnnotation {
217 Annotation { range: TextRange, content: String },
218 Continuation { offset: TextSize, content: String },
219}
220
221fn 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
63impl MarkChecker { 63impl 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"
3version = "0.1.0" 3version = "0.1.0"
4authors = ["rust-analyzer developers"] 4authors = ["rust-analyzer developers"]
5edition = "2018" 5edition = "2018"
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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"
3version = "0.1.0" 3version = "0.1.0"
4authors = ["rust-analyzer developers"] 4authors = ["rust-analyzer developers"]
5edition = "2018" 5edition = "2018"
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = 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"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6publish = false 6publish = false
7license = "MIT OR Apache-2.0"
7 8
8[lib] 9[lib]
9doctest = false 10doctest = 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
21use crate::{ 21use 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
26pub use anyhow::{bail, Context as _, Result}; 26pub 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
57macro_rules! _run { 57#[macro_export]
58macro_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}
68pub(crate) use _run as run; 69pub use crate::run;
69 70
70pub struct Pushd { 71pub 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
6use xtask::{ 6use 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]
53fn check_licenses() {
54 let expected = "
550BSD OR MIT OR Apache-2.0
56Apache-2.0
57Apache-2.0 / MIT
58Apache-2.0 OR BSL-1.0
59Apache-2.0 OR MIT
60Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT
61Apache-2.0/MIT
62BSD-2-Clause
63BSD-3-Clause
64CC0-1.0
65ISC
66MIT
67MIT / Apache-2.0
68MIT OR Apache-2.0
69MIT/Apache-2.0
70MIT/Apache-2.0 AND BSD-2-Clause
71Unlicense OR MIT
72Unlicense/MIT
73Zlib
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
52fn check_todo(path: &Path, text: &str) { 91fn 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!).