From 93dc6f511bedb7c18319bbf3efe47a7db4b2aa53 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 23 Oct 2020 19:27:04 +0200 Subject: Diagnose #[cfg]s in bodies --- crates/hir_def/src/body/diagnostics.rs | 20 +++++++++ crates/hir_def/src/body/lower.rs | 60 ++++++++++++++++++++------- crates/hir_def/src/body/tests.rs | 75 ++++++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+), 14 deletions(-) create mode 100644 crates/hir_def/src/body/diagnostics.rs create mode 100644 crates/hir_def/src/body/tests.rs (limited to 'crates/hir_def/src/body') 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 @@ +//! Diagnostics emitted during body lowering. + +use hir_expand::diagnostics::DiagnosticSink; + +use crate::diagnostics::InactiveCode; + +#[derive(Debug, Eq, PartialEq)] +pub enum BodyDiagnostic { + InactiveCode(InactiveCode), +} + +impl BodyDiagnostic { + pub fn add_to(&self, sink: &mut DiagnosticSink<'_>) { + match self { + BodyDiagnostic::InactiveCode(diag) => { + sink.push(diag.clone()); + } + } + } +} 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::{ self, ArgListOwner, ArrayExprKind, AstChildren, LiteralKind, LoopBodyOwner, NameOwner, SlicePatComponents, }, - AstNode, AstPtr, + AstNode, AstPtr, SyntaxNodePtr, }; use test_utils::mark; @@ -25,6 +25,7 @@ use crate::{ body::{Body, BodySourceMap, Expander, PatPtr, SyntheticSyntax}, builtin_type::{BuiltinFloat, BuiltinInt}, db::DefDatabase, + diagnostics::InactiveCode, expr::{ dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, @@ -37,7 +38,7 @@ use crate::{ StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, }; -use super::{ExprSource, PatSource}; +use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; pub(crate) struct LowerCtx { hygiene: Hygiene, @@ -176,7 +177,7 @@ impl ExprCollector<'_> { fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { let syntax_ptr = AstPtr::new(&expr); - if !self.expander.is_cfg_enabled(&expr) { + if self.check_cfg(&expr).is_none() { return self.missing_expr(); } @@ -354,13 +355,15 @@ impl ExprCollector<'_> { let arms = if let Some(match_arm_list) = e.match_arm_list() { match_arm_list .arms() - .map(|arm| MatchArm { - pat: self.collect_pat_opt(arm.pat()), - expr: self.collect_expr_opt(arm.expr()), - guard: arm - .guard() - .and_then(|guard| guard.expr()) - .map(|e| self.collect_expr(e)), + .filter_map(|arm| { + self.check_cfg(&arm).map(|()| MatchArm { + pat: self.collect_pat_opt(arm.pat()), + expr: self.collect_expr_opt(arm.expr()), + guard: arm + .guard() + .and_then(|guard| guard.expr()) + .map(|e| self.collect_expr(e)), + }) }) .collect() } else { @@ -406,9 +409,8 @@ impl ExprCollector<'_> { .fields() .inspect(|field| field_ptrs.push(AstPtr::new(field))) .filter_map(|field| { - if !self.expander.is_cfg_enabled(&field) { - return None; - } + self.check_cfg(&field)?; + let name = field.field_name()?.as_name(); Some(RecordLitField { @@ -620,15 +622,23 @@ impl ExprCollector<'_> { .filter_map(|s| { let stmt = match s { ast::Stmt::LetStmt(stmt) => { + self.check_cfg(&stmt)?; + let pat = self.collect_pat_opt(stmt.pat()); let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it)); let initializer = stmt.initializer().map(|e| self.collect_expr(e)); Statement::Let { pat, type_ref, initializer } } ast::Stmt::ExprStmt(stmt) => { + self.check_cfg(&stmt)?; + Statement::Expr(self.collect_expr_opt(stmt.expr())) } - ast::Stmt::Item(_) => return None, + ast::Stmt::Item(item) => { + self.check_cfg(&item)?; + + return None; + } }; Some(stmt) }) @@ -872,6 +882,28 @@ impl ExprCollector<'_> { (args, ellipsis) } + + /// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when + /// not. + fn check_cfg(&mut self, owner: &dyn ast::AttrsOwner) -> Option<()> { + match self.expander.parse_attrs(owner).cfg() { + Some(cfg) => { + if self.expander.cfg_options().check(&cfg) != Some(false) { + return Some(()); + } + + self.source_map.diagnostics.push(BodyDiagnostic::InactiveCode(InactiveCode { + file: self.expander.current_file_id, + node: SyntaxNodePtr::new(owner.syntax()), + cfg, + opts: self.expander.cfg_options().clone(), + })); + + None + } + None => Some(()), + } + } } impl From 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 @@ +use base_db::{fixture::WithFixture, SourceDatabase}; +use test_utils::mark; + +use crate::{test_db::TestDB, ModuleDefId}; + +use super::*; + +fn lower(ra_fixture: &str) -> Arc { + let (db, file_id) = crate::test_db::TestDB::with_single_file(ra_fixture); + + let krate = db.crate_graph().iter().next().unwrap(); + let def_map = db.crate_def_map(krate); + let module = def_map.modules_for_file(file_id).next().unwrap(); + let module = &def_map[module]; + let fn_def = match module.scope.declarations().next().unwrap() { + ModuleDefId::FunctionId(it) => it, + _ => panic!(), + }; + + db.body(fn_def.into()) +} + +fn check_diagnostics(ra_fixture: &str) { + let db: TestDB = TestDB::with_files(ra_fixture); + db.check_diagnostics(); +} + +#[test] +fn your_stack_belongs_to_me() { + mark::check!(your_stack_belongs_to_me); + lower( + " +macro_rules! n_nuple { + ($e:tt) => (); + ($($rest:tt)*) => {{ + (n_nuple!($($rest)*)None,) + }}; +} +fn main() { n_nuple!(1,2,3); } +", + ); +} + +#[test] +fn cfg_diagnostics() { + check_diagnostics( + r" +fn f() { + // The three g̶e̶n̶d̶e̶r̶s̶ statements: + + #[cfg(a)] fn f() {} // Item statement + //^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled + #[cfg(a)] {} // Expression statement + //^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled + #[cfg(a)] let x = 0; // let statement + //^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled + + abc(#[cfg(a)] 0); + //^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled + let x = Struct { + #[cfg(a)] f: 0, + //^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled + }; + match () { + () => (), + #[cfg(a)] () => (), + //^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled + } + + #[cfg(a)] 0 // Trailing expression of block + //^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled +} + ", + ); +} -- cgit v1.2.3