diff --git a/Cargo.lock b/Cargo.lock index 7f1cf4850c8a2..44dc8a8f87c80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3505,6 +3505,7 @@ dependencies = [ "rustc_hir", "rustc_lexer", "rustc_macros", + "rustc_middle", "rustc_parse", "rustc_parse_format", "rustc_session", @@ -4105,7 +4106,6 @@ dependencies = [ name = "rustc_lint_defs" version = "0.0.0" dependencies = [ - "rustc_ast", "rustc_data_structures", "rustc_error_messages", "rustc_hir_id", diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index f20b979588c57..d8b72997c729c 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -300,6 +300,7 @@ impl<'hir> LoweringContext<'_, 'hir> { DUMMY_NODE_ID, Some(self.tcx.features()), ShouldEmit::Nothing, + self.tcx.registered_tools(()).to_owned(), )); } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index d2aae3c07ca7e..1fb35f99d640b 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -150,7 +150,7 @@ struct LoweringContext<'a, 'hir> { impl<'a, 'hir> LoweringContext<'a, 'hir> { fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self { - let registered_tools = tcx.registered_tools(()).iter().map(|x| x.name).collect(); + let registered_tools = tcx.registered_tools(()).to_owned(); Self { // Pseudo-globals. tcx, diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml index 886df58e8d6f0..8c9d7d9df4ede 100644 --- a/compiler/rustc_attr_parsing/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -14,6 +14,7 @@ rustc_feature = { path = "../rustc_feature" } rustc_hir = { path = "../rustc_hir" } rustc_lexer = { path = "../rustc_lexer" } 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/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 4909e0d35173c..11aae5ce00d04 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -294,10 +294,11 @@ impl AttributeParser for NakedParser { let span = self.span?; + let tools = cx.tools.iter().map(|tool| tool.name).collect::>(); // only if we found a naked attribute do we do the somewhat expensive check 'outer: for other_attr in cx.all_attrs { for allowed_attr in ALLOW_LIST { - if other_attr.segments().next().is_some_and(|i| cx.tools.contains(&i.name)) { + if other_attr.segments().next().is_some_and(|i| tools.contains(&i.name)) { // effectively skips the error message being emitted below // if it's a tool attribute continue 'outer; 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..134773e13f94a --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/lint.rs @@ -0,0 +1,367 @@ +use std::marker::PhantomData; + +use rustc_ast::LitKind; +use rustc_hir::HashIgnoredAttrId; +use rustc_hir::attrs::{LintAttribute, LintAttributeKind, LintInstance}; +use rustc_hir::lints::AttributeLintKind; +use rustc_hir::target::GenericParamKind; +use rustc_middle::bug; +use rustc_session::lint::CheckLintNameResult; +use rustc_session::lint::builtin::{RENAMED_AND_REMOVED_LINTS, UNKNOWN_LINTS}; + +use super::prelude::*; +use crate::session_diagnostics::UnknownToolInScopedLint; + +pub(crate) trait Lint { + const KIND: LintAttributeKind; + const ATTR_SYMBOL: Symbol; +} +#[derive(Default)] +pub(crate) struct Expect; +pub(crate) type ExpectParser = LintParser; + +impl Lint for Expect { + const KIND: LintAttributeKind = LintAttributeKind::Expect; + const ATTR_SYMBOL: Symbol = sym::expect; +} + +#[derive(Default)] +pub(crate) struct Allow; +pub(crate) type AllowParser = LintParser; + +impl Lint for Allow { + const KIND: LintAttributeKind = LintAttributeKind::Allow; + const ATTR_SYMBOL: Symbol = sym::allow; +} +#[derive(Default)] +pub(crate) struct Warn; +pub(crate) type WarnParser = LintParser; + +impl Lint for Warn { + const KIND: LintAttributeKind = LintAttributeKind::Warn; + const ATTR_SYMBOL: Symbol = sym::warn; +} + +#[derive(Default)] +pub(crate) struct Deny; +pub(crate) type DenyParser = LintParser; + +impl Lint for Deny { + const KIND: LintAttributeKind = LintAttributeKind::Deny; + const ATTR_SYMBOL: Symbol = sym::deny; +} + +#[derive(Default)] +pub(crate) struct Forbid; +pub(crate) type ForbidParser = LintParser; + +impl Lint for Forbid { + const KIND: LintAttributeKind = LintAttributeKind::Forbid; + const ATTR_SYMBOL: Symbol = sym::forbid; +} + +#[derive(Default)] +pub(crate) struct LintParser { + lint_attrs: ThinVec, + attr_index: usize, + phantom: PhantomData, +} + +impl AttributeParser for LintParser { + const ATTRIBUTES: AcceptMapping = &[( + &[T::ATTR_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, T::ATTR_SYMBOL, this.attr_index) { + this.lint_attrs.push(lint_attr); + } + this.attr_index += 1; + }, + )]; + + 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<'_, '_, S>) -> Option { + if !self.lint_attrs.is_empty() { + Some(AttributeKind::LintAttribute { sub_attrs: self.lint_attrs, kind: T::KIND }) + } else { + None + } + } +} + +#[inline(always)] +fn validate_lint_attr( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + attr_name: Symbol, + attr_index: usize, +) -> Option { + let Some(list) = args.list() else { + cx.expected_list(cx.inner_span, args); + return None; + }; + let mut list = list.mixed().peekable(); + + let mut skip_reason_check = false; + let mut errored = false; + let mut reason = None; + let mut lint_instances = ThinVec::new(); + let mut lint_index = 0; + + while let Some(item) = list.next() { + let Some(meta_item) = item.meta_item() else { + cx.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() { + cx.expected_nv_as_last_argument(meta_item.span(), attr_name, sym::reason); + errored = true; + continue; + } + + let val_lit = nv_parser.value_as_lit(); + let LitKind::Str(reason_sym, _) = val_lit.kind else { + cx.expected_string_literal(nv_parser.value_span, Some(val_lit)); + errored = true; + continue; + }; + reason = Some(reason_sym); + } + ArgParser::NameValue(_) => { + cx.expected_specific_argument(meta_item.span(), &[sym::reason]); + errored = true; + } + ArgParser::List(list) => { + cx.expected_no_args(list.span); + errored = true; + } + ArgParser::NoArgs => { + let mut segments = meta_item.path().segments(); + + let Some(tool_or_name) = segments.next() else { + bug!("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) + }; + + if let Some(ids) = check_lint( + cx, + Symbol::intern(&name), + tool_name, + tool_span, + meta_item.span(), + lint_index, + ) { + lint_instances.extend(ids); + skip_reason_check = true + } else { + skip_reason_check = true + } + lint_index += 1; + } + } + } + if !skip_reason_check && !errored && lint_instances.is_empty() { + cx.warn_empty_attribute(cx.attr_span); + } + + (!errored).then_some(LintAttribute { + reason, + lint_instances, + attr_span: cx.attr_span, + attr_style: cx.attr_style, + attr_id: HashIgnoredAttrId { attr_id: cx.attr_id }, + attr_index, + }) +} + +fn check_lint( + cx: &mut AcceptContext<'_, '_, S>, + original_name: Symbol, + tool_name: Option, + tool_span: Option, + span: Span, + lint_index: usize, +) -> Option> { + let Some(lint_store) = &cx.sess.lint_store else { + bug!("lint_store required while parsing attributes"); + }; + let full_name = tool_name + .map(|tool| Symbol::intern(&format!("{tool}::{}", &original_name))) + .unwrap_or(original_name); + let mut lint_ids = Vec::new(); + match lint_store.check_lint_name(original_name.as_str(), tool_name, &cx.tools) { + CheckLintNameResult::Ok(ids) => { + for id in ids { + lint_ids.push(LintInstance::new(full_name, id.to_string(), span, lint_index)); + } + } + 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); + cx.emit_lint( + RENAMED_AND_REMOVED_LINTS, + AttributeLintKind::DeprecatedLintName { + name: full_name, + suggestion: span, + replace: new_lint_name, + }, + span, + ); + new_lint_name + } + }; + for id in ids { + lint_ids.push(LintInstance::new(full_name, id.to_string(), span, lint_index)); + } + } + + 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. + return None; + } + + CheckLintNameResult::NoTool => { + if cx.tools.is_empty() { + bug!("tools should never be empty") + } + 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(), + }); + return None; + } + + CheckLintNameResult::Renamed(replace) => { + cx.emit_lint( + RENAMED_AND_REMOVED_LINTS, + AttributeLintKind::RenamedLint { name: full_name, replace, suggestion: span }, + span, + ); + + // 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 ids = match lint_store.check_lint_name(replace.as_str(), None, &cx.tools) { + CheckLintNameResult::Ok(ids) => ids, + _ => panic!("renamed lint does not exist: {replace}"), + }; + + for id in ids { + lint_ids.push(LintInstance::new(full_name, id.to_string(), span, lint_index)); + } + } + + CheckLintNameResult::RenamedToolLint(new_name) => { + cx.emit_lint( + RENAMED_AND_REMOVED_LINTS, + AttributeLintKind::RenamedLint { + name: full_name, + replace: new_name, + suggestion: span, + }, + span, + ); + return None; + } + + CheckLintNameResult::Removed(reason) => { + cx.emit_lint( + RENAMED_AND_REMOVED_LINTS, + AttributeLintKind::RemovedLint { name: full_name, reason }, + span, + ); + return None; + } + + CheckLintNameResult::NoLint(suggestion) => { + cx.emit_lint( + UNKNOWN_LINTS, + AttributeLintKind::UnknownLint { name: full_name, suggestion, span }, + span, + ); + return None; + } + } + Some(lint_ids) +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 223c88972d75e..dbb1577bcc753 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -46,6 +46,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 802ee56f504b0..6d5bf0c3f9508 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -14,7 +14,7 @@ use rustc_hir::{AttrPath, HirId}; use rustc_parse::parser::Recovery; use rustc_session::Session; use rustc_session::lint::{Lint, LintId}; -use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_span::{AttrId, ErrorGuaranteed, Span, Symbol}; use crate::AttributeParser; // Glob imports to avoid big, bitrotty import lists @@ -35,6 +35,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::*; @@ -142,10 +143,14 @@ macro_rules! attribute_parsers { attribute_parsers!( pub(crate) static ATTRIBUTE_PARSERS = [ // tidy-alphabetical-start + AllowParser, BodyStabilityParser, ConfusablesParser, ConstStabilityParser, + DenyParser, DocParser, + ExpectParser, + ForbidParser, MacroUseParser, NakedParser, OnConstParser, @@ -155,6 +160,7 @@ attribute_parsers!( RustcCguTestAttributeParser, StabilityParser, UsedParser, + WarnParser, // tidy-alphabetical-end // tidy-alphabetical-start @@ -435,6 +441,8 @@ pub struct AcceptContext<'f, 'sess, S: Stage> { /// The name of the attribute we're currently accepting. pub(crate) attr_path: AttrPath, + + pub(crate) attr_id: AttrId, } impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> { @@ -656,6 +664,22 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { ) } + pub(crate) fn expected_nv_as_last_argument( + &self, + span: Span, + attr_name: Symbol, + name_value_key: Symbol, + ) -> ErrorGuaranteed { + self.emit_parse_error( + span, + AttributeParseErrorReason::ExpectedNameValueAsLastArgument { + span, + attr_name, + name_value_key, + }, + ) + } + pub(crate) fn warn_empty_attribute(&mut self, span: Span) { let attr_path = self.attr_path.clone().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 f75f63a0e811a..51586a8f2ccb0 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -8,6 +8,7 @@ use rustc_feature::{AttributeTemplate, Features}; use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::AttributeLintKind; use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Target}; +use rustc_middle::ty::RegisteredTools; use rustc_session::Session; use rustc_session::lint::{BuiltinLintDiag, LintId}; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; @@ -21,7 +22,7 @@ use crate::{Early, Late, OmitDoc, ShouldEmit}; /// Context created once, for example as part of the ast lowering /// context, through which all attributes can be lowered. pub struct AttributeParser<'sess, S: Stage = Late> { - pub(crate) tools: Vec, + pub(crate) tools: RegisteredTools, pub(crate) features: Option<&'sess Features>, pub(crate) sess: &'sess Session, pub(crate) stage: S, @@ -47,6 +48,8 @@ impl<'sess> AttributeParser<'sess, Early> { /// No diagnostics will be emitted when parsing limited. Lints are not emitted at all, while /// errors will be emitted as a delayed bugs. in other words, we *expect* attributes parsed /// with `parse_limited` to be reparsed later during ast lowering where we *do* emit the errors + /// + /// Due to this function not taking in RegisteredTools, *do not* use this for parsing any lint attributes pub fn parse_limited( sess: &'sess Session, attrs: &[ast::Attribute], @@ -68,6 +71,8 @@ impl<'sess> AttributeParser<'sess, Early> { /// This does the same as `parse_limited`, except it has a `should_emit` parameter which allows it to emit errors. /// Usually you want `parse_limited`, which emits no errors. + /// + /// Due to this function not taking in RegisteredTools, *do not* use this for parsing any lint attributes pub fn parse_limited_should_emit( sess: &'sess Session, attrs: &[ast::Attribute], @@ -86,6 +91,7 @@ impl<'sess> AttributeParser<'sess, Early> { target_node_id, features, should_emit, + RegisteredTools::default(), ); assert!(parsed.len() <= 1); parsed.pop() @@ -98,18 +104,18 @@ impl<'sess> AttributeParser<'sess, Early> { /// `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, target: Target, target_span: Span, target_node_id: NodeId, features: Option<&'sess Features>, emit_errors: ShouldEmit, + tools: RegisteredTools, ) -> Vec { - let mut p = - Self { features, tools: Vec::new(), parse_only, sess, stage: Early { emit_errors } }; + let mut p = Self { features, tools, parse_only, sess, stage: Early { emit_errors } }; p.parse_attribute_list( attrs, target_span, @@ -127,6 +133,32 @@ impl<'sess> AttributeParser<'sess, Early> { ) } + /// 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: RegisteredTools, + ) -> 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, + 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( @@ -193,7 +225,7 @@ impl<'sess> AttributeParser<'sess, Early> { ) -> T { let mut parser = Self { features, - tools: Vec::new(), + tools: RegisteredTools::default(), parse_only: None, sess, stage: Early { emit_errors }, @@ -209,6 +241,7 @@ impl<'sess> AttributeParser<'sess, Early> { if let Some(safety) = attr_safety { parser.check_attribute_safety(&attr_path, inner_span, safety, &mut emit_lint) } + let attr_id = sess.psess.attr_id_generator.mk_attr_id(); let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext { shared: SharedContext { cx: &mut parser, @@ -222,6 +255,7 @@ impl<'sess> AttributeParser<'sess, Early> { parsed_description, template, attr_path, + attr_id, }; parse_fn(&mut cx, args) } @@ -231,7 +265,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { pub fn new( sess: &'sess Session, features: &'sess Features, - tools: Vec, + tools: RegisteredTools, stage: S, ) -> Self { Self { features: Some(features), tools, parse_only: None, sess, stage } @@ -257,9 +291,9 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { /// /// `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, @@ -270,9 +304,9 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { let mut attr_paths: Vec> = Vec::new(); let mut early_parsed_state = EarlyParsedState::default(); - let mut finalizers: Vec<&FinalizeFn> = Vec::with_capacity(attrs.len()); + let mut finalizers: Vec<&FinalizeFn> = 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.has_name(expected) { @@ -381,6 +415,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { parsed_description: ParsedDescription::Attribute, template: &accept.template, attr_path: attr_path.clone(), + attr_id: attr.id, }; (accept.accept_fn)(&mut cx, &args); diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 987c0cb04c84d..38f321ddf3090 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -579,6 +579,11 @@ pub(crate) enum AttributeParseErrorReason<'a> { list: bool, }, ExpectedIdentifier, + ExpectedNameValueAsLastArgument { + span: Span, + attr_name: Symbol, + name_value_key: Symbol, + }, } /// A description of a thing that can be parsed using an attribute parser. @@ -759,6 +764,18 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { AttributeParseErrorReason::ExpectedIdentifier => { diag.span_label(self.span, "expected a valid identifier here"); } + AttributeParseErrorReason::ExpectedNameValueAsLastArgument { + span, + attr_name, + name_value_key, + } => { + diag.span_label( + span, + format!( + "the name value key `{name_value_key}` must come last in `#[{attr_name}]`" + ), + ); + } } if let Some(link) = self.template.docs { @@ -1030,3 +1047,14 @@ pub(crate) struct UnsupportedInstructionSet<'a> { pub instruction_set: Symbol, pub current_target: &'a TargetTuple, } + +#[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_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 225906dfba2de..eaf321c95abcb 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}; @@ -1187,7 +1187,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 76a9a6f9d03d9..2321224c37bc9 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1400,7 +1400,6 @@ impl InvocationCollectorNode for Box { ecx.ecfg.features, ecx.resolver.registered_tools(), ecx.current_expansion.lint_node_id, - &attrs, &items, ident.name, ); @@ -2172,6 +2171,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.cx.current_expansion.lint_node_id, Some(self.cx.ecfg.features), ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, + self.cx.resolver.registered_tools().to_owned(), ); let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span }; diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 91409108a7533..9822e4bea3b9a 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -22,7 +22,9 @@ 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, HashStable_Generic, Encodable, Decodable, PrintAttribute)] pub enum EiiImplResolution { @@ -894,6 +896,116 @@ impl fmt::Display for AutoDiffItem { } } +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct LintAttribute { + pub lint_instances: ThinVec, + pub attr_span: Span, + pub reason: Option, + pub attr_style: AttrStyle, + pub attr_id: HashIgnoredAttrId, + pub attr_index: usize, +} + +#[derive(Debug, Clone, PrintAttribute, Encodable, Decodable, HashStable_Generic)] +pub struct LintInstance { + span: Span, + lint_name: Symbol, + original_name: Option, + lint_index: usize, + kind: LintAttrTool, +} + +impl LintInstance { + pub fn new( + original_name: Symbol, + long_lint_name: String, + span: Span, + lint_index: usize, + ) -> Self { + let original_name = (original_name.as_str() != long_lint_name).then_some(original_name); + let mut tool_name = None; + + let lint_name = match long_lint_name.split_once("::") { + Some((new_tool_name, lint_name)) => { + tool_name = Some(Symbol::intern(new_tool_name)); + Symbol::intern(lint_name) + } + None => Symbol::intern(&long_lint_name), + }; + let kind = match tool_name { + Some(tool_name) => { + let full_lint = Symbol::intern(&format!("{tool_name}::{lint_name}",)); + LintAttrTool::Present { tool_name, full_lint } + } + None => LintAttrTool::NoTool, + }; + + Self { original_name, span, lint_index, lint_name, kind } + } + + pub fn full_lint(&self) -> Symbol { + match self.kind { + LintAttrTool::Present { full_lint, .. } => full_lint, + LintAttrTool::NoTool => 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.kind { + LintAttrTool::Present { tool_name, .. } => Symbol::intern( + full_original_lint_name + .as_str() + .trim_start_matches(tool_name.as_str()) + .trim_start_matches("::"), + ), + LintAttrTool::NoTool => full_original_lint_name, + } + } + + pub fn tool_name(&self) -> Option { + if let LintAttrTool::Present { tool_name, .. } = self.kind { Some(tool_name) } else { None } + } + + 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, HashStable_Generic)] +enum LintAttrTool { + Present { tool_name: Symbol, full_lint: Symbol }, + NoTool, +} + +#[derive(Clone, Copy, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum LintAttributeKind { + Allow, + Warn, + Deny, + Forbid, + Expect, +} + /// Represents parsed *built-in* inert attributes. /// /// ## Overview @@ -1097,6 +1209,11 @@ pub enum AttributeKind { /// Represents `#[linkage]`. Linkage(Linkage, Span), + /// Represents `#[allow]`, `#[expect]`, `#[warn]`, `#[deny]`, `#[forbid]` + LintAttribute { + kind: LintAttributeKind, + sub_attrs: 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 cd41a2b9b28c7..1e2b75092295c 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, + LintAttribute { .. } => 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..37b810c274dbf 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -17,6 +17,7 @@ use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; use rustc_target::spec::SanitizerSet; use thin_vec::ThinVec; +use crate::HashIgnoredAttrId; use crate::limit::Limit; /// This trait is used to print attributes in `rustc_hir_pretty`. @@ -191,7 +192,7 @@ macro_rules! print_tup { } print_tup!(A B C D E F G H); -print_skip!(Span, (), ErrorGuaranteed, AttrId); +print_skip!(Span, (), ErrorGuaranteed, AttrId, HashIgnoredAttrId); print_disp!(u8, u16, u32, u128, usize, bool, NonZero, Limit); print_debug!( Symbol, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index de00cbc8e7098..dd5e130082504 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1380,6 +1380,9 @@ 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::LintAttribute { 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 fd83f6f9ae1eb..b398a084f0749 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -15,10 +15,11 @@ use rustc_errors::{ Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, Subdiagnostic, listify, pluralize, struct_span_code_err, }; +use rustc_hir::attrs::AttributeKind; 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::{Attribute, 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 _; @@ -71,7 +72,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // let y: u32 = (x?).try_into().unwrap(); // + +++++++++++++++++++++ - if attr.span().desugaring_kind().is_none() { + let span = match attr { + Attribute::Unparsed(attr) => attr.span, + Attribute::Parsed(AttributeKind::LintAttribute { sub_attrs, .. }) => { + for attr in sub_attrs { + if attr.attr_span.desugaring_kind().is_none() { + return true; + } + } + return false; + } + Attribute::Parsed(AttributeKind::Deprecated { span, .. }) => *span, + Attribute::Parsed(attr) => bug!("can't get span of parsed attr: {:?}", attr), + }; + + if span.desugaring_kind().is_none() { return true; } } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 15addd2407857..f364b5a7caed4 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -116,11 +116,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/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 85b881cddc2b8..0cc1431cfe02a 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1902,9 +1902,10 @@ impl KeywordIdents { return; } - cx.emit_span_lint( + cx.sess().psess.buffer_lint( lint, ident.span, + CRATE_NODE_ID, BuiltinKeywordIdents { kw: ident, next: edition, suggestion: ident.span, prefix }, ); } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 971170aaba304..1af1ff427a0f1 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -13,17 +13,20 @@ use rustc_data_structures::sync; use rustc_data_structures::unord::UnordMap; use rustc_errors::{Diag, Diagnostic, LintBuffer, LintDiagnostic, MultiSpan}; use rustc_feature::Features; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::Res; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; -use rustc_hir::{Pat, PatKind}; +use rustc_hir::{Attribute, Pat, PatKind}; use rustc_middle::bug; use rustc_middle::lint::LevelAndSource; use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths}; use rustc_middle::ty::{self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode}; -use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintExpectationId, LintId}; +use rustc_session::lint::{ + CheckLintNameResult, FutureIncompatibleInfo, Lint, LintExpectationId, LintId, TargetLint, +}; use rustc_session::{DynLintStore, Session}; use rustc_span::edit_distance::find_best_match_for_names; use rustc_span::{Ident, Span, Symbol, sym}; @@ -68,26 +71,19 @@ impl DynLintStore for LintStore { rustc_session::LintGroup { name, lints, is_externally_loaded } })) } -} - -/// The target of the `by_name` map, which accounts for renaming/deprecation. -#[derive(Debug)] -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), + 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) + } - /// 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, + fn find_lints(&self, lint_name: &str) -> Option<&[LintId]> { + self.find_lints(lint_name) + } } struct LintAlias { @@ -102,29 +98,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 { @@ -303,6 +276,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)), @@ -392,7 +369,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::` @@ -925,7 +902,21 @@ impl<'tcx> LateContext<'tcx> { pub fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence { let has_attr = |id: hir::HirId| -> bool { for attr in self.tcx.hir_attrs(id) { - if attr.span().desugaring_kind().is_none() { + let span = match attr { + Attribute::Unparsed(attr) => attr.span, + Attribute::Parsed(AttributeKind::LintAttribute { sub_attrs, .. }) => { + for attr in sub_attrs { + if attr.attr_span.desugaring_kind().is_none() { + return true; + } + } + return false; + } + Attribute::Parsed(AttributeKind::Deprecated { span, .. }) => *span, + Attribute::Parsed(attr) => bug!("can't get span of parsed attr: {:?}", attr), + }; + + if span.desugaring_kind().is_none() { return true; } } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 0295df2feca56..a2ed2810cf157 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, TyCtxt}; 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::context::{EarlyContext, LintContext, LintStore}; @@ -48,13 +48,17 @@ impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, 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, None); + let push = self.context.builder.push(attrs, id, target_span); debug!("early context: enter_attrs({:?})", attrs); lint_callback!(self, check_attributes, attrs); @@ -73,24 +77,39 @@ impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast> } 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) { @@ -100,23 +119,38 @@ impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast> } 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) { @@ -128,10 +162,15 @@ impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast> // // 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) { @@ -140,16 +179,26 @@ impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast> } 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) { @@ -162,10 +211,15 @@ impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast> } 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) { @@ -174,10 +228,15 @@ impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast> } 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) { @@ -186,10 +245,15 @@ impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast> } 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) { @@ -209,25 +273,30 @@ impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast> } 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) { @@ -299,16 +368,16 @@ 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, 'tcx, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'ecx, 'tcx, T>) { - walk_list!(cx, visit_attribute, self.1); - walk_list!(cx, visit_item, self.2); + //walk_list!(cx, visit_attribute, self.0); + walk_list!(cx, visit_item, self.1); } } @@ -356,7 +425,7 @@ fn check_ast_node_inner<'a, T: EarlyLintPass>( ) { let mut cx = EarlyContextAndPass { context, tcx, 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/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 31a6351c6e70b..f8ab78a956c3e 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -471,5 +471,25 @@ pub fn decorate_attribute_lint( &AttributeLintKind::MissingOptionsForOnConst => { lints::MissingOptionsForOnConstAttr.decorate_lint(diag) } + + &AttributeLintKind::RenamedLint { name, replace, suggestion } => lints::RenamedLint { + name, + replace, + suggestion: lints::RenamedLintSuggestion::WithSpan { suggestion, replace }, + } + .decorate_lint(diag), + &AttributeLintKind::DeprecatedLintName { name, suggestion, replace } => { + lints::DeprecatedLintName { name, suggestion, replace }.decorate_lint(diag) + } + &AttributeLintKind::RemovedLint { name, ref reason } => { + lints::RemovedLint { name, reason }.decorate_lint(diag) + } + &AttributeLintKind::UnknownLint { name, span, suggestion } => lints::UnknownLint { + name, + suggestion: suggestion.map(|(replace, from_rustc)| { + lints::UnknownLintSuggestion::WithSpan { suggestion: span, replace, from_rustc } + }), + } + .decorate_lint(diag), } } 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 481e116d06e01..28dceb0c5c069 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -30,19 +30,9 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option) { let fulfilled_expectations = tcx.dcx().steal_fulfilled_expectation_ids(); // Turn a `LintExpectationId` into a `(AttrId, lint_index)` pair. - let canonicalize_id = |expect_id: &LintExpectationId| { - match *expect_id { - LintExpectationId::Unstable { attr_id, lint_index: Some(lint_index) } => { - (attr_id, lint_index) - } - LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => { - // We are an `eval_always` query, so looking at the attribute's `AttrId` is ok. - let attr_id = tcx.hir_attrs(hir_id)[attr_index as usize].id(); - - (attr_id, lint_index) - } - _ => panic!("fulfilled expectations must have a lint index"), - } + let canonicalize_id = |expect_id: &LintExpectationId| match *expect_id { + LintExpectationId::Unstable { attr_id, lint_index, .. } => (attr_id, lint_index), + LintExpectationId::Stable { attr_id, lint_index, .. } => (attr_id, lint_index), }; let fulfilled_expectations: FxHashSet<_> = diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index a134b623d7bd4..2ec33cee2a4d0 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -1,11 +1,13 @@ 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, Diagnostic, MultiSpan, msg}; use rustc_feature::{Features, GateIssue}; -use rustc_hir::HirId; +use rustc_hir::attrs::{AttributeKind, LintAttribute, LintAttributeKind, LintInstance}; use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{Attribute, HirId, Target}; use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::hir::nested_filter; @@ -20,25 +22,36 @@ use rustc_session::lint::builtin::{ self, FORBIDDEN_LINT_GROUPS, RENAMED_AND_REMOVED_LINTS, SINGLE_USE_LIFETIMES, UNFULFILLED_LINT_EXPECTATIONS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES, }; -use rustc_session::lint::{Level, Lint, LintExpectationId, LintId}; +use rustc_session::lint::{ + CheckLintNameResult, Level, Lint, LintExpectationId, LintId, TargetLint, +}; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use tracing::{debug, instrument}; use {rustc_ast as ast, rustc_hir as hir}; 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, - UnknownLintSuggestion, + DeprecatedLintNameFromCommandLine, IgnoredUnlessCrateSpecified, 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. @@ -389,7 +402,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, None); + 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.to_owned(), + ); + + builder.add(&parsed_crate_attrs, true, None); builder } @@ -419,14 +444,31 @@ impl<'s> LintLevelsBuilder<'s, TopDown> { pub(crate) fn push( &mut self, attrs: &[ast::Attribute], - is_crate_node: bool, - source_hir_id: Option, + 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 }); + let attrs = if !attrs.is_empty() { + 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.clone(), + ) + } else { + vec![] + }; - self.add(attrs, is_crate_node, source_hir_id); + let is_crate_node = node_id == ast::CRATE_NODE_ID; + + self.add(&attrs, is_crate_node, None); if self.provider.current_specs().is_empty() { self.provider.sets.list.pop(); @@ -477,7 +519,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { .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 }; @@ -637,14 +679,33 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { }; } - fn add( + fn simple_add( &mut self, - attrs: &[impl AttributeExt], - is_crate_node: bool, - source_hir_id: Option, + level: Level, + lint: &LintInstance, + reason: Option, + expect_lint_id: Option, ) { - let sess = self.sess; - for (attr_index, attr) in attrs.iter().enumerate() { + // If this function returns none, it means the attribute parser has already emitted appropriate errors + + let src = + LintLevelSource::Node { name: lint.original_lint_name(), span: lint.span(), reason }; + + 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, + ), + }; + + if self.check_gated_lint(*id, lint.span(), false) { + self.insert_spec(*id, LevelAndSource { level, lint_id: expect_lint_id, src }); + } + } + + fn add(&mut self, attrs: &[hir::Attribute], is_crate_node: bool, source_hir_id: Option) { + for attr in attrs { if attr.is_automatically_derived_attr() { self.insert( LintId::of(SINGLE_USE_LIFETIMES), @@ -670,242 +731,57 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { continue; } - let (level, lint_id) = match Level::from_attr(attr) { - None => continue, - // This is the only lint level with a `LintExpectationId` that can be created from - // an attribute. - Some((Level::Expect, Some(unstable_id))) if let Some(hir_id) = source_hir_id => { - let LintExpectationId::Unstable { lint_index: None, attr_id: _ } = unstable_id - else { - bug!("stable id Level::from_attr") - }; - - let stable_id = LintExpectationId::Stable { - hir_id, - attr_index: attr_index.try_into().unwrap(), - lint_index: None, - }; - - (Level::Expect, Some(stable_id)) - } - Some((lvl, id)) => (lvl, id), - }; - - 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()]` + let Attribute::Parsed(AttributeKind::LintAttribute { kind, sub_attrs }) = attr else { 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() { - let mut lint_id = lint_id; - if let Some(id) = &mut lint_id { - id.set_lint_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) - } - - 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; - } - - 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; - } - - 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 }, + for LintAttribute { reason, lint_instances, attr_id, attr_index, .. } in sub_attrs { + 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 => { + let reason = reason.to_owned(); + 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 = match source_hir_id { + None => LintExpectationId::Unstable { attr_id, lint_index }, + Some(hir_id) => LintExpectationId::Stable { + hir_id, + attr_id, + lint_index, + attr_index, + }, + }; + + self.simple_add( + Level::Expect, + lint, + reason.to_owned(), + Some(expectation_id), ); - } - // 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)) - } - - 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 }, + 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(), + ), ); } - continue; - } - 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 src = LintLevelSource::Node { name, span: sp, reason }; - for &id in ids { - if self.check_gated_lint(id, sp, false) { - self.insert_spec(id, LevelAndSource { level, lint_id, src }); - } - } - - // 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, - ), - ); + for lint in lint_instances { + self.simple_add(level, lint, reason.to_owned(), None); } } } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 34276eb76cf39..0342e8464fe5b 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -130,7 +130,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::{decorate_attribute_lint, decorate_builtin_lint}; pub use early::{EarlyCheckNode, check_ast_node}; pub use late::{check_crate, late_lint_mod, unerased_lint_store}; @@ -138,7 +138,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 bd7ce30e2ccad..58324d6650220 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -475,7 +475,7 @@ pub(crate) enum BuiltinEllipsisInclusiveRangePatternsLint { }, } -#[derive(Diagnostic)] +#[derive(LintDiagnostic)] #[diag("`{$kw}` is a keyword in the {$next} edition")] pub(crate) struct BuiltinKeywordIdents { pub kw: Ident, @@ -1235,13 +1235,13 @@ pub(crate) struct OverruledAttributeLint<'a> { pub sub: OverruledAttributeSub, } -#[derive(Diagnostic)] +#[derive(LintDiagnostic)] #[diag("lint name `{$name}` is deprecated and may not have an effect in the future")] -pub(crate) struct DeprecatedLintName<'a> { - pub name: String, +pub(crate) struct DeprecatedLintName { + pub name: Symbol, #[suggestion("change it to", code = "{replace}", applicability = "machine-applicable")] pub suggestion: Span, - pub replace: &'a str, + pub replace: Symbol, } #[derive(Diagnostic)] @@ -1254,42 +1254,42 @@ pub(crate) struct DeprecatedLintNameFromCommandLine<'a> { pub requested_level: RequestedLevel<'a>, } -#[derive(Diagnostic)] +#[derive(LintDiagnostic)] #[diag("lint `{$name}` has been renamed to `{$replace}`")] -pub(crate) struct RenamedLint<'a> { - pub name: &'a str, - pub replace: &'a str, +pub(crate) struct RenamedLint { + pub name: Symbol, + pub replace: Symbol, #[subdiagnostic] - pub suggestion: RenamedLintSuggestion<'a>, + pub suggestion: RenamedLintSuggestion, } #[derive(Subdiagnostic)] -pub(crate) enum RenamedLintSuggestion<'a> { +pub(crate) enum RenamedLintSuggestion { #[suggestion("use the new name", code = "{replace}", applicability = "machine-applicable")] WithSpan { #[primary_span] suggestion: Span, - replace: &'a str, + replace: Symbol, }, #[help("use the new name `{$replace}`")] - WithoutSpan { replace: &'a str }, + WithoutSpan { replace: Symbol }, } #[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)] +#[derive(LintDiagnostic)] #[diag("lint `{$name}` has been removed: {$reason}")] pub(crate) struct RemovedLint<'a> { - pub name: &'a str, + pub name: Symbol, pub reason: &'a str, } @@ -1302,10 +1302,10 @@ pub(crate) struct RemovedLintFromCommandLine<'a> { pub requested_level: RequestedLevel<'a>, } -#[derive(Diagnostic)] +#[derive(LintDiagnostic)] #[diag("unknown lint: `{$name}`")] pub(crate) struct UnknownLint { - pub name: String, + pub name: Symbol, #[subdiagnostic] pub suggestion: Option, } diff --git a/compiler/rustc_lint_defs/Cargo.toml b/compiler/rustc_lint_defs/Cargo.toml index c8201d5ea8ccc..2ca62f7fa8cdc 100644 --- a/compiler/rustc_lint_defs/Cargo.toml +++ b/compiler/rustc_lint_defs/Cargo.toml @@ -5,7 +5,6 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_error_messages = { path = "../rustc_error_messages" } rustc_hir_id = { path = "../rustc_hir_id" } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 1492df50a418a..e243501d628c8 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -1,8 +1,6 @@ use std::borrow::Cow; use std::fmt::Display; -use rustc_ast::AttrId; -use rustc_ast::attr::AttributeExt; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::stable_hasher::{ HashStable, StableCompare, StableHasher, ToStableHashKey, @@ -12,7 +10,7 @@ use rustc_hir_id::{HashStableContext, HirId, ItemLocalId}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::def_id::DefPathHash; pub use rustc_span::edition::Edition; -use rustc_span::{Ident, Span, Symbol, sym}; +use rustc_span::{AttrId, Ident, Span, Symbol}; use serde::{Deserialize, Serialize}; pub use self::Level::*; @@ -107,12 +105,12 @@ pub enum Applicability { pub enum LintExpectationId { /// Used for lints emitted during the `EarlyLintPass`. This id is not /// hash stable and should not be cached. - Unstable { attr_id: AttrId, lint_index: Option }, + Unstable { attr_id: AttrId, lint_index: u16 }, /// The [`HirId`] that the lint expectation is attached to. This id is /// stable and can be cached. The additional index ensures that nodes with /// several expectations can correctly match diagnostics to the individual /// expectation. - Stable { hir_id: HirId, attr_index: u16, lint_index: Option }, + Stable { hir_id: HirId, attr_id: AttrId, attr_index: u16, lint_index: u16 }, } impl LintExpectationId { @@ -123,14 +121,14 @@ impl LintExpectationId { } } - pub fn get_lint_index(&self) -> Option { + pub fn get_lint_index(&self) -> u16 { let (LintExpectationId::Unstable { lint_index, .. } | LintExpectationId::Stable { lint_index, .. }) = self; *lint_index } - pub fn set_lint_index(&mut self, new_lint_index: Option) { + pub fn set_lint_index(&mut self, new_lint_index: u16) { let (LintExpectationId::Unstable { lint_index, .. } | LintExpectationId::Stable { lint_index, .. }) = self; @@ -142,7 +140,7 @@ impl HashStable for LintExpectationId { #[inline] fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { match self { - LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => { + LintExpectationId::Stable { hir_id, attr_index, lint_index, .. } => { hir_id.hash_stable(hcx, hasher); attr_index.hash_stable(hcx, hasher); lint_index.hash_stable(hcx, hasher); @@ -162,7 +160,7 @@ impl ToStableHashKey for LintExpectationId { #[inline] fn to_stable_hash_key(&self, hcx: &HCX) -> Self::KeyType { match self { - LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => { + LintExpectationId::Stable { hir_id, attr_index, lint_index, .. } => { let (def_path_hash, lint_idx) = hir_id.to_stable_hash_key(hcx); (def_path_hash, lint_idx, *attr_index, *lint_index) } @@ -247,35 +245,6 @@ impl Level { } } - /// Converts an `Attribute` to a level. - pub fn from_attr(attr: &impl AttributeExt) -> Option<(Self, Option)> { - attr.name().and_then(|name| Self::from_symbol(name, || Some(attr.id()))) - } - - /// Converts a `Symbol` to a level. - pub fn from_symbol( - s: Symbol, - id: impl FnOnce() -> Option, - ) -> Option<(Self, Option)> { - match s { - sym::allow => Some((Level::Allow, None)), - sym::expect => { - if let Some(attr_id) = id() { - Some(( - Level::Expect, - Some(LintExpectationId::Unstable { attr_id, lint_index: None }), - )) - } else { - None - } - } - sym::warn => Some((Level::Warn, None)), - sym::deny => Some((Level::Deny, None)), - sym::forbid => Some((Level::Forbid, None)), - _ => None, - } - } - pub fn to_cmd_flag(self) -> &'static str { match self { Level::Warn => "-W", @@ -583,6 +552,26 @@ 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, +} + /// Identifies a lint known to the compiler. #[derive(Clone, Copy, Debug)] pub struct LintId { @@ -855,6 +844,25 @@ pub enum AttributeLintKind { }, MissingOptionsForOnUnimplemented, MissingOptionsForOnConst, + RenamedLint { + name: Symbol, + replace: Symbol, + suggestion: Span, + }, + DeprecatedLintName { + name: Symbol, + suggestion: Span, + replace: Symbol, + }, + RemovedLint { + name: Symbol, + reason: String, + }, + UnknownLint { + name: Symbol, + span: Span, + suggestion: Option<(Symbol, bool)>, + }, } #[derive(Debug, Clone, HashStable_Generic)] @@ -863,6 +871,31 @@ pub enum FormatWarning { InvalidSpecifier { name: String, span: Span }, } +#[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), + /// Lint that previously was part of rustc, but now is part of external lint tool + 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, +} + pub type RegisteredTools = FxIndexSet; /// Declares a static item of type `&'static Lint`. diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index b10df60e0f75b..a5c7bda961646 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -85,7 +85,8 @@ use std::mem; use interpret::ErrorHandled; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::HirId; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{Attribute, HirId}; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::middle::region; use rustc_middle::mir::{self, *}; @@ -93,7 +94,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::source_map::Spanned; use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; @@ -1299,7 +1299,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { break; } - if self.tcx.hir_attrs(id).iter().any(|attr| Level::from_attr(attr).is_some()) { + if self + .tcx + .hir_attrs(id) + .iter() + .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::LintAttribute { .. }))) + { // 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 dacb02afe1612..e3e3d002ffe71 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -10,7 +10,7 @@ use std::collections::hash_map::Entry; use std::slice; use rustc_abi::{Align, ExternAbi, Size}; -use rustc_ast::{AttrStyle, MetaItemKind, ast}; +use rustc_ast::ast; use rustc_attr_parsing::{AttributeParser, Late}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::thin_vec::ThinVec; @@ -22,8 +22,8 @@ use rustc_feature::{ }; use rustc_hir::attrs::diagnostic::Directive; use rustc_hir::attrs::{ - AttributeKind, DocAttribute, DocInline, EiiDecl, EiiImpl, EiiImplResolution, InlineAttr, - MirDialect, MirPhase, ReprAttr, SanitizerSet, + AttributeKind, CrateType, DocAttribute, DocInline, EiiDecl, EiiImpl, EiiImplResolution, + InlineAttr, LintAttribute, MirDialect, MirPhase, ReprAttr, SanitizerSet, }; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalModDefId; @@ -41,7 +41,6 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, TyCtxt, TypingMode}; use rustc_middle::{bug, span_bug}; -use rustc_session::config::CrateType; use rustc_session::lint; use rustc_session::lint::builtin::{ CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, @@ -141,7 +140,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let mut seen = FxHashMap::default(); let attrs = self.tcx.hir_attrs(hir_id); for attr in attrs { - let mut style = None; match attr { Attribute::Parsed(AttributeKind::ProcMacro(_)) => { self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike) @@ -233,6 +231,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::DoNotRecommend{attr_span}) => {self.check_do_not_recommend(*attr_span, hir_id, target, item)}, Attribute::Parsed(AttributeKind::OnUnimplemented{span, directive}) => {self.check_diagnostic_on_unimplemented(*span, hir_id, target,directive.as_deref())}, Attribute::Parsed(AttributeKind::OnConst{span, ..}) => {self.check_diagnostic_on_const(*span, hir_id, target, item)} + Attribute::Parsed(AttributeKind::LintAttribute { sub_attrs, .. }) => self.check_lint_attr(hir_id, sub_attrs), Attribute::Parsed( // tidy-alphabetical-start AttributeKind::RustcAllowIncoherentImpl(..) @@ -387,18 +386,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::WindowsSubsystem(..) // tidy-alphabetical-end ) => { /* do nothing */ } - Attribute::Unparsed(attr_item) => { - style = Some(attr_item.style); + Attribute::Unparsed(_) => { match attr.path().as_slice() { - [ - // ok - sym::allow - | sym::expect - | sym::warn - | sym::deny - | sym::forbid, - .. - ] => {} [name, rest@..] => { match BUILTIN_ATTRIBUTE_MAP.get(name) { Some(_) => { @@ -480,8 +469,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { &mut seen, ); } - - self.check_unused_attribute(hir_id, attr, style) + self.check_unused_attribute(hir_id, attr) } self.check_repr(attrs, span, target, item, hir_id); @@ -1592,86 +1580,74 @@ 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) - }) - { - 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( + fn check_lint_attr(&self, hir_id: HirId, sub_attrs: &[LintAttribute]) { + for LintAttribute { attr_span, lint_instances, attr_style, .. } in sub_attrs { + if !lint_instances.iter().any(|id| id.lint_name() == sym::linker_messages) { + return; + }; + let note = 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; - } else { - let never_needs_link = self - .tcx - .crate_types() - .iter() - .all(|kind| matches!(kind, CrateType::Rlib | CrateType::StaticLib)); - if never_needs_link { - errors::UnusedNote::LinkerMessagesBinaryCrateOnly - } else { - return; + *attr_span, + errors::OuterCrateLevelAttr { + suggestion: errors::OuterCrateLevelAttrSuggestion { bang_position }, + }, + ) } - } - } else if attr.has_name(sym::default_method_body_is_const) { - errors::UnusedNote::DefaultMethodBodyConst - } else { + ast::AttrStyle::Inner => self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + *attr_span, + errors::InnerCrateLevelAttr, + ), + }; return; + } else { + let never_needs_link = self + .tcx + .crate_types() + .iter() + .all(|kind| matches!(kind, CrateType::Rlib | CrateType::StaticLib)); + if never_needs_link { + errors::UnusedNote::LinkerMessagesBinaryCrateOnly + } 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, hir_id, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 0cf0d1a5c80ff..59d59a8523690 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -305,8 +305,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_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 81820e049716b..ac50e491158db 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -146,7 +146,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { let mut parser = AttributeParser::<'_, Early>::new( &self.resolver.tcx.sess, self.resolver.tcx.features(), - Vec::new(), + self.resolver.tcx().registered_tools(()).to_owned(), Early { emit_errors: ShouldEmit::Nothing }, ); let attrs = parser.parse_attribute_list( diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index dc18b05c75763..8f3e9bbd9da17 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -40,7 +40,7 @@ use crate::config::{ Input, InstrumentCoverage, OptLevel, OutFileName, OutputType, SwitchWithOptPath, }; use crate::filesearch::FileSearch; -use crate::lint::LintId; +use crate::lint::{CheckLintNameResult, LintId, RegisteredTools}; use crate::parse::{ParseSess, add_feature_diagnostics}; use crate::search_paths::SearchPath; use crate::{errors, filesearch, lint}; @@ -81,6 +81,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/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index fa2951d919346..9654a5cba58a4 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -586,7 +586,7 @@ impl EarlyLintPass for PostExpansionEarlyAttributes { if matches!(name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) { allow_attributes_without_reason::check(cx, name, items, attr); } - if is_lint_level(name, attr.id) { + if is_lint_level(name) { blanket_clippy_restriction_lints::check(cx, name, items); } if items.is_empty() || !attr.has_name(sym::deprecated) { 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 6ee3290fa761d..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, || Some(attr.id)).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 9a1e315ae5306..c025b47dc787d 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.id)) + && attr.name().is_some_and(|name| is_lint_level(name)) { 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 7b66f91f6c073..a43ecab5d1b25 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/utils.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/utils.rs @@ -1,9 +1,9 @@ use clippy_utils::macros::{is_panic, macro_backtrace}; -use rustc_ast::{AttrId, MetaItemInner}; +use rustc_ast::{MetaItemInner}; use rustc_hir::{ Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, }; -use rustc_lint::{LateContext, Level}; +use rustc_lint::{LateContext}; use rustc_middle::ty; use rustc_span::sym; use rustc_span::symbol::Symbol; @@ -16,8 +16,8 @@ pub(super) fn is_word(nmi: &MetaItemInner, expected: Symbol) -> bool { } } -pub(super) fn is_lint_level(symbol: Symbol, attr_id: AttrId) -> bool { - Level::from_symbol(symbol, || Some(attr_id)).is_some() +pub(super) fn is_lint_level(symbol: Symbol) -> bool { + [sym::allow, sym::deny, sym::expect, sym::forbid, sym::warn].contains(&symbol) } pub(super) fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index 17e11b8b281d9..ab4e3b719ee5b 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::{IntoSpan as _, SpanRangeExt, snippet, snippet_block_with_applicability}; use clippy_utils::{can_use_if_let_chains, 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::source_map::SourceMap; use rustc_span::{BytePos, Span, Symbol}; @@ -238,19 +239,28 @@ impl CollapsibleIf { !span_contains_non_whitespace(cx, span, self.lint_commented_code) }, - [attr] - if matches!(Level::from_attr(attr), 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::LintAttribute { + kind: LintAttributeKind::Expect, + sub_attrs, + }), + ] => { + sub_attrs + .into_iter() + .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 04e4f379e37c1..216b9f9b8d398 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,18 +180,20 @@ fn check_final_expr<'tcx>( // actually fulfill the expectation (clippy::#12998) match cx.tcx.hir_attrs(expr.hir_id) { [] => {}, - [attr] => { - if matches!(Level::from_attr(attr), 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::LintAttribute { + kind: LintAttributeKind::Expect, + sub_attrs, + }), + ] => { + let lint = &sub_attrs[0].lint_instances[0]; + if !lint.tool_is_named(sym::clippy) { + return; + } + if 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/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 72493f557dfc2..84e3a8c67af8b 100644 --- a/tests/pretty/delegation-inherit-attributes.pp +++ b/tests/pretty/delegation-inherit-attributes.pp @@ -1,14 +1,16 @@ +#![attr = LintAttribute {kind: Allow, +sub_attrs: [LintAttribute {lint_instances: [LintInstance {lint_name: "incomplete_features", +lint_index: 0, kind: NoTool}], attr_style: Inner, attr_index: 0}]}] +#![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..9e046b8609b2e 100644 --- a/tests/pretty/delegation-inline-attribute.pp +++ b/tests/pretty/delegation-inline-attribute.pp @@ -1,12 +1,14 @@ -//@ pretty-compare-only -//@ pretty-mode:hir -//@ pp-exact:delegation-inline-attribute.pp - -#![allow(incomplete_features)] +#![attr = LintAttribute {kind: Allow, +sub_attrs: [LintAttribute {lint_instances: [LintInstance {lint_name: "incomplete_features", +lint_index: 0, kind: NoTool}], attr_style: Inner, attr_index: 0}]}] #![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 efa643313e6b4..e343cf8d5ca4f 100644 --- a/tests/pretty/hir-delegation.pp +++ b/tests/pretty/hir-delegation.pp @@ -1,12 +1,14 @@ -//@ pretty-compare-only -//@ pretty-mode:hir -//@ pp-exact:hir-delegation.pp - -#![allow(incomplete_features)] +#![attr = LintAttribute {kind: Allow, +sub_attrs: [LintAttribute {lint_instances: [LintInstance {lint_name: "incomplete_features", +lint_index: 0, kind: NoTool}], attr_style: Inner, attr_index: 0}]}] #![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..5fd9076f38f7f 100644 --- a/tests/pretty/hir-lifetimes.pp +++ b/tests/pretty/hir-lifetimes.pp @@ -1,13 +1,49 @@ +#![attr = LintAttribute {kind: Allow, +sub_attrs: [LintAttribute {lint_instances: [LintInstance {lint_name: "unused_imports", +original_name: "unused", lint_index: 0, kind: NoTool}, +LintInstance {lint_name: "unused_variables", original_name: "unused", +lint_index: 0, kind: NoTool}, LintInstance {lint_name: "unused_visibilities", +original_name: "unused", lint_index: 0, kind: NoTool}, +LintInstance {lint_name: "unused_assignments", original_name: "unused", +lint_index: 0, kind: NoTool}, LintInstance {lint_name: "dead_code", +original_name: "unused", lint_index: 0, kind: NoTool}, +LintInstance {lint_name: "unused_mut", original_name: "unused", lint_index: 0, +kind: NoTool}, LintInstance {lint_name: "unreachable_code", +original_name: "unused", lint_index: 0, kind: NoTool}, +LintInstance {lint_name: "unreachable_patterns", original_name: "unused", +lint_index: 0, kind: NoTool}, LintInstance {lint_name: "unused_must_use", +original_name: "unused", lint_index: 0, kind: NoTool}, +LintInstance {lint_name: "unused_unsafe", original_name: "unused", +lint_index: 0, kind: NoTool}, LintInstance {lint_name: "path_statements", +original_name: "unused", lint_index: 0, kind: NoTool}, +LintInstance {lint_name: "unused_attributes", original_name: "unused", +lint_index: 0, kind: NoTool}, LintInstance {lint_name: "unused_macros", +original_name: "unused", lint_index: 0, kind: NoTool}, +LintInstance {lint_name: "unused_macro_rules", original_name: "unused", +lint_index: 0, kind: NoTool}, LintInstance {lint_name: "unused_allocation", +original_name: "unused", lint_index: 0, kind: NoTool}, +LintInstance {lint_name: "unused_doc_comments", original_name: "unused", +lint_index: 0, kind: NoTool}, LintInstance {lint_name: "unused_extern_crates", +original_name: "unused", lint_index: 0, kind: NoTool}, +LintInstance {lint_name: "unused_features", original_name: "unused", +lint_index: 0, kind: NoTool}, LintInstance {lint_name: "unused_labels", +original_name: "unused", lint_index: 0, kind: NoTool}, +LintInstance {lint_name: "unused_parens", original_name: "unused", +lint_index: 0, kind: NoTool}, LintInstance {lint_name: "unused_braces", +original_name: "unused", lint_index: 0, kind: NoTool}, +LintInstance {lint_name: "redundant_semicolons", original_name: "unused", +lint_index: 0, kind: NoTool}, LintInstance {lint_name: "map_unit_fn", +original_name: "unused", lint_index: 0, kind: NoTool}], attr_style: Inner, +attr_index: 0}]}] +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..8d768ae952449 100644 --- a/tests/pretty/pin-ergonomics-hir.pp +++ b/tests/pretty/pin-ergonomics-hir.pp @@ -1,12 +1,15 @@ -//@ pretty-compare-only -//@ pretty-mode:hir -//@ pp-exact:pin-ergonomics-hir.pp - -#![allow(dead_code, incomplete_features)] #![attr = Feature([pin_ergonomics#0])] +#![attr = LintAttribute {kind: Allow, +sub_attrs: [LintAttribute {lint_instances: [LintInstance {lint_name: "dead_code", +lint_index: 0, kind: NoTool}, LintInstance {lint_name: "incomplete_features", +lint_index: 1, kind: NoTool}], attr_style: Inner, attr_index: 0}]}] 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/doctest/dead-code-items.stdout b/tests/rustdoc-ui/doctest/dead-code-items.stdout index ecfe09f09ce26..099056df72b29 100644 --- a/tests/rustdoc-ui/doctest/dead-code-items.stdout +++ b/tests/rustdoc-ui/doctest/dead-code-items.stdout @@ -1,8 +1,14 @@ -running 13 tests +running 2 tests +test $DIR/dead-code-items.rs - A::field (line 41) - compile ... ok +test $DIR/dead-code-items.rs - U::field (line 57) - compile ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 11 tests test $DIR/dead-code-items.rs - A (line 34) - compile ... ok test $DIR/dead-code-items.rs - A (line 90) - compile ... ok -test $DIR/dead-code-items.rs - A::field (line 41) - compile ... FAILED test $DIR/dead-code-items.rs - A::method (line 96) - compile ... ok test $DIR/dead-code-items.rs - C (line 24) - compile ... FAILED test $DIR/dead-code-items.rs - Enum (line 72) - compile ... FAILED @@ -11,27 +17,10 @@ test $DIR/dead-code-items.rs - MyTrait (line 105) - compile ... FAILED test $DIR/dead-code-items.rs - MyTrait::my_trait_fn (line 112) - compile ... FAILED test $DIR/dead-code-items.rs - S (line 16) - compile ... ok test $DIR/dead-code-items.rs - U (line 50) - compile ... ok -test $DIR/dead-code-items.rs - U::field (line 57) - compile ... FAILED test $DIR/dead-code-items.rs - U::field2 (line 63) - compile ... ok failures: ----- $DIR/dead-code-items.rs - A::field (line 41) stdout ---- -error: trait `DeadCodeInField` is never used - --> $DIR/dead-code-items.rs:42:7 - | -LL | trait DeadCodeInField {} - | ^^^^^^^^^^^^^^^ - | -note: the lint level is defined here - --> $DIR/dead-code-items.rs:40:9 - | -LL | #![deny(dead_code)] - | ^^^^^^^^^ - -error: aborting due to 1 previous error - -Couldn't compile the test. ---- $DIR/dead-code-items.rs - C (line 24) stdout ---- error: unused variable: `unused_error` --> $DIR/dead-code-items.rs:25:5 @@ -115,33 +104,15 @@ LL | #![deny(warnings)] error: aborting due to 1 previous error -Couldn't compile the test. ----- $DIR/dead-code-items.rs - U::field (line 57) stdout ---- -error: trait `DeadCodeInUnionField` is never used - --> $DIR/dead-code-items.rs:58:7 - | -LL | trait DeadCodeInUnionField {} - | ^^^^^^^^^^^^^^^^^^^^ - | -note: the lint level is defined here - --> $DIR/dead-code-items.rs:56:9 - | -LL | #![deny(dead_code)] - | ^^^^^^^^^ - -error: aborting due to 1 previous error - Couldn't compile the test. failures: - $DIR/dead-code-items.rs - A::field (line 41) $DIR/dead-code-items.rs - C (line 24) $DIR/dead-code-items.rs - Enum (line 72) $DIR/dead-code-items.rs - Enum::Variant1 (line 79) $DIR/dead-code-items.rs - MyTrait (line 105) $DIR/dead-code-items.rs - MyTrait::my_trait_fn (line 112) - $DIR/dead-code-items.rs - U::field (line 57) -test result: FAILED. 6 passed; 7 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +test result: FAILED. 6 passed; 5 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME all doctests ran in $TIME; merged doctests compilation took $TIME 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..1d7a974e24b0c --- /dev/null +++ b/tests/rustdoc-ui/lints/renamed-lint-still-applies-2.stderr @@ -0,0 +1,34 @@ +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)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[deny(rustdoc::broken_intra_doc_links)]` implied by `#[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)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[deny(rustdoc::bare_urls)]` implied by `#[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..08239ac686a67 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 @@ - +