compiler/rustc_ast_lowering/src/pat.rs RUST 569 lines View on github.com → Search inside
1use std::sync::Arc;23use rustc_ast::*;4use rustc_data_structures::stack::ensure_sufficient_stack;5use rustc_hir::def::{DefKind, Res};6use rustc_hir::{self as hir, LangItem, Target};7use rustc_middle::span_bug;8use rustc_span::{DesugaringKind, Ident, Span, Spanned, respan};910use super::errors::{11    ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding,12};13use super::{14    AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode,15};1617impl<'hir> LoweringContext<'_, 'hir> {18    pub(crate) fn lower_pat(&mut self, pattern: &Pat) -> &'hir hir::Pat<'hir> {19        self.arena.alloc(self.lower_pat_mut(pattern))20    }2122    fn lower_pat_mut(&mut self, mut pattern: &Pat) -> hir::Pat<'hir> {23        ensure_sufficient_stack(|| {24            // loop here to avoid recursion25            let pat_hir_id = self.lower_node_id(pattern.id);26            let node = loop {27                match &pattern.kind {28                    PatKind::Missing => break hir::PatKind::Missing,29                    PatKind::Wild => break hir::PatKind::Wild,30                    PatKind::Never => break hir::PatKind::Never,31                    PatKind::Ident(binding_mode, ident, sub) => {32                        let lower_sub = |this: &mut Self| sub.as_ref().map(|s| this.lower_pat(s));33                        break self.lower_pat_ident(34                            pattern,35                            *binding_mode,36                            *ident,37                            pat_hir_id,38                            lower_sub,39                        );40                    }41                    PatKind::Expr(e) => {42                        break hir::PatKind::Expr(self.lower_expr_within_pat(e, false));43                    }44                    PatKind::TupleStruct(qself, path, pats) => {45                        let qpath = self.lower_qpath(46                            pattern.id,47                            qself,48                            path,49                            ParamMode::Optional,50                            AllowReturnTypeNotation::No,51                            ImplTraitContext::Disallowed(ImplTraitPosition::Path),52                            None,53                        );54                        let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct");55                        break hir::PatKind::TupleStruct(qpath, pats, ddpos);56                    }57                    PatKind::Or(pats) => {58                        break hir::PatKind::Or(59                            self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat_mut(x))),60                        );61                    }62                    PatKind::Path(qself, path) => {63                        let qpath = self.lower_qpath(64                            pattern.id,65                            qself,66                            path,67                            ParamMode::Optional,68                            AllowReturnTypeNotation::No,69                            ImplTraitContext::Disallowed(ImplTraitPosition::Path),70                            None,71                        );72                        let kind = hir::PatExprKind::Path(qpath);73                        let span = self.lower_span(pattern.span);74                        let expr = hir::PatExpr { hir_id: pat_hir_id, span, kind };75                        let expr = self.arena.alloc(expr);76                        return hir::Pat {77                            hir_id: self.next_id(),78                            kind: hir::PatKind::Expr(expr),79                            span,80                            default_binding_modes: true,81                        };82                    }83                    PatKind::Struct(qself, path, fields, etc) => {84                        let qpath = self.lower_qpath(85                            pattern.id,86                            qself,87                            path,88                            ParamMode::Optional,89                            AllowReturnTypeNotation::No,90                            ImplTraitContext::Disallowed(ImplTraitPosition::Path),91                            None,92                        );9394                        let fs = self.arena.alloc_from_iter(fields.iter().map(|f| {95                            let hir_id = self.lower_node_id(f.id);96                            self.lower_attrs(hir_id, &f.attrs, f.span, Target::PatField);9798                            hir::PatField {99                                hir_id,100                                ident: self.lower_ident(f.ident),101                                pat: self.lower_pat(&f.pat),102                                is_shorthand: f.is_shorthand,103                                span: self.lower_span(f.span),104                            }105                        }));106                        break hir::PatKind::Struct(107                            qpath,108                            fs,109                            match etc {110                                ast::PatFieldsRest::Rest(sp) => Some(self.lower_span(*sp)),111                                ast::PatFieldsRest::Recovered(_) => Some(Span::default()),112                                _ => None,113                            },114                        );115                    }116                    PatKind::Tuple(pats) => {117                        let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple");118                        break hir::PatKind::Tuple(pats, ddpos);119                    }120                    PatKind::Box(inner) => {121                        break hir::PatKind::Box(self.lower_pat(inner));122                    }123                    PatKind::Deref(inner) => {124                        break hir::PatKind::Deref(self.lower_pat(inner));125                    }126                    PatKind::Ref(inner, pinned, mutbl) => {127                        break hir::PatKind::Ref(self.lower_pat(inner), *pinned, *mutbl);128                    }129                    PatKind::Range(e1, e2, Spanned { node: end, .. }) => {130                        break hir::PatKind::Range(131                            e1.as_deref().map(|e| self.lower_expr_within_pat(e, true)),132                            e2.as_deref().map(|e| self.lower_expr_within_pat(e, true)),133                            self.lower_range_end(end, e2.is_some()),134                        );135                    }136                    PatKind::Guard(inner, guard) => {137                        break hir::PatKind::Guard(138                            self.lower_pat(inner),139                            self.lower_expr(&guard.cond),140                        );141                    }142                    PatKind::Slice(pats) => break self.lower_pat_slice(pats),143                    PatKind::Rest => {144                        // If we reach here the `..` pattern is not semantically allowed.145                        break self.ban_illegal_rest_pat(pattern.span);146                    }147                    // return inner to be processed in next loop148                    PatKind::Paren(inner) => pattern = inner,149                    PatKind::MacCall(_) => {150                        panic!("{pattern:#?} shouldn't exist here")151                    }152                    PatKind::Err(guar) => break hir::PatKind::Err(*guar),153                }154            };155156            self.pat_with_node_id_of(pattern, node, pat_hir_id)157        })158    }159160    fn lower_pat_tuple(161        &mut self,162        pats: &[Pat],163        ctx: &str,164    ) -> (&'hir [hir::Pat<'hir>], hir::DotDotPos) {165        let mut elems = Vec::with_capacity(pats.len());166        let mut rest = None;167168        let mut iter = pats.iter().enumerate();169        for (idx, pat) in iter.by_ref() {170            // Interpret the first `..` pattern as a sub-tuple pattern.171            // Note that unlike for slice patterns,172            // where `xs @ ..` is a legal sub-slice pattern,173            // it is not a legal sub-tuple pattern.174            match &pat.kind {175                // Found a sub-tuple rest pattern176                PatKind::Rest => {177                    rest = Some((idx, pat.span));178                    break;179                }180                // Found a sub-tuple pattern `$binding_mode $ident @ ..`.181                // This is not allowed as a sub-tuple pattern182                PatKind::Ident(_, ident, Some(sub)) if sub.is_rest() => {183                    let sp = pat.span;184                    self.dcx().emit_err(SubTupleBinding {185                        span: sp,186                        ident_name: ident.name,187                        ident: *ident,188                        ctx,189                    });190                }191                _ => {}192            }193194            // It was not a sub-tuple pattern so lower it normally.195            elems.push(self.lower_pat_mut(pat));196        }197198        for (_, pat) in iter {199            // There was a previous sub-tuple pattern; make sure we don't allow more...200            if pat.is_rest() {201                // ...but there was one again, so error.202                self.ban_extra_rest_pat(pat.span, rest.unwrap().1, ctx);203            } else {204                elems.push(self.lower_pat_mut(pat));205            }206        }207208        (self.arena.alloc_from_iter(elems), hir::DotDotPos::new(rest.map(|(ddpos, _)| ddpos)))209    }210211    /// Lower a slice pattern of form `[pat_0, ..., pat_n]` into212    /// `hir::PatKind::Slice(before, slice, after)`.213    ///214    /// When encountering `($binding_mode $ident @)? ..` (`slice`),215    /// this is interpreted as a sub-slice pattern semantically.216    /// Patterns that follow, which are not like `slice` -- or an error occurs, are in `after`.217    fn lower_pat_slice(&mut self, pats: &[Pat]) -> hir::PatKind<'hir> {218        let mut before = Vec::new();219        let mut after = Vec::new();220        let mut slice = None;221        let mut prev_rest_span = None;222223        // Lowers `$bm $ident @ ..` to `$bm $ident @ _`.224        let lower_rest_sub = |this: &mut Self, pat: &Pat, &ann, &ident, sub: &Pat| {225            let sub_hir_id = this.lower_node_id(sub.id);226            let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub, sub_hir_id));227            let pat_hir_id = this.lower_node_id(pat.id);228            let node = this.lower_pat_ident(pat, ann, ident, pat_hir_id, lower_sub);229            this.pat_with_node_id_of(pat, node, pat_hir_id)230        };231232        let mut iter = pats.iter();233        // Lower all the patterns until the first occurrence of a sub-slice pattern.234        for pat in iter.by_ref() {235            match &pat.kind {236                // Found a sub-slice pattern `..`. Record, lower it to `_`, and stop here.237                PatKind::Rest => {238                    prev_rest_span = Some(pat.span);239                    let hir_id = self.lower_node_id(pat.id);240                    slice = Some(self.pat_wild_with_node_id_of(pat, hir_id));241                    break;242                }243                // Found a sub-slice pattern `$binding_mode $ident @ ..`.244                // Record, lower it to `$binding_mode $ident @ _`, and stop here.245                PatKind::Ident(ann, ident, Some(sub)) if sub.is_rest() => {246                    prev_rest_span = Some(sub.span);247                    slice = Some(self.arena.alloc(lower_rest_sub(self, pat, ann, ident, sub)));248                    break;249                }250                // It was not a subslice pattern so lower it normally.251                _ => before.push(self.lower_pat_mut(pat)),252            }253        }254255        // Lower all the patterns after the first sub-slice pattern.256        for pat in iter {257            // There was a previous subslice pattern; make sure we don't allow more.258            let rest_span = match &pat.kind {259                PatKind::Rest => Some(pat.span),260                PatKind::Ident(ann, ident, Some(sub)) if sub.is_rest() => {261                    // #69103: Lower into `binding @ _` as above to avoid ICEs.262                    after.push(lower_rest_sub(self, pat, ann, ident, sub));263                    Some(sub.span)264                }265                _ => None,266            };267            if let Some(rest_span) = rest_span {268                // We have e.g., `[a, .., b, ..]`. That's no good, error!269                self.ban_extra_rest_pat(rest_span, prev_rest_span.unwrap(), "slice");270            } else {271                // Lower the pattern normally.272                after.push(self.lower_pat_mut(pat));273            }274        }275276        hir::PatKind::Slice(277            self.arena.alloc_from_iter(before),278            slice,279            self.arena.alloc_from_iter(after),280        )281    }282283    fn lower_pat_ident(284        &mut self,285        p: &Pat,286        annotation: BindingMode,287        ident: Ident,288        hir_id: hir::HirId,289        lower_sub: impl FnOnce(&mut Self) -> Option<&'hir hir::Pat<'hir>>,290    ) -> hir::PatKind<'hir> {291        match self.get_partial_res(p.id).map(|d| d.expect_full_res()) {292            // `None` can occur in body-less function signatures293            res @ (None | Some(Res::Local(_))) => {294                let binding_id = match res {295                    Some(Res::Local(id)) => {296                        // In `Or` patterns like `VariantA(s) | VariantB(s, _)`, multiple identifier patterns297                        // will be resolved to the same `Res::Local`. Thus they just share a single298                        // `HirId`.299                        if id == p.id {300                            self.ident_and_label_to_local_id.insert(id, hir_id.local_id);301                            hir_id302                        } else {303                            hir::HirId {304                                owner: self.current_hir_id_owner,305                                local_id: self.ident_and_label_to_local_id[&id],306                            }307                        }308                    }309                    _ => {310                        self.ident_and_label_to_local_id.insert(p.id, hir_id.local_id);311                        hir_id312                    }313                };314                hir::PatKind::Binding(315                    annotation,316                    binding_id,317                    self.lower_ident(ident),318                    lower_sub(self),319                )320            }321            Some(res) => {322                let res = self.lower_res(res);323                let span = self.lower_span(ident.span);324                hir::PatKind::Expr(self.arena.alloc(hir::PatExpr {325                    kind: hir::PatExprKind::Path(hir::QPath::Resolved(326                        None,327                        self.arena.alloc(hir::Path {328                            span,329                            res,330                            segments: arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), self.next_id(), res)],331                        }),332                    )),333                    hir_id: self.next_id(),334                    span,335                }))336            }337        }338    }339340    fn pat_wild_with_node_id_of(&mut self, p: &Pat, hir_id: hir::HirId) -> &'hir hir::Pat<'hir> {341        self.arena.alloc(self.pat_with_node_id_of(p, hir::PatKind::Wild, hir_id))342    }343344    /// Construct a `Pat` with the `HirId` of `p.id` already lowered.345    fn pat_with_node_id_of(346        &mut self,347        p: &Pat,348        kind: hir::PatKind<'hir>,349        hir_id: hir::HirId,350    ) -> hir::Pat<'hir> {351        hir::Pat { hir_id, kind, span: self.lower_span(p.span), default_binding_modes: true }352    }353354    /// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.355    pub(crate) fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {356        self.dcx().emit_err(ExtraDoubleDot { span: sp, prev_span: prev_sp, ctx });357    }358359    /// Used to ban the `..` pattern in places it shouldn't be semantically.360    fn ban_illegal_rest_pat(&self, sp: Span) -> hir::PatKind<'hir> {361        self.dcx().emit_err(MisplacedDoubleDot { span: sp });362363        // We're not in a list context so `..` can be reasonably treated364        // as `_` because it should always be valid and roughly matches the365        // intent of `..` (notice that the rest of a single slot is that slot).366        hir::PatKind::Wild367    }368369    fn lower_range_end(&mut self, e: &RangeEnd, has_end: bool) -> hir::RangeEnd {370        match *e {371            RangeEnd::Excluded if has_end => hir::RangeEnd::Excluded,372            // No end; so `X..` behaves like `RangeFrom`.373            RangeEnd::Excluded | RangeEnd::Included(_) => hir::RangeEnd::Included,374        }375    }376377    /// Matches `'-' lit | lit (cf. parser::Parser::parse_literal_maybe_minus)`,378    /// or paths for ranges.379    //380    // FIXME: do we want to allow `expr -> pattern` conversion to create path expressions?381    // That means making this work:382    //383    // ```rust,ignore (FIXME)384    // struct S;385    // macro_rules! m {386    //     ($a:expr) => {387    //         let $a = S;388    //     }389    // }390    // m!(S);391    // ```392    fn lower_expr_within_pat(393        &mut self,394        expr: &Expr,395        allow_paths: bool,396    ) -> &'hir hir::PatExpr<'hir> {397        let span = self.lower_span(expr.span);398        let err =399            |guar| hir::PatExprKind::Lit { lit: respan(span, LitKind::Err(guar)), negated: false };400        let kind = match &expr.kind {401            ExprKind::Lit(lit) => {402                hir::PatExprKind::Lit { lit: self.lower_lit(lit, span), negated: false }403            }404            ExprKind::IncludedBytes(byte_sym) => hir::PatExprKind::Lit {405                lit: respan(span, LitKind::ByteStr(*byte_sym, StrStyle::Cooked)),406                negated: false,407            },408            ExprKind::Err(guar) => err(*guar),409            ExprKind::Dummy => span_bug!(span, "lowered ExprKind::Dummy"),410            ExprKind::Path(qself, path) if allow_paths => hir::PatExprKind::Path(self.lower_qpath(411                expr.id,412                qself,413                path,414                ParamMode::Optional,415                AllowReturnTypeNotation::No,416                ImplTraitContext::Disallowed(ImplTraitPosition::Path),417                None,418            )),419            ExprKind::Unary(UnOp::Neg, inner) if let ExprKind::Lit(lit) = &inner.kind => {420                hir::PatExprKind::Lit { lit: self.lower_lit(lit, span), negated: true }421            }422            _ => {423                let is_const_block = matches!(expr.kind, ExprKind::ConstBlock(_));424                let pattern_from_macro = expr.is_approximately_pattern()425                    || matches!(426                        expr.peel_parens().kind,427                        ExprKind::Binary(Spanned { node: BinOpKind::BitOr, .. }, ..)428                    );429                let guar = self.dcx().emit_err(ArbitraryExpressionInPattern {430                    span,431                    pattern_from_macro_note: pattern_from_macro,432                    const_block_in_pattern_help: is_const_block,433                });434                err(guar)435            }436        };437        self.arena.alloc(hir::PatExpr { hir_id: self.lower_node_id(expr.id), span, kind })438    }439440    pub(crate) fn lower_ty_pat(441        &mut self,442        pattern: &TyPat,443        base_type: Span,444    ) -> &'hir hir::TyPat<'hir> {445        self.arena.alloc(self.lower_ty_pat_mut(pattern, base_type))446    }447448    fn lower_ty_pat_mut(&mut self, pattern: &TyPat, base_type: Span) -> hir::TyPat<'hir> {449        // loop here to avoid recursion450        let pat_hir_id = self.lower_node_id(pattern.id);451        let node = match &pattern.kind {452            TyPatKind::Range(e1, e2, Spanned { node: end, span }) => hir::TyPatKind::Range(453                e1.as_deref()454                    .map(|e| self.lower_anon_const_to_const_arg_and_alloc(e))455                    .unwrap_or_else(|| {456                        self.lower_ty_pat_range_end(457                            hir::LangItem::RangeMin,458                            span.shrink_to_lo(),459                            base_type,460                        )461                    }),462                e2.as_deref()463                    .map(|e| match end {464                        RangeEnd::Included(..) => self.lower_anon_const_to_const_arg_and_alloc(e),465                        RangeEnd::Excluded => self.lower_excluded_range_end(e),466                    })467                    .unwrap_or_else(|| {468                        self.lower_ty_pat_range_end(469                            hir::LangItem::RangeMax,470                            span.shrink_to_hi(),471                            base_type,472                        )473                    }),474            ),475            TyPatKind::NotNull => hir::TyPatKind::NotNull,476            TyPatKind::Or(variants) => {477                hir::TyPatKind::Or(self.arena.alloc_from_iter(478                    variants.iter().map(|pat| self.lower_ty_pat_mut(pat, base_type)),479                ))480            }481            TyPatKind::Err(guar) => hir::TyPatKind::Err(*guar),482        };483484        hir::TyPat { hir_id: pat_hir_id, kind: node, span: self.lower_span(pattern.span) }485    }486487    /// Lowers the range end of an exclusive range (`2..5`) to an inclusive range 2..=(5 - 1).488    /// This way the type system doesn't have to handle the distinction between inclusive/exclusive ranges.489    fn lower_excluded_range_end(&mut self, e: &AnonConst) -> &'hir hir::ConstArg<'hir> {490        let span = self.lower_span(e.value.span);491        let unstable_span = self.mark_span_with_reason(492            DesugaringKind::PatTyRange,493            span,494            Some(Arc::clone(&self.allow_pattern_type)),495        );496        let anon_const = self.with_new_scopes(span, |this| {497            let def_id = this.local_def_id(e.id);498            let hir_id = this.lower_node_id(e.id);499            let body = this.lower_body(|this| {500                // Need to use a custom function as we can't just subtract `1` from a `char`.501                let kind = hir::ExprKind::Path(this.make_lang_item_qpath(502                    hir::LangItem::RangeSub,503                    unstable_span,504                    None,505                ));506                let fn_def = this.arena.alloc(hir::Expr { hir_id: this.next_id(), kind, span });507                let args = this.arena.alloc([this.lower_expr_mut(&e.value)]);508                (509                    &[],510                    hir::Expr {511                        hir_id: this.next_id(),512                        kind: hir::ExprKind::Call(fn_def, args),513                        span,514                    },515                )516            });517            hir::AnonConst { def_id, hir_id, body, span }518        });519        self.arena.alloc(hir::ConstArg {520            hir_id: self.next_id(),521            kind: hir::ConstArgKind::Anon(self.arena.alloc(anon_const)),522            span,523        })524    }525526    /// When a range has no end specified (`1..` or `1..=`) or no start specified (`..5` or `..=5`),527    /// we instead use a constant of the MAX/MIN of the type.528    /// This way the type system does not have to handle the lack of a start/end.529    fn lower_ty_pat_range_end(530        &mut self,531        lang_item: LangItem,532        span: Span,533        base_type: Span,534    ) -> &'hir hir::ConstArg<'hir> {535        let node_id = self.next_node_id();536537        // Add a definition for the in-band const def.538        // We're generating a range end that didn't exist in the AST,539        // so the def collector didn't create the def ahead of time. That's why we have to do540        // it here.541        let def_id = self.create_def(node_id, None, DefKind::AnonConst, span);542        let hir_id = self.lower_node_id(node_id);543544        let unstable_span = self.mark_span_with_reason(545            DesugaringKind::PatTyRange,546            self.lower_span(span),547            Some(Arc::clone(&self.allow_pattern_type)),548        );549        let span = self.lower_span(base_type);550551        let path_expr = hir::Expr {552            hir_id: self.next_id(),553            kind: hir::ExprKind::Path(self.make_lang_item_qpath(lang_item, unstable_span, None)),554            span,555        };556557        let ct = self.with_new_scopes(span, |this| {558            self.arena.alloc(hir::AnonConst {559                def_id,560                hir_id,561                body: this.lower_body(|_this| (&[], path_expr)),562                span,563            })564        });565        let hir_id = self.next_id();566        self.arena.alloc(hir::ConstArg { kind: hir::ConstArgKind::Anon(ct), hir_id, span })567    }568}

Code quality findings 11

Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
) -> (&'hir [hir::Pat<'hir>], hir::DotDotPos) {
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
self.ban_extra_rest_pat(pat.span, rest.unwrap().1, ctx);
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
self.ban_extra_rest_pat(rest_span, prev_rest_span.unwrap(), "slice");
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
local_id: self.ident_and_label_to_local_id[&id],
Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use rustc_ast::*;
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match etc {
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
elems.push(self.lower_pat_mut(pat));
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
elems.push(self.lower_pat_mut(pat));
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
_ => before.push(self.lower_pat_mut(pat)),
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let rest_span = match &pat.kind {
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
after.push(lower_rest_sub(self, pat, ann, ident, sub));

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.