about summary refs log tree commit diff
path: root/clippy_lints/src/utils
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-12-17 22:12:34 +0000
committerbors <bors@rust-lang.org>2021-12-17 22:12:34 +0000
commitaf1eea3f0a43f7eee0866b734a635a960ee74ad1 (patch)
tree9f8cc77f66d02aaaed38efa615f02fdf8dff8e19 /clippy_lints/src/utils
parentece0946d7f60bdee8b9544753a3f1a3acddcb1f7 (diff)
parent17c1ff9faa9748557fed92d5406eae5a6b17701b (diff)
Auto merge of #89841 - cormacrelf:let-else-typed, r=nagisa
Implement let-else type annotations natively

Tracking issue: #87335

Fixes #89688, fixes #89807, edit: fixes  #89960 as well

As explained in https://github.com/rust-lang/rust/issues/89688#issuecomment-940405082, the previous desugaring moved the let-else scrutinee into a dummy variable, which meant if you wanted to refer to it again in the else block, it had moved.

This introduces a new hir type, ~~`hir::LetExpr`~~ `hir::Let`, which takes over all the fields of `hir::ExprKind::Let(...)` and adds an optional type annotation. The `hir::Let` is then treated like a `hir::Local` when type checking a function body, specifically:

* `GatherLocalsVisitor` overrides a new `Visitor::visit_let_expr` and does pretty much exactly what it does for `visit_local`, assigning a local type to the `hir::Let` ~~(they could be deduplicated but they are right next to each other, so at least we know they're the same)~~
* It reuses the code in `check_decl_local` to typecheck the `hir::Let`, simply returning 'bool' for the expression type after doing that.

* ~~`FnCtxt::check_expr_let` passes this local type in to `demand_scrutinee_type`, and then imitates check_decl_local's pattern checking~~
* ~~`demand_scrutinee_type` (the blindest change for me, please give this extra scrutiny) uses this local type instead of of creating a new one~~
    * ~~Just realised the `check_expr_with_needs` was passing NoExpectation further down, need to pass the type there too. And apparently this Expectation API already exists.~~

Some other misc notes:

* ~~Is the clippy code supposed to be autoformatted? I tried not to give huge diffs but maybe some rustfmt changes simply haven't hit it yet.~~
* in `rustc_ast_lowering/src/block.rs`, I noticed some existing `self.alias_attrs()` calls in `LoweringContext::lower_stmts` seem to be copying attributes from the lowered locals/etc to the statements. Is that right? I'm new at this, I don't know.
Diffstat (limited to 'clippy_lints/src/utils')
-rw-r--r--clippy_lints/src/utils/author.rs17
-rw-r--r--clippy_lints/src/utils/inspector.rs7
2 files changed, 17 insertions, 7 deletions
diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs
index f186e1f05a0..c1b811c2174 100644
--- a/clippy_lints/src/utils/author.rs
+++ b/clippy_lints/src/utils/author.rs
@@ -373,11 +373,18 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
         }
 
         match expr.value.kind {
-            ExprKind::Let(pat, expr, _) => {
-                bind!(self, pat, expr);
-                kind!("Let({pat}, {expr}, _)");
-                self.pat(pat);
-                self.expr(expr);
+            ExprKind::Let(let_expr) => {
+                bind!(self, let_expr);
+                kind!("Let({let_expr})");
+                self.pat(field!(let_expr.pat));
+                // Does what ExprKind::Cast does, only adds a clause for the type
+                // if it's a path
+                if let Some(TyKind::Path(ref qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) {
+                    bind!(self, qpath);
+                    out!("if let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind;");
+                    self.qpath(qpath);
+                }
+                self.expr(field!(let_expr.init));
             },
             ExprKind::Box(inner) => {
                 bind!(self, inner);
diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs
index 43590cc7862..abf4826a069 100644
--- a/clippy_lints/src/utils/inspector.rs
+++ b/clippy_lints/src/utils/inspector.rs
@@ -142,9 +142,12 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) {
                 print_expr(cx, arg, indent + 1);
             }
         },
-        hir::ExprKind::Let(pat, expr, _) => {
+        hir::ExprKind::Let(hir::Let { pat, init, ty, .. }) => {
             print_pat(cx, pat, indent + 1);
-            print_expr(cx, expr, indent + 1);
+            if let Some(ty) = ty {
+                println!("{}  type annotation: {:?}", ind, ty);
+            }
+            print_expr(cx, init, indent + 1);
         },
         hir::ExprKind::MethodCall(path, _, args, _) => {
             println!("{}MethodCall", ind);