Repository: rust-analyzer/rust-analyzer
Pull request #9130 (2021-06-03 14:25:53)

Prefix/suffix parameter inlay hint hiding heuristic is more strict
Instead of just plainly checking prefix/suffix of the argument string to the parameter name we only check for prefixes and suffixes if they are split apart via an underscore meaning, with the argument `foo`, it will be hidden for the parameter name `foo_bar` but not for `foobar`.

bors r+
Closes https://github.com/rust-analyzer/rust-analyzer/issues/8878


Patch:
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs
index 52cb7777bb..d2bb381be0 100644
--- a/crates/hir_def/src/data.rs
+++ b/crates/hir_def/src/data.rs
@@ -143,10 +143,6 @@ pub struct TraitData {
     pub is_auto: bool,
     pub is_unsafe: bool,
     pub visibility: RawVisibility,
-    /// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore
-    /// method calls to this trait's methods when the receiver is an array and the crate edition is
-    /// 2015 or 2018.
-    pub skip_array_during_method_dispatch: bool,
 }
 
 impl TraitData {
@@ -161,10 +157,6 @@ impl TraitData {
         let container = AssocContainerId::TraitId(tr);
         let visibility = item_tree[tr_def.visibility].clone();
         let mut expander = Expander::new(db, tr_loc.id.file_id(), module_id);
-        let skip_array_during_method_dispatch = item_tree
-            .attrs(db, tr_loc.container.krate(), ModItem::from(tr_loc.id.value).into())
-            .by_key("rustc_skip_array_during_method_dispatch")
-            .exists();
 
         let items = collect_items(
             db,
@@ -176,14 +168,7 @@ impl TraitData {
             100,
         );
 
-        Arc::new(TraitData {
-            name,
-            items,
-            is_auto,
-            is_unsafe,
-            visibility,
-            skip_array_during_method_dispatch,
-        })
+        Arc::new(TraitData { name, items, is_auto, is_unsafe, visibility })
     }
 
     pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ {
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index cfda7cb321..6208facd54 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -130,7 +130,7 @@ impl<'a> Ctx<'a> {
             ast::Item::ExternBlock(ast) => self.lower_extern_block(ast).into(),
         };
 
-        self.add_attrs(item.into(), attrs);
+        self.add_attrs(item.into(), attrs.clone());
 
         Some(item)
     }
@@ -276,11 +276,10 @@ impl<'a> Ctx<'a> {
         let visibility = self.lower_visibility(enum_);
         let name = enum_.name()?.as_name();
         let generic_params = self.lower_generic_params(GenericsOwner::Enum, enum_);
-        let variants =
-            self.with_inherited_visibility(visibility, |this| match &enum_.variant_list() {
-                Some(variant_list) => this.lower_variants(variant_list),
-                None => IdRange::new(this.next_variant_idx()..this.next_variant_idx()),
-            });
+        let variants = match &enum_.variant_list() {
+            Some(variant_list) => self.lower_variants(variant_list),
+            None => IdRange::new(self.next_variant_idx()..self.next_variant_idx()),
+        };
         let ast_id = self.source_ast_id_map.ast_id(enum_);
         let res = Enum { name, visibility, generic_params, variants, ast_id };
         Some(id(self.data().enums.alloc(res)))
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index a2a4d61db5..3efbce7736 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -357,20 +357,17 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
             infer: &infer,
             db,
             pattern_arena: &pattern_arena,
-            panic_context: &|| {
+            eprint_panic_context: &|| {
                 use syntax::AstNode;
-                let match_expr_text = source_map
-                    .expr_syntax(match_expr)
-                    .ok()
-                    .and_then(|scrutinee_sptr| {
-                        let root = scrutinee_sptr.file_syntax(db.upcast());
-                        scrutinee_sptr.value.to_node(&root).syntax().parent()
-                    })
-                    .map(|node| node.to_string());
-                format!(
-                    "expression:\n{}",
-                    match_expr_text.as_deref().unwrap_or("<synthesized expr>")
-                )
+                if let Ok(scrutinee_sptr) = source_map.expr_syntax(match_expr) {
+                    let root = scrutinee_sptr.file_syntax(db.upcast());
+                    if let Some(match_ast) = scrutinee_sptr.value.to_node(&root).syntax().parent() {
+                        eprintln!(
+                            "Match checking is about to panic on this expression:\n{}",
+                            match_ast.to_string(),
+                        );
+                    }
+                }
             },
         };
         let report = compute_match_usefulness(&cx, &m_arms);
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index c8e1b23dee..a9a99f57a2 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -100,19 +100,10 @@ impl<'a> PatCtxt<'a> {
     }
 
     pub(crate) fn lower_pattern(&mut self, pat: hir_def::expr::PatId) -> Pat {
-        // XXX(iDawer): Collecting pattern adjustments feels imprecise to me.
-        // When lowering of & and box patterns are implemented this should be tested
-        // in a manner of `match_ergonomics_issue_9095` test.
-        // Pattern adjustment is part of RFC 2005-match-ergonomics.
+        // FIXME: implement pattern adjustments (implicit pattern dereference; "RFC 2005-match-ergonomics")
         // More info https://github.com/rust-lang/rust/issues/42640#issuecomment-313535089
         let unadjusted_pat = self.lower_pattern_unadjusted(pat);
-        self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold(
-            unadjusted_pat,
-            |subpattern, ref_ty| Pat {
-                ty: ref_ty.clone(),
-                kind: Box::new(PatKind::Deref { subpattern }),
-            },
-        )
+        unadjusted_pat
     }
 
     fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat {
@@ -1245,21 +1236,6 @@ fn main(f: Foo) {
         );
     }
 
-    #[test]
-    fn match_ergonomics_issue_9095() {
-        check_diagnostics(
-            r#"
-enum Foo<T> { A(T) }
-fn main() {
-    match &Foo::A(true) {
-        _ => {}
-        Foo::A(_) => {}
-    }
-}
-"#,
-        );
-    }
-
     mod false_negatives {
         //! The implementation of match checking here is a work in progress. As we roll this out, we
         //! prefer false negatives to false positives (ideally there would be no false positives). This
diff --git a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs
index bd76a606cb..83b094a893 100644
--- a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs
+++ b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs
@@ -295,7 +295,7 @@ pub(crate) struct MatchCheckCtx<'a> {
     pub(crate) db: &'a dyn HirDatabase,
     /// Lowered patterns from arms plus generated by the check.
     pub(crate) pattern_arena: &'a RefCell<PatternArena>,
-    pub(crate) panic_context: &'a dyn Fn() -> String,
+    pub(crate) eprint_panic_context: &'a dyn Fn(),
 }
 
 impl<'a> MatchCheckCtx<'a> {
@@ -331,7 +331,8 @@ impl<'a> MatchCheckCtx<'a> {
 
     #[track_caller]
     pub(super) fn bug(&self, info: &str) -> ! {
-        panic!("bug: {}\n{}", info, (self.panic_context)());
+        (self.eprint_panic_context)();
+        panic!("bug: {}", info);
     }
 }
 
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 0e9f777dab..7a4268819b 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -150,8 +150,6 @@ pub struct InferenceResult {
     type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
     /// Interned Unknown to return references to.
     standard_types: InternedStandardTypes,
-    /// Stores the types which were implicitly dereferenced in pattern binding modes.
-    pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
 }
 
 impl InferenceResult {
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs
index 25dff7e49d..83e0a7a9e9 100644
--- a/crates/hir_ty/src/infer/pat.rs
+++ b/crates/hir_ty/src/infer/pat.rs
@@ -101,9 +101,7 @@ impl<'a> InferenceContext<'a> {
         let mut expected = self.resolve_ty_shallow(expected);
 
         if is_non_ref_pat(&body, pat) {
-            let mut pat_adjustments = Vec::new();
             while let Some((inner, _lifetime, mutability)) = expected.as_reference() {
-                pat_adjustments.push(expected.clone());
                 expected = self.resolve_ty_shallow(inner);
                 default_bm = match default_bm {
                     BindingMode::Move => BindingMode::Ref(mutability),
@@ -111,11 +109,6 @@ impl<'a> InferenceContext<'a> {
                     BindingMode::Ref(Mutability::Mut) => BindingMode::Ref(mutability),
                 }
             }
-
-            if !pat_adjustments.is_empty() {
-                pat_adjustments.shrink_to_fit();
-                self.result.pat_adjustments.insert(pat, pat_adjustments);
-            }
         } else if let Pat::Ref { .. } = &body[pat] {
             cov_mark::hit!(match_ergonomics_ref);
             // When you encounter a `&pat` pattern, reset to Move.
@@ -297,10 +290,6 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
             Expr::Literal(Literal::String(..)) => false,
             _ => true,
         },
-        Pat::Bind { mode: BindingAnnotation::Mutable, subpat: Some(subpat), .. }
-        | Pat::Bind { mode: BindingAnnotation::Unannotated, subpat: Some(subpat), .. } => {
-            is_non_ref_pat(body, *subpat)
-        }
         Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false,
     }
 }
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index a23527f7df..af6b6cda70 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -5,7 +5,7 @@
 use std::{iter, sync::Arc};
 
 use arrayvec::ArrayVec;
-use base_db::{CrateId, Edition};
+use base_db::CrateId;
 use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
 use hir_def::{
     lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, FunctionId,
@@ -639,7 +639,6 @@ fn iterate_trait_method_candidates(
     receiver_ty: Option<&Canonical<Ty>>,
     callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
 ) -> bool {
-    let receiver_is_array = matches!(self_ty.value.kind(&Interner), chalk_ir::TyKind::Array(..));
     // if ty is `dyn Trait`, the trait doesn't need to be in scope
     let inherent_trait =
         self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t));
@@ -656,19 +655,6 @@ fn iterate_trait_method_candidates(
     'traits: for t in traits {
         let data = db.trait_data(t);
 
-        // Traits annotated with `#[rustc_skip_array_during_method_dispatch]` are skipped during
-        // method resolution, if the receiver is an array, and we're compiling for editions before
-        // 2021.
-        // This is to make `[a].into_iter()` not break code with the new `IntoIterator` impl for
-        // arrays.
-        if data.skip_array_during_method_dispatch && receiver_is_array {
-            // FIXME: this should really be using the edition of the method name's span, in case it
-            // comes from a macro
-            if db.crate_graph()[krate].edition < Edition::Edition2021 {
-                continue;
-            }
-        }
-
         // we'll be lazy about checking whether the type implements the
         // trait, but if we find out it doesn't, we'll skip the rest of the
         // iteration
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index e0d01fa96e..eebae5ebea 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -28,8 +28,8 @@ pub struct ExpandedMacro {
 pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> {
     let sema = Semantics::new(db);
     let file = sema.parse(position.file_id);
-    let mac = find_node_at_offset::<ast::MacroCall>(file.syntax(), position.offset)?;
-    let name = mac.path()?.segment()?.name_ref()?;
+    let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset)?;
+    let mac = name_ref.syntax().ancestors().find_map(ast::MacroCall::cast)?;
 
     let expanded = expand_macro_recur(&sema, &mac)?;
 
@@ -37,7 +37,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
     // macro expansion may lose all white space information
     // But we hope someday we can use ra_fmt for that
     let expansion = insert_whitespaces(expanded);
-    Some(ExpandedMacro { name: name.to_string(), expansion })
+    Some(ExpandedMacro { name: name_ref.text().to_string(), expansion })
 }
 
 fn expand_macro_recur(
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 85f8877371..d6dfa01839 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -150,17 +150,11 @@ fn get_param_name_hints(
         return None;
     }
 
-    let args = match &expr {
-        ast::Expr::CallExpr(expr) => expr.arg_list()?.args(),
-        ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(),
-        _ => return None,
-    };
-
-    let callable = get_callable(sema, &expr)?;
+    let (callable, arg_list) = get_callable(sema, &expr)?;
     let hints = callable
         .params(sema.db)
         .into_iter()
-        .zip(args)
+        .zip(arg_list.args())
         .filter_map(|((param, _ty), arg)| {
             let param_name = match param? {
                 Either::Left(_) => "self".to_string(),
@@ -171,7 +165,7 @@ fn get_param_name_hints(
             };
             Some((param_name, arg))
         })
-        .filter(|(param_name, arg)| should_show_param_name_hint(sema, &callable, param_name, &arg))
+        .filter(|(param_name, arg)| !should_hide_param_name_hint(sema, &callable, param_name, &arg))
         .map(|(param_name, arg)| InlayHint {
             range: arg.syntax().text_range(),
             kind: InlayKind::ParameterHint,
@@ -289,15 +283,9 @@ fn should_not_display_type_hint(
     for node in bind_pat.syntax().ancestors() {
         match_ast! {
             match node {
-                ast::LetStmt(it) => {
-                    return it.ty().is_some()
-                },
-                ast::Param(it) => {
-                    return it.ty().is_some()
-                },
-                ast::MatchArm(_it) => {
-                    return pat_is_enum_variant(db, bind_pat, pat_ty);
-                },
+                ast::LetStmt(it) => return it.ty().is_some(),
+                ast::Param(it) => return it.ty().is_some(),
+                ast::MatchArm(_it) => return pat_is_enum_variant(db, bind_pat, pat_ty),
                 ast::IfExpr(it) => {
                     return it.condition().and_then(|condition| condition.pat()).is_some()
                         && pat_is_enum_variant(db, bind_pat, pat_ty);
@@ -322,76 +310,66 @@ fn should_not_display_type_hint(
     false
 }
 
-fn should_show_param_name_hint(
+fn should_hide_param_name_hint(
     sema: &Semantics<RootDatabase>,
     callable: &hir::Callable,
     param_name: &str,
     argument: &ast::Expr,
 ) -> bool {
+    // hide when:
+    // - the parameter name is a suffix of the function's name
+    // - the argument is an enum whose name is equal to the parameter
+    // - exact argument<->parameter match(ignoring leading underscore) or argument is a prefix/suffix
+    //   of parameter with _ splitting it off
+    // - param starts with `ra_fixture`
+    // - param is a well known name in an unary function
+
     let param_name = param_name.trim_start_matches('_');
+    if param_name.is_empty() {
+        return true;
+    }
+
     let fn_name = match callable.kind() {
         hir::CallableKind::Function(it) => Some(it.name(sema.db).to_string()),
-        hir::CallableKind::TupleStruct(_)
-        | hir::CallableKind::TupleEnumVariant(_)
-        | hir::CallableKind::Closure => None,
+        _ => None,
     };
-
-    if param_name.is_empty()
-        || Some(param_name) == fn_name.as_ref().map(|s| s.trim_start_matches('_'))
-        || is_argument_similar_to_param_name(sema, argument, param_name)
-        || is_param_name_similar_to_fn_name(param_name, callable, fn_name.as_ref())
+    let fn_name = fn_name.as_deref();
+    is_param_name_suffix_of_fn_name(param_name, callable, fn_name)
+        || is_enum_name_similar_to_param_name(sema, argument, param_name)
+        || is_argument_similar_to_param_name(argument, param_name)
         || param_name.starts_with("ra_fixture")
-    {
-        return false;
-    }
-
-    // avoid displaying hints for common functions like map, filter, etc.
-    // or other obvious words used in std
-    !(callable.n_params() == 1 && is_obvious_param(param_name))
+        || (callable.n_params() == 1 && is_obvious_param(param_name))
 }
 
-fn is_argument_similar_to_param_name(
-    sema: &Semantics<RootDatabase>,
-    argument: &ast::Expr,
-    param_name: &str,
-) -> bool {
-    if is_enum_name_similar_to_param_name(sema, argument, param_name) {
-        return true;
-    }
+fn is_argument_similar_to_param_name(argument: &ast::Expr, param_name: &str) -> bool {
     match get_string_representation(argument) {
         None => false,
-        Some(argument_string) => {
-            let num_leading_underscores =
-                argument_string.bytes().take_while(|&c| c == b'_').count();
-
-            // Does the argument name begin with the parameter name? Ignore leading underscores.
-            let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores);
-            let starts_with_pattern = param_name.bytes().all(
-                |expected| matches!(arg_bytes.next(), Some(actual) if expected.eq_ignore_ascii_case(&actual)),
-            );
-
-            if starts_with_pattern {
-                return true;
+        Some(argument) => {
+            let mut res = false;
+            if let Some(first) = argument.bytes().skip_while(|&c| c == b'_').position(|c| c == b'_')
+            {
+                res |= param_name == argument[..first].trim_start_matches('_');
             }
-
-            // Does the argument name end with the parameter name?
-            let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores);
-            param_name.bytes().rev().all(
-                |expected| matches!(arg_bytes.next_back(), Some(actual) if expected.eq_ignore_ascii_case(&actual)),
-            )
+            if let Some(last) =
+                argument.bytes().rev().skip_while(|&c| c == b'_').position(|c| c == b'_')
+            {
+                res |= param_name == argument[last..].trim_end_matches('_');
+            }
+            res |= argument == param_name;
+            res
         }
     }
 }
 
-fn is_param_name_similar_to_fn_name(
+/// Hide the parameter name of an unary function if it is a `_` - prefixed suffix of the function's name, or equal.
+///
+/// `fn strip_suffix(suffix)` will be hidden.
+/// `fn stripsuffix(suffix)` will not be hidden.
+fn is_param_name_suffix_of_fn_name(
     param_name: &str,
     callable: &Callable,
-    fn_name: Option<&String>,
+    fn_name: Option<&str>,
 ) -> bool {
-    // if it's the only parameter, don't show it if:
-    // - is the same as the function name, or
-    // - the function ends with '_' + param_name
-
     match (callable.n_params(), fn_name) {
         (1, Some(function)) => {
             function == param_name
@@ -424,7 +402,7 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> {
             }
         }
         ast::Expr::FieldExpr(field_expr) => Some(field_expr.name_ref()?.to_string()),
-        ast::Expr::PathExpr(path_expr) => Some(path_expr.to_string()),
+        ast::Expr::PathExpr(path_expr) => Some(path_expr.path()?.segment()?.to_string()),
         ast::Expr::PrefixExpr(prefix_expr) => get_string_representation(&prefix_expr.expr()?),
         ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?),
         _ => None,
@@ -432,15 +410,24 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> {
 }
 
 fn is_obvious_param(param_name: &str) -> bool {
+    // avoid displaying hints for common functions like map, filter, etc.
+    // or other obvious words used in std
     let is_obvious_param_name =
         matches!(param_name, "predicate" | "value" | "pat" | "rhs" | "other");
     param_name.len() == 1 || is_obvious_param_name
 }
 
-fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Callable> {
+fn get_callable(
+    sema: &Semantics<RootDatabase>,
+    expr: &ast::Expr,
+) -> Option<(hir::Callable, ast::ArgList)> {
     match expr {
-        ast::Expr::CallExpr(expr) => sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db),
-        ast::Expr::MethodCallExpr(expr) => sema.resolve_method_call_as_callable(expr),
+        ast::Expr::CallExpr(expr) => {
+            sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db).zip(expr.arg_list())
+        }
+        ast::Expr::MethodCallExpr(expr) => {
+            sema.resolve_method_call_as_callable(expr).zip(expr.arg_list())
+        }
         _ => None,
     }
 }
diff --git a/crates/ide_assists/src/handlers/extract_type_alias.rs b/crates/ide_assists/src/handlers/extract_type_alias.rs
index eac8857c67..998e0de7b7 100644
--- a/crates/ide_assists/src/handlers/extract_type_alias.rs
+++ b/crates/ide_assists/src/handlers/extract_type_alias.rs
@@ -1,7 +1,4 @@
-use syntax::{
-    ast::{self, edit::IndentLevel, AstNode},
-    match_ast,
-};
+use syntax::ast::{self, AstNode};
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
 
@@ -28,15 +25,12 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext) -> Opti
     }
 
     let node = ctx.find_node_at_range::<ast::Type>()?;
-    let item = ctx.find_node_at_offset::<ast::Item>()?;
-    let insert = match_ast! {
-        match (item.syntax().parent()?) {
-            ast::AssocItemList(it) => it.syntax().parent()?,
-            _ => item.syntax().clone(),
-        }
-    };
-    let indent = IndentLevel::from_node(&insert);
-    let insert = insert.text_range().start();
+    let insert = ctx
+        .find_node_at_offset::<ast::Impl>()
+        .map(|imp| imp.syntax().clone())
+        .or_else(|| ctx.find_node_at_offset::<ast::Item>().map(|item| item.syntax().clone()))?
+        .text_range()
+        .start();
     let target = node.syntax().text_range();
 
     acc.add(
@@ -48,14 +42,10 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext) -> Opti
             builder.replace(target, "Type");
             match ctx.config.snippet_cap {
                 Some(cap) => {
-                    builder.insert_snippet(
-                        cap,
-                        insert,
-                        format!("type $0Type = {};\n\n{}", node, indent),
-                    );
+                    builder.insert_snippet(cap, insert, format!("type $0Type = {};\n\n", node));
                 }
                 None => {
-                    builder.insert(insert, format!("type Type = {};\n\n{}", node, indent));
+                    builder.insert(insert, format!("type Type = {};\n\n", node));
                 }
             }
         },
@@ -163,9 +153,9 @@ struct S {
     }
 
     #[test]
-    fn extract_from_impl_or_trait() {
-        // When invoked in an impl/trait, extracted type alias should be placed next to the
-        // impl/trait, not inside.
+    fn extract_from_impl() {
+        // When invoked in an impl, extracted type alias should be placed next to the impl, not
+        // inside.
         check_assist(
             extract_type_alias,
             r#"
@@ -177,40 +167,6 @@ impl S {
 type $0Type = (u8, u8);
 
 impl S {
-    fn f() -> Type {}
-}
-            "#,
-        );
-        check_assist(
-            extract_type_alias,
-            r#"
-trait Tr {
-    fn f() -> $0(u8, u8)$0 {}
-}
-            "#,
-            r#"
-type $0Type = (u8, u8);
-
-trait Tr {
-    fn f() -> Type {}
-}
-            "#,
-        );
-    }
-
-    #[test]
-    fn indentation() {
-        check_assist(
-            extract_type_alias,
-            r#"
-mod m {
-    fn f() -> $0u8$0 {}
-}
-            "#,
-            r#"
-mod m {
-    type $0Type = u8;
-
     fn f() -> Type {}
 }
             "#,
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index cb4f08e535..6f685c02f3 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -567,7 +567,7 @@ impl<'a> CompletionContext<'a> {
             None => return,
         };
 
-        if let Some(segment) = ast::PathSegment::cast(parent) {
+        if let Some(segment) = ast::PathSegment::cast(parent.clone()) {
             let path = segment.parent_path();
             self.is_call = path
                 .syntax()
diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs
index 18eb97f0d5..38707ffa5b 100644
--- a/crates/mbe/src/benchmark.rs
+++ b/crates/mbe/src/benchmark.rs
@@ -187,7 +187,7 @@ fn invocation_fixtures(rules: &FxHashMap<String, MacroRules>) -> Vec<(String, tt
             let a = 1664525;
             let c = 1013904223;
             *seed = usize::wrapping_add(usize::wrapping_mul(*seed, a), c);
-            *seed
+            return *seed;
         }
         fn make_ident(ident: &str) -> tt::TokenTree {
             tt::Leaf::Ident(tt::Ident { id: tt::TokenId::unspecified(), text: SmolStr::new(ident) })
diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs
index c982eb58f1..84ca3ff876 100644
--- a/crates/mbe/src/expander/matcher.rs
+++ b/crates/mbe/src/expander/matcher.rs
@@ -219,7 +219,7 @@ impl BindingsBuilder {
         bindings
     }
 
-    fn build_inner(&self, bindings: &mut Bindings, link_nodes: &[LinkNode<Rc<BindingKind>>]) {
+    fn build_inner(&self, bindings: &mut Bindings, link_nodes: &Vec<LinkNode<Rc<BindingKind>>>) {
         let mut nodes = Vec::new();
         self.collect_nodes(&link_nodes, &mut nodes);
 
@@ -301,7 +301,7 @@ impl BindingsBuilder {
 
     fn collect_nodes<'a>(
         &'a self,
-        link_nodes: &'a [LinkNode<Rc<BindingKind>>],
+        link_nodes: &'a Vec<LinkNode<Rc<BindingKind>>>,
         nodes: &mut Vec<&'a Rc<BindingKind>>,
     ) {
         link_nodes.iter().for_each(|it| match it {
@@ -494,8 +494,15 @@ fn match_loop_inner<'t>(
                         }
                         Some(err) => {
                             res.add_err(err);
-                            if let Some(fragment) = match_res.value {
-                                bindings_builder.push_fragment(&mut item.bindings, &name, fragment);
+                            match match_res.value {
+                                Some(fragment) => {
+                                    bindings_builder.push_fragment(
+                                        &mut item.bindings,
+                                        &name,
+                                        fragment,
+                                    );
+                                }
+                                _ => {}
                             }
                             item.is_error = true;
                             error_items.push(item);
@@ -571,9 +578,9 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
         );
         stdx::always!(cur_items.is_empty());
 
-        if !error_items.is_empty() {
+        if error_items.len() > 0 {
             error_recover_item = error_items.pop().map(|it| it.bindings);
-        } else if !eof_items.is_empty() {
+        } else if eof_items.len() > 0 {
             error_recover_item = Some(eof_items[0].bindings.clone());
         }
 
@@ -786,7 +793,7 @@ impl<'a> TtIter<'a> {
             _ => (),
         }
 
-        let tt = self.next().ok_or(())?.clone();
+        let tt = self.next().ok_or_else(|| ())?.clone();
         let punct = match tt {
             tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if punct.spacing == tt::Spacing::Joint => {
                 punct
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 380a507443..b95374b76d 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -295,8 +295,8 @@ fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> {
                 // Checks that no repetition which could match an empty token
                 // https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558
 
-                if separator.is_none()
-                    && subtree.iter().all(|child_op| {
+                if separator.is_none() {
+                    if subtree.iter().all(|child_op| {
                         match child_op {
                             Op::Var { kind, .. } => {
                                 // vis is optional
@@ -314,9 +314,9 @@ fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> {
                             Op::Subtree { .. } => {}
                         }
                         false
-                    })
-                {
-                    return Err(ParseError::RepetitionEmptyTokenTree);
+                    }) {
+                        return Err(ParseError::RepetitionEmptyTokenTree);
+                    }
                 }
                 validate(subtree)?
             }
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index 04c0d3e754..61b2a49551 100644
--- a/crates/mbe/src/parser.rs
+++ b/crates/mbe/src/parser.rs
@@ -213,7 +213,7 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul
     Ok(res)
 }
 
-fn eat_fragment_kind(src: &mut TtIter<'_>, mode: Mode) -> Result<Option<SmolStr>, ParseError> {
+fn eat_fragment_kind<'a>(src: &mut TtIter<'a>, mode: Mode) -> Result<Option<SmolStr>, ParseError> {
     if let Mode::Pattern = mode {
         src.expect_char(':').map_err(|()| err!("bad fragment specifier 1"))?;
         let ident = src.expect_ident().map_err(|()| err!("bad fragment specifier 1"))?;
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index 978c75747a..b11172caf0 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -243,7 +243,8 @@ trait TokenConvertor {
     type Token: SrcToken;
 
     fn go(&mut self) -> tt::Subtree {
-        let mut subtree = tt::Subtree { delimiter: None, ..Default::default() };
+        let mut subtree = tt::Subtree::default();
+        subtree.delimiter = None;
         while self.peek().is_some() {
             self.collect_leaf(&mut subtree.token_trees);
         }
@@ -505,7 +506,7 @@ impl TokenConvertor for Convertor {
 
     fn peek(&self) -> Option<Self::Token> {
         if let Some((punct, mut offset)) = self.punct_offset.clone() {
-            offset += TextSize::of('.');
+            offset = offset + TextSize::of('.');
             if usize::from(offset) < punct.text().len() {
                 return Some(SynToken::Punch(punct, offset));
             }
diff --git a/crates/mbe/src/tt_iter.rs b/crates/mbe/src/tt_iter.rs
index bd54f2442b..99a8d250b7 100644
--- a/crates/mbe/src/tt_iter.rs
+++ b/crates/mbe/src/tt_iter.rs
@@ -138,7 +138,7 @@ impl<'a> TtIter<'a> {
             }
         }
         self.inner = self.inner.as_slice()[res.len()..].iter();
-        if res.is_empty() && err.is_none() {
+        if res.len() == 0 && err.is_none() {
             err = Some(err!("no tokens consumed"));
         }
         let res = match res.len() {
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index abdfca1feb..269f223e6e 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -252,10 +252,12 @@ fn closure_expr(p: &mut Parser) -> CompletedMarker {
         // test lambda_ret_block
         // fn main() { || -> i32 { 92 }(); }
         block_expr(p);
-    } else if p.at_ts(EXPR_FIRST) {
-        expr(p);
     } else {
-        p.error("expected expression");
+        if p.at_ts(EXPR_FIRST) {
+            expr(p);
+        } else {
+            p.error("expected expression");
+        }
     }
     m.complete(p, CLOSURE_EXPR)
 }
diff --git a/crates/proc_macro_api/src/msg.rs b/crates/proc_macro_api/src/msg.rs
index 14eed42890..f525df1525 100644
--- a/crates/proc_macro_api/src/msg.rs
+++ b/crates/proc_macro_api/src/msg.rs
@@ -92,7 +92,7 @@ fn read_json<'a>(
 
         // Some ill behaved macro try to use stdout for debugging
         // We ignore it here
-        if !buf.starts_with('{') {
+        if !buf.starts_with("{") {
             log::error!("proc-macro tried to print : {}", buf);
             continue;
         }
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index c33cdb7409..ae78fd4f61 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -92,7 +92,6 @@ config_data! {
         checkOnSave_overrideCommand: Option<Vec<String>> = "null",
 
         /// Whether to add argument snippets when completing functions.
-        /// Only applies when `#rust-analyzer.completion.addCallParenthesis#` is set.
         completion_addCallArgumentSnippets: bool = "true",
         /// Whether to add parenthesis when completing functions.
         completion_addCallParenthesis: bool      = "true",
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 49ee4b9224..4567446036 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -1640,7 +1640,7 @@ fn run_rustfmt(
                     .into());
                 }
 
-                let frange = from_proto::file_range(&snap, text_document, range)?;
+                let frange = from_proto::file_range(&snap, text_document.clone(), range)?;
                 let start_line = line_index.index.line_col(frange.range.start()).line;
                 let end_line = line_index.index.line_col(frange.range.end()).line;
 
diff --git a/crates/syntax/src/parsing/reparsing.rs b/crates/syntax/src/parsing/reparsing.rs
index 304f47b3dc..4ad50ab729 100644
--- a/crates/syntax/src/parsing/reparsing.rs
+++ b/crates/syntax/src/parsing/reparsing.rs
@@ -36,8 +36,8 @@ pub(crate) fn incremental_reparse(
     None
 }
 
-fn reparse_token(
-    root: &SyntaxNode,
+fn reparse_token<'node>(
+    root: &'node SyntaxNode,
     edit: &Indel,
 ) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
     let prev_token = root.covering_element(edit.delete).as_token()?.clone();
@@ -84,8 +84,8 @@ fn reparse_token(
     }
 }
 
-fn reparse_block(
-    root: &SyntaxNode,
+fn reparse_block<'node>(
+    root: &'node SyntaxNode,
     edit: &Indel,
 ) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
     let (node, reparser) = find_reparsable_node(root, edit.delete)?;
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 7f405b4d7a..dbd9a3503b 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -119,7 +119,6 @@ similar option.
 +
 --
 Whether to add argument snippets when completing functions.
-Only applies when `#rust-analyzer.completion.addCallParenthesis#` is set.
 --
 [[rust-analyzer.completion.addCallParenthesis]]rust-analyzer.completion.addCallParenthesis (default: `true`)::
 +
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index 9a8d76700d..1f95df56e8 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -187,20 +187,6 @@ Install it with pacman, for example:
 $ pacman -S rust-analyzer
 ----
 
-==== Gentoo Linux
-
-`rust-analyzer` is available in the GURU repository:
-
-- https://gitweb.gentoo.org/repo/proj/guru.git/tree/dev-util/rust-analyzer-bin/rust-analyzer-bin-9999.ebuild[`dev-util/rust-analyzer-bin-9999`] (the https://github.com/rust-analyzer/rust-analyzer/releases/latest[latest release] as a live binary ebuild)
-
-If not already, GURU must be enabled (e.g. using `app-eselect/eselect-repository`) and sync'd before running `emerge`:
-
-[source,bash]
-----
-$ eselect repository enable guru && emaint sync -r guru
-$ emerge rust-analyzer-bin
-----
-
 === Emacs
 
 Note this excellent https://robert.kra.hn/posts/2021-02-07_rust-with-emacs/[guide] from https://github.com/rksm[@rksm].
diff --git a/editors/code/package.json b/editors/code/package.json
index 4a5070d021..42a06e1374 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -553,7 +553,7 @@
                     }
                 },
                 "rust-analyzer.completion.addCallArgumentSnippets": {
-                    "markdownDescription": "Whether to add argument snippets when completing functions.\nOnly applies when `#rust-analyzer.completion.addCallParenthesis#` is set.",
+                    "markdownDescription": "Whether to add argument snippets when completing functions.",
                     "default": true,
                     "type": "boolean"
                 },
diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs
index b0b9e30db2..ba4b248489 100644
--- a/xtask/src/codegen/gen_syntax.rs
+++ b/xtask/src/codegen/gen_syntax.rs
@@ -94,16 +94,18 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: &AstSrc) -> Result<String> {
                             support::children(&self.syntax)
                         }
                     }
-                } else if let Some(token_kind) = field.token_kind() {
-                    quote! {
-                        pub fn #method_name(&self) -> Option<#ty> {
-                            support::token(&self.syntax, #token_kind)
-                        }
-                    }
                 } else {
-                    quote! {
-                        pub fn #method_name(&self) -> Option<#ty> {
-                            support::child(&self.syntax)
+                    if let Some(token_kind) = field.token_kind() {
+                        quote! {
+                            pub fn #method_name(&self) -> Option<#ty> {
+                                support::token(&self.syntax, #token_kind)
+                            }
+                        }
+                    } else {
+                        quote! {
+                            pub fn #method_name(&self) -> Option<#ty> {
+                                support::child(&self.syntax)
+                            }
                         }
                     }
                 }
diff --git a/xtask/src/release/changelog.rs b/xtask/src/release/changelog.rs
index 2384a746f3..ffcae2cf72 100644
--- a/xtask/src/release/changelog.rs
+++ b/xtask/src/release/changelog.rs
@@ -132,7 +132,7 @@ fn parse_changelog_line(s: &str) -> Option<PrInfo> {
             return Some(PrInfo { kind, message: Some(message) });
         }
     };
-    let res = PrInfo { message, kind };
+    let res = PrInfo { kind, message };
     Some(res)
 }
 
@@ -152,7 +152,7 @@ fn parse_title_line(s: &str) -> PrInfo {
                 PrKind::Skip => None,
                 _ => Some(s[prefix.len()..].to_string()),
             };
-            return PrInfo { message, kind };
+            return PrInfo { kind, message };
         }
     }
     PrInfo { kind: PrKind::Other, message: Some(s.to_string()) }
diff --git a/xtask/src/tidy.rs b/xtask/src/tidy.rs
index 618cf12fb1..6f687a7883 100644
--- a/xtask/src/tidy.rs
+++ b/xtask/src/tidy.rs
@@ -33,7 +33,7 @@ fn check_code_formatting() {
     let _e = pushenv("RUSTUP_TOOLCHAIN", "stable");
     crate::ensure_rustfmt().unwrap();
     let res = cmd!("cargo fmt -- --check").run();
-    if res.is_err() {
+    if !res.is_ok() {
         let _ = cmd!("cargo fmt").run();
     }
     res.unwrap()
@@ -244,19 +244,19 @@ Zlib OR Apache-2.0 OR MIT
         .map(|it| it.trim())
         .map(|it| it[r#""license":"#.len()..].trim_matches('"'))
         .collect::<Vec<_>>();
-    licenses.sort_unstable();
+    licenses.sort();
     licenses.dedup();
     if licenses != expected {
         let mut diff = String::new();
 
-        diff.push_str("New Licenses:\n");
+        diff += &format!("New Licenses:\n");
         for &l in licenses.iter() {
             if !expected.contains(&l) {
                 diff += &format!("  {}\n", l)
             }
         }
 
-        diff.push_str("\nMissing Licenses:\n");
+        diff += &format!("\nMissing Licenses:\n");
         for &l in expected.iter() {
             if !licenses.contains(&l) {
                 diff += &format!("  {}\n", l)


Test patch:
diff --git a/crates/hir_def/src/item_tree/tests.rs b/crates/hir_def/src/item_tree/tests.rs
index 57686dc6e7..b362add5c9 100644
--- a/crates/hir_def/src/item_tree/tests.rs
+++ b/crates/hir_def/src/item_tree/tests.rs
@@ -359,41 +359,3 @@ trait Tr<'a, T: 'a>: Super {}
         "#]],
     )
 }
-
-#[test]
-fn inherit_visibility() {
-    check(
-        r#"
-pub(crate) enum En {
-    Var1(u8),
-    Var2 {
-        fld: u8,
-    },
-}
-
-pub(crate) trait Tr {
-    fn f();
-    fn method(&self) {}
-}
-        "#,
-        expect![[r#"
-            pub(crate) enum En {
-                Var1(
-                    pub(crate) 0: u8,
-                ),
-                Var2 {
-                    pub(crate) fld: u8,
-                },
-            }
-
-            pub(crate) trait Tr<Self> {
-                pub(crate) fn f() -> ();
-
-                // flags = 0x3
-                pub(crate) fn method(
-                    _: &Self,
-                ) -> ();
-            }
-        "#]],
-    )
-}
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index f26b2c8a79..058eb91299 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -1349,52 +1349,3 @@ fn f() {
     "#,
     );
 }
-
-#[test]
-fn skip_array_during_method_dispatch() {
-    check_types(
-        r#"
-//- /main2018.rs crate:main2018 deps:core
-use core::IntoIterator;
-
-fn f() {
-    let v = [4].into_iter();
-    v;
-  //^ &i32
-
-    let a = [0, 1].into_iter();
-    a;
-  //^ &i32
-}
-
-//- /main2021.rs crate:main2021 deps:core edition:2021
-use core::IntoIterator;
-
-fn f() {
-    let v = [4].into_iter();
-    v;
-  //^ i32
-
-    let a = [0, 1].into_iter();
-    a;
-  //^ &i32
-}
-
-//- /core.rs crate:core
-#[rustc_skip_array_during_method_dispatch]
-pub trait IntoIterator {
-    type Out;
-    fn into_iter(self) -> Self::Out;
-}
-
-impl<T> IntoIterator for [T; 1] {
-    type Out = T;
-    fn into_iter(self) -> Self::Out {}
-}
-impl<'a, T> IntoIterator for &'a [T] {
-    type Out = &'a T;
-    fn into_iter(self) -> Self::Out {}
-}
-    "#,
-    );
-}
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs
index 7d00cee9b3..cd08b5c7a5 100644
--- a/crates/hir_ty/src/tests/patterns.rs
+++ b/crates/hir_ty/src/tests/patterns.rs
@@ -20,8 +20,6 @@ fn infer_pattern() {
                 let h = val;
             }
 
-            if let x @ true = &true {}
-
             let lambda = |a: u64, b, c: i32| { a + b; c };
 
             let ref ref_to_x = x;
@@ -32,7 +30,7 @@ fn infer_pattern() {
         "#,
         expect![[r#"
             8..9 'x': &i32
-            17..400 '{     ...o_x; }': ()
+            17..368 '{     ...o_x; }': ()
             27..28 'y': &i32
             31..32 'x': &i32
             42..44 '&z': &i32
@@ -61,31 +59,24 @@ fn infer_pattern() {
             176..204 '{     ...     }': ()
             190..191 'h': {unknown}
             194..197 'val': {unknown}
-            210..236 'if let...rue {}': ()
-            217..225 'x @ true': &bool
-            221..225 'true': bool
-            221..225 'true': bool
-            228..233 '&true': &bool
-            229..233 'true': bool
-            234..236 '{}': ()
-            246..252 'lambda': |u64, u64, i32| -> i32
-            255..287 '|a: u6...b; c }': |u64, u64, i32| -> i32
-            256..257 'a': u64
-            264..265 'b': u64
-            267..268 'c': i32
-            275..287 '{ a + b; c }': i32
-            277..278 'a': u64
-            277..282 'a + b': u64
-            281..282 'b': u64
-            284..285 'c': i32
-            298..310 'ref ref_to_x': &&i32
-            313..314 'x': &i32
-            324..333 'mut mut_x': &i32
-            336..337 'x': &i32
-            347..367 'ref mu...f_to_x': &mut &i32
-            370..371 'x': &i32
-            381..382 'k': &mut &i32
-            385..397 'mut_ref_to_x': &mut &i32
+            214..220 'lambda': |u64, u64, i32| -> i32
+            223..255 '|a: u6...b; c }': |u64, u64, i32| -> i32
+            224..225 'a': u64
+            232..233 'b': u64
+            235..236 'c': i32
+            243..255 '{ a + b; c }': i32
+            245..246 'a': u64
+            245..250 'a + b': u64
+            249..250 'b': u64
+            252..253 'c': i32
+            266..278 'ref ref_to_x': &&i32
+            281..282 'x': &i32
+            292..301 'mut mut_x': &i32
+            304..305 'x': &i32
+            315..335 'ref mu...f_to_x': &mut &i32
+            338..339 'x': &i32
+            349..350 'k': &mut &i32
+            353..365 'mut_ref_to_x': &mut &i32
         "#]],
     );
 }
diff --git a/crates/syntax/src/tests.rs b/crates/syntax/src/tests.rs
index 9f24261717..45f3c800fc 100644
--- a/crates/syntax/src/tests.rs
+++ b/crates/syntax/src/tests.rs
@@ -236,7 +236,7 @@ where
         }
     });
     dir_tests(&test_data_dir(), err_paths, "rast", |text, path| {
-        if f(text).is_ok() {
+        if let Ok(_) = f(text) {
             panic!("'{:?}' successfully parsed when it should have errored", path);
         } else {
             "ERROR\n".to_owned()
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index ac5a9509d8..bd017567ca 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -238,9 +238,14 @@ fn extract_line_annotations(mut line: &str) -> Vec<LineAnnotation> {
     let mut res = Vec::new();
     let mut offset: TextSize = 0.into();
     let marker: fn(char) -> bool = if line.contains('^') { |c| c == '^' } else { |c| c == '|' };
-    while let Some(idx) = line.find(marker) {
-        offset += TextSize::try_from(idx).unwrap();
-        line = &line[idx..];
+    loop {
+        match line.find(marker) {
+            Some(idx) => {
+                offset += TextSize::try_from(idx).unwrap();
+                line = &line[idx..];
+            }
+            None => break,
+        };
 
         let mut len = line.chars().take_while(|&it| it == '^').count();
         let mut continuation = false;