From f1ba5c6b84609efa061be42fff8d756ac9a11ae7 Mon Sep 17 00:00:00 2001 From: Edvin Bryntesson Date: Fri, 22 May 2026 14:16:32 +0200 Subject: [PATCH] unrevert lint attrs --- Cargo.lock | 1 + compiler/rustc_attr_parsing/Cargo.toml | 1 + .../rustc_attr_parsing/src/attributes/lint.rs | 431 ++++++++++++++++++ .../rustc_attr_parsing/src/attributes/mod.rs | 1 + compiler/rustc_attr_parsing/src/context.rs | 17 +- compiler/rustc_attr_parsing/src/interface.rs | 55 ++- compiler/rustc_attr_parsing/src/parser.rs | 25 +- .../src/session_diagnostics.rs | 21 + .../rustc_attr_parsing/src/validate_attr.rs | 20 +- .../src/error_codes/E0452.md | 3 +- compiler/rustc_expand/src/base.rs | 3 +- compiler/rustc_expand/src/expand.rs | 1 - .../rustc_hir/src/attrs/data_structures.rs | 135 +++++- .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + .../rustc_hir/src/attrs/pretty_printing.rs | 6 +- compiler/rustc_hir/src/hir.rs | 1 + compiler/rustc_hir_typeck/src/expr.rs | 3 +- compiler/rustc_interface/src/passes.rs | 3 +- compiler/rustc_lint/src/context.rs | 54 +-- compiler/rustc_lint/src/early.rs | 214 ++++++--- compiler/rustc_lint/src/errors.rs | 30 -- compiler/rustc_lint/src/expect.rs | 45 +- compiler/rustc_lint/src/levels.rs | 419 ++++++----------- compiler/rustc_lint/src/lib.rs | 6 +- compiler/rustc_lint/src/lints.rs | 83 +--- compiler/rustc_lint_defs/src/lib.rs | 61 ++- compiler/rustc_metadata/src/rmeta/encoder.rs | 7 +- compiler/rustc_middle/src/error.rs | 82 ++++ compiler/rustc_mir_build/src/builder/scope.rs | 10 +- compiler/rustc_passes/src/check_attr.rs | 156 +++---- compiler/rustc_passes/src/errors.rs | 2 - compiler/rustc_session/src/session.rs | 11 +- .../src/attrs/unnecessary_clippy_cfg.rs | 7 +- .../src/attrs/useless_attribute.rs | 2 +- .../clippy/clippy_lints/src/attrs/utils.rs | 2 +- .../clippy/clippy_lints/src/collapsible_if.rs | 38 +- .../src/returns/needless_return.rs | 31 +- .../tests/ui/expect_tool_lint_rfc_2383.rs | 2 - .../tests/ui/expect_tool_lint_rfc_2383.stderr | 18 +- .../tests/ui/unknown_clippy_lints.stderr | 30 +- tests/pretty/delegation-inherit-attributes.pp | 11 +- tests/pretty/delegation-inline-attribute.pp | 11 +- tests/pretty/hir-delegation.pp | 11 +- tests/pretty/hir-lifetimes.pp | 14 +- tests/pretty/pin-ergonomics-hir.pp | 11 +- .../lints/renamed-lint-still-applies-2.rs | 12 + .../lints/renamed-lint-still-applies-2.stderr | 32 ++ .../lints/renamed-lint-still-applies.rs | 5 +- .../lints/renamed-lint-still-applies.stderr | 36 +- ...-highlight-span-extra-arguments-147070.svg | 2 +- tests/ui/attributes/malformed-attrs.stderr | 170 +++---- .../unsafe/proc-unsafe-attributes.rs | 9 +- .../unsafe/proc-unsafe-attributes.stderr | 62 ++- ...deduplicate-diagnostics.deduplicate.stderr | 29 +- .../deduplicate-diagnostics.duplicate.stderr | 35 +- .../deduplicate-diagnostics.rs | 4 +- tests/ui/error-codes/E0452.rs | 8 - tests/ui/error-codes/E0452.stderr | 49 -- ...issue-43106-gating-of-builtin-attrs.stderr | 206 ++++----- ...between-expected-trait-and-found-trait.svg | 2 +- tests/ui/inference/issue-71309.stderr | 4 +- tests/ui/lint/empty-lint-attributes.stderr | 8 +- tests/ui/lint/inert-attr-macro.rs | 6 +- tests/ui/lint/inert-attr-macro.stderr | 9 +- tests/ui/lint/keyword-idents/multi-file.rs | 1 - tests/ui/lint/lint-malformed.rs | 8 +- tests/ui/lint/lint-malformed.stderr | 63 +-- tests/ui/lint/reasons-erroneous.rs | 34 +- tests/ui/lint/reasons-erroneous.stderr | 141 +++++- tests/ui/lint/register-tool-lint.rs | 2 - tests/ui/lint/register-tool-lint.stderr | 11 +- .../ui/lint/renamed-lints-still-apply.stderr | 16 +- .../expect_lint_from_macro.rs | 4 +- .../expect_lint_from_macro.stderr | 9 +- .../expect_tool_lint_rfc_2383.rs | 2 - .../expect_tool_lint_rfc_2383.stderr | 10 +- .../expect_unfulfilled_expectation.rs | 7 - .../expect_unfulfilled_expectation.stderr | 23 +- .../lint-attribute-only-with-reason.stderr | 10 +- .../semicolon-in-expressions-from-macros.rs | 3 +- ...emicolon-in-expressions-from-macros.stderr | 9 +- .../unknown-lints-in-cfg-attr-97094.stderr | 36 +- tests/ui/lint/unused/empty-attributes.stderr | 48 +- tests/ui/proc-macro/cfg-eval.stderr | 2 +- tests/ui/tool-attributes/tool_lints.rs | 2 - tests/ui/tool-attributes/tool_lints.stderr | 20 +- .../tool-attributes/unknown-lint-tool-name.rs | 10 +- .../unknown-lint-tool-name.stderr | 40 +- tests/ui/unpretty/exhaustive.hir.stdout | 56 ++- ...ct-exprs-tuple-call-pretty-printing.stdout | 11 +- tests/ui/unpretty/unpretty-expr-fn-arg.stdout | 9 +- .../ui/where-clauses/unsupported_attribute.rs | 4 +- .../unsupported_attribute.stderr | 8 +- 93 files changed, 1959 insertions(+), 1424 deletions(-) create mode 100644 compiler/rustc_attr_parsing/src/attributes/lint.rs create mode 100644 tests/rustdoc-ui/lints/renamed-lint-still-applies-2.rs create mode 100644 tests/rustdoc-ui/lints/renamed-lint-still-applies-2.stderr delete mode 100644 tests/ui/error-codes/E0452.rs delete mode 100644 tests/ui/error-codes/E0452.stderr diff --git a/Cargo.lock b/Cargo.lock index 059b203fb06c4..7cf7b4b66502f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3681,6 +3681,7 @@ dependencies = [ "rustc_lexer", "rustc_lint_defs", "rustc_macros", + "rustc_middle", "rustc_parse", "rustc_parse_format", "rustc_session", diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml index fccc36a128057..8c52457019cd0 100644 --- a/compiler/rustc_attr_parsing/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -15,6 +15,7 @@ rustc_hir = { path = "../rustc_hir" } rustc_lexer = { path = "../rustc_lexer" } rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } +rustc_middle = { path = "../rustc_middle" } rustc_parse = { path = "../rustc_parse" } rustc_parse_format = { path = "../rustc_parse_format" } rustc_session = { path = "../rustc_session" } diff --git a/compiler/rustc_attr_parsing/src/attributes/lint.rs b/compiler/rustc_attr_parsing/src/attributes/lint.rs new file mode 100644 index 0000000000000..f4ab072d4c70d --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/lint.rs @@ -0,0 +1,431 @@ +use rustc_ast::LitKind; +use rustc_hir::HashIgnoredAttrId; +use rustc_hir::attrs::{LintAttribute, LintAttributeKind, LintInstance}; +use rustc_hir::target::GenericParamKind; +use rustc_middle::error::lint_lints::*; +use rustc_session::DynLintStore; +use rustc_session::lint::builtin::{RENAMED_AND_REMOVED_LINTS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES}; +use rustc_session::lint::{CheckLintNameResult, LintId}; + +use super::prelude::*; +use crate::ShouldEmit; +use crate::attributes::AcceptFn; +use crate::session_diagnostics::UnknownToolInScopedLint; + +pub(crate) trait Lint { + const KIND: LintAttributeKind; +} + +pub(crate) struct Allow; + +impl Lint for Allow { + const KIND: LintAttributeKind = LintAttributeKind::Allow; +} +pub(crate) struct Deny; + +impl Lint for Deny { + const KIND: LintAttributeKind = LintAttributeKind::Deny; +} +pub(crate) struct Expect; + +impl Lint for Expect { + const KIND: LintAttributeKind = LintAttributeKind::Expect; +} +pub(crate) struct Forbid; + +impl Lint for Forbid { + const KIND: LintAttributeKind = LintAttributeKind::Forbid; +} +pub(crate) struct Warn; + +impl Lint for Warn { + const KIND: LintAttributeKind = LintAttributeKind::Warn; +} + +#[derive(Default)] +pub(crate) struct LintParser { + lint_attrs: ThinVec, +} + +trait Mapping { + const MAPPING: (&'static [Symbol], AttributeTemplate, AcceptFn); +} +impl Mapping for T { + const MAPPING: (&'static [Symbol], AttributeTemplate, AcceptFn) = ( + &[T::KIND.symbol()], + template!( + List: &["lint1", "lint1, lint2, ...", r#"lint1, lint2, lint3, reason = "...""#], + "https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes" + ), + |this, cx, args| { + if let Some(lint_attr) = validate_lint_attr::(cx, args) { + this.lint_attrs.push(lint_attr); + } + }, + ); +} + +impl AttributeParser for LintParser { + const ATTRIBUTES: AcceptMapping = + &[Allow::MAPPING, Deny::MAPPING, Expect::MAPPING, Forbid::MAPPING, Warn::MAPPING]; + + const ALLOWED_TARGETS: AllowedTargets = { + use super::prelude::{Allow, Warn}; + AllowedTargets::AllowList(&[ + Allow(Target::ExternCrate), + Allow(Target::Use), + Allow(Target::Static), + Allow(Target::Const), + Allow(Target::Fn), + Allow(Target::Closure), + Allow(Target::Mod), + Allow(Target::ForeignMod), + Allow(Target::GlobalAsm), + Allow(Target::TyAlias), + Allow(Target::Enum), + Allow(Target::Variant), + Allow(Target::Struct), + Allow(Target::Field), + Allow(Target::Union), + Allow(Target::Trait), + Allow(Target::TraitAlias), + Allow(Target::Impl { of_trait: false }), + Allow(Target::Impl { of_trait: true }), + Allow(Target::Expression), + Allow(Target::Statement), + Allow(Target::Arm), + Allow(Target::AssocConst), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::AssocTy), + Allow(Target::ForeignFn), + Allow(Target::ForeignStatic), + Allow(Target::ForeignTy), + Allow(Target::MacroDef), + Allow(Target::Param), + Allow(Target::PatField), + Allow(Target::ExprField), + Allow(Target::Crate), + Allow(Target::Delegation { mac: false }), + Allow(Target::Delegation { mac: true }), + Allow(Target::GenericParam { kind: GenericParamKind::Type, has_default: false }), + Allow(Target::GenericParam { kind: GenericParamKind::Lifetime, has_default: false }), + Allow(Target::GenericParam { kind: GenericParamKind::Const, has_default: false }), + Allow(Target::GenericParam { kind: GenericParamKind::Type, has_default: true }), + Allow(Target::GenericParam { kind: GenericParamKind::Lifetime, has_default: true }), + Allow(Target::GenericParam { kind: GenericParamKind::Const, has_default: true }), + Warn(Target::MacroCall), + ]) + }; + + fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { + if !self.lint_attrs.is_empty() { + Some(AttributeKind::LintAttributes(self.lint_attrs)) + } else { + None + } + } +} + +#[inline(always)] +fn validate_lint_attr( + cx: &mut AcceptContext<'_, '_>, + args: &ArgParser, +) -> Option { + // ShouldEmit is Nothing during early parsing, so to avoid delayed bugs, we just dont emit + // The reason for why we don't want to delay bugs, is that when compiling lib + // they usually skip this attr parsing but since lint-attrs is parsed in pre expansion, + // these delayed bugs would never be emitted as errors and therefore ICE + let early = matches!(cx.should_emit, ShouldEmit::Nothing); + + let Some(lint_store) = cx.sess.lint_store.as_ref().map(|store| store.to_owned()) else { + unreachable!("lint_store required while parsing attributes"); + }; + let lint_store = lint_store.as_ref(); + let Some(list) = args.as_list() else { + let span = cx.inner_span; + if !early { + cx.adcx().expected_list(span, args); + } + return None; + }; + let mut list = list.mixed().peekable(); + + let mut skip_unused_check = false; + let mut errored = false; + let mut reason = None; + let mut lint_instances = ThinVec::new(); + let mut lint_index = 0; + let targeting_crate = matches!(cx.target, Target::Crate); + while let Some(item) = list.next() { + let Some(meta_item) = item.meta_item() else { + if !early { + cx.adcx().expected_identifier(item.span()); + } + errored = true; + continue; + }; + + match meta_item.args() { + ArgParser::NameValue(nv_parser) if meta_item.path().word_is(sym::reason) => { + //FIXME replace this with duplicate check? + if list.peek().is_some() { + if !early { + cx.adcx().expected_nv_as_last_argument(meta_item.span(), sym::reason); + } + errored = true; + continue; + } + + let val_lit = nv_parser.value_as_lit(); + let LitKind::Str(reason_sym, _) = val_lit.kind else { + if !early { + cx.adcx().expected_string_literal(nv_parser.value_span, Some(val_lit)); + } + errored = true; + continue; + }; + reason = Some(reason_sym); + } + ArgParser::NameValue(_) => { + if !early { + cx.adcx().expected_specific_argument(meta_item.span(), &[sym::reason]); + } + errored = true; + } + ArgParser::List(list) => { + if !early { + cx.adcx().expected_no_args(list.span); + } + errored = true; + } + ArgParser::NoArgs => { + skip_unused_check = true; + let mut segments = meta_item.path().segments(); + + let Some(tool_or_name) = segments.next() else { + unreachable!("first segment should always exist"); + }; + + let rest = segments.collect::>(); + let (tool_name, tool_span, name): (Option, Option, _) = + if rest.is_empty() { + let name = tool_or_name.name; + (None, None, name.to_string()) + } else { + let tool = tool_or_name; + let name = rest + .into_iter() + .map(|ident| ident.to_string()) + .collect::>() + .join("::"); + (Some(tool.name), Some(tool.span), name) + }; + + let meta_item_span = meta_item.span(); + let original_name = Symbol::intern(&name); + let mut full_name = tool_name + .map(|tool| Symbol::intern(&format!("{tool}::{}", original_name))) + .unwrap_or(original_name); + + if let Some(ids) = check_lint( + cx, + lint_store, + original_name, + &mut full_name, + tool_name, + tool_span, + meta_item_span, + early, + ) { + if !early + && !targeting_crate + && ids.iter().any(|lint_id| lint_id.lint.crate_level_only) + { + cx.emit_lint( + UNUSED_ATTRIBUTES, + IgnoredUnlessCrateSpecified { + level: T::KIND.symbol(), + name: original_name, + }, + meta_item_span, + ); + } + lint_instances.extend(ids.into_iter().map(|id| { + LintInstance::new( + full_name, + Symbol::intern(&id.to_string()), + meta_item_span, + lint_index, + ) + })); + } + lint_index += 1; + } + } + } + + if errored { + return None; + } + + if !early && !skip_unused_check && lint_instances.is_empty() { + let span = cx.attr_span; + cx.adcx().warn_empty_attribute(span); + } + + Some(LintAttribute { + reason, + lint_instances, + attr_span: cx.attr_span, + attr_style: cx.attr_style, + attr_id: HashIgnoredAttrId { attr_id: cx.attr_id }, + kind: T::KIND, + }) +} + +fn check_lint<'a>( + cx: &mut AcceptContext<'_, '_>, + lint_store: &'a dyn DynLintStore, + original_name: Symbol, + full_name: &mut Symbol, + tool_name: Option, + tool_span: Option, + span: Span, + early: bool, +) -> Option<&'a [LintId]> { + let Some(tools) = cx.tools else { + unreachable!("tools required while parsing attributes"); + }; + if tools.is_empty() { + unreachable!("tools should never be empty") + } + let check_lint_name = lint_store.check_lint_name(original_name.as_str(), tool_name, tools); + match check_lint_name { + CheckLintNameResult::Ok(ids) => Some(ids), + CheckLintNameResult::Tool(ids, new_lint_name) => { + let _name = match new_lint_name { + None => original_name, + Some(new_lint_name) => { + let new_lint_name = Symbol::intern(&new_lint_name); + if !early { + cx.emit_lint( + RENAMED_AND_REMOVED_LINTS, + DeprecatedLintName { + name: *full_name, + suggestion: span, + replace: new_lint_name, + }, + span, + ); + } + new_lint_name + } + }; + Some(ids) + } + + CheckLintNameResult::MissingTool => { + // If `MissingTool` is returned, then either the lint does not + // exist in the tool or the code was not compiled with the tool and + // therefore the lint was never added to the `LintStore`. To detect + // this is the responsibility of the lint tool. + None + } + + CheckLintNameResult::NoTool => { + if !early { + cx.emit_err(UnknownToolInScopedLint { + span: tool_span, + tool_name: tool_name.unwrap(), + full_lint_name: *full_name, + is_nightly_build: cx.sess.is_nightly_build(), + }); + } + + None + } + + CheckLintNameResult::Renamed(replace) => { + if !early { + cx.emit_lint( + RENAMED_AND_REMOVED_LINTS, + RenamedLint { + name: *full_name, + replace, + suggestion: RenamedLintSuggestion::WithSpan { suggestion: span, replace }, + }, + span, + ); + } + + // Since it was renamed, and we have emitted the warning + // we replace the "full_name", to ensure we don't get notes with: + // `#[allow(NEW_NAME)]` implied by `#[allow(OLD_NAME)]` + // Other lints still have access to the original name as the user wrote it, + // through `original_name` + *full_name = replace; + + // If this lint was renamed, apply the new lint instead of ignoring the + // attribute. Ignore any errors or warnings that happen because the new + // name is inaccurate. + // NOTE: `new_name` already includes the tool name, so we don't + // have to add it again. + match lint_store.check_lint_name(replace.as_str(), None, tools) { + CheckLintNameResult::Ok(ids) => Some(ids), + _ => panic!("renamed lint does not exist: {replace}"), + } + } + + CheckLintNameResult::RenamedToolLint(new_name) => { + if !early { + cx.emit_lint( + RENAMED_AND_REMOVED_LINTS, + RenamedLint { + name: *full_name, + replace: new_name, + suggestion: RenamedLintSuggestion::WithSpan { + suggestion: span, + replace: new_name, + }, + }, + span, + ); + } + None + } + + CheckLintNameResult::Removed(reason) => { + if !early { + cx.emit_lint( + RENAMED_AND_REMOVED_LINTS, + RemovedLint { name: *full_name, reason }, + span, + ); + } + None + } + + CheckLintNameResult::NoLint(suggestion) => { + if !early { + cx.emit_lint( + UNKNOWN_LINTS, + UnknownLint { + name: *full_name, + suggestion: suggestion.map(|(symbol, is_rustc)| { + UnknownLintSuggestion::WithSpan { + suggestion: span, + replace: symbol, + from_rustc: is_rustc, + } + }), + }, + span, + ); + } + None + } + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 42c6828ef57b7..abf292fd13f1b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -47,6 +47,7 @@ pub(crate) mod dummy; pub(crate) mod inline; pub(crate) mod instruction_set; pub(crate) mod link_attrs; +pub(crate) mod lint; pub(crate) mod lint_helpers; pub(crate) mod loop_match; pub(crate) mod macro_attrs; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 750fe10a483fc..27382a5dfd78d 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -14,7 +14,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_parse::parser::Recovery; use rustc_session::Session; use rustc_session::lint::{Lint, LintId}; -use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; +use rustc_span::{AttrId, ErrorGuaranteed, Ident, Span, Symbol}; // Glob imports to avoid big, bitrotty import lists use crate::attributes::allow_unstable::*; @@ -37,6 +37,7 @@ use crate::attributes::dummy::*; use crate::attributes::inline::*; use crate::attributes::instruction_set::*; use crate::attributes::link_attrs::*; +use crate::attributes::lint::*; use crate::attributes::lint_helpers::*; use crate::attributes::loop_match::*; use crate::attributes::macro_attrs::*; @@ -135,6 +136,7 @@ attribute_parsers!( ConfusablesParser, ConstStabilityParser, DocParser, + LintParser, MacroUseParser, NakedParser, OnConstParser, @@ -362,6 +364,8 @@ pub struct AcceptContext<'f, 'sess> { /// The name of the attribute we're currently accepting. pub(crate) attr_path: AttrPath, + + pub(crate) attr_id: AttrId, } impl<'f, 'sess: 'f> SharedContext<'f, 'sess> { @@ -1030,6 +1034,17 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { ) } + pub(crate) fn expected_nv_as_last_argument( + &mut self, + span: Span, + name_value_key: Symbol, + ) -> ErrorGuaranteed { + self.emit_parse_error( + span, + AttributeParseErrorReason::ExpectedNameValueAsLastArgument { span, name_value_key }, + ) + } + pub(crate) fn warn_empty_attribute(&mut self, span: Span) { let attr_path = self.attr_path.to_string(); let valid_without_list = self.template.word; diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 4a0ad63c0c053..ea23adf02a75a 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -3,6 +3,7 @@ use std::convert::identity; use rustc_ast as ast; use rustc_ast::token::DocFragmentKind; use rustc_ast::{AttrItemKind, AttrStyle, CRATE_NODE_ID, NodeId, Safety}; +use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan}; use rustc_feature::{AttributeTemplate, Features}; @@ -11,7 +12,7 @@ use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Targ use rustc_lint_defs::RegisteredTools; use rustc_session::Session; use rustc_session::lint::LintId; -use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; +use rustc_span::{DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, sym}; use crate::attributes::AttributeSafety; use crate::context::{ @@ -116,9 +117,9 @@ impl<'sess> AttributeParser<'sess> { /// `rustc_ast_lowering`. Some attributes require access to features to parse, which would /// crash if you tried to do so through [`parse_limited_all`](Self::parse_limited_all). /// Therefore, if `parse_only` is None, then features *must* be provided. - pub fn parse_limited_all( + pub fn parse_limited_all<'a>( sess: &'sess Session, - attrs: &[ast::Attribute], + attrs: impl IntoIterator, parse_only: Option<&'static [Symbol]>, target: Target, target_span: Span, @@ -140,6 +141,32 @@ impl<'sess> AttributeParser<'sess> { ) } + /// This method provides the same functionality as [`parse_limited_all`](Self::parse_limited_all) except filtered, + /// making sure that only allow-listed symbols are parsed + pub fn parse_limited_all_filtered<'a>( + sess: &'sess Session, + attrs: impl IntoIterator, + filter: &[Symbol], + target: Target, + target_span: Span, + target_node_id: NodeId, + features: Option<&'sess Features>, + emit_errors: ShouldEmit, + tools: &'sess FxIndexSet, + ) -> Vec { + Self::parse_limited_all( + sess, + attrs.into_iter().filter(|attr| attr.has_any_name(filter)), + None, + target, + target_span, + target_node_id, + features, + emit_errors, + Some(tools), + ) + } + /// This method parses a single attribute, using `parse_fn`. /// This is useful if you already know what exact attribute this is, and want to parse it. pub fn parse_single( @@ -222,6 +249,7 @@ impl<'sess> AttributeParser<'sess> { &mut emit_lint, ); } + let attr_id = sess.psess.attr_id_generator.mk_attr_id(); let mut cx: AcceptContext<'_, 'sess> = AcceptContext { shared: SharedContext { cx: &mut parser, @@ -236,6 +264,7 @@ impl<'sess> AttributeParser<'sess> { template, attr_safety: attr_safety.unwrap_or(Safety::Default), attr_path, + attr_id, }; parse_fn(&mut cx, args) } @@ -275,9 +304,9 @@ impl<'sess> AttributeParser<'sess> { /// /// `target_span` is the span of the thing this list of attributes is applied to, /// and when `omit_doc` is set, doc attributes are filtered out. - pub fn parse_attribute_list( + pub fn parse_attribute_list<'a>( &mut self, - attrs: &[ast::Attribute], + attrs: impl IntoIterator, target_span: Span, target: Target, omit_doc: OmitDoc, @@ -293,9 +322,9 @@ impl<'sess> AttributeParser<'sess> { let mut attr_paths: Vec> = Vec::new(); let mut early_parsed_state = EarlyParsedState::default(); - let mut finalizers: Vec = Vec::with_capacity(attrs.len()); + let mut finalizers: Vec = Vec::new(); - for attr in attrs { + for attr in attrs.into_iter() { // If we're only looking for a single attribute, skip all the ones we don't care about. if let Some(expected) = self.parse_only { if !attr.path_matches(expected) { @@ -407,6 +436,7 @@ impl<'sess> AttributeParser<'sess> { template: &accept.template, attr_safety: n.item.unsafety, attr_path: attr_path.clone(), + attr_id: attr.id, }; (accept.accept_fn)(&mut cx, &args); @@ -441,13 +471,10 @@ impl<'sess> AttributeParser<'sess> { let attr = Attribute::Unparsed(Box::new(attr)); - if self.tools.is_some_and(|tools| { - tools.iter().any(|tool| tool.name == parts[0]) - // FIXME: this can be removed once #152369 has been merged. - // https://github.com/rust-lang/rust/pull/152369 - || [sym::allow, sym::deny, sym::expect, sym::forbid, sym::warn] - .contains(&parts[0]) - }) { + if self + .tools + .is_some_and(|tools| tools.iter().any(|tool| tool.name == parts[0])) + { attributes.push(attr); } else { dropped_attributes.push(attr); diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 48790f273adfc..c2a4ac94daf98 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -141,10 +141,27 @@ impl ArgParser { } if args.delim != Delimiter::Parenthesis { - should_emit.emit_err(psess.dcx().create_err(MetaBadDelim { - span: args.dspan.entire(), - sugg: MetaBadDelimSugg { open: args.dspan.open, close: args.dspan.close }, - })); + // FIXME we need a way to just turn off delayed bugs during early parsing of certain attrs, + // in the case that they are parsed before expansion + if !(matches!(should_emit, ShouldEmit::Nothing) + && matches!( + parts, + [sym::allow] + | [sym::deny] + | [sym::expect] + | [sym::forbid] + | [sym::warn] + )) + { + should_emit.emit_err(psess.dcx().create_err(MetaBadDelim { + span: args.dspan.entire(), + sugg: MetaBadDelimSugg { + open: args.dspan.open, + close: args.dspan.close, + }, + })); + } + return None; } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 5b715005f1076..8fb09ffcd4ac4 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -591,6 +591,10 @@ pub(crate) enum AttributeParseErrorReason<'a> { list: bool, }, ExpectedIdentifier, + ExpectedNameValueAsLastArgument { + span: Span, + name_value_key: Symbol, + }, } /// A description of a thing that can be parsed using an attribute parser. @@ -858,6 +862,12 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { AttributeParseErrorReason::ExpectedIdentifier => { diag.span_label(self.span, "expected a valid identifier here"); } + AttributeParseErrorReason::ExpectedNameValueAsLastArgument { span, name_value_key } => { + diag.span_label( + *span, + format!("expected {name_value_key} = \"...\" to be the last argument"), + ); + } } if let Some(link) = self.template.docs { @@ -1163,3 +1173,14 @@ pub(crate) enum InvalidMachoSectionReason { #[note("section name `{$section}` is longer than 16 bytes")] SectionTooLong { section: String }, } + +#[derive(Diagnostic)] +#[diag("unknown tool name `{$tool_name}` found in scoped lint: `{$full_lint_name}`", code = E0710)] +pub(crate) struct UnknownToolInScopedLint { + #[primary_span] + pub span: Option, + pub tool_name: Symbol, + pub full_lint_name: Symbol, + #[help("add `#![register_tool({$tool_name})]` to the crate root")] + pub is_nightly_build: bool, +} diff --git a/compiler/rustc_attr_parsing/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs index fad773aed5af6..8e1109d6a77a2 100644 --- a/compiler/rustc_attr_parsing/src/validate_attr.rs +++ b/compiler/rustc_attr_parsing/src/validate_attr.rs @@ -9,7 +9,7 @@ use rustc_ast::{ self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, Safety, }; use rustc_errors::{Applicability, Diagnostic, PResult}; -use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, template}; +use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; use rustc_hir::AttrPath; use rustc_parse::parse_in; use rustc_session::errors::report_lit_error; @@ -33,22 +33,8 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) { if AttributeParser::is_parsed_attribute(slice::from_ref(&name)) { return; } - match parse_meta(psess, attr) { - // Don't check safety again, we just did that - Ok(meta) => { - // FIXME The only unparsed builtin attributes that are left are the lint attributes, so we can hardcode the template here - let lint_attrs = [sym::forbid, sym::allow, sym::warn, sym::deny, sym::expect]; - assert!(lint_attrs.contains(name)); - - let template = template!( - List: &["lint1", "lint1, lint2, ...", r#"lint1, lint2, lint3, reason = "...""#], - "https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes" - ); - check_builtin_meta_item(psess, &meta, attr.style, *name, template, false) - } - Err(err) => { - err.emit(); - } + if let Err(err) = parse_meta(psess, attr) { + err.emit(); } } _ => { diff --git a/compiler/rustc_error_codes/src/error_codes/E0452.md b/compiler/rustc_error_codes/src/error_codes/E0452.md index 429813a7cdd4e..a2471ec78eed5 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0452.md +++ b/compiler/rustc_error_codes/src/error_codes/E0452.md @@ -1,8 +1,9 @@ +#### Note: this error code is no longer emitted by the compiler An invalid lint attribute has been given. Erroneous code example: -```compile_fail,E0452 +```compile_fail #![allow(foo = "")] // error: malformed lint attribute ``` diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 7f1b58dd1de08..e3936536f5fad 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -9,7 +9,7 @@ use std::sync::Arc; use rustc_ast::attr::MarkedAttrs; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{AssocCtxt, Visitor}; -use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind, Safety}; +use rustc_ast::{self as ast, AttrVec, HasAttrs, Item, NodeId, PatKind, Safety}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::sync; use rustc_errors::{BufferedEarlyLint, DiagCtxtHandle, ErrorGuaranteed, PResult}; @@ -1189,7 +1189,6 @@ pub trait LintStoreExpand { features: &Features, registered_tools: &RegisteredTools, node_id: NodeId, - attrs: &[Attribute], items: &[Box], name: Symbol, ); diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 741c34e0304af..f5d2e9bd9905e 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1404,7 +1404,6 @@ impl InvocationCollectorNode for Box { ecx.ecfg.features, ecx.resolver.registered_tools(), ecx.current_expansion.lint_node_id, - &attrs, &items, ident.name, ); diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 4ff56a640c19e..0a443474d171d 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -15,14 +15,16 @@ use rustc_hir::LangItem; use rustc_macros::{Decodable, Encodable, PrintAttribute, StableHash}; use rustc_span::def_id::DefId; use rustc_span::hygiene::Transparency; -use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; +use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, sym}; pub use rustc_target::spec::SanitizerSet; use thin_vec::ThinVec; use crate::attrs::diagnostic::*; use crate::attrs::pretty_printing::PrintAttribute; use crate::limit::Limit; -use crate::{DefaultBodyStability, PartialConstStability, RustcVersion, Stability}; +use crate::{ + DefaultBodyStability, HashIgnoredAttrId, PartialConstStability, RustcVersion, Stability, +}; #[derive(Copy, Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] pub enum EiiImplResolution { @@ -886,6 +888,132 @@ pub struct UnstableRemovedFeature { pub since: RustcVersion, } +#[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] +pub struct LintAttribute { + /// See RFC #2383 + pub reason: Option, + pub kind: LintAttributeKind, + pub attr_style: AttrStyle, + pub attr_span: Span, + /// Needed by `LintExpectationId` to track fulfilled expectations + pub attr_id: HashIgnoredAttrId, + pub lint_instances: ThinVec, +} + +#[derive(Debug, Clone, Encodable, Decodable, StableHash)] +pub struct LintInstance { + /// The span of the `MetaItem` that produced this `LintInstance` + span: Span, + /// The fully resolved name of the lint + /// for renamed lints, this gets updated to match the new name + lint_name: Symbol, + /// The identifier original used for resolving this lint + /// if this is none, lint_name never diffed from the original + /// name after parsing, original_name.unwrap_or(self.lint_name) + original_name: Option, + /// Index of this lint, used to keep track of lint groups + lint_index: usize, + tool: Option, +} + +impl fmt::Display for LintInstance { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.full_lint().fmt(f) + } +} + +impl LintInstance { + pub fn new(original_name: Symbol, full_lint: Symbol, span: Span, lint_index: usize) -> Self { + let original_name = (original_name != full_lint).then_some(original_name); + let mut tool_name = None; + + let lint_name = match full_lint.as_str().split_once("::") { + Some((new_tool_name, lint_name)) => { + tool_name = Some(Symbol::intern(new_tool_name)); + Symbol::intern(lint_name) + } + None => full_lint, + }; + let tool = tool_name.map(|tool_name| LintAttrTool { tool_name, full_lint }); + + LintInstance { original_name, span, lint_index, lint_name, tool } + } + + pub fn full_lint(&self) -> Symbol { + match self.tool { + Some(LintAttrTool { full_lint, .. }) => full_lint, + None => self.lint_name, + } + } + + pub fn span(&self) -> Span { + self.span + } + + pub fn lint_index(&self) -> usize { + self.lint_index + } + + pub fn lint_name(&self) -> Symbol { + self.lint_name + } + + pub fn original_name_without_tool(&self) -> Symbol { + let full_original_lint_name = self.original_lint_name(); + match self.tool { + Some(LintAttrTool { tool_name, .. }) => Symbol::intern( + full_original_lint_name + .as_str() + .trim_start_matches(tool_name.as_str()) + .trim_start_matches("::"), + ), + None => full_original_lint_name, + } + } + + pub fn tool_name(&self) -> Option { + self.tool.as_ref().map(|tool| tool.tool_name) + } + + pub fn tool_is_named(&self, other: Symbol) -> bool { + self.tool_name().is_some_and(|tool_name| tool_name == other) + } + + pub fn original_lint_name(&self) -> Symbol { + match self.original_name { + Some(name) => name, + None => self.full_lint(), + } + } +} + +#[derive(Debug, Clone, PrintAttribute, Encodable, Decodable, StableHash)] +struct LintAttrTool { + tool_name: Symbol, + full_lint: Symbol, +} + +#[derive(Clone, Copy, Debug, StableHash, Encodable, Decodable, PrintAttribute, PartialEq)] +pub enum LintAttributeKind { + Allow, + Deny, + Expect, + Forbid, + Warn, +} + +impl LintAttributeKind { + pub const fn symbol(&self) -> Symbol { + match self { + Self::Allow => sym::allow, + Self::Deny => sym::deny, + Self::Expect => sym::expect, + Self::Forbid => sym::forbid, + Self::Warn => sym::warn, + } + } +} + /// Represents parsed *built-in* inert attributes. /// /// ## Overview @@ -1086,6 +1214,9 @@ pub enum AttributeKind { /// Represents `#[linkage]`. Linkage(Linkage, Span), + /// Represents `#[allow]`, `#[expect]`, `#[warn]`, `#[deny]`, `#[forbid]` + LintAttributes(ThinVec), + /// Represents `#[loop_match]`. LoopMatch(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 7169fa433ffd8..433713aaef978 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -56,6 +56,7 @@ impl AttributeKind { LinkOrdinal { .. } => No, LinkSection { .. } => Yes, // Needed for rustdoc Linkage(..) => No, + LintAttributes { .. } => No, LoopMatch(..) => No, MacroEscape => No, MacroExport { .. } => Yes, diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index 9d14f9de3078d..811b250b6fcd6 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -17,6 +17,8 @@ use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; use rustc_target::spec::SanitizerSet; use thin_vec::ThinVec; +use crate::HashIgnoredAttrId; +use crate::attrs::LintInstance; use crate::limit::Limit; /// This trait is used to print attributes in `rustc_hir_pretty`. @@ -191,8 +193,8 @@ macro_rules! print_tup { } print_tup!(A B C D E F G H); -print_skip!(Span, (), ErrorGuaranteed, AttrId); -print_disp!(u8, u16, u32, u128, usize, bool, NonZero, Limit); +print_skip!(Span, (), ErrorGuaranteed, AttrId, HashIgnoredAttrId); +print_disp!(u8, u16, u32, u128, usize, bool, NonZero, Limit, LintInstance); print_debug!( Symbol, Ident, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 0569c1b986d1a..b5e4c84152411 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1475,6 +1475,7 @@ impl AttributeExt for Attribute { Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span, Attribute::Parsed(AttributeKind::Deprecated { span, .. }) => *span, Attribute::Parsed(AttributeKind::CfgTrace(cfgs)) => cfgs[0].1, + Attribute::Parsed(AttributeKind::LintAttributes(sub_attrs)) => sub_attrs[0].attr_span, a => panic!("can't get the span of an arbitrary parsed attribute: {a:?}"), } } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index f1d9100a1d5f6..1a9d2eecf3a9e 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -16,11 +16,10 @@ use rustc_errors::{ Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, Subdiagnostic, listify, pluralize, struct_span_code_err, }; -use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; -use rustc_hir::{ExprKind, HirId, QPath, find_attr, is_range_literal}; +use rustc_hir::{self as hir, ExprKind, HirId, QPath, find_attr, is_range_literal}; use rustc_hir_analysis::NoVariantNamed; use rustc_hir_analysis::errors::NoFieldOnType; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer as _; diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index bcd1a52ce9dcd..f58fdd5a9a30d 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -118,11 +118,10 @@ impl LintStoreExpand for LintStoreExpandImpl<'_> { features: &Features, registered_tools: &RegisteredTools, node_id: ast::NodeId, - attrs: &[ast::Attribute], items: &[Box], name: Symbol, ) { - pre_expansion_lint(sess, features, self.0, registered_tools, (node_id, attrs, items), name); + pre_expansion_lint(sess, features, self.0, registered_tools, (node_id, items), name); } } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 3cbd02e2ed8d9..eeb63b1c7053e 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -28,7 +28,7 @@ use rustc_middle::ty::{ self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode, Unnormalized, }; use rustc_session::lint::{ - FutureIncompatibleInfo, Lint, LintExpectationId, LintId, StableLintExpectationId, + FutureIncompatibleInfo, Lint, LintExpectationId, LintId, StableLintExpectationId, TargetLint, UnstableLintExpectationId, }; use rustc_session::{DynLintStore, Session}; @@ -36,10 +36,10 @@ use rustc_span::edit_distance::find_best_match_for_names; use rustc_span::{Ident, Span, Symbol, sym}; use tracing::debug; -use self::TargetLint::*; +use crate::CheckLintNameResult; +use crate::context::TargetLint::*; use crate::levels::LintLevelsBuilder; use crate::passes::{EarlyLintPassObject, LateLintPassObject}; - type EarlyLintPassFactory = dyn Fn() -> EarlyLintPassObject + sync::DynSend + sync::DynSync; type LateLintPassFactory = dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync; @@ -74,8 +74,21 @@ impl DynLintStore for LintStore { rustc_session::LintGroup { name, lints, is_externally_loaded } })) } -} + fn check_lint_name( + &self, + lint_name: &str, + tool_name: Option, + registered_tools: &RegisteredTools, + ) -> CheckLintNameResult<'_> { + self.check_lint_name(lint_name, tool_name, registered_tools) + } + + fn find_lints(&self, lint_name: &str) -> Option<&[LintId]> { + self.find_lints(lint_name) + } +} +/* /// The target of the `by_name` map, which accounts for renaming/deprecation. #[derive(Debug)] enum TargetLint { @@ -94,7 +107,7 @@ enum TargetLint { /// This is used by rustc to avoid warning about old rustdoc lints before rustdoc registers /// them as tool lints. Ignored, -} +}*/ struct LintAlias { name: &'static str, @@ -108,29 +121,6 @@ struct LintGroup { depr: Option, } -#[derive(Debug)] -pub enum CheckLintNameResult<'a> { - Ok(&'a [LintId]), - /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name. - NoLint(Option<(Symbol, bool)>), - /// The lint refers to a tool that has not been registered. - NoTool, - /// The lint has been renamed to a new name. - Renamed(String), - /// The lint has been removed due to the given reason. - Removed(String), - - /// The lint is from a tool. The `LintId` will be returned as if it were a - /// rustc lint. The `Option` indicates if the lint has been - /// renamed. - Tool(&'a [LintId], Option), - - /// The lint is from a tool. Either the lint does not exist in the tool or - /// the code was not compiled with the tool and therefore the lint was - /// never added to the `LintStore`. - MissingTool, -} - impl LintStore { pub fn new() -> LintStore { LintStore { @@ -309,6 +299,10 @@ impl LintStore { self.by_name.insert(name.into(), Removed(reason.into())); } + pub fn get_lint_by_name(&self, lint_name: &str) -> Option<&TargetLint> { + self.by_name.get(lint_name) + } + pub fn find_lints(&self, lint_name: &str) -> Option<&[LintId]> { match self.by_name.get(lint_name) { Some(Id(lint_id)) => Some(slice::from_ref(lint_id)), @@ -398,7 +392,7 @@ impl LintStore { } } match self.by_name.get(&complete_name) { - Some(Renamed(new_name, _)) => CheckLintNameResult::Renamed(new_name.to_string()), + Some(Renamed(new_name, _)) => CheckLintNameResult::Renamed(Symbol::intern(new_name)), Some(Removed(reason)) => CheckLintNameResult::Removed(reason.to_string()), None => match self.lint_groups.get(&*complete_name) { // If neither the lint, nor the lint group exists check if there is a `clippy::` @@ -611,6 +605,8 @@ impl<'tcx> LintContext for LateContext<'tcx> { } } + /// Only appropriate for use inside of the compiler + /// since the compiler doesn't track levels of tool lints fn get_lint_level_spec(&self, lint: &'static Lint) -> StableLevelSpec { self.tcx.lint_level_spec_at_node(lint, self.last_node_with_lint_attrs) } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index df6683c14df42..18c7676c9b951 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -12,7 +12,7 @@ use rustc_feature::Features; use rustc_middle::ty::RegisteredTools; use rustc_session::Session; use rustc_session::lint::LintPass; -use rustc_span::{Ident, Span}; +use rustc_span::{DUMMY_SP, Ident, Span}; use tracing::debug; use crate::DiagAndSess; @@ -47,13 +47,17 @@ impl<'ecx, T: EarlyLintPass> EarlyContextAndPass<'ecx, T> { /// Merge the lints specified by any lint attributes into the /// current lint context, call the provided function, then reset the /// lints in effect to their previous state. - fn with_lint_attrs(&mut self, id: ast::NodeId, attrs: &'_ [ast::Attribute], f: F) - where + fn with_lint_attrs( + &mut self, + id: ast::NodeId, + attrs: &'_ [ast::Attribute], + f: F, + target_span: Span, + ) where F: FnOnce(&mut Self), { - let is_crate_node = id == ast::CRATE_NODE_ID; debug!(?id); - let push = self.context.builder.push(attrs, is_crate_node); + let push = self.context.builder.push(attrs, id, target_span); debug!("early context: enter_attrs({:?})", attrs); lint_callback!(self, check_attributes, attrs); @@ -70,24 +74,39 @@ impl<'ast, 'ecx, T: EarlyLintPass> ast_visit::Visitor<'ast> for EarlyContextAndP } fn visit_param(&mut self, param: &'ast ast::Param) { - self.with_lint_attrs(param.id, ¶m.attrs, |cx| { - lint_callback!(cx, check_param, param); - ast_visit::walk_param(cx, param); - }); + self.with_lint_attrs( + param.id, + ¶m.attrs, + |cx| { + lint_callback!(cx, check_param, param); + ast_visit::walk_param(cx, param); + }, + param.span, + ); } fn visit_item(&mut self, it: &'ast ast::Item) { - self.with_lint_attrs(it.id, &it.attrs, |cx| { - lint_callback!(cx, check_item, it); - ast_visit::walk_item(cx, it); - lint_callback!(cx, check_item_post, it); - }) + self.with_lint_attrs( + it.id, + &it.attrs, + |cx| { + lint_callback!(cx, check_item, it); + ast_visit::walk_item(cx, it); + lint_callback!(cx, check_item_post, it); + }, + it.span, + ) } fn visit_foreign_item(&mut self, it: &'ast ast::ForeignItem) { - self.with_lint_attrs(it.id, &it.attrs, |cx| { - ast_visit::walk_item(cx, it); - }) + self.with_lint_attrs( + it.id, + &it.attrs, + |cx| { + ast_visit::walk_item(cx, it); + }, + it.span, + ) } fn visit_pat(&mut self, p: &'ast ast::Pat) { @@ -97,23 +116,38 @@ impl<'ast, 'ecx, T: EarlyLintPass> ast_visit::Visitor<'ast> for EarlyContextAndP } fn visit_pat_field(&mut self, field: &'ast ast::PatField) { - self.with_lint_attrs(field.id, &field.attrs, |cx| { - ast_visit::walk_pat_field(cx, field); - }); + self.with_lint_attrs( + field.id, + &field.attrs, + |cx| { + ast_visit::walk_pat_field(cx, field); + }, + field.span, + ); } fn visit_expr(&mut self, e: &'ast ast::Expr) { - self.with_lint_attrs(e.id, &e.attrs, |cx| { - lint_callback!(cx, check_expr, e); - ast_visit::walk_expr(cx, e); - lint_callback!(cx, check_expr_post, e); - }) + self.with_lint_attrs( + e.id, + &e.attrs, + |cx| { + lint_callback!(cx, check_expr, e); + ast_visit::walk_expr(cx, e); + lint_callback!(cx, check_expr_post, e); + }, + e.span, + ) } fn visit_expr_field(&mut self, f: &'ast ast::ExprField) { - self.with_lint_attrs(f.id, &f.attrs, |cx| { - ast_visit::walk_expr_field(cx, f); - }) + self.with_lint_attrs( + f.id, + &f.attrs, + |cx| { + ast_visit::walk_expr_field(cx, f); + }, + f.span, + ) } fn visit_stmt(&mut self, s: &'ast ast::Stmt) { @@ -125,10 +159,15 @@ impl<'ast, 'ecx, T: EarlyLintPass> ast_visit::Visitor<'ast> for EarlyContextAndP // // Note that statements get their attributes from // the AST struct that they wrap (e.g. an item) - self.with_lint_attrs(s.id, s.attrs(), |cx| { - lint_callback!(cx, check_stmt, s); - ast_visit::walk_stmt(cx, s); - }); + self.with_lint_attrs( + s.id, + s.attrs(), + |cx| { + lint_callback!(cx, check_stmt, s); + ast_visit::walk_stmt(cx, s); + }, + s.span, + ); } fn visit_fn(&mut self, fk: ast_visit::FnKind<'ast>, _: &AttrVec, span: Span, id: ast::NodeId) { @@ -137,16 +176,26 @@ impl<'ast, 'ecx, T: EarlyLintPass> ast_visit::Visitor<'ast> for EarlyContextAndP } fn visit_field_def(&mut self, s: &'ast ast::FieldDef) { - self.with_lint_attrs(s.id, &s.attrs, |cx| { - ast_visit::walk_field_def(cx, s); - }) + self.with_lint_attrs( + s.id, + &s.attrs, + |cx| { + ast_visit::walk_field_def(cx, s); + }, + s.span, + ) } fn visit_variant(&mut self, v: &'ast ast::Variant) { - self.with_lint_attrs(v.id, &v.attrs, |cx| { - lint_callback!(cx, check_variant, v); - ast_visit::walk_variant(cx, v); - }) + self.with_lint_attrs( + v.id, + &v.attrs, + |cx| { + lint_callback!(cx, check_variant, v); + ast_visit::walk_variant(cx, v); + }, + v.span, + ) } fn visit_ty(&mut self, t: &'ast ast::Ty) { @@ -159,10 +208,15 @@ impl<'ast, 'ecx, T: EarlyLintPass> ast_visit::Visitor<'ast> for EarlyContextAndP } fn visit_local(&mut self, l: &'ast ast::Local) { - self.with_lint_attrs(l.id, &l.attrs, |cx| { - lint_callback!(cx, check_local, l); - ast_visit::walk_local(cx, l); - }) + self.with_lint_attrs( + l.id, + &l.attrs, + |cx| { + lint_callback!(cx, check_local, l); + ast_visit::walk_local(cx, l); + }, + l.span, + ) } fn visit_block(&mut self, b: &'ast ast::Block) { @@ -171,10 +225,15 @@ impl<'ast, 'ecx, T: EarlyLintPass> ast_visit::Visitor<'ast> for EarlyContextAndP } fn visit_arm(&mut self, a: &'ast ast::Arm) { - self.with_lint_attrs(a.id, &a.attrs, |cx| { - lint_callback!(cx, check_arm, a); - ast_visit::walk_arm(cx, a); - }) + self.with_lint_attrs( + a.id, + &a.attrs, + |cx| { + lint_callback!(cx, check_arm, a); + ast_visit::walk_arm(cx, a); + }, + a.span, + ) } fn visit_generic_arg(&mut self, arg: &'ast ast::GenericArg) { @@ -183,10 +242,15 @@ impl<'ast, 'ecx, T: EarlyLintPass> ast_visit::Visitor<'ast> for EarlyContextAndP } fn visit_generic_param(&mut self, param: &'ast ast::GenericParam) { - self.with_lint_attrs(param.id, ¶m.attrs, |cx| { - lint_callback!(cx, check_generic_param, param); - ast_visit::walk_generic_param(cx, param); - }); + self.with_lint_attrs( + param.id, + ¶m.attrs, + |cx| { + lint_callback!(cx, check_generic_param, param); + ast_visit::walk_generic_param(cx, param); + }, + param.span(), + ); } fn visit_generics(&mut self, g: &'ast ast::Generics) { @@ -206,25 +270,30 @@ impl<'ast, 'ecx, T: EarlyLintPass> ast_visit::Visitor<'ast> for EarlyContextAndP } fn visit_assoc_item(&mut self, item: &'ast ast::AssocItem, ctxt: ast_visit::AssocCtxt) { - self.with_lint_attrs(item.id, &item.attrs, |cx| { - match ctxt { - ast_visit::AssocCtxt::Trait => { - lint_callback!(cx, check_trait_item, item); + self.with_lint_attrs( + item.id, + &item.attrs, + |cx| { + match ctxt { + ast_visit::AssocCtxt::Trait => { + lint_callback!(cx, check_trait_item, item); + } + ast_visit::AssocCtxt::Impl { .. } => { + lint_callback!(cx, check_impl_item, item); + } } - ast_visit::AssocCtxt::Impl { .. } => { - lint_callback!(cx, check_impl_item, item); + ast_visit::walk_assoc_item(cx, item, ctxt); + match ctxt { + ast_visit::AssocCtxt::Trait => { + lint_callback!(cx, check_trait_item_post, item); + } + ast_visit::AssocCtxt::Impl { .. } => { + lint_callback!(cx, check_impl_item_post, item); + } } - } - ast_visit::walk_assoc_item(cx, item, ctxt); - match ctxt { - ast_visit::AssocCtxt::Trait => { - lint_callback!(cx, check_trait_item_post, item); - } - ast_visit::AssocCtxt::Impl { .. } => { - lint_callback!(cx, check_impl_item_post, item); - } - } - }); + }, + item.span, + ); } fn visit_attribute(&mut self, attr: &'ast ast::Attribute) { @@ -296,16 +365,15 @@ impl<'a> EarlyCheckNode<'a> for (&'a ast::Crate, &'a [ast::Attribute]) { } } -impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [Box]) { +impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [Box]) { fn id(self) -> ast::NodeId { self.0 } fn attrs(self) -> &'a [ast::Attribute] { - self.1 + &[] } fn check<'ecx, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'ecx, T>) { - walk_list!(cx, visit_attribute, self.1); - walk_list!(cx, visit_item, self.2); + walk_list!(cx, visit_item, self.1); } } @@ -351,7 +419,7 @@ fn check_ast_node_inner<'a, T: EarlyLintPass>( ) { let mut cx = EarlyContextAndPass { context, pass }; - cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx)); + cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx), DUMMY_SP); // All of the buffered lints should have been emitted at this point. // If not, that means that we somehow buffered a lint for a node id diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs index 8fec30816bd13..33ba7f6edbda6 100644 --- a/compiler/rustc_lint/src/errors.rs +++ b/compiler/rustc_lint/src/errors.rs @@ -44,36 +44,6 @@ impl Subdiagnostic for OverruledAttributeSub { } } -#[derive(Diagnostic)] -#[diag("malformed lint attribute input", code = E0452)] -pub(crate) struct MalformedAttribute { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub sub: MalformedAttributeSub, -} - -#[derive(Subdiagnostic)] -pub(crate) enum MalformedAttributeSub { - #[label("bad attribute argument")] - BadAttributeArgument(#[primary_span] Span), - #[label("reason must be a string literal")] - ReasonMustBeStringLiteral(#[primary_span] Span), - #[label("reason in lint attribute must come last")] - ReasonMustComeLast(#[primary_span] Span), -} - -#[derive(Diagnostic)] -#[diag("unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}`", code = E0710)] -pub(crate) struct UnknownToolInScopedLint { - #[primary_span] - pub span: Option, - pub tool_name: Symbol, - pub lint_name: String, - #[help("add `#![register_tool({$tool_name})]` to the crate root")] - pub is_nightly_build: bool, -} - #[derive(Diagnostic)] #[diag("`...` range patterns are deprecated", code = E0783)] pub(crate) struct BuiltinEllipsisInclusiveRangePatterns { diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index 5364a4141805b..5be0f1f49897a 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -1,9 +1,10 @@ use rustc_data_structures::fx::FxHashSet; +use rustc_hir::find_attr; use rustc_middle::lint::LintExpectation; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::UNFULFILLED_LINT_EXPECTATIONS; -use rustc_session::lint::{LintExpectationId, StableLintExpectationId}; +use rustc_session::lint::{LintExpectationId, StableLintExpectationId, UnstableLintExpectationId}; use rustc_span::Symbol; use crate::lints::{Expectation, ExpectationNote}; @@ -18,35 +19,49 @@ fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(StableLintExpectationId, L let mut expectations = Vec::new(); for owner in krate.owners() { + // Deduplicate expectations + let mut inner_expectations: Vec<(StableLintExpectationId, LintExpectation)> = Vec::new(); let lints = tcx.shallow_lint_levels_on(owner); - expectations.extend_from_slice(&lints.expectations); + for expectation in &lints.expectations { + let canonicalized = canonicalize_id(tcx, &LintExpectationId::Stable(expectation.0)); + if !inner_expectations.iter().any(|(id, _)| { + canonicalize_id(tcx, &LintExpectationId::Stable(*id)) == canonicalized + }) { + inner_expectations.push(expectation.clone()); + } + } + expectations.extend(inner_expectations); } expectations } +fn canonicalize_id(tcx: TyCtxt<'_>, expect_id: &LintExpectationId) -> (rustc_span::AttrId, u16) { + match *expect_id { + LintExpectationId::Unstable(UnstableLintExpectationId { attr_id, lint_index }) => { + (attr_id, lint_index) + } + LintExpectationId::Stable(StableLintExpectationId { hir_id, attr_index, lint_index }) => { + // We are an `eval_always` query, so looking at the attribute's `AttrId` is ok. + let attrs = find_attr!(tcx, hir_id, LintAttributes(lints) => lints).unwrap(); + let attr_id = attrs[attr_index as usize].attr_id.attr_id; + + (attr_id, lint_index) + } + } +} + fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option) { let lint_expectations = tcx.lint_expectations(()); let fulfilled_expectations = tcx.dcx().steal_fulfilled_expectation_ids(); // Turn a `LintExpectationId` into a `(AttrId, lint_index)` pair. - let canonicalize_id = |expect_id: &LintExpectationId| { - let (attr_id, lint_index) = match *expect_id { - LintExpectationId::Unstable(id) => (id.attr_id, id.lint_index), - LintExpectationId::Stable(id) => { - // We are an `eval_always` query, so looking at the attribute's `AttrId` is ok. - (tcx.hir_attrs(id.hir_id)[id.attr_index as usize].id(), id.lint_index) - } - }; - (attr_id, lint_index.expect("fulfilled expectations must have a lint index")) - }; - let fulfilled_expectations: FxHashSet<_> = - fulfilled_expectations.iter().map(canonicalize_id).collect(); + fulfilled_expectations.iter().map(|id| canonicalize_id(tcx, id)).collect(); for (expect_id, expectation) in lint_expectations { let hir_id = expect_id.hir_id; - let expect_id = canonicalize_id(&LintExpectationId::Stable(*expect_id)); + let expect_id = canonicalize_id(tcx, &LintExpectationId::Stable(*expect_id)); if !fulfilled_expectations.contains(&expect_id) && tool_filter.is_none_or(|filter| expectation.lint_tool == Some(filter)) diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index b8da46594cfc2..2d7ee9f10f0eb 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -1,16 +1,17 @@ use std::fmt::Debug; use rustc_ast as ast; -use rustc_ast::attr::AttributeExt; -use rustc_ast_pretty::pprust; +use rustc_ast::{DUMMY_NODE_ID, NodeId}; +use rustc_attr_parsing::AttributeParser; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::unord::UnordSet; use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, MultiSpan, msg}; use rustc_feature::{Features, GateIssue}; -use rustc_hir as hir; -use rustc_hir::HirId; +use rustc_hir::attrs::{LintAttribute, LintAttributeKind, LintInstance}; use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{self as hir, HirId, Target, find_attr}; use rustc_index::IndexVec; +use rustc_middle::bug; use rustc_middle::hir::nested_filter; use rustc_middle::lint::{ LevelSpec, LintExpectation, LintLevelSource, ShallowLintLevelMap, StableLevelSpec, @@ -21,28 +22,38 @@ use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::Session; use rustc_session::lint::builtin::{ self, FORBIDDEN_LINT_GROUPS, RENAMED_AND_REMOVED_LINTS, SINGLE_USE_LIFETIMES, - UNFULFILLED_LINT_EXPECTATIONS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES, + UNFULFILLED_LINT_EXPECTATIONS, UNKNOWN_LINTS, }; use rustc_session::lint::{ - Level, Lint, LintExpectationId, LintId, StableLintExpectationId, UnstableLintExpectationId, + CheckLintNameResult, Level, Lint, LintExpectationId, LintId, StableLintExpectationId, + TargetLint, UnstableLintExpectationId, }; use rustc_span::{AttrId, DUMMY_SP, Span, Symbol, sym}; use tracing::{debug, instrument}; use crate::builtin::MISSING_DOCS; -use crate::context::{CheckLintNameResult, LintStore}; +use crate::context::LintStore; use crate::errors::{ - CheckNameUnknownTool, MalformedAttribute, MalformedAttributeSub, OverruledAttribute, - OverruledAttributeSub, RequestedLevel, UnknownToolInScopedLint, UnsupportedGroup, + CheckNameUnknownTool, OverruledAttribute, OverruledAttributeSub, RequestedLevel, + UnsupportedGroup, }; use crate::late::unerased_lint_store; use crate::lints::{ - DeprecatedLintName, DeprecatedLintNameFromCommandLine, IgnoredUnlessCrateSpecified, - OverruledAttributeLint, RemovedLint, RemovedLintFromCommandLine, RenamedLint, - RenamedLintFromCommandLine, RenamedLintSuggestion, UnknownLint, UnknownLintFromCommandLine, + DeprecatedLintNameFromCommandLine, OverruledAttributeLint, RemovedLintFromCommandLine, + RenamedLintFromCommandLine, RenamedLintSuggestion, UnknownLintFromCommandLine, UnknownLintSuggestion, }; +const ALLOW_LISTED_ATTRS: &[Symbol] = &[ + sym::allow, + sym::deny, + sym::expect, + sym::forbid, + sym::warn, + sym::automatically_derived, + sym::doc, +]; + /// Collection of lint levels for the whole crate. /// This is used by AST-based lints, which do not /// wait until we have built HIR to be emitted. @@ -233,7 +244,7 @@ pub trait LintLevelsProvider { &self, attr_id: AttrId, attr_index: usize, - lint_index: Option, + lint_index: u16, ) -> Self::LintExpectationId; } @@ -258,7 +269,7 @@ impl LintLevelsProvider for TopDown { &self, attr_id: AttrId, _attr_index: usize, - lint_index: Option, + lint_index: u16, ) -> Self::LintExpectationId { UnstableLintExpectationId { attr_id, lint_index } } @@ -296,7 +307,7 @@ impl LintLevelsProvider for LintLevelQueryMap<'_> { &self, _attr_id: AttrId, attr_index: usize, - lint_index: Option, + lint_index: u16, ) -> Self::LintExpectationId { let attr_index = attr_index.try_into().unwrap(); StableLintExpectationId { hir_id: self.cur, attr_index, lint_index } @@ -306,7 +317,7 @@ impl LintLevelsProvider for LintLevelQueryMap<'_> { impl<'tcx> LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> { fn add_id(&mut self, hir_id: HirId) { self.provider.cur = hir_id; - self.add(self.provider.attrs.get(hir_id.local_id), hir_id == hir::CRATE_HIR_ID); + self.add(self.provider.attrs.get(hir_id.local_id)); } } @@ -426,7 +437,19 @@ impl<'s> LintLevelsBuilder<'s, TopDown> { crate_attrs: &[ast::Attribute], ) -> Self { let mut builder = Self::new(sess, features, lint_added_lints, store, registered_tools); - builder.add(crate_attrs, true); + let parsed_crate_attrs = AttributeParser::parse_limited_all_filtered( + sess, + crate_attrs, + ALLOW_LISTED_ATTRS, + Target::Crate, + DUMMY_SP, + DUMMY_NODE_ID, + Some(features), + rustc_attr_parsing::ShouldEmit::Nothing, + registered_tools, + ); + + builder.add(&parsed_crate_attrs); builder } @@ -453,16 +476,34 @@ impl<'s> LintLevelsBuilder<'s, TopDown> { /// `#[allow]` /// /// Don't forget to call `pop`! - pub(crate) fn push(&mut self, attrs: &[ast::Attribute], is_crate_node: bool) -> BuilderPush { + pub(crate) fn push( + &mut self, + attrs: &[ast::Attribute], + node_id: NodeId, + target_span: Span, + ) -> BuilderPush { let prev = self.provider.cur; self.provider.cur = self.provider.sets.list.push(LintSet { specs: FxIndexMap::default(), parent: prev }); + if !attrs.is_empty() { + let attrs = AttributeParser::parse_limited_all_filtered( + self.sess, + attrs, + ALLOW_LISTED_ATTRS, + Target::Fn, + target_span, + node_id, + Some(self.features), + rustc_attr_parsing::ShouldEmit::Nothing, + self.registered_tools, + ); - self.add(attrs, is_crate_node); + self.add(&attrs); - if self.provider.current_specs().is_empty() { - self.provider.sets.list.pop(); - self.provider.cur = prev; + if self.provider.current_specs().is_empty() { + self.provider.sets.list.pop(); + self.provider.cur = prev; + } } BuilderPush { prev } @@ -504,7 +545,7 @@ where .emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() }); } match self.store.check_lint_name(lint_name_only, tool_name, self.registered_tools) { - CheckLintNameResult::Renamed(ref replace) => { + CheckLintNameResult::Renamed(replace) => { let name = lint_name.as_str(); let suggestion = RenamedLintSuggestion::WithoutSpan { replace }; let requested_level = RequestedLevel { level, lint_name }; @@ -667,277 +708,87 @@ where }; } - fn add(&mut self, attrs: &[impl AttributeExt], is_crate_node: bool) { - let sess = self.sess; - for (attr_index, attr) in attrs.iter().enumerate() { - if attr.is_automatically_derived_attr() { - self.provider.insert( - LintId::of(SINGLE_USE_LIFETIMES), - LevelSpec::new(Level::Allow, None, LintLevelSource::Default), - ); - continue; - } - - // `#[doc(hidden)]` disables missing_docs check. - if attr.is_doc_hidden() { - self.provider.insert( - LintId::of(MISSING_DOCS), - LevelSpec::new(Level::Allow, None, LintLevelSource::Default), - ); - continue; - } - - let level = match Level::from_opt_symbol(attr.name()) { - None => continue, - Some(level) => level, - }; - - let Some(mut metas) = attr.meta_item_list() else { continue }; - - // Check whether `metas` is empty, and get its last element. - let Some(tail_li) = metas.last() else { - // This emits the unused_attributes lint for `#[level()]` - continue; - }; - - // Before processing the lint names, look for a reason (RFC 2383) - // at the end. - let mut reason = None; - if let Some(item) = tail_li.meta_item() { - match item.kind { - ast::MetaItemKind::Word => {} // actual lint names handled later - ast::MetaItemKind::NameValue(ref name_value) => { - if item.path == sym::reason { - if let ast::LitKind::Str(rationale, _) = name_value.kind { - reason = Some(rationale); - } else { - sess.dcx().emit_err(MalformedAttribute { - span: name_value.span, - sub: MalformedAttributeSub::ReasonMustBeStringLiteral( - name_value.span, - ), - }); - } - // found reason, reslice meta list to exclude it - metas.pop().unwrap(); - } else { - sess.dcx().emit_err(MalformedAttribute { - span: item.span, - sub: MalformedAttributeSub::BadAttributeArgument(item.span), - }); - } - } - ast::MetaItemKind::List(_) => { - sess.dcx().emit_err(MalformedAttribute { - span: item.span, - sub: MalformedAttributeSub::BadAttributeArgument(item.span), - }); - } - } - } - - for (lint_index, li) in metas.iter_mut().enumerate() { - // `Expect` is the only lint level with a `LintExpectationId` that can be created - // from an attribute. - let lint_id = (level == Level::Expect).then(|| { - self.provider.mk_lint_expectation_id( - attr.id(), - attr_index, - Some(lint_index as u16), - ) - }); - - let sp = li.span(); - let meta_item = match li { - ast::MetaItemInner::MetaItem(meta_item) if meta_item.is_word() => meta_item, - _ => { - let sub = if let Some(item) = li.meta_item() - && let ast::MetaItemKind::NameValue(_) = item.kind - && item.path == sym::reason - { - MalformedAttributeSub::ReasonMustComeLast(sp) - } else { - MalformedAttributeSub::BadAttributeArgument(sp) - }; - - sess.dcx().emit_err(MalformedAttribute { span: sp, sub }); - continue; - } - }; - let tool_ident = if meta_item.path.segments.len() > 1 { - Some(meta_item.path.segments.remove(0).ident) - } else { - None - }; - let tool_name = tool_ident.map(|ident| ident.name); - let name = pprust::path_to_string(&meta_item.path); - let lint_result = - self.store.check_lint_name(&name, tool_name, self.registered_tools); - - let (ids, name) = match lint_result { - CheckLintNameResult::Ok(ids) => { - let name = - meta_item.path.segments.last().expect("empty lint name").ident.name; - (ids, name) - } - - CheckLintNameResult::Tool(ids, new_lint_name) => { - let name = match new_lint_name { - None => { - let complete_name = - &format!("{}::{}", tool_ident.unwrap().name, name); - Symbol::intern(complete_name) - } - Some(new_lint_name) => { - self.emit_span_lint( - builtin::RENAMED_AND_REMOVED_LINTS, - sp.into(), - DeprecatedLintName { - name, - suggestion: sp, - replace: &new_lint_name, - }, - ); - Symbol::intern(&new_lint_name) - } - }; - (ids, name) - } + fn simple_add( + &mut self, + level: Level, + lint: &LintInstance, + reason: Option, + expect_lint_id: Option, + ) { + // If this function returns none, it means the attribute parser has already emitted appropriate errors - CheckLintNameResult::MissingTool => { - // If `MissingTool` is returned, then either the lint does not - // exist in the tool or the code was not compiled with the tool and - // therefore the lint was never added to the `LintStore`. To detect - // this is the responsibility of the lint tool. - continue; - } + let src = + LintLevelSource::Node { name: lint.original_lint_name(), span: lint.span(), reason }; - CheckLintNameResult::NoTool => { - sess.dcx().emit_err(UnknownToolInScopedLint { - span: tool_ident.map(|ident| ident.span), - tool_name: tool_name.unwrap(), - lint_name: pprust::path_to_string(&meta_item.path), - is_nightly_build: sess.is_nightly_build(), - }); - continue; - } + let id = match self.store.get_lint_by_name(lint.full_lint().as_str()) { + Some(TargetLint::Id(id)) => id, + None | Some(_) => bug!( + "guaranteed to find id due to previous parsing, happened while parsing {:?}", + lint, + ), + }; - CheckLintNameResult::Renamed(ref replace) => { - if self.lint_added_lints { - let suggestion = - RenamedLintSuggestion::WithSpan { suggestion: sp, replace }; - let name = - tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name); - self.emit_span_lint( - RENAMED_AND_REMOVED_LINTS, - sp.into(), - RenamedLint { name: name.as_str(), replace, suggestion }, - ); - } - - // If this lint was renamed, apply the new lint instead of ignoring the - // attribute. Ignore any errors or warnings that happen because the new - // name is inaccurate. - // NOTE: `new_name` already includes the tool name, so we don't - // have to add it again. - let CheckLintNameResult::Ok(ids) = - self.store.check_lint_name(replace, None, self.registered_tools) - else { - panic!("renamed lint does not exist: {replace}"); - }; - - (ids, Symbol::intern(&replace)) - } + if self.check_gated_lint(*id, lint.span(), false) { + self.insert_spec(*id, LevelSpec::new(level, expect_lint_id, src)); + } + } - CheckLintNameResult::Removed(ref reason) => { - if self.lint_added_lints { - let name = - tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name); - self.emit_span_lint( - RENAMED_AND_REMOVED_LINTS, - sp.into(), - RemovedLint { name: name.as_str(), reason }, - ); - } - continue; - } + fn add(&mut self, attrs: &[hir::Attribute]) { + if find_attr!(attrs, AutomaticallyDerived) { + self.provider.insert( + LintId::of(SINGLE_USE_LIFETIMES), + LevelSpec::new(Level::Allow, None, LintLevelSource::Default), + ); + } + // `#[doc(hidden)]` disables missing_docs check. + if find_attr!(attrs, Doc(d) if d.hidden.is_some()) { + self.provider.insert( + LintId::of(MISSING_DOCS), + LevelSpec::new(Level::Allow, None, LintLevelSource::Default), + ); + } - CheckLintNameResult::NoLint(suggestion) => { - if self.lint_added_lints { - let name = - tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name); - let suggestion = suggestion.map(|(replace, from_rustc)| { - UnknownLintSuggestion::WithSpan { - suggestion: sp, - replace, - from_rustc, - } - }); - self.emit_span_lint( - UNKNOWN_LINTS, - sp.into(), - UnknownLint { name, suggestion }, - ); - } - continue; - } - }; + let Some(attrs) = find_attr!(attrs, LintAttributes(sub_attrs) => sub_attrs.into_iter()) + else { + return; + }; - let src = LintLevelSource::Node { name, span: sp, reason }; - for &id in ids { - if self.check_gated_lint(id, sp, false) { - self.insert_spec(id, LevelSpec::new(level, lint_id, src)); + for (attr_index, LintAttribute { reason, lint_instances, attr_id, kind, .. }) in + attrs.enumerate() + { + let attr_id = attr_id.attr_id; + let level = match kind { + LintAttributeKind::Allow => Level::Allow, + LintAttributeKind::Deny => Level::Deny, + LintAttributeKind::Forbid => Level::Forbid, + LintAttributeKind::Warn => Level::Warn, + LintAttributeKind::Expect => { + for lint in lint_instances { + let lint_index = lint.lint_index().try_into().unwrap(); + let attr_index = attr_index.try_into().unwrap(); + let expectation_id = + self.provider.mk_lint_expectation_id(attr_id, attr_index, lint_index); + + self.simple_add(Level::Expect, lint, *reason, Some(expectation_id)); + + let is_unfulfilled_lint_expectations = + lint.lint_name().as_str() == UNFULFILLED_LINT_EXPECTATIONS.name_lower(); + self.provider.push_expectation( + expectation_id, + LintExpectation::new( + *reason, + lint.span(), + is_unfulfilled_lint_expectations, + lint.tool_name(), + ), + ); } - } - - // This checks for instances where the user writes - // `#[expect(unfulfilled_lint_expectations)]` in that case we want to avoid - // overriding the lint level but instead add an expectation that can't be - // fulfilled. The lint message will include an explanation, that the - // `unfulfilled_lint_expectations` lint can't be expected. - if let (Level::Expect, Some(expect_id)) = (level, lint_id) { - // The `unfulfilled_lint_expectations` lint is not part of any lint - // groups. Therefore. we only need to check the slice if it contains a - // single lint. - let is_unfulfilled_lint_expectations = match ids { - [lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS), - _ => false, - }; - self.provider.push_expectation( - expect_id, - LintExpectation::new( - reason, - sp, - is_unfulfilled_lint_expectations, - tool_name, - ), - ); - } - } - } - if self.lint_added_lints && !is_crate_node { - for (id, level_spec) in self.provider.current_specs().iter() { - if !id.lint.crate_level_only { continue; } - - let LintLevelSource::Node { name: lint_attr_name, span: lint_attr_span, .. } = - level_spec.src - else { - continue; - }; - - self.emit_span_lint( - UNUSED_ATTRIBUTES, - lint_attr_span.into(), - IgnoredUnlessCrateSpecified { - level: level_spec.level().as_str(), - name: lint_attr_name, - }, - ); - // don't set a separate error for every lint in the group - break; + }; + for lint in lint_instances { + self.simple_add(level, lint, *reason, None); } } } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 2ae338d34b1f3..42e1104f9d482 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -129,7 +129,7 @@ use unused::*; #[rustfmt::skip] pub use builtin::{MissingDoc, SoftLints}; -pub use context::{CheckLintNameResult, EarlyContext, LateContext, LintContext, LintStore}; +pub use context::{EarlyContext, LateContext, LintContext, LintStore}; pub use early::diagnostics::DiagAndSess; pub use early::{EarlyCheckNode, check_ast_node}; pub use late::{check_crate, late_lint_mod, unerased_lint_store}; @@ -137,7 +137,9 @@ pub use levels::LintLevelsBuilder; pub use passes::{EarlyLintPass, LateLintPass}; pub use rustc_errors::BufferedEarlyLint; pub use rustc_session::lint::Level::{self, *}; -pub use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintId, LintPass, LintVec}; +pub use rustc_session::lint::{ + CheckLintNameResult, FutureIncompatibleInfo, Lint, LintId, LintPass, LintVec, +}; pub fn provide(providers: &mut Providers) { levels::provide(providers); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 091b7ac228b54..773041c898f40 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -13,6 +13,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::VisitorExt; use rustc_macros::{Diagnostic, Subdiagnostic}; +pub(crate) use rustc_middle::error::lint_lints::*; use rustc_middle::ty::inhabitedness::InhabitedPredicate; use rustc_middle::ty::{Clause, PolyExistentialTraitRef, Ty, TyCtxt}; use rustc_session::Session; @@ -23,7 +24,6 @@ use crate::LateContext; use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds}; use crate::errors::{OverruledAttributeSub, RequestedLevel}; use crate::lifetime_syntax::LifetimeSyntaxCategories; - // array_into_iter.rs #[derive(Diagnostic)] #[diag( @@ -1248,15 +1248,6 @@ pub(crate) struct OverruledAttributeLint<'a> { pub sub: OverruledAttributeSub, } -#[derive(Diagnostic)] -#[diag("lint name `{$name}` is deprecated and may not have an effect in the future")] -pub(crate) struct DeprecatedLintName<'a> { - pub name: String, - #[suggestion("change it to", code = "{replace}", applicability = "machine-applicable")] - pub suggestion: Span, - pub replace: &'a str, -} - #[derive(Diagnostic)] #[diag("lint name `{$name}` is deprecated and may not have an effect in the future")] #[help("change it to {$replace}")] @@ -1267,45 +1258,17 @@ pub(crate) struct DeprecatedLintNameFromCommandLine<'a> { pub requested_level: RequestedLevel<'a>, } -#[derive(Diagnostic)] -#[diag("lint `{$name}` has been renamed to `{$replace}`")] -pub(crate) struct RenamedLint<'a> { - pub name: &'a str, - pub replace: &'a str, - #[subdiagnostic] - pub suggestion: RenamedLintSuggestion<'a>, -} - -#[derive(Subdiagnostic)] -pub(crate) enum RenamedLintSuggestion<'a> { - #[suggestion("use the new name", code = "{replace}", applicability = "machine-applicable")] - WithSpan { - #[primary_span] - suggestion: Span, - replace: &'a str, - }, - #[help("use the new name `{$replace}`")] - WithoutSpan { replace: &'a str }, -} - #[derive(Diagnostic)] #[diag("lint `{$name}` has been renamed to `{$replace}`")] pub(crate) struct RenamedLintFromCommandLine<'a> { pub name: &'a str, - pub replace: &'a str, + pub replace: Symbol, #[subdiagnostic] - pub suggestion: RenamedLintSuggestion<'a>, + pub suggestion: RenamedLintSuggestion, #[subdiagnostic] pub requested_level: RequestedLevel<'a>, } -#[derive(Diagnostic)] -#[diag("lint `{$name}` has been removed: {$reason}")] -pub(crate) struct RemovedLint<'a> { - pub name: &'a str, - pub reason: &'a str, -} - #[derive(Diagnostic)] #[diag("lint `{$name}` has been removed: {$reason}")] pub(crate) struct RemovedLintFromCommandLine<'a> { @@ -1315,39 +1278,6 @@ pub(crate) struct RemovedLintFromCommandLine<'a> { pub requested_level: RequestedLevel<'a>, } -#[derive(Diagnostic)] -#[diag("unknown lint: `{$name}`")] -pub(crate) struct UnknownLint { - pub name: String, - #[subdiagnostic] - pub suggestion: Option, -} - -#[derive(Subdiagnostic)] -pub(crate) enum UnknownLintSuggestion { - #[suggestion( - "{$from_rustc -> - [true] a lint with a similar name exists in `rustc` lints - *[false] did you mean - }", - code = "{replace}", - applicability = "maybe-incorrect" - )] - WithSpan { - #[primary_span] - suggestion: Span, - replace: Symbol, - from_rustc: bool, - }, - #[help( - "{$from_rustc -> - [true] a lint with a similar name exists in `rustc` lints: `{$replace}` - *[false] did you mean: `{$replace}` - }" - )] - WithoutSpan { replace: Symbol, from_rustc: bool }, -} - #[derive(Diagnostic)] #[diag("unknown lint: `{$name}`", code = E0602)] pub(crate) struct UnknownLintFromCommandLine<'a> { @@ -1358,13 +1288,6 @@ pub(crate) struct UnknownLintFromCommandLine<'a> { pub requested_level: RequestedLevel<'a>, } -#[derive(Diagnostic)] -#[diag("{$level}({$name}) is ignored unless specified at crate level")] -pub(crate) struct IgnoredUnlessCrateSpecified<'a> { - pub level: &'a str, - pub name: Symbol, -} - // dangling.rs #[derive(Diagnostic)] #[diag("this creates a dangling pointer because temporary `{$ty}` is dropped at end of statement")] diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index fc8b2b65a8d48..cb8dcc19d3b3d 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -109,7 +109,7 @@ pub enum LintExpectationId { #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Encodable, Decodable)] pub struct UnstableLintExpectationId { pub attr_id: AttrId, - pub lint_index: Option, + pub lint_index: u16, } impl From for LintExpectationId { @@ -125,17 +125,17 @@ impl From for LintExpectationId { pub struct StableLintExpectationId { pub hir_id: HirId, pub attr_index: u16, - pub lint_index: Option, + pub lint_index: u16, } impl StableHash for StableLintExpectationId { #[inline] fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { - let StableLintExpectationId { hir_id, attr_index, lint_index } = self; + let StableLintExpectationId { hir_id, attr_index, lint_index, .. } = self; hir_id.stable_hash(hcx, hasher); attr_index.stable_hash(hcx, hasher); - lint_index.expect("must be filled to call `stable_hash`").stable_hash(hcx, hasher); + lint_index.stable_hash(hcx, hasher); } } @@ -209,6 +209,11 @@ impl Level { } } + /// Converts an `Attribute` to a level. + pub fn from_attr(attr_name: Option) -> Option { + attr_name.and_then(Self::from_symbol) + } + /// Converts an `Option` to a level. pub fn from_opt_symbol(s: Option) -> Option { s.and_then(Self::from_symbol) @@ -218,10 +223,10 @@ impl Level { pub fn from_symbol(s: Symbol) -> Option { match s { sym::allow => Some(Level::Allow), - sym::expect => Some(Level::Expect), - sym::warn => Some(Level::Warn), sym::deny => Some(Level::Deny), + sym::expect => Some(Level::Expect), sym::forbid => Some(Level::Forbid), + sym::warn => Some(Level::Warn), _ => None, } } @@ -537,6 +542,50 @@ impl Lint { } } +/// The target of the `by_name` map, which accounts for renaming/deprecation. +#[derive(Debug)] +pub enum TargetLint { + /// A direct lint target + Id(LintId), + + /// Temporary renaming, used for easing migration pain; see #16545 + Renamed(String, LintId), + + /// Lint with this name existed previously, but has been removed/deprecated. + /// The string argument is the reason for removal. + Removed(String), + + /// A lint name that should give no warnings and have no effect. + /// + /// This is used by rustc to avoid warning about old rustdoc lints before rustdoc registers + /// them as tool lints. + Ignored, +} + +#[derive(Debug)] +pub enum CheckLintNameResult<'a> { + Ok(&'a [LintId]), + /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name. + NoLint(Option<(Symbol, bool)>), + /// The lint refers to a tool that has not been registered. + NoTool, + /// The lint has been renamed to a new name. + Renamed(Symbol), + RenamedToolLint(Symbol), + /// The lint has been removed due to the given reason. + Removed(String), + + /// The lint is from a tool. The `LintId` will be returned as if it were a + /// rustc lint. The `Option` indicates if the lint has been + /// renamed. + Tool(&'a [LintId], Option), + + /// The lint is from a tool. Either the lint does not exist in the tool or + /// the code was not compiled with the tool and therefore the lint was + /// never added to the `LintStore`. + MissingTool, +} + /// Identifies a lint known to the compiler. #[derive(Clone, Copy, Debug)] pub struct LintId { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index a0db004b7f4c4..ac094be5c8f2d 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -31,7 +31,7 @@ use rustc_session::config::{CrateType, OptLevel, TargetModifier}; use rustc_span::hygiene::HygieneEncodeContext; use rustc_span::{ ByteSymbol, ExternalSource, FileName, SourceFile, SpanData, SpanEncoder, StableSourceFileId, - Symbol, SyntaxContext, sym, + Symbol, SyntaxContext, }; use tracing::{debug, instrument, trace}; @@ -866,11 +866,6 @@ fn analyze_attr(attr: &hir::Attribute, state: &mut AnalyzeAttrState) -> bool { && p.encode_cross_crate() == EncodeCrossCrate::No { // Attributes not marked encode-cross-crate don't need to be encoded for downstream crates. - } else if let Some(name) = attr.name() - && [sym::warn, sym::allow, sym::expect, sym::forbid, sym::deny].contains(&name) - { - // Lint attributes don't need to be encoded for downstream crates. - // FIXME remove this when #152369 is re-merged } else if let hir::Attribute::Parsed(AttributeKind::DocComment { .. }) = attr { // We keep all doc comments reachable to rustdoc because they might be "imported" into // downstream crates if they use `#[doc(inline)]` to copy an item's documentation into diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index 2823b7ba4e22e..c5d1d18944958 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -160,3 +160,85 @@ pub(crate) struct IncrementCompilation { pub run_cmd: String, pub dep_node: String, } + +pub mod lint_lints { + + use rustc_macros::{Diagnostic, Subdiagnostic}; + use rustc_span::{Span, Symbol}; + + #[derive(Diagnostic)] + #[diag("lint `{$name}` has been renamed to `{$replace}`")] + pub struct RenamedLint { + pub name: Symbol, + pub replace: Symbol, + #[subdiagnostic] + pub suggestion: RenamedLintSuggestion, + } + + #[derive(Diagnostic)] + #[diag("lint name `{$name}` is deprecated and may not have an effect in the future")] + pub struct DeprecatedLintName { + pub name: Symbol, + #[suggestion("change it to", code = "{replace}", applicability = "machine-applicable")] + pub suggestion: Span, + pub replace: Symbol, + } + + #[derive(Subdiagnostic)] + pub enum RenamedLintSuggestion { + #[suggestion("use the new name", code = "{replace}", applicability = "machine-applicable")] + WithSpan { + #[primary_span] + suggestion: Span, + replace: Symbol, + }, + #[help("use the new name `{$replace}`")] + WithoutSpan { replace: Symbol }, + } + + #[derive(Diagnostic)] + #[diag("lint `{$name}` has been removed: {$reason}")] + pub struct RemovedLint { + pub name: Symbol, + pub reason: String, + } + + #[derive(Diagnostic)] + #[diag("unknown lint: `{$name}`")] + pub struct UnknownLint { + pub name: Symbol, + #[subdiagnostic] + pub suggestion: Option, + } + #[derive(Subdiagnostic)] + pub enum UnknownLintSuggestion { + #[suggestion( + "{$from_rustc -> + [true] a lint with a similar name exists in `rustc` lints + *[false] did you mean + }", + code = "{replace}", + applicability = "maybe-incorrect" + )] + WithSpan { + #[primary_span] + suggestion: Span, + replace: Symbol, + from_rustc: bool, + }, + #[help( + "{$from_rustc -> + [true] a lint with a similar name exists in `rustc` lints: `{$replace}` + *[false] did you mean: `{$replace}` + }" + )] + WithoutSpan { replace: Symbol, from_rustc: bool }, + } + + #[derive(Diagnostic)] + #[diag("{$level}({$name}) is ignored unless specified at crate level")] + pub struct IgnoredUnlessCrateSpecified { + pub level: Symbol, + pub name: Symbol, + } +} diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 3343bec30caf5..b1dab5424068b 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -85,7 +85,7 @@ use std::mem; use interpret::ErrorHandled; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::HirId; +use rustc_hir::{HirId, find_attr}; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::middle::region; use rustc_middle::mir::{self, *}; @@ -93,7 +93,6 @@ use rustc_middle::thir::{AdtExpr, AdtExprBase, ArmId, ExprId, ExprKind}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, ValTree}; use rustc_middle::{bug, span_bug}; use rustc_pattern_analysis::rustc::RustcPatCtxt; -use rustc_session::lint::Level; use rustc_span::{DUMMY_SP, Span, Spanned}; use tracing::{debug, instrument}; @@ -1298,12 +1297,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { break; } - if self - .tcx - .hir_attrs(id) - .iter() - .any(|attr| Level::from_opt_symbol(attr.name()).is_some()) - { + if find_attr!(self.tcx.hir_attrs(id), LintAttributes { .. }) { // This is a rare case. It's for a node path that doesn't reach the root due to an // intervening lint level attribute. This result doesn't get cached. return id; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index f6765d604acb3..af5335d3c9c98 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -9,7 +9,7 @@ use std::cell::Cell; use std::slice; use rustc_abi::ExternAbi; -use rustc_ast::{AttrStyle, MetaItemKind, ast}; +use rustc_ast::ast; use rustc_attr_parsing::AttributeParser; use rustc_data_structures::thin_vec::ThinVec; use rustc_data_structures::unord::UnordMap; @@ -17,8 +17,8 @@ use rustc_errors::{DiagCtxtHandle, IntoDiagArg, MultiSpan, msg}; use rustc_feature::BUILTIN_ATTRIBUTE_MAP; use rustc_hir::attrs::diagnostic::Directive; use rustc_hir::attrs::{ - AttributeKind, DocAttribute, DocInline, EiiDecl, EiiImpl, EiiImplResolution, InlineAttr, - ReprAttr, SanitizerSet, + AttributeKind, CrateType, DocAttribute, DocInline, EiiDecl, EiiImpl, EiiImplResolution, + InlineAttr, LintAttribute, ReprAttr, SanitizerSet, }; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalModDefId; @@ -35,7 +35,6 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, TyCtxt, TypingMode, Unnormalized}; use rustc_middle::{bug, span_bug}; -use rustc_session::config::CrateType; use rustc_session::errors::feature_err; use rustc_session::lint; use rustc_session::lint::builtin::{ @@ -126,13 +125,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match attr { Attribute::Parsed(attr_kind) => { self.check_one_parsed_attribute(hir_id, span, target, item, attrs, attr_kind); - self.check_unused_attribute(hir_id, attr, None); + self.check_unused_attribute(hir_id, attr); } - Attribute::Unparsed(attr_item) => { + Attribute::Unparsed(_) => { match attr.path().as_slice() { - // ok - [sym::allow | sym::expect | sym::warn | sym::deny | sym::forbid, ..] => {} - [name, rest @ ..] => { if let Some(_) = BUILTIN_ATTRIBUTE_MAP.get(name) { if rest.len() > 0 @@ -155,7 +151,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [] => unreachable!(), } - self.check_unused_attribute(hir_id, attr, Some(attr_item.style)); + self.check_unused_attribute(hir_id, attr); } } } @@ -234,6 +230,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::MacroExport { span, .. } => { self.check_macro_export(hir_id, *span, target) } + AttributeKind::RustcLegacyConstGenerics { attr_span, fn_indexes } => { self.check_rustc_legacy_const_generics(item, *attr_span, fn_indexes) } @@ -251,6 +248,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::OnMove { directive } => { self.check_diagnostic_on_move(hir_id, directive.as_deref()) } + AttributeKind::LintAttributes(sub_attrs) => self.check_lint_attr(hir_id, sub_attrs), // All of the following attributes have no specific checks. // tidy-alphabetical-start @@ -1457,70 +1455,50 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute, style: Option) { - // Warn on useless empty attributes. - // FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute` - let note = - if attr.has_any_name(&[sym::allow, sym::expect, sym::warn, sym::deny, sym::forbid]) - && attr.meta_item_list().is_some_and(|list| list.is_empty()) - { - errors::UnusedNote::EmptyList { name: attr.name().unwrap() } - } else if attr.has_any_name(&[ - sym::allow, - sym::warn, - sym::deny, - sym::forbid, - sym::expect, - ]) && let Some(meta) = attr.meta_item_list() - && let [meta] = meta.as_slice() - && let Some(item) = meta.meta_item() - && let MetaItemKind::NameValue(_) = &item.kind - && item.path == sym::reason - { - errors::UnusedNote::NoLints { name: attr.name().unwrap() } - } else if attr.has_any_name(&[ - sym::allow, - sym::warn, - sym::deny, - sym::forbid, - sym::expect, - ]) && let Some(meta) = attr.meta_item_list() - && meta.iter().any(|meta| { - meta.meta_item().map_or(false, |item| { - item.path == sym::linker_messages || item.path == sym::linker_info - }) + fn check_lint_attr(&self, hir_id: HirId, sub_attrs: &[LintAttribute]) { + for LintAttribute { attr_span, lint_instances, attr_style, .. } in sub_attrs { + let dead_code_lint_in_instances = + lint_instances.iter().any(|id| id.lint_name() == sym::dead_code_pub_in_binary); + if !dead_code_lint_in_instances + && !lint_instances.iter().any(|id| { + id.lint_name() == sym::linker_messages || id.lint_name() == sym::linker_info }) { - if hir_id != CRATE_HIR_ID { - match style { - Some(ast::AttrStyle::Outer) => { - let attr_span = attr.span(); - let bang_position = self - .tcx - .sess - .source_map() - .span_until_char(attr_span, '[') - .shrink_to_hi(); - - self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, - hir_id, - attr_span, - errors::OuterCrateLevelAttr { - suggestion: errors::OuterCrateLevelAttrSuggestion { - bang_position, - }, - }, - ) - } - Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint( + continue; + }; + if hir_id != CRATE_HIR_ID { + match attr_style { + ast::AttrStyle::Outer => { + let attr_span = attr_span; + let bang_position = self + .tcx + .sess + .source_map() + .span_until_char(*attr_span, '[') + .shrink_to_hi(); + + self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), - errors::InnerCrateLevelAttr, - ), - }; - return; + *attr_span, + errors::OuterCrateLevelAttr { + suggestion: errors::OuterCrateLevelAttrSuggestion { bang_position }, + }, + ) + } + ast::AttrStyle::Inner => self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + *attr_span, + errors::InnerCrateLevelAttr, + ), + }; + continue; + } else { + let note = if dead_code_lint_in_instances + && !self.tcx.crate_types().contains(&CrateType::Executable) + { + errors::UnusedNote::NoEffectDeadCodePubInBinary } else { let never_needs_link = self .tcx @@ -1530,23 +1508,31 @@ impl<'tcx> CheckAttrVisitor<'tcx> { if never_needs_link { errors::UnusedNote::LinkerMessagesBinaryCrateOnly } else { - return; + continue; } - } - } else if hir_id == CRATE_HIR_ID - && attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect]) - && let Some(meta) = attr.meta_item_list() - && meta.iter().any(|meta| { - meta.meta_item().is_some_and(|item| item.path == sym::dead_code_pub_in_binary) - }) - && !self.tcx.crate_types().contains(&CrateType::Executable) - { - errors::UnusedNote::NoEffectDeadCodePubInBinary - } else if attr.has_name(sym::default_method_body_is_const) { - errors::UnusedNote::DefaultMethodBodyConst - } else { - return; + }; + self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + *attr_span, + errors::Unused { attr_span: *attr_span, note }, + ); }; + } + } + + fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute) { + // Warn on useless empty attributes. + // FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute` + let note = if attr.has_any_name(&[sym::feature]) + && attr.meta_item_list().is_some_and(|list| list.is_empty()) + { + errors::UnusedNote::EmptyList { name: attr.name().unwrap() } + } else if attr.has_name(sym::default_method_body_is_const) { + errors::UnusedNote::DefaultMethodBodyConst + } else { + return; + }; self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index b87a43e9fcde0..3bf56f4dc1bec 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -290,8 +290,6 @@ pub(crate) enum MacroExport { pub(crate) enum UnusedNote { #[note("attribute `{$name}` with an empty list has no effect")] EmptyList { name: Symbol }, - #[note("attribute `{$name}` without any lints has no effect")] - NoLints { name: Symbol }, #[note("`default_method_body_is_const` has been replaced with `const` on traits")] DefaultMethodBodyConst, #[note( diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 003164e8f9054..df00d4896bfff 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -42,7 +42,7 @@ use crate::config::{ SwitchWithOptPath, }; use crate::filesearch::FileSearch; -use crate::lint::LintId; +use crate::lint::{CheckLintNameResult, LintId, RegisteredTools}; use crate::parse::ParseSess; use crate::search_paths::SearchPath; use crate::{errors, filesearch, lint}; @@ -83,6 +83,15 @@ pub struct CompilerIO { pub trait DynLintStore: Any + DynSync + DynSend { /// Provides a way to access lint groups without depending on `rustc_lint` fn lint_groups_iter(&self) -> Box + '_>; + + fn check_lint_name( + &self, + lint_name: &str, + tool_name: Option, + registered_tools: &RegisteredTools, + ) -> CheckLintNameResult<'_>; + + fn find_lints(&self, lint_name: &str) -> Option<&[LintId]>; } /// Represents the data associated with a compilation diff --git a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs index 6efc931df2429..5d095c9b27ade 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs @@ -1,10 +1,12 @@ +use crate::attrs::is_lint_level; + use super::{Attribute, UNNECESSARY_CLIPPY_CFG}; use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg}; use clippy_utils::source::SpanRangeExt; use itertools::Itertools; use rustc_ast::AttrStyle; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, Level}; +use rustc_lint::{EarlyContext}; use rustc_span::sym; pub(super) fn check( @@ -13,9 +15,10 @@ pub(super) fn check( behind_cfg_attr: &rustc_ast::MetaItem, attr: &Attribute, ) { + // FIXME use proper attr parsing here if cfg_attr.has_name(sym::clippy) && let Some(ident) = behind_cfg_attr.ident() - && Level::from_symbol(ident.name).is_some() + && is_lint_level(ident.name) && let Some(items) = behind_cfg_attr.meta_item_list() { let nb_items = items.len(); diff --git a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs index c025b47dc787d..2d56086a96024 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs @@ -15,7 +15,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { return; } if let Some(lint_list) = &attr.meta_item_list() - && attr.name().is_some_and(|name| is_lint_level(name)) + && attr.name().is_some_and(is_lint_level) { for lint in lint_list { match item.kind { diff --git a/src/tools/clippy/clippy_lints/src/attrs/utils.rs b/src/tools/clippy/clippy_lints/src/attrs/utils.rs index 4822bdcb9bde2..512f961228b15 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/utils.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/utils.rs @@ -1,5 +1,5 @@ use clippy_utils::macros::{is_panic, macro_backtrace}; -use rustc_ast::MetaItemInner; +use rustc_ast::{MetaItemInner}; use rustc_hir::{ Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, }; diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index 3b9e0171f587d..ca04ce1764ff4 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -3,11 +3,12 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::source::{HasSession, IntoSpan as _, SpanRangeExt, snippet, snippet_block_with_applicability}; use clippy_utils::{can_use_if_let_chains, span_contains_cfg, span_contains_non_whitespace, sym, tokenize_with_text}; -use rustc_ast::{BinOpKind, MetaItemInner}; +use rustc_ast::BinOpKind; use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, StmtKind}; +use rustc_hir::attrs::{AttributeKind, LintAttributeKind}; +use rustc_hir::{Attribute, Block, Expr, ExprKind, StmtKind}; use rustc_lexer::TokenKind; -use rustc_lint::{LateContext, LateLintPass, Level}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::{BytePos, Span, Symbol}; @@ -237,19 +238,24 @@ impl CollapsibleIf { !span_contains_non_whitespace(cx, span, self.lint_commented_code) }, - [attr] - if matches!(Level::from_opt_symbol(attr.name()), Some(Level::Expect)) - && let Some(metas) = attr.meta_item_list() - && let Some(MetaItemInner::MetaItem(meta_item)) = metas.first() - && let [tool, lint_name] = meta_item.path.segments.as_slice() - && tool.ident.name == sym::clippy - && [expected_lint_name, sym::style, sym::all].contains(&lint_name.ident.name) => - { - // There is an `expect` attribute -- check that there is no _other_ significant text - let span_before_attr = inner_if.span.split_at(1).1.until(attr.span()); - let span_after_attr = attr.span().between(inner_if_expr.span); - !span_contains_non_whitespace(cx, span_before_attr, self.lint_commented_code) - && !span_contains_non_whitespace(cx, span_after_attr, self.lint_commented_code) + [Attribute::Parsed(AttributeKind::LintAttributes(sub_attrs))] => { + sub_attrs + .into_iter() + .filter(|attr| attr.kind == LintAttributeKind::Expect) + .flat_map(|attr| attr.lint_instances.iter().map(|group| (attr.attr_span, group))) + .filter(|(_, lint_id)| { + lint_id.tool_is_named(sym::clippy) + && (expected_lint_name == lint_id.lint_name() + || [expected_lint_name, sym::style, sym::all] + .contains(&lint_id.original_name_without_tool())) + }) + .any(|(attr_span, _)| { + // There is an `expect` attribute -- check that there is no _other_ significant text + let span_before_attr = inner_if.span.split_at(1).1.until(attr_span); + let span_after_attr = attr_span.between(inner_if_expr.span); + !span_contains_non_whitespace(cx, span_before_attr, self.lint_commented_code) + && !span_contains_non_whitespace(cx, span_after_attr, self.lint_commented_code) + }) }, // There are other attributes, which are significant tokens -- check failed diff --git a/src/tools/clippy/clippy_lints/src/returns/needless_return.rs b/src/tools/clippy/clippy_lints/src/returns/needless_return.rs index f3d6324e1885e..c886356d685b8 100644 --- a/src/tools/clippy/clippy_lints/src/returns/needless_return.rs +++ b/src/tools/clippy/clippy_lints/src/returns/needless_return.rs @@ -4,11 +4,11 @@ use clippy_utils::{ binary_expr_needs_parentheses, is_from_proc_macro, leaks_droppable_temporary_with_limited_lifetime, span_contains_cfg, span_find_starting_semi, sym, }; -use rustc_ast::MetaItemInner; use rustc_errors::Applicability; +use rustc_hir::attrs::{AttributeKind, LintAttributeKind}; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, Expr, ExprKind, HirId, LangItem, MatchSource, StmtKind}; -use rustc_lint::{LateContext, Level, LintContext}; +use rustc_hir::{Attribute, Body, Expr, ExprKind, HirId, LangItem, MatchSource, StmtKind}; +use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::{self, Ty}; use rustc_span::{BytePos, Pos, Span}; use std::borrow::Cow; @@ -180,20 +180,19 @@ fn check_final_expr<'tcx>( // actually fulfill the expectation (clippy::#12998) match cx.tcx.hir_attrs(expr.hir_id) { [] => {}, - [attr] => { - if matches!(Level::from_opt_symbol(attr.name()), Some(Level::Expect)) - && let metas = attr.meta_item_list() - && let Some(lst) = metas - && let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice() - && let [tool, lint_name] = meta_item.path.segments.as_slice() - && tool.ident.name == sym::clippy - && matches!( - lint_name.ident.name, - sym::needless_return | sym::style | sym::all | sym::warnings - ) + [Attribute::Parsed(AttributeKind::LintAttributes(sub_attrs))] => { + if !sub_attrs + .into_iter() + .filter(|attr| attr.kind == LintAttributeKind::Expect) + .flat_map(|attr| &attr.lint_instances) + .filter(|lint| lint.tool_name().is_some_and(|name| name == sym::clippy)) + .any(|lint| { + matches!( + lint.original_name_without_tool(), + sym::needless_return | sym::style | sym::all | sym::warnings + ) + }) { - // This is an expectation of the `needless_return` lint - } else { return; } }, diff --git a/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs b/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs index 82ac4db172d88..68271048fcb93 100644 --- a/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs +++ b/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs @@ -36,8 +36,6 @@ mod rustc_warn { #[expect(invalid_nan_comparisons)] //~^ ERROR: this lint expectation is unfulfilled - //~| NOTE: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - //~| ERROR: this lint expectation is unfulfilled let _b = x == 5; } } diff --git a/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.stderr b/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.stderr index b274d5c236933..aa187f351f458 100644 --- a/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.stderr +++ b/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.stderr @@ -14,36 +14,28 @@ LL | #[expect(invalid_nan_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^ error: this lint expectation is unfulfilled - --> tests/ui/expect_tool_lint_rfc_2383.rs:37:18 - | -LL | #[expect(invalid_nan_comparisons)] - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: this lint expectation is unfulfilled - --> tests/ui/expect_tool_lint_rfc_2383.rs:110:14 + --> tests/ui/expect_tool_lint_rfc_2383.rs:108:14 | LL | #[expect(clippy::almost_swapped)] | ^^^^^^^^^^^^^^^^^^^^^^ error: this lint expectation is unfulfilled - --> tests/ui/expect_tool_lint_rfc_2383.rs:118:14 + --> tests/ui/expect_tool_lint_rfc_2383.rs:116:14 | LL | #[expect(clippy::bytes_nth)] | ^^^^^^^^^^^^^^^^^ error: this lint expectation is unfulfilled - --> tests/ui/expect_tool_lint_rfc_2383.rs:124:14 + --> tests/ui/expect_tool_lint_rfc_2383.rs:122:14 | LL | #[expect(clippy::if_same_then_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this lint expectation is unfulfilled - --> tests/ui/expect_tool_lint_rfc_2383.rs:130:14 + --> tests/ui/expect_tool_lint_rfc_2383.rs:128:14 | LL | #[expect(clippy::overly_complex_bool_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr index 592fdfbebd43a..974c24bdc3bf3 100644 --- a/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr +++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr @@ -1,23 +1,11 @@ -error: unknown lint: `clippy::All` - --> tests/ui/unknown_clippy_lints.rs:3:10 - | -LL | #![allow(clippy::All)] - | ^^^^^^^^^^^ help: did you mean: `clippy::all` - | - = note: `-D unknown-lints` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(unknown_lints)]` - -error: unknown lint: `clippy::CMP_OWNED` - --> tests/ui/unknown_clippy_lints.rs:5:9 - | -LL | #![warn(clippy::CMP_OWNED)] - | ^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::cmp_owned` - error: unknown lint: `clippy::if_not_els` --> tests/ui/unknown_clippy_lints.rs:9:8 | LL | #[warn(clippy::if_not_els)] | ^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::if_not_else` + | + = note: `-D unknown-lints` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unknown_lints)]` error: unknown lint: `clippy::UNNecsaRy_cAst` --> tests/ui/unknown_clippy_lints.rs:11:8 @@ -67,5 +55,17 @@ LL - #[warn(clippy::missing_docs)] LL + #[warn(missing_docs)] | +error: unknown lint: `clippy::All` + --> tests/ui/unknown_clippy_lints.rs:3:10 + | +LL | #![allow(clippy::All)] + | ^^^^^^^^^^^ help: did you mean: `clippy::all` + +error: unknown lint: `clippy::CMP_OWNED` + --> tests/ui/unknown_clippy_lints.rs:5:9 + | +LL | #![warn(clippy::CMP_OWNED)] + | ^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::cmp_owned` + error: aborting due to 9 previous errors diff --git a/tests/pretty/delegation-inherit-attributes.pp b/tests/pretty/delegation-inherit-attributes.pp index e29f76089256d..57f74a4401087 100644 --- a/tests/pretty/delegation-inherit-attributes.pp +++ b/tests/pretty/delegation-inherit-attributes.pp @@ -1,14 +1,15 @@ +#![attr = LintAttributes([LintAttribute {kind: Allow, attr_style: Inner, +lint_instances: [incomplete_features]}])] +#![attr = Feature([fn_delegation#0])] +extern crate std; +#[attr = PreludeImport] +use std::prelude::rust_2021::*; //@ edition:2021 //@ aux-crate:to_reuse_functions=to-reuse-functions.rs //@ pretty-mode:hir //@ pretty-compare-only //@ pp-exact:delegation-inherit-attributes.pp -#![allow(incomplete_features)] -#![attr = Feature([fn_delegation#0])] -extern crate std; -#[attr = PreludeImport] -use std::prelude::rust_2021::*; extern crate to_reuse_functions; diff --git a/tests/pretty/delegation-inline-attribute.pp b/tests/pretty/delegation-inline-attribute.pp index 125ed1c298262..4828f2e6c80f6 100644 --- a/tests/pretty/delegation-inline-attribute.pp +++ b/tests/pretty/delegation-inline-attribute.pp @@ -1,12 +1,13 @@ -//@ pretty-compare-only -//@ pretty-mode:hir -//@ pp-exact:delegation-inline-attribute.pp - -#![allow(incomplete_features)] +#![attr = LintAttributes([LintAttribute {kind: Allow, attr_style: Inner, +lint_instances: [incomplete_features]}])] #![attr = Feature([fn_delegation#0])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:delegation-inline-attribute.pp + mod to_reuse { fn foo(x: usize) -> usize { x } diff --git a/tests/pretty/hir-delegation.pp b/tests/pretty/hir-delegation.pp index 28bb49458ce1d..5337dd2e96dfa 100644 --- a/tests/pretty/hir-delegation.pp +++ b/tests/pretty/hir-delegation.pp @@ -1,12 +1,13 @@ -//@ pretty-compare-only -//@ pretty-mode:hir -//@ pp-exact:hir-delegation.pp - -#![allow(incomplete_features)] +#![attr = LintAttributes([LintAttribute {kind: Allow, attr_style: Inner, +lint_instances: [incomplete_features]}])] #![attr = Feature([fn_delegation#0])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:hir-delegation.pp + fn b(e: C) { } diff --git a/tests/pretty/hir-lifetimes.pp b/tests/pretty/hir-lifetimes.pp index c35a40eed0c50..07ac4ccfdd450 100644 --- a/tests/pretty/hir-lifetimes.pp +++ b/tests/pretty/hir-lifetimes.pp @@ -1,13 +1,19 @@ +#![attr = LintAttributes([LintAttribute {kind: Allow, attr_style: Inner, +lint_instances: [unused_imports, unused_variables, unused_visibilities, +unused_assignments, dead_code, unused_mut, unreachable_code, +unreachable_patterns, unused_must_use, unused_unsafe, path_statements, +unused_attributes, unused_macros, unused_macro_rules, unused_allocation, +unused_doc_comments, unused_extern_crates, unused_features, unused_labels, +unused_parens, unused_braces, redundant_semicolons, map_unit_fn]}])] +extern crate std; +#[attr = PreludeImport] +use ::std::prelude::rust_2015::*; //@ pretty-compare-only //@ pretty-mode:hir //@ pp-exact:hir-lifetimes.pp // This tests the pretty-printing of lifetimes in lots of ways. -#![allow(unused)] -extern crate std; -#[attr = PreludeImport] -use ::std::prelude::rust_2015::*; struct Foo<'a> { x: &'a u32, diff --git a/tests/pretty/pin-ergonomics-hir.pp b/tests/pretty/pin-ergonomics-hir.pp index 6c9dec2bfb1fb..5b024bfff3b59 100644 --- a/tests/pretty/pin-ergonomics-hir.pp +++ b/tests/pretty/pin-ergonomics-hir.pp @@ -1,12 +1,13 @@ -//@ pretty-compare-only -//@ pretty-mode:hir -//@ pp-exact:pin-ergonomics-hir.pp - -#![allow(dead_code, incomplete_features)] #![attr = Feature([pin_ergonomics#0])] +#![attr = LintAttributes([LintAttribute {kind: Allow, attr_style: Inner, +lint_instances: [dead_code, incomplete_features]}])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:pin-ergonomics-hir.pp + use std::pin::Pin; diff --git a/tests/rustdoc-ui/lints/renamed-lint-still-applies-2.rs b/tests/rustdoc-ui/lints/renamed-lint-still-applies-2.rs new file mode 100644 index 0000000000000..6fe663518ad68 --- /dev/null +++ b/tests/rustdoc-ui/lints/renamed-lint-still-applies-2.rs @@ -0,0 +1,12 @@ +// compile-args: --crate-type lib + +// This file does not emit the rename warnings +// due to compilation aborting before we emit delayed lints + +#![deny(broken_intra_doc_links)] +//! [x] +//~^ ERROR unresolved link + +#![deny(rustdoc::non_autolinks)] +//! http://example.com +//~^ ERROR not a hyperlink diff --git a/tests/rustdoc-ui/lints/renamed-lint-still-applies-2.stderr b/tests/rustdoc-ui/lints/renamed-lint-still-applies-2.stderr new file mode 100644 index 0000000000000..484566587d9e2 --- /dev/null +++ b/tests/rustdoc-ui/lints/renamed-lint-still-applies-2.stderr @@ -0,0 +1,32 @@ +error: unresolved link to `x` + --> $DIR/renamed-lint-still-applies-2.rs:7:6 + | +LL | //! [x] + | ^ no item named `x` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` +note: the lint level is defined here + --> $DIR/renamed-lint-still-applies-2.rs:6:9 + | +LL | #![deny(broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: this URL is not a hyperlink + --> $DIR/renamed-lint-still-applies-2.rs:11:5 + | +LL | //! http://example.com + | ^^^^^^^^^^^^^^^^^^ + | + = note: bare URLs are not automatically turned into clickable links +note: the lint level is defined here + --> $DIR/renamed-lint-still-applies-2.rs:10:9 + | +LL | #![deny(rustdoc::non_autolinks)] + | ^^^^^^^^^^^^^^^^^^^^^^ +help: use an automatic link instead + | +LL | //! + | + + + +error: aborting due to 2 previous errors + diff --git a/tests/rustdoc-ui/lints/renamed-lint-still-applies.rs b/tests/rustdoc-ui/lints/renamed-lint-still-applies.rs index a4d3a4b497117..8dded5460f124 100644 --- a/tests/rustdoc-ui/lints/renamed-lint-still-applies.rs +++ b/tests/rustdoc-ui/lints/renamed-lint-still-applies.rs @@ -1,10 +1,7 @@ +//@ check-pass // compile-args: --crate-type lib #![deny(broken_intra_doc_links)] //~^ WARNING renamed to `rustdoc::broken_intra_doc_links` -//! [x] -//~^ ERROR unresolved link #![deny(rustdoc::non_autolinks)] //~^ WARNING renamed to `rustdoc::bare_urls` -//! http://example.com -//~^ ERROR not a hyperlink diff --git a/tests/rustdoc-ui/lints/renamed-lint-still-applies.stderr b/tests/rustdoc-ui/lints/renamed-lint-still-applies.stderr index 88807dfb495d0..b9dde5fbc7fee 100644 --- a/tests/rustdoc-ui/lints/renamed-lint-still-applies.stderr +++ b/tests/rustdoc-ui/lints/renamed-lint-still-applies.stderr @@ -1,5 +1,5 @@ warning: lint `broken_intra_doc_links` has been renamed to `rustdoc::broken_intra_doc_links` - --> $DIR/renamed-lint-still-applies.rs:2:9 + --> $DIR/renamed-lint-still-applies.rs:3:9 | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `rustdoc::broken_intra_doc_links` @@ -7,40 +7,10 @@ LL | #![deny(broken_intra_doc_links)] = note: `#[warn(renamed_and_removed_lints)]` on by default warning: lint `rustdoc::non_autolinks` has been renamed to `rustdoc::bare_urls` - --> $DIR/renamed-lint-still-applies.rs:7:9 + --> $DIR/renamed-lint-still-applies.rs:6:9 | LL | #![deny(rustdoc::non_autolinks)] | ^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `rustdoc::bare_urls` -error: unresolved link to `x` - --> $DIR/renamed-lint-still-applies.rs:4:6 - | -LL | //! [x] - | ^ no item named `x` in scope - | - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -note: the lint level is defined here - --> $DIR/renamed-lint-still-applies.rs:2:9 - | -LL | #![deny(broken_intra_doc_links)] - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: this URL is not a hyperlink - --> $DIR/renamed-lint-still-applies.rs:9:5 - | -LL | //! http://example.com - | ^^^^^^^^^^^^^^^^^^ - | - = note: bare URLs are not automatically turned into clickable links -note: the lint level is defined here - --> $DIR/renamed-lint-still-applies.rs:7:9 - | -LL | #![deny(rustdoc::non_autolinks)] - | ^^^^^^^^^^^^^^^^^^^^^^ -help: use an automatic link instead - | -LL | //! - | + + - -error: aborting due to 2 previous errors; 2 warnings emitted +warning: 2 warnings emitted diff --git a/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.svg b/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.svg index 549acee7cee54..af41631479cf5 100644 --- a/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.svg +++ b/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.svg @@ -1,4 +1,4 @@ - +