compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs RUST 1,132 lines View on github.com → Search inside
1use std::path::PathBuf;23use rustc_ast::{LitIntType, LitKind, MetaItemLit};4use rustc_feature::AttributeStability;5use rustc_hir::LangItem;6use rustc_hir::attrs::{7    BorrowckGraphvizFormatKind, CguFields, CguKind, DivergingBlockBehavior,8    DivergingFallbackBehavior, RustcCleanAttribute, RustcCleanQueries, RustcMirKind,9};10use rustc_span::Symbol;1112use super::prelude::*;13use super::util::parse_single_integer;14use crate::diagnostics;15use crate::session_diagnostics::{16    AttributeRequiresOpt, CguFieldsMissing, RustcScalableVectorCountOutOfRange, UnknownLangItem,17};1819pub(crate) struct RustcMainParser;2021impl NoArgsAttributeParser for RustcMainParser {22    const PATH: &[Symbol] = &[sym::rustc_main];23    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);24    const STABILITY: AttributeStability = unstable!(25        rustc_attrs,26        "the `#[rustc_main]` attribute is used internally to specify test entry point function"27    );28    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcMain;29}3031pub(crate) struct RustcMustImplementOneOfParser;3233impl SingleAttributeParser for RustcMustImplementOneOfParser {34    const PATH: &[Symbol] = &[sym::rustc_must_implement_one_of];35    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);36    const STABILITY: AttributeStability = unstable!(37        rustc_attrs,38        "the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete definition of a trait. Its syntax and semantics are highly experimental and will be subject to change before stabilization"39    );40    const TEMPLATE: AttributeTemplate = template!(List: &["function1, function2, ..."]);41    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {42        let list = cx.expect_list(args, cx.attr_span)?;4344        let mut fn_names = ThinVec::new();4546        let inputs: Vec<_> = list.mixed().collect();4748        if inputs.len() < 2 {49            cx.adcx().expected_list_with_num_args_or_more(2, list.span);50            return None;51        }5253        let mut errored = false;54        for argument in inputs {55            let Some(meta) = argument.meta_item_no_args() else {56                cx.adcx().expected_identifier(argument.span());57                return None;58            };5960            let Some(ident) = meta.ident() else {61                cx.dcx()62                    .emit_err(diagnostics::MustBeNameOfAssociatedFunction { span: meta.span() });63                errored = true;64                continue;65            };6667            fn_names.push(ident);68        }69        if errored {70            return None;71        }7273        Some(AttributeKind::RustcMustImplementOneOf { attr_span: cx.attr_span, fn_names })74    }75}7677pub(crate) struct RustcNeverReturnsNullPtrParser;7879impl NoArgsAttributeParser for RustcNeverReturnsNullPtrParser {80    const PATH: &[Symbol] = &[sym::rustc_never_returns_null_ptr];81    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[82        Allow(Target::Fn),83        Allow(Target::Method(MethodKind::Inherent)),84        Allow(Target::Method(MethodKind::Trait { body: false })),85        Allow(Target::Method(MethodKind::Trait { body: true })),86        Allow(Target::Method(MethodKind::TraitImpl)),87    ]);88    const STABILITY: AttributeStability = unstable!(rustc_attrs);8990    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNeverReturnsNullPtr;91}92pub(crate) struct RustcNoImplicitAutorefsParser;9394impl NoArgsAttributeParser for RustcNoImplicitAutorefsParser {95    const PATH: &[Symbol] = &[sym::rustc_no_implicit_autorefs];96    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[97        Allow(Target::Fn),98        Allow(Target::Method(MethodKind::Inherent)),99        Allow(Target::Method(MethodKind::Trait { body: false })),100        Allow(Target::Method(MethodKind::Trait { body: true })),101        Allow(Target::Method(MethodKind::TraitImpl)),102    ]);103    const STABILITY: AttributeStability = unstable!(rustc_attrs);104105    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoImplicitAutorefs;106}107108pub(crate) struct RustcLegacyConstGenericsParser;109110impl SingleAttributeParser for RustcLegacyConstGenericsParser {111    const PATH: &[Symbol] = &[sym::rustc_legacy_const_generics];112    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);113    const TEMPLATE: AttributeTemplate = template!(List: &["N"]);114    const STABILITY: AttributeStability = unstable!(rustc_attrs);115116    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {117        let meta_items = cx.expect_list(args, cx.attr_span)?;118119        let mut parsed_indexes = ThinVec::new();120        let mut errored = false;121122        for possible_index in meta_items.mixed() {123            if let MetaItemOrLitParser::Lit(MetaItemLit {124                kind: LitKind::Int(index, LitIntType::Unsuffixed),125                ..126            }) = possible_index127            {128                parsed_indexes.push((index.0 as usize, possible_index.span()));129            } else {130                cx.adcx().expected_integer_literal(possible_index.span());131                errored = true;132            }133        }134        if errored {135            return None;136        } else if parsed_indexes.is_empty() {137            cx.adcx().expected_at_least_one_argument(args.span()?);138            return None;139        }140141        Some(AttributeKind::RustcLegacyConstGenerics {142            fn_indexes: parsed_indexes,143            attr_span: cx.attr_span,144        })145    }146}147148pub(crate) struct RustcInheritOverflowChecksParser;149150impl NoArgsAttributeParser for RustcInheritOverflowChecksParser {151    const PATH: &[Symbol] = &[sym::rustc_inherit_overflow_checks];152    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[153        Allow(Target::Fn),154        Allow(Target::Method(MethodKind::Inherent)),155        Allow(Target::Method(MethodKind::TraitImpl)),156        Allow(Target::Closure),157    ]);158    const STABILITY: AttributeStability = unstable!(rustc_attrs);159    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcInheritOverflowChecks;160}161162pub(crate) struct RustcLintOptDenyFieldAccessParser;163164impl SingleAttributeParser for RustcLintOptDenyFieldAccessParser {165    const PATH: &[Symbol] = &[sym::rustc_lint_opt_deny_field_access];166    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Field)]);167    const TEMPLATE: AttributeTemplate = template!(Word);168    const STABILITY: AttributeStability = unstable!(rustc_attrs);169    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {170        let arg = cx.expect_single_element_list(args, cx.attr_span)?;171        let lint_message = cx.expect_string_literal(arg)?;172173        Some(AttributeKind::RustcLintOptDenyFieldAccess { lint_message })174    }175}176177pub(crate) struct RustcLintOptTyParser;178179impl NoArgsAttributeParser for RustcLintOptTyParser {180    const PATH: &[Symbol] = &[sym::rustc_lint_opt_ty];181    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);182    const STABILITY: AttributeStability = unstable!(rustc_attrs);183    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcLintOptTy;184}185186fn parse_cgu_fields(187    cx: &mut AcceptContext<'_, '_>,188    args: &ArgParser,189    accepts_kind: bool,190) -> Option<(Symbol, Symbol, Option<CguKind>)> {191    let args = cx.expect_list(args, cx.attr_span)?;192193    let mut cfg = None::<(Symbol, Span)>;194    let mut module = None::<(Symbol, Span)>;195    let mut kind = None::<(Symbol, Span)>;196197    for arg in args.mixed() {198        let Some((ident, arg)) = cx.expect_name_value(arg, arg.span(), None) else {199            continue;200        };201202        let res = match ident.name {203            sym::cfg => &mut cfg,204            sym::module => &mut module,205            sym::kind if accepts_kind => &mut kind,206            _ => {207                cx.adcx().expected_specific_argument(208                    ident.span,209                    if accepts_kind {210                        &[sym::cfg, sym::module, sym::kind]211                    } else {212                        &[sym::cfg, sym::module]213                    },214                );215                continue;216            }217        };218219        let str = cx.expect_string_literal(arg)?;220221        if res.is_some() {222            cx.adcx().duplicate_key(ident.span.to(arg.args_span()), ident.name);223            continue;224        }225226        *res = Some((str, arg.value_span));227    }228229    let Some((cfg, _)) = cfg else {230        cx.emit_err(CguFieldsMissing { span: args.span, name: &cx.attr_path, field: sym::cfg });231        return None;232    };233    let Some((module, _)) = module else {234        cx.emit_err(CguFieldsMissing { span: args.span, name: &cx.attr_path, field: sym::module });235        return None;236    };237    let kind = if let Some((kind, span)) = kind {238        Some(match kind {239            sym::no => CguKind::No,240            sym::pre_dash_lto => CguKind::PreDashLto,241            sym::post_dash_lto => CguKind::PostDashLto,242            sym::any => CguKind::Any,243            _ => {244                cx.adcx().expected_specific_argument_strings(245                    span,246                    &[sym::no, sym::pre_dash_lto, sym::post_dash_lto, sym::any],247                );248                return None;249            }250        })251    } else {252        // return None so that an unwrap for the attributes that need it is ok.253        if accepts_kind {254            cx.emit_err(CguFieldsMissing {255                span: args.span,256                name: &cx.attr_path,257                field: sym::kind,258            });259            return None;260        };261262        None263    };264265    Some((cfg, module, kind))266}267268#[derive(Default)]269pub(crate) struct RustcCguTestAttributeParser {270    items: ThinVec<(Span, CguFields)>,271}272273impl AttributeParser for RustcCguTestAttributeParser {274    const ATTRIBUTES: AcceptMapping<Self> = &[275        (276            &[sym::rustc_partition_reused],277            template!(List: &[r#"cfg = "...", module = "...""#]),278            unstable!(rustc_attrs),279            |this, cx, args| {280                this.items.extend(parse_cgu_fields(cx, args, false).map(|(cfg, module, _)| {281                    (cx.attr_span, CguFields::PartitionReused { cfg, module })282                }));283            },284        ),285        (286            &[sym::rustc_partition_codegened],287            template!(List: &[r#"cfg = "...", module = "...""#]),288            unstable!(rustc_attrs),289            |this, cx, args| {290                this.items.extend(parse_cgu_fields(cx, args, false).map(|(cfg, module, _)| {291                    (cx.attr_span, CguFields::PartitionCodegened { cfg, module })292                }));293            },294        ),295        (296            &[sym::rustc_expected_cgu_reuse],297            template!(List: &[r#"cfg = "...", module = "...", kind = "...""#]),298            unstable!(rustc_attrs),299            |this, cx, args| {300                this.items.extend(parse_cgu_fields(cx, args, true).map(|(cfg, module, kind)| {301                    // unwrap ok because if not given, we return None in `parse_cgu_fields`.302                    (cx.attr_span, CguFields::ExpectedCguReuse { cfg, module, kind: kind.unwrap() })303                }));304            },305        ),306    ];307308    const ALLOWED_TARGETS: AllowedTargets =309        AllowedTargets::AllowList(&[Allow(Target::Mod), Allow(Target::Crate)]);310311    fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {312        Some(AttributeKind::RustcCguTestAttr(self.items))313    }314}315316pub(crate) struct RustcDeprecatedSafe2024Parser;317318impl SingleAttributeParser for RustcDeprecatedSafe2024Parser {319    const PATH: &[Symbol] = &[sym::rustc_deprecated_safe_2024];320    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[321        Allow(Target::Fn),322        Allow(Target::Method(MethodKind::Inherent)),323        Allow(Target::Method(MethodKind::Trait { body: false })),324        Allow(Target::Method(MethodKind::Trait { body: true })),325        Allow(Target::Method(MethodKind::TraitImpl)),326    ]);327    const TEMPLATE: AttributeTemplate = template!(List: &[r#"audit_that = "...""#]);328    const STABILITY: AttributeStability = unstable!(rustc_attrs);329330    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {331        let single = cx.expect_single_element_list(args, cx.attr_span)?;332333        let (path, arg) = cx.expect_name_value(single, cx.attr_span, None)?;334335        if path.name != sym::audit_that {336            cx.adcx().expected_specific_argument(path.span, &[sym::audit_that]);337            return None;338        };339340        let suggestion = cx.expect_string_literal(arg)?;341342        Some(AttributeKind::RustcDeprecatedSafe2024 { suggestion })343    }344}345346pub(crate) struct RustcConversionSuggestionParser;347348impl NoArgsAttributeParser for RustcConversionSuggestionParser {349    const PATH: &[Symbol] = &[sym::rustc_conversion_suggestion];350    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[351        Allow(Target::Fn),352        Allow(Target::Method(MethodKind::Inherent)),353        Allow(Target::Method(MethodKind::Trait { body: false })),354        Allow(Target::Method(MethodKind::Trait { body: true })),355        Allow(Target::Method(MethodKind::TraitImpl)),356    ]);357    const STABILITY: AttributeStability = unstable!(rustc_attrs);358    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcConversionSuggestion;359}360361pub(crate) struct RustcCaptureAnalysisParser;362363impl NoArgsAttributeParser for RustcCaptureAnalysisParser {364    const PATH: &[Symbol] = &[sym::rustc_capture_analysis];365    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Closure)]);366    const STABILITY: AttributeStability = unstable!(rustc_attrs);367    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcCaptureAnalysis;368}369370pub(crate) struct RustcNeverTypeOptionsParser;371372impl SingleAttributeParser for RustcNeverTypeOptionsParser {373    const PATH: &[Symbol] = &[sym::rustc_never_type_options];374    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);375    const TEMPLATE: AttributeTemplate = template!(List: &[376        r#"fallback = "unit", "never", "no""#,377        r#"diverging_block_default = "unit", "never""#,378    ]);379    const STABILITY: AttributeStability = unstable!(380        rustc_attrs,381        "`rustc_never_type_options` is used to experiment with never type fallback and work on never type stabilization"382    );383384    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {385        let list = cx.expect_list(args, cx.attr_span)?;386387        let mut fallback = None::<Ident>;388        let mut diverging_block_default = None::<Ident>;389390        for arg in list.mixed() {391            let Some((ident, arg)) = cx.expect_name_value(arg, arg.span(), None) else {392                continue;393            };394395            let res = match ident.name {396                sym::fallback => &mut fallback,397                sym::diverging_block_default => &mut diverging_block_default,398                _ => {399                    cx.adcx().expected_specific_argument(400                        ident.span,401                        &[sym::fallback, sym::diverging_block_default],402                    );403                    continue;404                }405            };406407            let field = cx.expect_string_literal(arg)?;408409            if res.is_some() {410                cx.adcx().duplicate_key(ident.span, ident.name);411                continue;412            }413414            *res = Some(Ident { name: field, span: arg.value_span });415        }416417        let fallback = match fallback {418            None => None,419            Some(Ident { name: sym::unit, .. }) => Some(DivergingFallbackBehavior::ToUnit),420            Some(Ident { name: sym::never, .. }) => Some(DivergingFallbackBehavior::ToNever),421            Some(Ident { name: sym::no, .. }) => Some(DivergingFallbackBehavior::NoFallback),422            Some(Ident { span, .. }) => {423                cx.adcx()424                    .expected_specific_argument_strings(span, &[sym::unit, sym::never, sym::no]);425                return None;426            }427        };428429        let diverging_block_default = match diverging_block_default {430            None => None,431            Some(Ident { name: sym::unit, .. }) => Some(DivergingBlockBehavior::Unit),432            Some(Ident { name: sym::never, .. }) => Some(DivergingBlockBehavior::Never),433            Some(Ident { span, .. }) => {434                cx.adcx().expected_specific_argument_strings(span, &[sym::unit, sym::no]);435                return None;436            }437        };438439        Some(AttributeKind::RustcNeverTypeOptions { fallback, diverging_block_default })440    }441}442443pub(crate) struct RustcTrivialFieldReadsParser;444445impl NoArgsAttributeParser for RustcTrivialFieldReadsParser {446    const PATH: &[Symbol] = &[sym::rustc_trivial_field_reads];447    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);448    const STABILITY: AttributeStability = unstable!(rustc_attrs);449    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcTrivialFieldReads;450}451452pub(crate) struct RustcNoMirInlineParser;453454impl NoArgsAttributeParser for RustcNoMirInlineParser {455    const PATH: &[Symbol] = &[sym::rustc_no_mir_inline];456    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[457        Allow(Target::Fn),458        Allow(Target::Method(MethodKind::Inherent)),459        Allow(Target::Method(MethodKind::Trait { body: false })),460        Allow(Target::Method(MethodKind::Trait { body: true })),461        Allow(Target::Method(MethodKind::TraitImpl)),462    ]);463    const STABILITY: AttributeStability = unstable!(rustc_attrs);464    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoMirInline;465}466467pub(crate) struct RustcNoWritableParser;468469impl NoArgsAttributeParser for RustcNoWritableParser {470    const PATH: &[Symbol] = &[sym::rustc_no_writable];471    const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error;472    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[473        Allow(Target::Fn),474        Allow(Target::Closure),475        Allow(Target::Method(MethodKind::Inherent)),476        Allow(Target::Method(MethodKind::TraitImpl)),477        Allow(Target::Method(MethodKind::Trait { body: true })),478    ]);479    const STABILITY: AttributeStability = unstable!(rustc_attrs);480    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoWritable;481}482483pub(crate) struct RustcLintQueryInstabilityParser;484485impl NoArgsAttributeParser for RustcLintQueryInstabilityParser {486    const PATH: &[Symbol] = &[sym::rustc_lint_query_instability];487    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[488        Allow(Target::Fn),489        Allow(Target::Method(MethodKind::Inherent)),490        Allow(Target::Method(MethodKind::Trait { body: false })),491        Allow(Target::Method(MethodKind::Trait { body: true })),492        Allow(Target::Method(MethodKind::TraitImpl)),493    ]);494    const STABILITY: AttributeStability = unstable!(rustc_attrs);495    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcLintQueryInstability;496}497498pub(crate) struct RustcRegionsParser;499500impl NoArgsAttributeParser for RustcRegionsParser {501    const PATH: &[Symbol] = &[sym::rustc_regions];502    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[503        Allow(Target::Fn),504        Allow(Target::Method(MethodKind::Inherent)),505        Allow(Target::Method(MethodKind::Trait { body: false })),506        Allow(Target::Method(MethodKind::Trait { body: true })),507        Allow(Target::Method(MethodKind::TraitImpl)),508    ]);509    const STABILITY: AttributeStability = unstable!(rustc_attrs);510    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcRegions;511}512513pub(crate) struct RustcLintUntrackedQueryInformationParser;514515impl NoArgsAttributeParser for RustcLintUntrackedQueryInformationParser {516    const PATH: &[Symbol] = &[sym::rustc_lint_untracked_query_information];517    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[518        Allow(Target::Fn),519        Allow(Target::Method(MethodKind::Inherent)),520        Allow(Target::Method(MethodKind::Trait { body: false })),521        Allow(Target::Method(MethodKind::Trait { body: true })),522        Allow(Target::Method(MethodKind::TraitImpl)),523    ]);524    const STABILITY: AttributeStability = unstable!(rustc_attrs);525    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcLintUntrackedQueryInformation;526}527528pub(crate) struct RustcSimdMonomorphizeLaneLimitParser;529530impl SingleAttributeParser for RustcSimdMonomorphizeLaneLimitParser {531    const PATH: &[Symbol] = &[sym::rustc_simd_monomorphize_lane_limit];532    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);533    const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");534    const STABILITY: AttributeStability = unstable!(rustc_attrs);535536    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {537        let nv = cx.expect_name_value(args, cx.attr_span, None)?;538        Some(AttributeKind::RustcSimdMonomorphizeLaneLimit(cx.parse_limit_int(nv)?))539    }540}541542pub(crate) struct RustcScalableVectorParser;543544impl SingleAttributeParser for RustcScalableVectorParser {545    const PATH: &[Symbol] = &[sym::rustc_scalable_vector];546    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);547    const TEMPLATE: AttributeTemplate = template!(Word, List: &["count"]);548    const STABILITY: AttributeStability = unstable!(rustc_attrs);549550    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {551        if args.as_no_args().is_ok() {552            return Some(AttributeKind::RustcScalableVector { element_count: None });553        }554555        let n = parse_single_integer(cx, args)?;556        let Ok(n) = n.try_into() else {557            cx.emit_err(RustcScalableVectorCountOutOfRange { span: cx.attr_span, n });558            return None;559        };560        Some(AttributeKind::RustcScalableVector { element_count: Some(n) })561    }562}563564pub(crate) struct LangParser;565566impl SingleAttributeParser for LangParser {567    const PATH: &[Symbol] = &[sym::lang];568    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); // Targets are checked per lang item in `rustc_passes`569    const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");570    const STABILITY: AttributeStability = unstable!(lang_items);571572    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {573        let nv = cx.expect_name_value(args, cx.attr_span, None)?;574        let name = cx.expect_string_literal(nv)?;575        let Some(lang_item) = LangItem::from_name(name) else {576            cx.emit_err(UnknownLangItem { span: cx.attr_span, name });577            return None;578        };579        Some(AttributeKind::Lang(lang_item))580    }581}582583pub(crate) struct RustcHasIncoherentInherentImplsParser;584585impl NoArgsAttributeParser for RustcHasIncoherentInherentImplsParser {586    const PATH: &[Symbol] = &[sym::rustc_has_incoherent_inherent_impls];587    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[588        Allow(Target::Trait),589        Allow(Target::Struct),590        Allow(Target::Enum),591        Allow(Target::Union),592        Allow(Target::ForeignTy),593    ]);594    const STABILITY: AttributeStability = unstable!(rustc_attrs);595    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcHasIncoherentInherentImpls;596}597598pub(crate) struct PanicHandlerParser;599600impl NoArgsAttributeParser for PanicHandlerParser {601    const PATH: &[Symbol] = &[sym::panic_handler];602    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); // Targets are checked per lang item in `rustc_passes`603    const STABILITY: AttributeStability = AttributeStability::Stable;604    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::Lang(LangItem::PanicImpl);605}606607pub(crate) struct RustcNounwindParser;608609impl NoArgsAttributeParser for RustcNounwindParser {610    const PATH: &[Symbol] = &[sym::rustc_nounwind];611    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[612        Allow(Target::Fn),613        Allow(Target::ForeignFn),614        Allow(Target::Method(MethodKind::Inherent)),615        Allow(Target::Method(MethodKind::TraitImpl)),616        Allow(Target::Method(MethodKind::Trait { body: true })),617    ]);618    const STABILITY: AttributeStability = unstable!(rustc_attrs);619    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNounwind;620}621622pub(crate) struct RustcOffloadKernelParser;623624impl NoArgsAttributeParser for RustcOffloadKernelParser {625    const PATH: &[Symbol] = &[sym::rustc_offload_kernel];626    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);627    const STABILITY: AttributeStability = unstable!(rustc_attrs);628    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcOffloadKernel;629}630631pub(crate) struct RustcMirParser;632633impl CombineAttributeParser for RustcMirParser {634    const PATH: &[Symbol] = &[sym::rustc_mir];635636    type Item = RustcMirKind;637638    const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::RustcMir(items);639    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[640        Allow(Target::Fn),641        Allow(Target::Method(MethodKind::Inherent)),642        Allow(Target::Method(MethodKind::TraitImpl)),643        Allow(Target::Method(MethodKind::Trait { body: false })),644        Allow(Target::Method(MethodKind::Trait { body: true })),645    ]);646    const TEMPLATE: AttributeTemplate = template!(List: &["arg1, arg2, ..."]);647    const STABILITY: AttributeStability = unstable!(rustc_attrs);648649    fn extend(650        cx: &mut AcceptContext<'_, '_>,651        args: &ArgParser,652    ) -> impl IntoIterator<Item = Self::Item> {653        let Some(list) = cx.expect_list(args, cx.attr_span) else {654            return ThinVec::new();655        };656657        list.mixed()658            .filter_map(|arg| arg.meta_item())659            .filter_map(|mi| {660                if let Some(ident) = mi.ident() {661                    match ident.name {662                        sym::rustc_peek_maybe_init => Some(RustcMirKind::PeekMaybeInit),663                        sym::rustc_peek_maybe_uninit => Some(RustcMirKind::PeekMaybeUninit),664                        sym::rustc_peek_liveness => Some(RustcMirKind::PeekLiveness),665                        sym::stop_after_dataflow => Some(RustcMirKind::StopAfterDataflow),666                        sym::borrowck_graphviz_postflow => {667                            let nv = cx.expect_name_value(668                                mi.args(),669                                mi.span(),670                                Some(sym::borrowck_graphviz_postflow),671                            )?;672                            let path = cx.expect_string_literal(nv)?;673                            let path = PathBuf::from(path.to_string());674                            if path.file_name().is_some() {675                                Some(RustcMirKind::BorrowckGraphvizPostflow { path })676                            } else {677                                cx.adcx().expected_filename_literal(nv.value_span);678                                None679                            }680                        }681                        sym::borrowck_graphviz_format => {682                            let nv = cx.expect_name_value(683                                mi.args(),684                                mi.span(),685                                Some(sym::borrowck_graphviz_format),686                            )?;687                            let Some(format) = nv.value_as_ident() else {688                                cx.adcx().expected_identifier(nv.value_span);689                                return None;690                            };691                            match format.name {692                                sym::two_phase => Some(RustcMirKind::BorrowckGraphvizFormat {693                                    format: BorrowckGraphvizFormatKind::TwoPhase,694                                }),695                                _ => {696                                    cx.adcx()697                                        .expected_specific_argument(format.span, &[sym::two_phase]);698                                    None699                                }700                            }701                        }702                        _ => None,703                    }704                } else {705                    None706                }707            })708            .collect()709    }710}711pub(crate) struct RustcNonConstTraitMethodParser;712713impl NoArgsAttributeParser for RustcNonConstTraitMethodParser {714    const PATH: &[Symbol] = &[sym::rustc_non_const_trait_method];715    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[716        Allow(Target::Method(MethodKind::Trait { body: true })),717        Allow(Target::Method(MethodKind::Trait { body: false })),718    ]);719    const STABILITY: AttributeStability = unstable!(720        rustc_attrs,721        "`#[rustc_non_const_trait_method]` should only used by the standard library to mark trait methods as non-const to allow large traits an easier transition to const"722    );723    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNonConstTraitMethod;724}725726pub(crate) struct RustcCleanParser;727728impl CombineAttributeParser for RustcCleanParser {729    const PATH: &[Symbol] = &[sym::rustc_clean];730731    type Item = RustcCleanAttribute;732733    const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::RustcClean(items);734    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[735        // tidy-alphabetical-start736        Allow(Target::AssocConst),737        Allow(Target::AssocTy),738        Allow(Target::Const),739        Allow(Target::Enum),740        Allow(Target::Expression),741        Allow(Target::Field),742        Allow(Target::Fn),743        Allow(Target::ForeignMod),744        Allow(Target::Impl { of_trait: false }),745        Allow(Target::Impl { of_trait: true }),746        Allow(Target::Method(MethodKind::Inherent)),747        Allow(Target::Method(MethodKind::Trait { body: false })),748        Allow(Target::Method(MethodKind::Trait { body: true })),749        Allow(Target::Method(MethodKind::TraitImpl)),750        Allow(Target::Mod),751        Allow(Target::Static),752        Allow(Target::Struct),753        Allow(Target::Trait),754        Allow(Target::TyAlias),755        Allow(Target::Union),756        // tidy-alphabetical-end757    ]);758    const STABILITY: AttributeStability = unstable!(rustc_attrs);759    const TEMPLATE: AttributeTemplate =760        template!(List: &[r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#]);761762    fn extend(763        cx: &mut AcceptContext<'_, '_>,764        args: &ArgParser,765    ) -> impl IntoIterator<Item = Self::Item> {766        if !cx.cx.sess.opts.unstable_opts.query_dep_graph {767            cx.emit_err(AttributeRequiresOpt { span: cx.attr_span, opt: "-Z query-dep-graph" });768        }769        let list = cx.expect_list(args, cx.attr_span)?;770771        let mut except = None;772        let mut loaded_from_disk = None;773        let mut cfg = None;774775        for item in list.mixed() {776            let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {777                continue;778            };779            let value_span = value.value_span;780            let Some(value) = cx.expect_string_literal(value) else {781                continue;782            };783            match ident.name {784                sym::cfg if cfg.is_some() => {785                    cx.adcx().duplicate_key(item.span(), sym::cfg);786                }787                sym::cfg => {788                    cfg = Some(value);789                }790                sym::except if except.is_some() => {791                    cx.adcx().duplicate_key(item.span(), sym::except);792                }793                sym::except => {794                    let entries =795                        value.as_str().split(',').map(|s| Symbol::intern(s.trim())).collect();796                    except = Some(RustcCleanQueries { entries, span: value_span });797                }798                sym::loaded_from_disk if loaded_from_disk.is_some() => {799                    cx.adcx().duplicate_key(item.span(), sym::loaded_from_disk);800                }801                sym::loaded_from_disk => {802                    let entries =803                        value.as_str().split(',').map(|s| Symbol::intern(s.trim())).collect();804                    loaded_from_disk = Some(RustcCleanQueries { entries, span: value_span });805                }806                _ => {807                    cx.adcx().expected_specific_argument(808                        ident.span,809                        &[sym::cfg, sym::except, sym::loaded_from_disk],810                    );811                }812            }813        }814        let Some(cfg) = cfg else {815            cx.adcx().expected_specific_argument(list.span, &[sym::cfg]);816            return None;817        };818819        Some(RustcCleanAttribute { span: cx.attr_span, cfg, except, loaded_from_disk })820    }821}822823pub(crate) struct RustcIfThisChangedParser;824825impl SingleAttributeParser for RustcIfThisChangedParser {826    const PATH: &[Symbol] = &[sym::rustc_if_this_changed];827    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[828        // tidy-alphabetical-start829        Allow(Target::AssocConst),830        Allow(Target::AssocTy),831        Allow(Target::Const),832        Allow(Target::Enum),833        Allow(Target::Expression),834        Allow(Target::Field),835        Allow(Target::Fn),836        Allow(Target::ForeignMod),837        Allow(Target::Impl { of_trait: false }),838        Allow(Target::Impl { of_trait: true }),839        Allow(Target::Method(MethodKind::Inherent)),840        Allow(Target::Method(MethodKind::Trait { body: false })),841        Allow(Target::Method(MethodKind::Trait { body: true })),842        Allow(Target::Method(MethodKind::TraitImpl)),843        Allow(Target::Mod),844        Allow(Target::Static),845        Allow(Target::Struct),846        Allow(Target::Trait),847        Allow(Target::TyAlias),848        Allow(Target::Union),849        // tidy-alphabetical-end850    ]);851    const TEMPLATE: AttributeTemplate = template!(Word, List: &["DepNode"]);852    const STABILITY: AttributeStability = unstable!(rustc_attrs);853854    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {855        if !cx.cx.sess.opts.unstable_opts.query_dep_graph {856            cx.emit_err(AttributeRequiresOpt { span: cx.attr_span, opt: "-Z query-dep-graph" });857        }858        match args {859            ArgParser::NoArgs => Some(AttributeKind::RustcIfThisChanged(cx.attr_span, None)),860            ArgParser::List(list) => {861                let item = cx.expect_single(list)?;862                let Some(ident) = item.meta_item_no_args().and_then(|item| item.ident()) else {863                    cx.adcx().expected_identifier(item.span());864                    return None;865                };866                Some(AttributeKind::RustcIfThisChanged(cx.attr_span, Some(ident.name)))867            }868            ArgParser::NameValue(_) => {869                let inner_span = cx.inner_span;870                cx.adcx().expected_list_or_no_args(inner_span);871                None872            }873        }874    }875}876877pub(crate) struct RustcThenThisWouldNeedParser;878879impl CombineAttributeParser for RustcThenThisWouldNeedParser {880    const PATH: &[Symbol] = &[sym::rustc_then_this_would_need];881    type Item = Ident;882883    const CONVERT: ConvertFn<Self::Item> =884        |items, _span| AttributeKind::RustcThenThisWouldNeed(items);885    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[886        // tidy-alphabetical-start887        Allow(Target::AssocConst),888        Allow(Target::AssocTy),889        Allow(Target::Const),890        Allow(Target::Enum),891        Allow(Target::Expression),892        Allow(Target::Field),893        Allow(Target::Fn),894        Allow(Target::ForeignMod),895        Allow(Target::Impl { of_trait: false }),896        Allow(Target::Impl { of_trait: true }),897        Allow(Target::Method(MethodKind::Inherent)),898        Allow(Target::Method(MethodKind::Trait { body: false })),899        Allow(Target::Method(MethodKind::Trait { body: true })),900        Allow(Target::Method(MethodKind::TraitImpl)),901        Allow(Target::Mod),902        Allow(Target::Static),903        Allow(Target::Struct),904        Allow(Target::Trait),905        Allow(Target::TyAlias),906        Allow(Target::Union),907        // tidy-alphabetical-end908    ]);909    const TEMPLATE: AttributeTemplate = template!(List: &["DepNode"]);910    const STABILITY: AttributeStability = unstable!(rustc_attrs);911912    fn extend(913        cx: &mut AcceptContext<'_, '_>,914        args: &ArgParser,915    ) -> impl IntoIterator<Item = Self::Item> {916        if !cx.cx.sess.opts.unstable_opts.query_dep_graph {917            cx.emit_err(AttributeRequiresOpt { span: cx.attr_span, opt: "-Z query-dep-graph" });918        }919        let item = cx.expect_single_element_list(args, cx.attr_span)?;920        let Some(ident) = item.meta_item_no_args().and_then(|item| item.ident()) else {921            cx.adcx().expected_identifier(item.span());922            return None;923        };924        Some(ident)925    }926}927928pub(crate) struct RustcInsignificantDtorParser;929930impl NoArgsAttributeParser for RustcInsignificantDtorParser {931    const PATH: &[Symbol] = &[sym::rustc_insignificant_dtor];932    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[933        Allow(Target::Enum),934        Allow(Target::Struct),935        Allow(Target::ForeignTy),936    ]);937    const STABILITY: AttributeStability = unstable!(rustc_attrs);938    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcInsignificantDtor;939}940941pub(crate) struct RustcEffectiveVisibilityParser;942943impl NoArgsAttributeParser for RustcEffectiveVisibilityParser {944    const PATH: &[Symbol] = &[sym::rustc_effective_visibility];945    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[946        Allow(Target::Use),947        Allow(Target::Static),948        Allow(Target::Const),949        Allow(Target::Fn),950        Allow(Target::Closure),951        Allow(Target::Mod),952        Allow(Target::ForeignMod),953        Allow(Target::TyAlias),954        Allow(Target::Enum),955        Allow(Target::Variant),956        Allow(Target::Struct),957        Allow(Target::Field),958        Allow(Target::Union),959        Allow(Target::Trait),960        Allow(Target::TraitAlias),961        Allow(Target::Impl { of_trait: false }),962        Allow(Target::Impl { of_trait: true }),963        Allow(Target::AssocConst),964        Allow(Target::Method(MethodKind::Inherent)),965        Allow(Target::Method(MethodKind::Trait { body: false })),966        Allow(Target::Method(MethodKind::Trait { body: true })),967        Allow(Target::Method(MethodKind::TraitImpl)),968        Allow(Target::AssocTy),969        Allow(Target::ForeignFn),970        Allow(Target::ForeignStatic),971        Allow(Target::ForeignTy),972        Allow(Target::MacroDef),973        Allow(Target::PatField),974        Allow(Target::Crate),975    ]);976    const STABILITY: AttributeStability = unstable!(rustc_attrs);977    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEffectiveVisibility;978}979980pub(crate) struct RustcDiagnosticItemParser;981982impl SingleAttributeParser for RustcDiagnosticItemParser {983    const PATH: &[Symbol] = &[sym::rustc_diagnostic_item];984    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[985        Allow(Target::Trait),986        Allow(Target::Struct),987        Allow(Target::Enum),988        Allow(Target::MacroDef),989        Allow(Target::TyAlias),990        Allow(Target::AssocTy),991        Allow(Target::AssocConst),992        Allow(Target::Fn),993        Allow(Target::Const),994        Allow(Target::Mod),995        Allow(Target::Impl { of_trait: false }),996        Allow(Target::Method(MethodKind::Inherent)),997        Allow(Target::Method(MethodKind::Trait { body: false })),998        Allow(Target::Method(MethodKind::Trait { body: true })),999        Allow(Target::Method(MethodKind::TraitImpl)),1000        Allow(Target::Crate),1001    ]);1002    const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");1003    const STABILITY: AttributeStability = unstable!(1004        rustc_attrs,1005        "the `#[rustc_diagnostic_item]` attribute allows the compiler to reference types from the standard library for diagnostic purposes"1006    );10071008    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {1009        let nv = cx.expect_name_value(args, cx.attr_span, None)?;1010        let value = cx.expect_string_literal(nv)?;1011        Some(AttributeKind::RustcDiagnosticItem(value))1012    }1013}10141015pub(crate) struct RustcDoNotConstCheckParser;10161017impl NoArgsAttributeParser for RustcDoNotConstCheckParser {1018    const PATH: &[Symbol] = &[sym::rustc_do_not_const_check];1019    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[1020        Allow(Target::Fn),1021        Allow(Target::Method(MethodKind::Inherent)),1022        Allow(Target::Method(MethodKind::TraitImpl)),1023        Allow(Target::Method(MethodKind::Trait { body: false })),1024        Allow(Target::Method(MethodKind::Trait { body: true })),1025    ]);1026    const STABILITY: AttributeStability = unstable!(1027        rustc_attrs,1028        "`#[rustc_do_not_const_check]` skips const-check for this function's body"1029    );1030    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDoNotConstCheck;1031}10321033pub(crate) struct RustcNonnullOptimizationGuaranteedParser;10341035impl NoArgsAttributeParser for RustcNonnullOptimizationGuaranteedParser {1036    const PATH: &[Symbol] = &[sym::rustc_nonnull_optimization_guaranteed];1037    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);1038    const STABILITY: AttributeStability = unstable!(1039        rustc_attrs,1040        "the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document guaranteed niche optimizations in the standard library",1041        "the compiler does not even check whether the type indeed is being non-null-optimized; it is your responsibility to ensure that the attribute is only used on types that are optimized"1042    );1043    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNonnullOptimizationGuaranteed;1044}10451046pub(crate) struct RustcStrictCoherenceParser;10471048impl NoArgsAttributeParser for RustcStrictCoherenceParser {1049    const PATH: &[Symbol] = &[sym::rustc_strict_coherence];1050    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[1051        Allow(Target::Trait),1052        Allow(Target::Struct),1053        Allow(Target::Enum),1054        Allow(Target::Union),1055        Allow(Target::ForeignTy),1056    ]);1057    const STABILITY: AttributeStability = unstable!(rustc_attrs);1058    const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcStrictCoherence;1059}10601061pub(crate) struct RustcReservationImplParser;10621063impl SingleAttributeParser for RustcReservationImplParser {1064    const PATH: &[Symbol] = &[sym::rustc_reservation_impl];1065    const ALLOWED_TARGETS: AllowedTargets =1066        AllowedTargets::AllowList(&[Allow(Target::Impl { of_trait: true })]);1067    const TEMPLATE: AttributeTemplate = template!(NameValueStr: "reservation message");1068    const STABILITY: AttributeStability = unstable!(rustc_attrs);10691070    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {1071        let nv = cx.expect_name_value(args, cx.attr_span, None)?;1072        let value_str = cx.expect_string_literal(nv)?;10731074        Some(AttributeKind::RustcReservationImpl(value_str))1075    }1076}10771078pub(crate) struct PreludeImportParser;10791080impl NoArgsAttributeParser for PreludeImportParser {1081    const PATH: &[Symbol] = &[sym::prelude_import];1082    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Use)]);1083    const STABILITY: AttributeStability = unstable!(prelude_import);1084    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::PreludeImport;1085}10861087pub(crate) struct RustcDocPrimitiveParser;10881089impl SingleAttributeParser for RustcDocPrimitiveParser {1090    const PATH: &[Symbol] = &[sym::rustc_doc_primitive];1091    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Mod)]);1092    const TEMPLATE: AttributeTemplate = template!(NameValueStr: "primitive name");1093    const STABILITY: AttributeStability = unstable!(1094        rustc_attrs,1095        "the `#[rustc_doc_primitive]` attribute is used by the standard library to provide a way to generate documentation for primitive types"1096    );10971098    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {1099        let nv = cx.expect_name_value(args, cx.attr_span, None)?;1100        let value_str = cx.expect_string_literal(nv)?;11011102        Some(AttributeKind::RustcDocPrimitive(cx.attr_span, value_str))1103    }1104}11051106pub(crate) struct RustcIntrinsicParser;11071108impl NoArgsAttributeParser for RustcIntrinsicParser {1109    const PATH: &[Symbol] = &[sym::rustc_intrinsic];1110    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);1111    const STABILITY: AttributeStability = unstable!(intrinsics);1112    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcIntrinsic;1113}11141115pub(crate) struct RustcIntrinsicConstStableIndirectParser;11161117impl NoArgsAttributeParser for RustcIntrinsicConstStableIndirectParser {1118    const PATH: &'static [Symbol] = &[sym::rustc_intrinsic_const_stable_indirect];1119    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);1120    const STABILITY: AttributeStability = unstable!(rustc_attrs);1121    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcIntrinsicConstStableIndirect;1122}11231124pub(crate) struct RustcExhaustiveParser;11251126impl NoArgsAttributeParser for RustcExhaustiveParser {1127    const PATH: &'static [Symbol] = &[sym::rustc_must_match_exhaustively];1128    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Enum)]);1129    const STABILITY: AttributeStability = unstable!(rustc_attrs);1130    const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcMustMatchExhaustively;1131}

Findings

✓ No findings reported for this file.

Get this view in your editor

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