diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/hir_def/src/data.rs | 4 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree.rs | 7 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree/lower.rs | 13 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree/tests.rs | 439 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/decl_check.rs | 26 |
5 files changed, 43 insertions, 446 deletions
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs index 146045938..dd3a906af 100644 --- a/crates/hir_def/src/data.rs +++ b/crates/hir_def/src/data.rs | |||
@@ -28,6 +28,7 @@ pub struct FunctionData { | |||
28 | pub has_body: bool, | 28 | pub has_body: bool, |
29 | pub is_unsafe: bool, | 29 | pub is_unsafe: bool, |
30 | pub is_varargs: bool, | 30 | pub is_varargs: bool, |
31 | pub is_extern: bool, | ||
31 | pub visibility: RawVisibility, | 32 | pub visibility: RawVisibility, |
32 | } | 33 | } |
33 | 34 | ||
@@ -46,6 +47,7 @@ impl FunctionData { | |||
46 | has_body: func.has_body, | 47 | has_body: func.has_body, |
47 | is_unsafe: func.is_unsafe, | 48 | is_unsafe: func.is_unsafe, |
48 | is_varargs: func.is_varargs, | 49 | is_varargs: func.is_varargs, |
50 | is_extern: func.is_extern, | ||
49 | visibility: item_tree[func.visibility].clone(), | 51 | visibility: item_tree[func.visibility].clone(), |
50 | }) | 52 | }) |
51 | } | 53 | } |
@@ -191,6 +193,7 @@ pub struct StaticData { | |||
191 | pub type_ref: TypeRef, | 193 | pub type_ref: TypeRef, |
192 | pub visibility: RawVisibility, | 194 | pub visibility: RawVisibility, |
193 | pub mutable: bool, | 195 | pub mutable: bool, |
196 | pub is_extern: bool, | ||
194 | } | 197 | } |
195 | 198 | ||
196 | impl StaticData { | 199 | impl StaticData { |
@@ -204,6 +207,7 @@ impl StaticData { | |||
204 | type_ref: statik.type_ref.clone(), | 207 | type_ref: statik.type_ref.clone(), |
205 | visibility: item_tree[statik.visibility].clone(), | 208 | visibility: item_tree[statik.visibility].clone(), |
206 | mutable: statik.mutable, | 209 | mutable: statik.mutable, |
210 | is_extern: statik.is_extern, | ||
207 | }) | 211 | }) |
208 | } | 212 | } |
209 | } | 213 | } |
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 7eb388bae..c017b352d 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs | |||
@@ -1,8 +1,6 @@ | |||
1 | //! A simplified AST that only contains items. | 1 | //! A simplified AST that only contains items. |
2 | 2 | ||
3 | mod lower; | 3 | mod lower; |
4 | #[cfg(test)] | ||
5 | mod tests; | ||
6 | 4 | ||
7 | use std::{ | 5 | use std::{ |
8 | any::type_name, | 6 | any::type_name, |
@@ -507,6 +505,9 @@ pub struct Function { | |||
507 | pub has_self_param: bool, | 505 | pub has_self_param: bool, |
508 | pub has_body: bool, | 506 | pub has_body: bool, |
509 | pub is_unsafe: bool, | 507 | pub is_unsafe: bool, |
508 | /// Whether the function is located in an `extern` block (*not* whether it is an | ||
509 | /// `extern "abi" fn`). | ||
510 | pub is_extern: bool, | ||
510 | pub params: Box<[TypeRef]>, | 511 | pub params: Box<[TypeRef]>, |
511 | pub is_varargs: bool, | 512 | pub is_varargs: bool, |
512 | pub ret_type: TypeRef, | 513 | pub ret_type: TypeRef, |
@@ -565,6 +566,8 @@ pub struct Static { | |||
565 | pub name: Name, | 566 | pub name: Name, |
566 | pub visibility: RawVisibilityId, | 567 | pub visibility: RawVisibilityId, |
567 | pub mutable: bool, | 568 | pub mutable: bool, |
569 | /// Whether the static is in an `extern` block. | ||
570 | pub is_extern: bool, | ||
568 | pub type_ref: TypeRef, | 571 | pub type_ref: TypeRef, |
569 | pub ast_id: FileAstId<ast::Static>, | 572 | pub ast_id: FileAstId<ast::Static>, |
570 | } | 573 | } |
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index ca7fb4a43..63b2826f8 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -340,6 +340,7 @@ impl Ctx { | |||
340 | has_self_param, | 340 | has_self_param, |
341 | has_body, | 341 | has_body, |
342 | is_unsafe: func.unsafe_token().is_some(), | 342 | is_unsafe: func.unsafe_token().is_some(), |
343 | is_extern: false, | ||
343 | params: params.into_boxed_slice(), | 344 | params: params.into_boxed_slice(), |
344 | is_varargs, | 345 | is_varargs, |
345 | ret_type, | 346 | ret_type, |
@@ -378,7 +379,7 @@ impl Ctx { | |||
378 | let visibility = self.lower_visibility(static_); | 379 | let visibility = self.lower_visibility(static_); |
379 | let mutable = static_.mut_token().is_some(); | 380 | let mutable = static_.mut_token().is_some(); |
380 | let ast_id = self.source_ast_id_map.ast_id(static_); | 381 | let ast_id = self.source_ast_id_map.ast_id(static_); |
381 | let res = Static { name, visibility, mutable, type_ref, ast_id }; | 382 | let res = Static { name, visibility, mutable, type_ref, ast_id, is_extern: false }; |
382 | Some(id(self.data().statics.alloc(res))) | 383 | Some(id(self.data().statics.alloc(res))) |
383 | } | 384 | } |
384 | 385 | ||
@@ -554,13 +555,15 @@ impl Ctx { | |||
554 | let attrs = Attrs::new(&item, &self.hygiene); | 555 | let attrs = Attrs::new(&item, &self.hygiene); |
555 | let id: ModItem = match item { | 556 | let id: ModItem = match item { |
556 | ast::ExternItem::Fn(ast) => { | 557 | ast::ExternItem::Fn(ast) => { |
557 | let func = self.lower_function(&ast)?; | 558 | let func_id = self.lower_function(&ast)?; |
558 | self.data().functions[func.index].is_unsafe = | 559 | let func = &mut self.data().functions[func_id.index]; |
559 | is_intrinsic_fn_unsafe(&self.data().functions[func.index].name); | 560 | func.is_unsafe = is_intrinsic_fn_unsafe(&func.name); |
560 | func.into() | 561 | func.is_extern = true; |
562 | func_id.into() | ||
561 | } | 563 | } |
562 | ast::ExternItem::Static(ast) => { | 564 | ast::ExternItem::Static(ast) => { |
563 | let statik = self.lower_static(&ast)?; | 565 | let statik = self.lower_static(&ast)?; |
566 | self.data().statics[statik.index].is_extern = true; | ||
564 | statik.into() | 567 | statik.into() |
565 | } | 568 | } |
566 | ast::ExternItem::TypeAlias(ty) => { | 569 | ast::ExternItem::TypeAlias(ty) => { |
diff --git a/crates/hir_def/src/item_tree/tests.rs b/crates/hir_def/src/item_tree/tests.rs deleted file mode 100644 index 4b354c4c1..000000000 --- a/crates/hir_def/src/item_tree/tests.rs +++ /dev/null | |||
@@ -1,439 +0,0 @@ | |||
1 | use base_db::fixture::WithFixture; | ||
2 | use expect_test::{expect, Expect}; | ||
3 | use hir_expand::{db::AstDatabase, HirFileId, InFile}; | ||
4 | use rustc_hash::FxHashSet; | ||
5 | use std::sync::Arc; | ||
6 | use stdx::format_to; | ||
7 | use syntax::{ast, AstNode}; | ||
8 | |||
9 | use crate::{db::DefDatabase, test_db::TestDB}; | ||
10 | |||
11 | use super::{ItemTree, ModItem, ModKind}; | ||
12 | |||
13 | fn test_inner_items(ra_fixture: &str) { | ||
14 | let (db, file_id) = TestDB::with_single_file(ra_fixture); | ||
15 | let file_id = HirFileId::from(file_id); | ||
16 | let tree = db.item_tree(file_id); | ||
17 | let root = db.parse_or_expand(file_id).unwrap(); | ||
18 | let ast_id_map = db.ast_id_map(file_id); | ||
19 | |||
20 | // Traverse the item tree and collect all module/impl/trait-level items as AST nodes. | ||
21 | let mut outer_items = FxHashSet::default(); | ||
22 | let mut worklist = tree.top_level_items().to_vec(); | ||
23 | while let Some(item) = worklist.pop() { | ||
24 | let node: ast::Item = match item { | ||
25 | ModItem::Import(it) => tree.source(&db, InFile::new(file_id, it)).into(), | ||
26 | ModItem::ExternCrate(it) => tree.source(&db, InFile::new(file_id, it)).into(), | ||
27 | ModItem::Function(it) => tree.source(&db, InFile::new(file_id, it)).into(), | ||
28 | ModItem::Struct(it) => tree.source(&db, InFile::new(file_id, it)).into(), | ||
29 | ModItem::Union(it) => tree.source(&db, InFile::new(file_id, it)).into(), | ||
30 | ModItem::Enum(it) => tree.source(&db, InFile::new(file_id, it)).into(), | ||
31 | ModItem::Const(it) => tree.source(&db, InFile::new(file_id, it)).into(), | ||
32 | ModItem::Static(it) => tree.source(&db, InFile::new(file_id, it)).into(), | ||
33 | ModItem::TypeAlias(it) => tree.source(&db, InFile::new(file_id, it)).into(), | ||
34 | ModItem::Mod(it) => { | ||
35 | if let ModKind::Inline { items } = &tree[it].kind { | ||
36 | worklist.extend(&**items); | ||
37 | } | ||
38 | tree.source(&db, InFile::new(file_id, it)).into() | ||
39 | } | ||
40 | ModItem::Trait(it) => { | ||
41 | worklist.extend(tree[it].items.iter().map(|item| ModItem::from(*item))); | ||
42 | tree.source(&db, InFile::new(file_id, it)).into() | ||
43 | } | ||
44 | ModItem::Impl(it) => { | ||
45 | worklist.extend(tree[it].items.iter().map(|item| ModItem::from(*item))); | ||
46 | tree.source(&db, InFile::new(file_id, it)).into() | ||
47 | } | ||
48 | ModItem::MacroCall(_) => continue, | ||
49 | }; | ||
50 | |||
51 | outer_items.insert(node); | ||
52 | } | ||
53 | |||
54 | // Now descend the root node and check that all `ast::ModuleItem`s are either recorded above, or | ||
55 | // registered as inner items. | ||
56 | for item in root.descendants().skip(1).filter_map(ast::Item::cast) { | ||
57 | if outer_items.contains(&item) { | ||
58 | continue; | ||
59 | } | ||
60 | |||
61 | let ast_id = ast_id_map.ast_id(&item); | ||
62 | assert!(!tree.inner_items(ast_id).is_empty()); | ||
63 | } | ||
64 | } | ||
65 | |||
66 | fn item_tree(ra_fixture: &str) -> Arc<ItemTree> { | ||
67 | let (db, file_id) = TestDB::with_single_file(ra_fixture); | ||
68 | db.item_tree(file_id.into()) | ||
69 | } | ||
70 | |||
71 | fn print_item_tree(ra_fixture: &str) -> String { | ||
72 | let tree = item_tree(ra_fixture); | ||
73 | let mut out = String::new(); | ||
74 | |||
75 | format_to!(out, "inner attrs: {:?}\n\n", tree.top_level_attrs()); | ||
76 | format_to!(out, "top-level items:\n"); | ||
77 | for item in tree.top_level_items() { | ||
78 | fmt_mod_item(&mut out, &tree, *item); | ||
79 | format_to!(out, "\n"); | ||
80 | } | ||
81 | |||
82 | if !tree.inner_items.is_empty() { | ||
83 | format_to!(out, "\ninner items:\n\n"); | ||
84 | for (ast_id, items) in &tree.inner_items { | ||
85 | format_to!(out, "for AST {:?}:\n", ast_id); | ||
86 | for inner in items { | ||
87 | fmt_mod_item(&mut out, &tree, *inner); | ||
88 | format_to!(out, "\n\n"); | ||
89 | } | ||
90 | } | ||
91 | } | ||
92 | |||
93 | out | ||
94 | } | ||
95 | |||
96 | fn fmt_mod_item(out: &mut String, tree: &ItemTree, item: ModItem) { | ||
97 | let attrs = tree.attrs(item.into()); | ||
98 | if !attrs.is_empty() { | ||
99 | format_to!(out, "#[{:?}]\n", attrs); | ||
100 | } | ||
101 | |||
102 | let mut children = String::new(); | ||
103 | match item { | ||
104 | ModItem::ExternCrate(it) => { | ||
105 | format_to!(out, "{:?}", tree[it]); | ||
106 | } | ||
107 | ModItem::Import(it) => { | ||
108 | format_to!(out, "{:?}", tree[it]); | ||
109 | } | ||
110 | ModItem::Function(it) => { | ||
111 | format_to!(out, "{:?}", tree[it]); | ||
112 | } | ||
113 | ModItem::Struct(it) => { | ||
114 | format_to!(out, "{:?}", tree[it]); | ||
115 | } | ||
116 | ModItem::Union(it) => { | ||
117 | format_to!(out, "{:?}", tree[it]); | ||
118 | } | ||
119 | ModItem::Enum(it) => { | ||
120 | format_to!(out, "{:?}", tree[it]); | ||
121 | } | ||
122 | ModItem::Const(it) => { | ||
123 | format_to!(out, "{:?}", tree[it]); | ||
124 | } | ||
125 | ModItem::Static(it) => { | ||
126 | format_to!(out, "{:?}", tree[it]); | ||
127 | } | ||
128 | ModItem::Trait(it) => { | ||
129 | format_to!(out, "{:?}", tree[it]); | ||
130 | for item in &*tree[it].items { | ||
131 | fmt_mod_item(&mut children, tree, ModItem::from(*item)); | ||
132 | format_to!(children, "\n"); | ||
133 | } | ||
134 | } | ||
135 | ModItem::Impl(it) => { | ||
136 | format_to!(out, "{:?}", tree[it]); | ||
137 | for item in &*tree[it].items { | ||
138 | fmt_mod_item(&mut children, tree, ModItem::from(*item)); | ||
139 | format_to!(children, "\n"); | ||
140 | } | ||
141 | } | ||
142 | ModItem::TypeAlias(it) => { | ||
143 | format_to!(out, "{:?}", tree[it]); | ||
144 | } | ||
145 | ModItem::Mod(it) => { | ||
146 | format_to!(out, "{:?}", tree[it]); | ||
147 | match &tree[it].kind { | ||
148 | ModKind::Inline { items } => { | ||
149 | for item in &**items { | ||
150 | fmt_mod_item(&mut children, tree, *item); | ||
151 | format_to!(children, "\n"); | ||
152 | } | ||
153 | } | ||
154 | ModKind::Outline {} => {} | ||
155 | } | ||
156 | } | ||
157 | ModItem::MacroCall(it) => { | ||
158 | format_to!(out, "{:?}", tree[it]); | ||
159 | } | ||
160 | } | ||
161 | |||
162 | for line in children.lines() { | ||
163 | format_to!(out, "\n> {}", line); | ||
164 | } | ||
165 | } | ||
166 | |||
167 | fn check(ra_fixture: &str, expect: Expect) { | ||
168 | let actual = print_item_tree(ra_fixture); | ||
169 | expect.assert_eq(&actual); | ||
170 | } | ||
171 | |||
172 | #[test] | ||
173 | fn smoke() { | ||
174 | check( | ||
175 | r" | ||
176 | #![attr] | ||
177 | |||
178 | #[attr_on_use] | ||
179 | use {a, b::*}; | ||
180 | |||
181 | #[ext_crate] | ||
182 | extern crate krate; | ||
183 | |||
184 | #[on_trait] | ||
185 | trait Tr<U> { | ||
186 | #[assoc_ty] | ||
187 | type AssocTy: Tr<()>; | ||
188 | |||
189 | #[assoc_const] | ||
190 | const CONST: u8; | ||
191 | |||
192 | #[assoc_method] | ||
193 | fn method(&self); | ||
194 | |||
195 | #[assoc_dfl_method] | ||
196 | fn dfl_method(&mut self) {} | ||
197 | } | ||
198 | |||
199 | #[struct0] | ||
200 | struct Struct0<T = ()>; | ||
201 | |||
202 | #[struct1] | ||
203 | struct Struct1<T>(#[struct1fld] u8); | ||
204 | |||
205 | #[struct2] | ||
206 | struct Struct2<T> { | ||
207 | #[struct2fld] | ||
208 | fld: (T, ), | ||
209 | } | ||
210 | |||
211 | #[en] | ||
212 | enum En { | ||
213 | #[enum_variant] | ||
214 | Variant { | ||
215 | #[enum_field] | ||
216 | field: u8, | ||
217 | }, | ||
218 | } | ||
219 | |||
220 | #[un] | ||
221 | union Un { | ||
222 | #[union_fld] | ||
223 | fld: u16, | ||
224 | } | ||
225 | ", | ||
226 | expect![[r##" | ||
227 | inner attrs: Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr"))] }, input: None }]) } | ||
228 | |||
229 | top-level items: | ||
230 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }] | ||
231 | Import { path: ModPath { kind: Plain, segments: [Name(Text("a"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: false, is_prelude: false, ast_id: FileAstId::<syntax::ast::generated::nodes::Use>(0), index: 0 } | ||
232 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }] | ||
233 | Import { path: ModPath { kind: Plain, segments: [Name(Text("b"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: true, is_prelude: false, ast_id: FileAstId::<syntax::ast::generated::nodes::Use>(0), index: 1 } | ||
234 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("ext_crate"))] }, input: None }]) }] | ||
235 | ExternCrate { name: Name(Text("krate")), alias: None, visibility: RawVisibilityId("pub(self)"), is_macro_use: false, ast_id: FileAstId::<syntax::ast::generated::nodes::ExternCrate>(1) } | ||
236 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_trait"))] }, input: None }]) }] | ||
237 | Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [TypeAlias(Idx::<TypeAlias>(0)), Const(Idx::<Const>(0)), Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<syntax::ast::generated::nodes::Trait>(2) } | ||
238 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_ty"))] }, input: None }]) }] | ||
239 | > TypeAlias { name: Name(Text("AssocTy")), visibility: RawVisibilityId("pub(self)"), bounds: [Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Tr"))] }, generic_args: [Some(GenericArgs { args: [Type(Tuple([]))], has_self_type: false, bindings: [] })] })], generic_params: GenericParamsId(4294967295), type_ref: None, is_extern: false, ast_id: FileAstId::<syntax::ast::generated::nodes::TypeAlias>(8) } | ||
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::<syntax::ast::generated::nodes::Const>(9) } | ||
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, has_body: false, 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::<syntax::ast::generated::nodes::Fn>(10) } | ||
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, has_body: 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::<syntax::ast::generated::nodes::Fn>(11) } | ||
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::<syntax::ast::generated::nodes::Struct>(3), kind: Unit } | ||
248 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct1"))] }, input: None }]) }] | ||
249 | Struct { name: Name(Text("Struct1")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(2), fields: Tuple(IdRange::<hir_def::item_tree::Field>(0..1)), ast_id: FileAstId::<syntax::ast::generated::nodes::Struct>(4), kind: Tuple } | ||
250 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct2"))] }, input: None }]) }] | ||
251 | Struct { name: Name(Text("Struct2")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(3), fields: Record(IdRange::<hir_def::item_tree::Field>(1..2)), ast_id: FileAstId::<syntax::ast::generated::nodes::Struct>(5), kind: Record } | ||
252 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("en"))] }, input: None }]) }] | ||
253 | Enum { name: Name(Text("En")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), variants: IdRange::<hir_def::item_tree::Variant>(0..1), ast_id: FileAstId::<syntax::ast::generated::nodes::Enum>(6) } | ||
254 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("un"))] }, input: None }]) }] | ||
255 | Union { name: Name(Text("Un")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), fields: Record(IdRange::<hir_def::item_tree::Field>(3..4)), ast_id: FileAstId::<syntax::ast::generated::nodes::Union>(7) } | ||
256 | "##]], | ||
257 | ); | ||
258 | } | ||
259 | |||
260 | #[test] | ||
261 | fn simple_inner_items() { | ||
262 | check( | ||
263 | r" | ||
264 | impl<T:A> D for Response<T> { | ||
265 | fn foo() { | ||
266 | end(); | ||
267 | fn end<W: Write>() { | ||
268 | let _x: T = loop {}; | ||
269 | } | ||
270 | } | ||
271 | } | ||
272 | ", | ||
273 | expect![[r#" | ||
274 | inner attrs: Attrs { entries: None } | ||
275 | |||
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::<syntax::ast::generated::nodes::Impl>(0) } | ||
278 | > Function { name: Name(Text("foo")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, has_body: true, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(1) } | ||
279 | |||
280 | inner items: | ||
281 | |||
282 | for AST FileAstId::<syntax::ast::generated::nodes::Item>(2): | ||
283 | Function { name: Name(Text("end")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(1), has_self_param: false, has_body: true, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(2) } | ||
284 | |||
285 | "#]], | ||
286 | ); | ||
287 | } | ||
288 | |||
289 | #[test] | ||
290 | fn extern_attrs() { | ||
291 | check( | ||
292 | r#" | ||
293 | #[block_attr] | ||
294 | extern "C" { | ||
295 | #[attr_a] | ||
296 | fn a() {} | ||
297 | #[attr_b] | ||
298 | fn b() {} | ||
299 | } | ||
300 | "#, | ||
301 | expect![[r##" | ||
302 | inner attrs: Attrs { entries: None } | ||
303 | |||
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 }]) }] | ||
306 | Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, has_body: true, is_unsafe: true, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(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 }]) }] | ||
308 | Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, has_body: true, is_unsafe: true, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(2) } | ||
309 | "##]], | ||
310 | ); | ||
311 | } | ||
312 | |||
313 | #[test] | ||
314 | fn trait_attrs() { | ||
315 | check( | ||
316 | r#" | ||
317 | #[trait_attr] | ||
318 | trait Tr { | ||
319 | #[attr_a] | ||
320 | fn a() {} | ||
321 | #[attr_b] | ||
322 | fn b() {} | ||
323 | } | ||
324 | "#, | ||
325 | expect![[r##" | ||
326 | inner attrs: Attrs { entries: None } | ||
327 | |||
328 | top-level items: | ||
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::<syntax::ast::generated::nodes::Trait>(0) } | ||
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, has_body: true, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(1) } | ||
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, has_body: true, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(2) } | ||
335 | "##]], | ||
336 | ); | ||
337 | } | ||
338 | |||
339 | #[test] | ||
340 | fn impl_attrs() { | ||
341 | check( | ||
342 | r#" | ||
343 | #[impl_attr] | ||
344 | impl Ty { | ||
345 | #[attr_a] | ||
346 | fn a() {} | ||
347 | #[attr_b] | ||
348 | fn b() {} | ||
349 | } | ||
350 | "#, | ||
351 | expect![[r##" | ||
352 | inner attrs: Attrs { entries: None } | ||
353 | |||
354 | top-level items: | ||
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::<syntax::ast::generated::nodes::Impl>(0) } | ||
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, has_body: true, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(1) } | ||
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, has_body: true, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(2) } | ||
361 | "##]], | ||
362 | ); | ||
363 | } | ||
364 | |||
365 | #[test] | ||
366 | fn cursed_inner_items() { | ||
367 | test_inner_items( | ||
368 | r" | ||
369 | struct S<T: Trait = [u8; { fn f() {} 0 }]>(T); | ||
370 | |||
371 | enum En { | ||
372 | Var1 { | ||
373 | t: [(); { trait Inner {} 0 }], | ||
374 | }, | ||
375 | |||
376 | Var2([u16; { enum Inner {} 0 }]), | ||
377 | } | ||
378 | |||
379 | type Ty = [En; { struct Inner; 0 }]; | ||
380 | |||
381 | impl En { | ||
382 | fn assoc() { | ||
383 | trait InnerTrait<T = [u8; { fn f() {} }]> {} | ||
384 | struct InnerStruct<T = [u8; { fn f() {} }]> {} | ||
385 | impl<T = [u8; { fn f() {} }]> InnerTrait for InnerStruct {} | ||
386 | } | ||
387 | } | ||
388 | |||
389 | trait Tr<T = [u8; { fn f() {} }]> { | ||
390 | type AssocTy = [u8; { fn f() {} }]; | ||
391 | |||
392 | const AssocConst: [u8; { fn f() {} }]; | ||
393 | } | ||
394 | ", | ||
395 | ); | ||
396 | } | ||
397 | |||
398 | #[test] | ||
399 | fn inner_item_attrs() { | ||
400 | check( | ||
401 | r" | ||
402 | fn foo() { | ||
403 | #[on_inner] | ||
404 | fn inner() {} | ||
405 | } | ||
406 | ", | ||
407 | expect![[r##" | ||
408 | inner attrs: Attrs { entries: None } | ||
409 | |||
410 | top-level items: | ||
411 | Function { name: Name(Text("foo")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, has_body: true, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(0) } | ||
412 | |||
413 | inner items: | ||
414 | |||
415 | for AST FileAstId::<syntax::ast::generated::nodes::Item>(1): | ||
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, has_body: true, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(1) } | ||
418 | |||
419 | "##]], | ||
420 | ); | ||
421 | } | ||
422 | |||
423 | #[test] | ||
424 | fn assoc_item_macros() { | ||
425 | check( | ||
426 | r" | ||
427 | impl S { | ||
428 | items!(); | ||
429 | } | ||
430 | ", | ||
431 | expect![[r#" | ||
432 | inner attrs: Attrs { entries: None } | ||
433 | |||
434 | top-level items: | ||
435 | Impl { generic_params: GenericParamsId(4294967295), target_trait: None, target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("S"))] }, generic_args: [None] }), is_negative: false, items: [MacroCall(Idx::<MacroCall>(0))], ast_id: FileAstId::<syntax::ast::generated::nodes::Impl>(0) } | ||
436 | > MacroCall { name: None, path: ModPath { kind: Plain, segments: [Name(Text("items"))] }, is_export: false, is_local_inner: false, is_builtin: false, ast_id: FileAstId::<syntax::ast::generated::nodes::MacroCall>(1) } | ||
437 | "#]], | ||
438 | ); | ||
439 | } | ||
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs index 4b3e2fa8f..25587e116 100644 --- a/crates/hir_ty/src/diagnostics/decl_check.rs +++ b/crates/hir_ty/src/diagnostics/decl_check.rs | |||
@@ -26,6 +26,7 @@ use syntax::{ | |||
26 | ast::{self, NameOwner}, | 26 | ast::{self, NameOwner}, |
27 | AstNode, AstPtr, | 27 | AstNode, AstPtr, |
28 | }; | 28 | }; |
29 | use test_utils::mark; | ||
29 | 30 | ||
30 | use crate::{ | 31 | use crate::{ |
31 | db::HirDatabase, | 32 | db::HirDatabase, |
@@ -87,6 +88,11 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
87 | 88 | ||
88 | fn validate_func(&mut self, db: &dyn HirDatabase, func: FunctionId) { | 89 | fn validate_func(&mut self, db: &dyn HirDatabase, func: FunctionId) { |
89 | let data = db.function_data(func); | 90 | let data = db.function_data(func); |
91 | if data.is_extern { | ||
92 | mark::hit!(extern_func_incorrect_case_ignored); | ||
93 | return; | ||
94 | } | ||
95 | |||
90 | let body = db.body(func.into()); | 96 | let body = db.body(func.into()); |
91 | 97 | ||
92 | // Recursively validate inner scope items, such as static variables and constants. | 98 | // Recursively validate inner scope items, such as static variables and constants. |
@@ -648,6 +654,10 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
648 | 654 | ||
649 | fn validate_static(&mut self, db: &dyn HirDatabase, static_id: StaticId) { | 655 | fn validate_static(&mut self, db: &dyn HirDatabase, static_id: StaticId) { |
650 | let data = db.static_data(static_id); | 656 | let data = db.static_data(static_id); |
657 | if data.is_extern { | ||
658 | mark::hit!(extern_static_incorrect_case_ignored); | ||
659 | return; | ||
660 | } | ||
651 | 661 | ||
652 | if self.allowed(db, static_id.into(), allow::NON_UPPER_CASE_GLOBAL) { | 662 | if self.allowed(db, static_id.into(), allow::NON_UPPER_CASE_GLOBAL) { |
653 | return; | 663 | return; |
@@ -709,6 +719,8 @@ fn pat_equals_to_name(pat: Option<ast::Pat>, name: &Name) -> bool { | |||
709 | 719 | ||
710 | #[cfg(test)] | 720 | #[cfg(test)] |
711 | mod tests { | 721 | mod tests { |
722 | use test_utils::mark; | ||
723 | |||
712 | use crate::diagnostics::tests::check_diagnostics; | 724 | use crate::diagnostics::tests::check_diagnostics; |
713 | 725 | ||
714 | #[test] | 726 | #[test] |
@@ -920,4 +932,18 @@ fn main() { | |||
920 | "#, | 932 | "#, |
921 | ); | 933 | ); |
922 | } | 934 | } |
935 | |||
936 | #[test] | ||
937 | fn ignores_extern_items() { | ||
938 | mark::check!(extern_func_incorrect_case_ignored); | ||
939 | mark::check!(extern_static_incorrect_case_ignored); | ||
940 | check_diagnostics( | ||
941 | r#" | ||
942 | extern { | ||
943 | fn NonSnakeCaseName(SOME_VAR: u8) -> u8; | ||
944 | pub static SomeStatic: u8 = 10; | ||
945 | } | ||
946 | "#, | ||
947 | ); | ||
948 | } | ||
923 | } | 949 | } |