aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def')
-rw-r--r--crates/hir_def/src/attr.rs14
-rw-r--r--crates/hir_def/src/body.rs69
-rw-r--r--crates/hir_def/src/body/diagnostics.rs20
-rw-r--r--crates/hir_def/src/body/lower.rs60
-rw-r--r--crates/hir_def/src/body/tests.rs75
-rw-r--r--crates/hir_def/src/diagnostics.rs54
-rw-r--r--crates/hir_def/src/import_map.rs6
-rw-r--r--crates/hir_def/src/item_tree.rs18
-rw-r--r--crates/hir_def/src/item_tree/lower.rs47
-rw-r--r--crates/hir_def/src/nameres.rs29
-rw-r--r--crates/hir_def/src/nameres/collector.rs37
-rw-r--r--crates/hir_def/src/nameres/tests.rs17
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs56
-rw-r--r--crates/hir_def/src/test_db.rs44
14 files changed, 428 insertions, 118 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index dea552a60..b2ce7ca3c 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -125,12 +125,20 @@ impl Attrs {
125 AttrQuery { attrs: self, key } 125 AttrQuery { attrs: self, key }
126 } 126 }
127 127
128 pub fn cfg(&self) -> impl Iterator<Item = CfgExpr> + '_ { 128 pub fn cfg(&self) -> Option<CfgExpr> {
129 // FIXME: handle cfg_attr :-) 129 // FIXME: handle cfg_attr :-)
130 self.by_key("cfg").tt_values().map(CfgExpr::parse) 130 let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse).collect::<Vec<_>>();
131 match cfgs.len() {
132 0 => None,
133 1 => Some(cfgs.pop().unwrap()),
134 _ => Some(CfgExpr::All(cfgs)),
135 }
131 } 136 }
132 pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool { 137 pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
133 self.cfg().all(|cfg| cfg_options.check(&cfg) != Some(false)) 138 match self.cfg() {
139 None => true,
140 Some(cfg) => cfg_options.check(&cfg) != Some(false),
141 }
134 } 142 }
135} 143}
136 144
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
index 9a9a605dd..d10b1af01 100644
--- a/crates/hir_def/src/body.rs
+++ b/crates/hir_def/src/body.rs
@@ -1,6 +1,9 @@
1//! Defines `Body`: a lowered representation of bodies of functions, statics and 1//! Defines `Body`: a lowered representation of bodies of functions, statics and
2//! consts. 2//! consts.
3mod lower; 3mod lower;
4mod diagnostics;
5#[cfg(test)]
6mod tests;
4pub mod scope; 7pub mod scope;
5 8
6use std::{mem, ops::Index, sync::Arc}; 9use std::{mem, ops::Index, sync::Arc};
@@ -10,7 +13,10 @@ use base_db::CrateId;
10use cfg::CfgOptions; 13use cfg::CfgOptions;
11use drop_bomb::DropBomb; 14use drop_bomb::DropBomb;
12use either::Either; 15use either::Either;
13use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId}; 16use hir_expand::{
17 ast_id_map::AstIdMap, diagnostics::DiagnosticSink, hygiene::Hygiene, AstId, HirFileId, InFile,
18 MacroDefId,
19};
14use rustc_hash::FxHashMap; 20use rustc_hash::FxHashMap;
15use syntax::{ast, AstNode, AstPtr}; 21use syntax::{ast, AstNode, AstPtr};
16use test_utils::mark; 22use test_utils::mark;
@@ -105,14 +111,16 @@ impl Expander {
105 111
106 let macro_call = InFile::new(self.current_file_id, &macro_call); 112 let macro_call = InFile::new(self.current_file_id, &macro_call);
107 113
108 if let Some(call_id) = macro_call.as_call_id(db, self.crate_def_map.krate, |path| { 114 let resolver = |path: ModPath| -> Option<MacroDefId> {
109 if let Some(local_scope) = local_scope { 115 if let Some(local_scope) = local_scope {
110 if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) { 116 if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) {
111 return Some(def); 117 return Some(def);
112 } 118 }
113 } 119 }
114 self.resolve_path_as_macro(db, &path) 120 self.resolve_path_as_macro(db, &path)
115 }) { 121 };
122
123 if let Some(call_id) = macro_call.as_call_id(db, self.crate_def_map.krate, resolver) {
116 let file_id = call_id.as_file(); 124 let file_id = call_id.as_file();
117 if let Some(node) = db.parse_or_expand(file_id) { 125 if let Some(node) = db.parse_or_expand(file_id) {
118 if let Some(expr) = T::cast(node) { 126 if let Some(expr) = T::cast(node) {
@@ -148,8 +156,12 @@ impl Expander {
148 InFile { file_id: self.current_file_id, value } 156 InFile { file_id: self.current_file_id, value }
149 } 157 }
150 158
151 pub(crate) fn is_cfg_enabled(&self, owner: &dyn ast::AttrsOwner) -> bool { 159 pub(crate) fn parse_attrs(&self, owner: &dyn ast::AttrsOwner) -> Attrs {
152 self.cfg_expander.is_cfg_enabled(owner) 160 self.cfg_expander.parse_attrs(owner)
161 }
162
163 pub(crate) fn cfg_options(&self) -> &CfgOptions {
164 &self.cfg_expander.cfg_options
153 } 165 }
154 166
155 fn parse_path(&mut self, path: ast::Path) -> Option<Path> { 167 fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
@@ -217,6 +229,10 @@ pub struct BodySourceMap {
217 pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>, 229 pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>,
218 field_map: FxHashMap<(ExprId, usize), InFile<AstPtr<ast::RecordExprField>>>, 230 field_map: FxHashMap<(ExprId, usize), InFile<AstPtr<ast::RecordExprField>>>,
219 expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>, 231 expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
232
233 /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in
234 /// the source map (since they're just as volatile).
235 diagnostics: Vec<diagnostics::BodyDiagnostic>,
220} 236}
221 237
222#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)] 238#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
@@ -316,45 +332,10 @@ impl BodySourceMap {
316 pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordExprField>> { 332 pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordExprField>> {
317 self.field_map[&(expr, field)].clone() 333 self.field_map[&(expr, field)].clone()
318 } 334 }
319}
320 335
321#[cfg(test)] 336 pub(crate) fn add_diagnostics(&self, _db: &dyn DefDatabase, sink: &mut DiagnosticSink<'_>) {
322mod tests { 337 for diag in &self.diagnostics {
323 use base_db::{fixture::WithFixture, SourceDatabase}; 338 diag.add_to(sink);
324 use test_utils::mark; 339 }
325
326 use crate::ModuleDefId;
327
328 use super::*;
329
330 fn lower(ra_fixture: &str) -> Arc<Body> {
331 let (db, file_id) = crate::test_db::TestDB::with_single_file(ra_fixture);
332
333 let krate = db.crate_graph().iter().next().unwrap();
334 let def_map = db.crate_def_map(krate);
335 let module = def_map.modules_for_file(file_id).next().unwrap();
336 let module = &def_map[module];
337 let fn_def = match module.scope.declarations().next().unwrap() {
338 ModuleDefId::FunctionId(it) => it,
339 _ => panic!(),
340 };
341
342 db.body(fn_def.into())
343 }
344
345 #[test]
346 fn your_stack_belongs_to_me() {
347 mark::check!(your_stack_belongs_to_me);
348 lower(
349 "
350macro_rules! n_nuple {
351 ($e:tt) => ();
352 ($($rest:tt)*) => {{
353 (n_nuple!($($rest)*)None,)
354 }};
355}
356fn main() { n_nuple!(1,2,3); }
357",
358 );
359 } 340 }
360} 341}
diff --git a/crates/hir_def/src/body/diagnostics.rs b/crates/hir_def/src/body/diagnostics.rs
new file mode 100644
index 000000000..cfa47d189
--- /dev/null
+++ b/crates/hir_def/src/body/diagnostics.rs
@@ -0,0 +1,20 @@
1//! Diagnostics emitted during body lowering.
2
3use hir_expand::diagnostics::DiagnosticSink;
4
5use crate::diagnostics::InactiveCode;
6
7#[derive(Debug, Eq, PartialEq)]
8pub enum BodyDiagnostic {
9 InactiveCode(InactiveCode),
10}
11
12impl BodyDiagnostic {
13 pub fn add_to(&self, sink: &mut DiagnosticSink<'_>) {
14 match self {
15 BodyDiagnostic::InactiveCode(diag) => {
16 sink.push(diag.clone());
17 }
18 }
19 }
20}
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 01e72690a..ddc267b83 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -16,7 +16,7 @@ use syntax::{
16 self, ArgListOwner, ArrayExprKind, AstChildren, LiteralKind, LoopBodyOwner, NameOwner, 16 self, ArgListOwner, ArrayExprKind, AstChildren, LiteralKind, LoopBodyOwner, NameOwner,
17 SlicePatComponents, 17 SlicePatComponents,
18 }, 18 },
19 AstNode, AstPtr, 19 AstNode, AstPtr, SyntaxNodePtr,
20}; 20};
21use test_utils::mark; 21use test_utils::mark;
22 22
@@ -25,6 +25,7 @@ use crate::{
25 body::{Body, BodySourceMap, Expander, PatPtr, SyntheticSyntax}, 25 body::{Body, BodySourceMap, Expander, PatPtr, SyntheticSyntax},
26 builtin_type::{BuiltinFloat, BuiltinInt}, 26 builtin_type::{BuiltinFloat, BuiltinInt},
27 db::DefDatabase, 27 db::DefDatabase,
28 diagnostics::InactiveCode,
28 expr::{ 29 expr::{
29 dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, 30 dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal,
30 LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, 31 LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
@@ -37,7 +38,7 @@ use crate::{
37 StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, 38 StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
38}; 39};
39 40
40use super::{ExprSource, PatSource}; 41use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource};
41 42
42pub(crate) struct LowerCtx { 43pub(crate) struct LowerCtx {
43 hygiene: Hygiene, 44 hygiene: Hygiene,
@@ -176,7 +177,7 @@ impl ExprCollector<'_> {
176 177
177 fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { 178 fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
178 let syntax_ptr = AstPtr::new(&expr); 179 let syntax_ptr = AstPtr::new(&expr);
179 if !self.expander.is_cfg_enabled(&expr) { 180 if self.check_cfg(&expr).is_none() {
180 return self.missing_expr(); 181 return self.missing_expr();
181 } 182 }
182 183
@@ -354,13 +355,15 @@ impl ExprCollector<'_> {
354 let arms = if let Some(match_arm_list) = e.match_arm_list() { 355 let arms = if let Some(match_arm_list) = e.match_arm_list() {
355 match_arm_list 356 match_arm_list
356 .arms() 357 .arms()
357 .map(|arm| MatchArm { 358 .filter_map(|arm| {
358 pat: self.collect_pat_opt(arm.pat()), 359 self.check_cfg(&arm).map(|()| MatchArm {
359 expr: self.collect_expr_opt(arm.expr()), 360 pat: self.collect_pat_opt(arm.pat()),
360 guard: arm 361 expr: self.collect_expr_opt(arm.expr()),
361 .guard() 362 guard: arm
362 .and_then(|guard| guard.expr()) 363 .guard()
363 .map(|e| self.collect_expr(e)), 364 .and_then(|guard| guard.expr())
365 .map(|e| self.collect_expr(e)),
366 })
364 }) 367 })
365 .collect() 368 .collect()
366 } else { 369 } else {
@@ -406,9 +409,8 @@ impl ExprCollector<'_> {
406 .fields() 409 .fields()
407 .inspect(|field| field_ptrs.push(AstPtr::new(field))) 410 .inspect(|field| field_ptrs.push(AstPtr::new(field)))
408 .filter_map(|field| { 411 .filter_map(|field| {
409 if !self.expander.is_cfg_enabled(&field) { 412 self.check_cfg(&field)?;
410 return None; 413
411 }
412 let name = field.field_name()?.as_name(); 414 let name = field.field_name()?.as_name();
413 415
414 Some(RecordLitField { 416 Some(RecordLitField {
@@ -620,15 +622,23 @@ impl ExprCollector<'_> {
620 .filter_map(|s| { 622 .filter_map(|s| {
621 let stmt = match s { 623 let stmt = match s {
622 ast::Stmt::LetStmt(stmt) => { 624 ast::Stmt::LetStmt(stmt) => {
625 self.check_cfg(&stmt)?;
626
623 let pat = self.collect_pat_opt(stmt.pat()); 627 let pat = self.collect_pat_opt(stmt.pat());
624 let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it)); 628 let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it));
625 let initializer = stmt.initializer().map(|e| self.collect_expr(e)); 629 let initializer = stmt.initializer().map(|e| self.collect_expr(e));
626 Statement::Let { pat, type_ref, initializer } 630 Statement::Let { pat, type_ref, initializer }
627 } 631 }
628 ast::Stmt::ExprStmt(stmt) => { 632 ast::Stmt::ExprStmt(stmt) => {
633 self.check_cfg(&stmt)?;
634
629 Statement::Expr(self.collect_expr_opt(stmt.expr())) 635 Statement::Expr(self.collect_expr_opt(stmt.expr()))
630 } 636 }
631 ast::Stmt::Item(_) => return None, 637 ast::Stmt::Item(item) => {
638 self.check_cfg(&item)?;
639
640 return None;
641 }
632 }; 642 };
633 Some(stmt) 643 Some(stmt)
634 }) 644 })
@@ -872,6 +882,28 @@ impl ExprCollector<'_> {
872 882
873 (args, ellipsis) 883 (args, ellipsis)
874 } 884 }
885
886 /// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when
887 /// not.
888 fn check_cfg(&mut self, owner: &dyn ast::AttrsOwner) -> Option<()> {
889 match self.expander.parse_attrs(owner).cfg() {
890 Some(cfg) => {
891 if self.expander.cfg_options().check(&cfg) != Some(false) {
892 return Some(());
893 }
894
895 self.source_map.diagnostics.push(BodyDiagnostic::InactiveCode(InactiveCode {
896 file: self.expander.current_file_id,
897 node: SyntaxNodePtr::new(owner.syntax()),
898 cfg,
899 opts: self.expander.cfg_options().clone(),
900 }));
901
902 None
903 }
904 None => Some(()),
905 }
906 }
875} 907}
876 908
877impl From<ast::BinOp> for BinaryOp { 909impl From<ast::BinOp> for BinaryOp {
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs
new file mode 100644
index 000000000..f07df5cee
--- /dev/null
+++ b/crates/hir_def/src/body/tests.rs
@@ -0,0 +1,75 @@
1use base_db::{fixture::WithFixture, SourceDatabase};
2use test_utils::mark;
3
4use crate::{test_db::TestDB, ModuleDefId};
5
6use super::*;
7
8fn lower(ra_fixture: &str) -> Arc<Body> {
9 let (db, file_id) = crate::test_db::TestDB::with_single_file(ra_fixture);
10
11 let krate = db.crate_graph().iter().next().unwrap();
12 let def_map = db.crate_def_map(krate);
13 let module = def_map.modules_for_file(file_id).next().unwrap();
14 let module = &def_map[module];
15 let fn_def = match module.scope.declarations().next().unwrap() {
16 ModuleDefId::FunctionId(it) => it,
17 _ => panic!(),
18 };
19
20 db.body(fn_def.into())
21}
22
23fn check_diagnostics(ra_fixture: &str) {
24 let db: TestDB = TestDB::with_files(ra_fixture);
25 db.check_diagnostics();
26}
27
28#[test]
29fn your_stack_belongs_to_me() {
30 mark::check!(your_stack_belongs_to_me);
31 lower(
32 "
33macro_rules! n_nuple {
34 ($e:tt) => ();
35 ($($rest:tt)*) => {{
36 (n_nuple!($($rest)*)None,)
37 }};
38}
39fn main() { n_nuple!(1,2,3); }
40",
41 );
42}
43
44#[test]
45fn cfg_diagnostics() {
46 check_diagnostics(
47 r"
48fn f() {
49 // The three g̶e̶n̶d̶e̶r̶s̶ statements:
50
51 #[cfg(a)] fn f() {} // Item statement
52 //^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
53 #[cfg(a)] {} // Expression statement
54 //^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
55 #[cfg(a)] let x = 0; // let statement
56 //^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
57
58 abc(#[cfg(a)] 0);
59 //^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
60 let x = Struct {
61 #[cfg(a)] f: 0,
62 //^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
63 };
64 match () {
65 () => (),
66 #[cfg(a)] () => (),
67 //^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
68 }
69
70 #[cfg(a)] 0 // Trailing expression of block
71 //^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled
72}
73 ",
74 );
75}
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs
index 001b3c5db..b221b290c 100644
--- a/crates/hir_def/src/diagnostics.rs
+++ b/crates/hir_def/src/diagnostics.rs
@@ -1,12 +1,23 @@
1//! Diagnostics produced by `hir_def`. 1//! Diagnostics produced by `hir_def`.
2 2
3use std::any::Any; 3use std::any::Any;
4use stdx::format_to;
4 5
5use hir_expand::diagnostics::{Diagnostic, DiagnosticCode}; 6use cfg::{CfgExpr, CfgOptions, DnfExpr};
7use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink};
8use hir_expand::{HirFileId, InFile};
6use syntax::{ast, AstPtr, SyntaxNodePtr}; 9use syntax::{ast, AstPtr, SyntaxNodePtr};
7 10
8use hir_expand::{HirFileId, InFile}; 11use crate::{db::DefDatabase, DefWithBodyId};
9 12
13pub fn validate_body(db: &dyn DefDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) {
14 let source_map = db.body_with_source_map(owner).1;
15 source_map.add_diagnostics(db, sink);
16}
17
18// Diagnostic: unresolved-module
19//
20// This diagnostic is triggered if rust-analyzer is unable to discover referred module.
10#[derive(Debug)] 21#[derive(Debug)]
11pub struct UnresolvedModule { 22pub struct UnresolvedModule {
12 pub file: HirFileId, 23 pub file: HirFileId,
@@ -29,6 +40,9 @@ impl Diagnostic for UnresolvedModule {
29 } 40 }
30} 41}
31 42
43// Diagnostic: unresolved-extern-crate
44//
45// This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate.
32#[derive(Debug)] 46#[derive(Debug)]
33pub struct UnresolvedExternCrate { 47pub struct UnresolvedExternCrate {
34 pub file: HirFileId, 48 pub file: HirFileId,
@@ -50,6 +64,9 @@ impl Diagnostic for UnresolvedExternCrate {
50 } 64 }
51} 65}
52 66
67// Diagnostic: unresolved-import
68//
69// This diagnostic is triggered if rust-analyzer is unable to discover imported module.
53#[derive(Debug)] 70#[derive(Debug)]
54pub struct UnresolvedImport { 71pub struct UnresolvedImport {
55 pub file: HirFileId, 72 pub file: HirFileId,
@@ -77,3 +94,36 @@ impl Diagnostic for UnresolvedImport {
77 true 94 true
78 } 95 }
79} 96}
97
98// Diagnostic: inactive-code
99//
100// This diagnostic is shown for code with inactive `#[cfg]` attributes.
101#[derive(Debug, Clone, Eq, PartialEq)]
102pub struct InactiveCode {
103 pub file: HirFileId,
104 pub node: SyntaxNodePtr,
105 pub cfg: CfgExpr,
106 pub opts: CfgOptions,
107}
108
109impl Diagnostic for InactiveCode {
110 fn code(&self) -> DiagnosticCode {
111 DiagnosticCode("inactive-code")
112 }
113 fn message(&self) -> String {
114 let inactive = DnfExpr::new(self.cfg.clone()).why_inactive(&self.opts);
115 let mut buf = "code is inactive due to #[cfg] directives".to_string();
116
117 if let Some(inactive) = inactive {
118 format_to!(buf, ": {}", inactive);
119 }
120
121 buf
122 }
123 fn display_source(&self) -> InFile<SyntaxNodePtr> {
124 InFile::new(self.file, self.node.clone())
125 }
126 fn as_any(&self) -> &(dyn Any + Send + 'static) {
127 self
128 }
129}
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
index 028cae2e7..1e24f29a8 100644
--- a/crates/hir_def/src/import_map.rs
+++ b/crates/hir_def/src/import_map.rs
@@ -356,7 +356,7 @@ mod tests {
356 let krate = crate_graph 356 let krate = crate_graph
357 .iter() 357 .iter()
358 .find(|krate| { 358 .find(|krate| {
359 crate_graph[*krate].declaration_name.as_ref().map(|n| n.to_string()) 359 crate_graph[*krate].display_name.as_ref().map(|n| n.to_string())
360 == Some(crate_name.to_string()) 360 == Some(crate_name.to_string())
361 }) 361 })
362 .unwrap(); 362 .unwrap();
@@ -375,7 +375,7 @@ mod tests {
375 let path = map.path_of(item).unwrap(); 375 let path = map.path_of(item).unwrap();
376 format!( 376 format!(
377 "{}::{} ({})\n", 377 "{}::{} ({})\n",
378 crate_graph[krate].declaration_name.as_ref().unwrap(), 378 crate_graph[krate].display_name.as_ref().unwrap(),
379 path, 379 path,
380 mark 380 mark
381 ) 381 )
@@ -416,7 +416,7 @@ mod tests {
416 .iter() 416 .iter()
417 .filter_map(|krate| { 417 .filter_map(|krate| {
418 let cdata = &crate_graph[krate]; 418 let cdata = &crate_graph[krate];
419 let name = cdata.declaration_name.as_ref()?; 419 let name = cdata.display_name.as_ref()?;
420 420
421 let map = db.import_map(krate); 421 let map = db.import_map(krate);
422 422
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 8a1121bbd..7eb388bae 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -672,6 +672,24 @@ impl ModItem {
672 pub fn downcast<N: ItemTreeNode>(self) -> Option<FileItemTreeId<N>> { 672 pub fn downcast<N: ItemTreeNode>(self) -> Option<FileItemTreeId<N>> {
673 N::id_from_mod_item(self) 673 N::id_from_mod_item(self)
674 } 674 }
675
676 pub fn ast_id(&self, tree: &ItemTree) -> FileAstId<ast::Item> {
677 match self {
678 ModItem::Import(it) => tree[it.index].ast_id().upcast(),
679 ModItem::ExternCrate(it) => tree[it.index].ast_id().upcast(),
680 ModItem::Function(it) => tree[it.index].ast_id().upcast(),
681 ModItem::Struct(it) => tree[it.index].ast_id().upcast(),
682 ModItem::Union(it) => tree[it.index].ast_id().upcast(),
683 ModItem::Enum(it) => tree[it.index].ast_id().upcast(),
684 ModItem::Const(it) => tree[it.index].ast_id().upcast(),
685 ModItem::Static(it) => tree[it.index].ast_id().upcast(),
686 ModItem::Trait(it) => tree[it.index].ast_id().upcast(),
687 ModItem::Impl(it) => tree[it.index].ast_id().upcast(),
688 ModItem::TypeAlias(it) => tree[it.index].ast_id().upcast(),
689 ModItem::Mod(it) => tree[it.index].ast_id().upcast(),
690 ModItem::MacroCall(it) => tree[it.index].ast_id().upcast(),
691 }
692 }
675} 693}
676 694
677#[derive(Debug, Copy, Clone, Eq, PartialEq)] 695#[derive(Debug, Copy, Clone, Eq, PartialEq)]
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index 3328639cf..ca7fb4a43 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -3,7 +3,7 @@
3use std::{collections::hash_map::Entry, mem, sync::Arc}; 3use std::{collections::hash_map::Entry, mem, sync::Arc};
4 4
5use arena::map::ArenaMap; 5use arena::map::ArenaMap;
6use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId}; 6use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, name::known, HirFileId};
7use smallvec::SmallVec; 7use smallvec::SmallVec;
8use syntax::{ 8use syntax::{
9 ast::{self, ModuleItemOwner}, 9 ast::{self, ModuleItemOwner},
@@ -555,7 +555,8 @@ impl Ctx {
555 let id: ModItem = match item { 555 let id: ModItem = match item {
556 ast::ExternItem::Fn(ast) => { 556 ast::ExternItem::Fn(ast) => {
557 let func = self.lower_function(&ast)?; 557 let func = self.lower_function(&ast)?;
558 self.data().functions[func.index].is_unsafe = true; 558 self.data().functions[func.index].is_unsafe =
559 is_intrinsic_fn_unsafe(&self.data().functions[func.index].name);
559 func.into() 560 func.into()
560 } 561 }
561 ast::ExternItem::Static(ast) => { 562 ast::ExternItem::Static(ast) => {
@@ -713,3 +714,45 @@ enum GenericsOwner<'a> {
713 TypeAlias, 714 TypeAlias,
714 Impl, 715 Impl,
715} 716}
717
718/// Returns `true` if the given intrinsic is unsafe to call, or false otherwise.
719fn is_intrinsic_fn_unsafe(name: &Name) -> bool {
720 // Should be kept in sync with https://github.com/rust-lang/rust/blob/c6e4db620a7d2f569f11dcab627430921ea8aacf/compiler/rustc_typeck/src/check/intrinsic.rs#L68
721 ![
722 known::abort,
723 known::min_align_of,
724 known::needs_drop,
725 known::caller_location,
726 known::size_of_val,
727 known::min_align_of_val,
728 known::add_with_overflow,
729 known::sub_with_overflow,
730 known::mul_with_overflow,
731 known::wrapping_add,
732 known::wrapping_sub,
733 known::wrapping_mul,
734 known::saturating_add,
735 known::saturating_sub,
736 known::rotate_left,
737 known::rotate_right,
738 known::ctpop,
739 known::ctlz,
740 known::cttz,
741 known::bswap,
742 known::bitreverse,
743 known::discriminant_value,
744 known::type_id,
745 known::likely,
746 known::unlikely,
747 known::ptr_guaranteed_eq,
748 known::ptr_guaranteed_ne,
749 known::minnumf32,
750 known::minnumf64,
751 known::maxnumf32,
752 known::rustc_peek,
753 known::maxnumf64,
754 known::type_name,
755 known::variant_count,
756 ]
757 .contains(&name)
758}
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index 464ffef21..eb41d324e 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -172,11 +172,7 @@ pub struct ModuleData {
172impl CrateDefMap { 172impl CrateDefMap {
173 pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { 173 pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<CrateDefMap> {
174 let _p = profile::span("crate_def_map_query").detail(|| { 174 let _p = profile::span("crate_def_map_query").detail(|| {
175 db.crate_graph()[krate] 175 db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string()
176 .declaration_name
177 .as_ref()
178 .map(ToString::to_string)
179 .unwrap_or_default()
180 }); 176 });
181 let def_map = { 177 let def_map = {
182 let edition = db.crate_graph()[krate].edition; 178 let edition = db.crate_graph()[krate].edition;
@@ -287,10 +283,11 @@ pub enum ModuleSource {
287} 283}
288 284
289mod diagnostics { 285mod diagnostics {
286 use cfg::{CfgExpr, CfgOptions};
290 use hir_expand::diagnostics::DiagnosticSink; 287 use hir_expand::diagnostics::DiagnosticSink;
291 use hir_expand::hygiene::Hygiene; 288 use hir_expand::hygiene::Hygiene;
292 use hir_expand::InFile; 289 use hir_expand::InFile;
293 use syntax::{ast, AstPtr}; 290 use syntax::{ast, AstPtr, SyntaxNodePtr};
294 291
295 use crate::path::ModPath; 292 use crate::path::ModPath;
296 use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId}; 293 use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId};
@@ -302,6 +299,8 @@ mod diagnostics {
302 UnresolvedExternCrate { ast: AstId<ast::ExternCrate> }, 299 UnresolvedExternCrate { ast: AstId<ast::ExternCrate> },
303 300
304 UnresolvedImport { ast: AstId<ast::Use>, index: usize }, 301 UnresolvedImport { ast: AstId<ast::Use>, index: usize },
302
303 UnconfiguredCode { ast: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions },
305 } 304 }
306 305
307 #[derive(Debug, PartialEq, Eq)] 306 #[derive(Debug, PartialEq, Eq)]
@@ -340,6 +339,15 @@ mod diagnostics {
340 Self { in_module: container, kind: DiagnosticKind::UnresolvedImport { ast, index } } 339 Self { in_module: container, kind: DiagnosticKind::UnresolvedImport { ast, index } }
341 } 340 }
342 341
342 pub(super) fn unconfigured_code(
343 container: LocalModuleId,
344 ast: InFile<SyntaxNodePtr>,
345 cfg: CfgExpr,
346 opts: CfgOptions,
347 ) -> Self {
348 Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast, cfg, opts } }
349 }
350
343 pub(super) fn add_to( 351 pub(super) fn add_to(
344 &self, 352 &self,
345 db: &dyn DefDatabase, 353 db: &dyn DefDatabase,
@@ -389,6 +397,15 @@ mod diagnostics {
389 sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) }); 397 sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) });
390 } 398 }
391 } 399 }
400
401 DiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
402 sink.push(InactiveCode {
403 file: ast.file_id,
404 node: ast.value.clone(),
405 cfg: cfg.clone(),
406 opts: opts.clone(),
407 });
408 }
392 } 409 }
393 } 410 }
394 } 411 }
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index c8cd04264..1ff45d244 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -6,7 +6,7 @@
6use std::iter; 6use std::iter;
7 7
8use base_db::{CrateId, FileId, ProcMacroId}; 8use base_db::{CrateId, FileId, ProcMacroId};
9use cfg::CfgOptions; 9use cfg::{CfgExpr, CfgOptions};
10use hir_expand::InFile; 10use hir_expand::InFile;
11use hir_expand::{ 11use hir_expand::{
12 ast_id_map::FileAstId, 12 ast_id_map::FileAstId,
@@ -218,15 +218,18 @@ impl DefCollector<'_> {
218 let item_tree = self.db.item_tree(file_id.into()); 218 let item_tree = self.db.item_tree(file_id.into());
219 let module_id = self.def_map.root; 219 let module_id = self.def_map.root;
220 self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; 220 self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id };
221 ModCollector { 221 let mut root_collector = ModCollector {
222 def_collector: &mut *self, 222 def_collector: &mut *self,
223 macro_depth: 0, 223 macro_depth: 0,
224 module_id, 224 module_id,
225 file_id: file_id.into(), 225 file_id: file_id.into(),
226 item_tree: &item_tree, 226 item_tree: &item_tree,
227 mod_dir: ModDir::root(), 227 mod_dir: ModDir::root(),
228 };
229 if item_tree.top_level_attrs().cfg().map_or(true, |cfg| root_collector.is_cfg_enabled(&cfg))
230 {
231 root_collector.collect(item_tree.top_level_items());
228 } 232 }
229 .collect(item_tree.top_level_items());
230 233
231 // main name resolution fixed-point loop. 234 // main name resolution fixed-point loop.
232 let mut i = 0; 235 let mut i = 0;
@@ -900,7 +903,8 @@ impl ModCollector<'_, '_> {
900 // `#[macro_use] extern crate` is hoisted to imports macros before collecting 903 // `#[macro_use] extern crate` is hoisted to imports macros before collecting
901 // any other items. 904 // any other items.
902 for item in items { 905 for item in items {
903 if self.is_cfg_enabled(self.item_tree.attrs((*item).into())) { 906 let attrs = self.item_tree.attrs((*item).into());
907 if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) {
904 if let ModItem::ExternCrate(id) = item { 908 if let ModItem::ExternCrate(id) = item {
905 let import = self.item_tree[*id].clone(); 909 let import = self.item_tree[*id].clone();
906 if import.is_macro_use { 910 if import.is_macro_use {
@@ -912,8 +916,11 @@ impl ModCollector<'_, '_> {
912 916
913 for &item in items { 917 for &item in items {
914 let attrs = self.item_tree.attrs(item.into()); 918 let attrs = self.item_tree.attrs(item.into());
915 if !self.is_cfg_enabled(attrs) { 919 if let Some(cfg) = attrs.cfg() {
916 continue; 920 if !self.is_cfg_enabled(&cfg) {
921 self.emit_unconfigured_diagnostic(item, &cfg);
922 continue;
923 }
917 } 924 }
918 let module = 925 let module =
919 ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id }; 926 ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
@@ -1320,8 +1327,22 @@ impl ModCollector<'_, '_> {
1320 } 1327 }
1321 } 1328 }
1322 1329
1323 fn is_cfg_enabled(&self, attrs: &Attrs) -> bool { 1330 fn is_cfg_enabled(&self, cfg: &CfgExpr) -> bool {
1324 attrs.is_cfg_enabled(self.def_collector.cfg_options) 1331 self.def_collector.cfg_options.check(cfg) != Some(false)
1332 }
1333
1334 fn emit_unconfigured_diagnostic(&mut self, item: ModItem, cfg: &CfgExpr) {
1335 let ast_id = item.ast_id(self.item_tree);
1336 let id_map = self.def_collector.db.ast_id_map(self.file_id);
1337 let syntax_ptr = id_map.get(ast_id).syntax_node_ptr();
1338
1339 let ast_node = InFile::new(self.file_id, syntax_ptr);
1340 self.def_collector.def_map.diagnostics.push(DefDiagnostic::unconfigured_code(
1341 self.module_id,
1342 ast_node,
1343 cfg.clone(),
1344 self.def_collector.cfg_options.clone(),
1345 ));
1325 } 1346 }
1326} 1347}
1327 1348
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs
index 11d84f808..9c19bf572 100644
--- a/crates/hir_def/src/nameres/tests.rs
+++ b/crates/hir_def/src/nameres/tests.rs
@@ -691,3 +691,20 @@ mod tr {
691 "#]], 691 "#]],
692 ); 692 );
693} 693}
694
695#[test]
696fn cfg_the_entire_crate() {
697 check(
698 r#"
699//- /main.rs
700#![cfg(never)]
701
702pub struct S;
703pub enum E {}
704pub fn f() {}
705 "#,
706 expect![[r#"
707 crate
708 "#]],
709 );
710}
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs
index 576b813d2..1a7b98831 100644
--- a/crates/hir_def/src/nameres/tests/diagnostics.rs
+++ b/crates/hir_def/src/nameres/tests/diagnostics.rs
@@ -1,42 +1,10 @@
1use base_db::fixture::WithFixture; 1use base_db::fixture::WithFixture;
2use base_db::FileId;
3use base_db::SourceDatabaseExt;
4use hir_expand::db::AstDatabase;
5use rustc_hash::FxHashMap;
6use syntax::TextRange;
7use syntax::TextSize;
8 2
9use crate::test_db::TestDB; 3use crate::test_db::TestDB;
10 4
11fn check_diagnostics(ra_fixture: &str) { 5fn check_diagnostics(ra_fixture: &str) {
12 let db: TestDB = TestDB::with_files(ra_fixture); 6 let db: TestDB = TestDB::with_files(ra_fixture);
13 let annotations = db.extract_annotations(); 7 db.check_diagnostics();
14 assert!(!annotations.is_empty());
15
16 let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
17 db.diagnostics(|d| {
18 let src = d.display_source();
19 let root = db.parse_or_expand(src.file_id).unwrap();
20 // FIXME: macros...
21 let file_id = src.file_id.original_file(&db);
22 let range = src.value.to_node(&root).text_range();
23 let message = d.message().to_owned();
24 actual.entry(file_id).or_default().push((range, message));
25 });
26
27 for (file_id, diags) in actual.iter_mut() {
28 diags.sort_by_key(|it| it.0.start());
29 let text = db.file_text(*file_id);
30 // For multiline spans, place them on line start
31 for (range, content) in diags {
32 if text[*range].contains('\n') {
33 *range = TextRange::new(range.start(), range.start() + TextSize::from(1));
34 *content = format!("... {}", content);
35 }
36 }
37 }
38
39 assert_eq!(annotations, actual);
40} 8}
41 9
42#[test] 10#[test]
@@ -129,3 +97,25 @@ fn unresolved_module() {
129 ", 97 ",
130 ); 98 );
131} 99}
100
101#[test]
102fn inactive_item() {
103 // Additional tests in `cfg` crate. This only tests disabled cfgs.
104
105 check_diagnostics(
106 r#"
107 //- /lib.rs
108 #[cfg(no)] pub fn f() {}
109 //^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
110
111 #[cfg(no)] #[cfg(no2)] mod m;
112 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no and no2 are disabled
113
114 #[cfg(all(not(a), b))] enum E {}
115 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: b is disabled
116
117 #[cfg(feature = "std")] use std;
118 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: feature = "std" is disabled
119 "#,
120 );
121}
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs
index fb1d3c974..2b36c824a 100644
--- a/crates/hir_def/src/test_db.rs
+++ b/crates/hir_def/src/test_db.rs
@@ -12,10 +12,10 @@ use hir_expand::diagnostics::Diagnostic;
12use hir_expand::diagnostics::DiagnosticSinkBuilder; 12use hir_expand::diagnostics::DiagnosticSinkBuilder;
13use rustc_hash::FxHashMap; 13use rustc_hash::FxHashMap;
14use rustc_hash::FxHashSet; 14use rustc_hash::FxHashSet;
15use syntax::TextRange; 15use syntax::{TextRange, TextSize};
16use test_utils::extract_annotations; 16use test_utils::extract_annotations;
17 17
18use crate::db::DefDatabase; 18use crate::{db::DefDatabase, ModuleDefId};
19 19
20#[salsa::database( 20#[salsa::database(
21 base_db::SourceDatabaseExtStorage, 21 base_db::SourceDatabaseExtStorage,
@@ -135,9 +135,47 @@ impl TestDB {
135 let crate_def_map = self.crate_def_map(krate); 135 let crate_def_map = self.crate_def_map(krate);
136 136
137 let mut sink = DiagnosticSinkBuilder::new().build(&mut cb); 137 let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
138 for (module_id, _) in crate_def_map.modules.iter() { 138 for (module_id, module) in crate_def_map.modules.iter() {
139 crate_def_map.add_diagnostics(self, module_id, &mut sink); 139 crate_def_map.add_diagnostics(self, module_id, &mut sink);
140
141 for decl in module.scope.declarations() {
142 if let ModuleDefId::FunctionId(it) = decl {
143 let source_map = self.body_with_source_map(it.into()).1;
144 source_map.add_diagnostics(self, &mut sink);
145 }
146 }
140 } 147 }
141 } 148 }
142 } 149 }
150
151 pub fn check_diagnostics(&self) {
152 let db: &TestDB = self;
153 let annotations = db.extract_annotations();
154 assert!(!annotations.is_empty());
155
156 let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
157 db.diagnostics(|d| {
158 let src = d.display_source();
159 let root = db.parse_or_expand(src.file_id).unwrap();
160 // FIXME: macros...
161 let file_id = src.file_id.original_file(db);
162 let range = src.value.to_node(&root).text_range();
163 let message = d.message().to_owned();
164 actual.entry(file_id).or_default().push((range, message));
165 });
166
167 for (file_id, diags) in actual.iter_mut() {
168 diags.sort_by_key(|it| it.0.start());
169 let text = db.file_text(*file_id);
170 // For multiline spans, place them on line start
171 for (range, content) in diags {
172 if text[*range].contains('\n') {
173 *range = TextRange::new(range.start(), range.start() + TextSize::from(1));
174 *content = format!("... {}", content);
175 }
176 }
177 }
178
179 assert_eq!(annotations, actual);
180 }
143} 181}