From ebbcf9f458522e76c5a84e6771e0ef434d6d5c5b Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Wed, 7 Apr 2021 12:45:17 +0100 Subject: Fix inference with conditionally compiled tails Fixes #8378 --- crates/hir_def/src/body/lower.rs | 24 +++++++++++++++++------- crates/hir_def/src/body/scope.rs | 2 +- crates/hir_def/src/expr.rs | 4 ++-- crates/hir_ty/src/diagnostics/expr.rs | 2 +- crates/hir_ty/src/infer/expr.rs | 2 +- 5 files changed, 22 insertions(+), 12 deletions(-) (limited to 'crates') diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index c11da30d2..820d5c17e 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs @@ -203,7 +203,7 @@ impl ExprCollector<'_> { self.maybe_collect_expr(expr).unwrap_or_else(|| self.missing_expr()) } - /// Returns `None` if the expression is `#[cfg]`d out. + /// Returns `None` if and only if the expression is `#[cfg]`d out. fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option { let syntax_ptr = AstPtr::new(&expr); self.check_cfg(&expr)?; @@ -665,7 +665,7 @@ impl ExprCollector<'_> { if self.check_cfg(&stmt).is_none() { return; } - + let has_semi = stmt.semicolon_token().is_some(); // Note that macro could be expended to multiple statements if let Some(ast::Expr::MacroCall(m)) = stmt.expr() { let macro_ptr = AstPtr::new(&m); @@ -682,18 +682,19 @@ impl ExprCollector<'_> { statements.statements().for_each(|stmt| this.collect_stmt(stmt)); if let Some(expr) = statements.expr() { let expr = this.collect_expr(expr); - this.statements_in_scope.push(Statement::Expr(expr)); + this.statements_in_scope + .push(Statement::Expr { expr, has_semi }); } } None => { let expr = this.alloc_expr(Expr::Missing, syntax_ptr.clone()); - this.statements_in_scope.push(Statement::Expr(expr)); + this.statements_in_scope.push(Statement::Expr { expr, has_semi }); } }, ); } else { let expr = self.collect_expr_opt(stmt.expr()); - self.statements_in_scope.push(Statement::Expr(expr)); + self.statements_in_scope.push(Statement::Expr { expr, has_semi }); } } ast::Stmt::Item(item) => { @@ -722,8 +723,17 @@ impl ExprCollector<'_> { let prev_statements = std::mem::take(&mut self.statements_in_scope); block.statements().for_each(|s| self.collect_stmt(s)); - - let tail = block.tail_expr().map(|e| self.collect_expr(e)); + block.tail_expr().and_then(|e| { + let expr = self.maybe_collect_expr(e)?; + Some(self.statements_in_scope.push(Statement::Expr { expr, has_semi: false })) + }); + + let mut tail = None; + if let Some(Statement::Expr { expr, has_semi: false }) = self.statements_in_scope.last() { + tail = Some(*expr); + self.statements_in_scope.pop(); + } + let tail = tail; let statements = std::mem::replace(&mut self.statements_in_scope, prev_statements); let syntax_node_ptr = AstPtr::new(&block.into()); let expr_id = self.alloc_expr( diff --git a/crates/hir_def/src/body/scope.rs b/crates/hir_def/src/body/scope.rs index bd7005ca6..6764de3a7 100644 --- a/crates/hir_def/src/body/scope.rs +++ b/crates/hir_def/src/body/scope.rs @@ -157,7 +157,7 @@ fn compute_block_scopes( scope = scopes.new_scope(scope); scopes.add_bindings(body, scope, *pat); } - Statement::Expr(expr) => { + Statement::Expr { expr, .. } => { scopes.set_scope(*expr, scope); compute_expr_scopes(*expr, body, scopes, scope); } diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs index b4ad984bd..0c3b41080 100644 --- a/crates/hir_def/src/expr.rs +++ b/crates/hir_def/src/expr.rs @@ -242,7 +242,7 @@ pub struct RecordLitField { #[derive(Debug, Clone, Eq, PartialEq)] pub enum Statement { Let { pat: PatId, type_ref: Option>, initializer: Option }, - Expr(ExprId), + Expr { expr: ExprId, has_semi: bool }, } impl Expr { @@ -265,7 +265,7 @@ impl Expr { f(*expr); } } - Statement::Expr(e) => f(*e), + Statement::Expr { expr: expression, .. } => f(*expression), } } if let Some(expr) = tail { diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index 79602c3dd..47709c1e8 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs @@ -83,7 +83,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { if let Expr::Block { statements, tail, .. } = body_expr { if let Some(t) = tail { self.validate_results_in_tail_expr(body.body_expr, *t, db); - } else if let Some(Statement::Expr(id)) = statements.last() { + } else if let Some(Statement::Expr { expr: id, .. }) = statements.last() { self.validate_missing_tail_expr(body.body_expr, *id, db); } } diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 50497eecb..9476e6297 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -809,7 +809,7 @@ impl<'a> InferenceContext<'a> { let ty = self.resolve_ty_as_possible(ty); self.infer_pat(*pat, &ty, BindingMode::default()); } - Statement::Expr(expr) => { + Statement::Expr { expr, .. } => { self.infer_expr(*expr, &Expectation::none()); } } -- cgit v1.2.3 From 11c926fd97d5efb61a771455fc233afe33939569 Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Sun, 11 Apr 2021 10:03:39 +0100 Subject: Add a test for conditionally compiled tails --- crates/hir_ty/src/tests/regression.rs | 49 +++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'crates') diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs index d14f5c9bb..e23bd4da9 100644 --- a/crates/hir_ty/src/tests/regression.rs +++ b/crates/hir_ty/src/tests/regression.rs @@ -1050,3 +1050,52 @@ fn test() { "#]], ); } + +#[test] +fn cfg_tail() { + // https://github.com/rust-analyzer/rust-analyzer/issues/8378 + check_infer( + r#" + fn fake_tail(){ + { "first" } + #[cfg(never)] 9 + } + fn multiple_fake(){ + { "fake" } + { "fake" } + { "second" } + #[cfg(never)] { 11 } + #[cfg(never)] 12; + #[cfg(never)] 13 + } + fn no_normal_tail(){ + { "third" } + #[cfg(never)] 14; + #[cfg(never)] 15; + } + fn no_actual_tail(){ + { "fourth" }; + #[cfg(never)] 14; + #[cfg(never)] 15 + } + "#, + expect![[r#" + 14..53 '{ ...)] 9 }': &str + 20..31 '{ "first" }': &str + 22..29 '"first"': &str + 72..190 '{ ...] 13 }': &str + 78..88 '{ "fake" }': &str + 80..86 '"fake"': &str + 93..103 '{ "fake" }': &str + 95..101 '"fake"': &str + 108..120 '{ "second" }': &str + 110..118 '"second"': &str + 210..273 '{ ... 15; }': &str + 216..227 '{ "third" }': &str + 218..225 '"third"': &str + 293..357 '{ ...] 15 }': () + 299..311 '{ "fourth" }': &str + 301..309 '"fourth"': &str + "#]], + ) +} -- cgit v1.2.3