diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 5e9674b7a042..df2eeec74cf2 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -41,7 +41,7 @@ use rustc_ast::node_id::NodeMap; use rustc_ast::visit::Visitor; use rustc_ast::{self as ast, *}; -use rustc_attr_parsing::{AttributeParser, EmitAttribute, Late, OmitDoc}; +use rustc_attr_parsing::{AttributeParser, Late, OmitDoc}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::sorted_map::SortedMap; @@ -52,7 +52,7 @@ use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId}; use rustc_hir::definitions::PerParentDisambiguatorState; -use rustc_hir::lints::{AttributeLint, DelayedLint, DynAttribute}; +use rustc_hir::lints::DelayedLint; use rustc_hir::{ self as hir, AngleBrackets, ConstArg, GenericArg, HirId, ItemLocalMap, LifetimeSource, LifetimeSyntax, ParamName, Target, TraitCandidate, find_attr, @@ -1096,23 +1096,18 @@ fn lower_attrs_vec( target, OmitDoc::Lower, |s| l.lower(s), - |lint_id, span, kind| match kind { - EmitAttribute::Static(attr_kind) => { - self.delayed_lints.push(DelayedLint::AttributeParsing(AttributeLint { - lint_id, - id: target_hir_id, - span, - kind: attr_kind, - })); - } - EmitAttribute::Dynamic(callback) => { - self.delayed_lints.push(DelayedLint::Dynamic(DynAttribute { - lint_id, - id: target_hir_id, - span, - callback, - })); - } + |lint_id, span, kind| { + self.delayed_lints.push(DelayedLint { + lint_id, + id: target_hir_id, + span, + callback: Box::new(move |dcx, level, sess: &dyn std::any::Any| { + let sess = sess + .downcast_ref::() + .expect("expected `Session`"); + (kind.0)(dcx, level, sess) + }), + }); }, ) } diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index c2dda74e9f51..84dd2b907aec 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -3,12 +3,11 @@ use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::DelimSpan; use rustc_ast::{AttrItem, Attribute, LitKind, ast, token}; -use rustc_errors::{Applicability, PResult, msg}; +use rustc_errors::{Applicability, Diagnostic, PResult, msg}; use rustc_feature::{ AttrSuggestionStyle, AttributeTemplate, Features, GatedCfg, find_gated_cfg, template, }; use rustc_hir::attrs::CfgEntry; -use rustc_hir::lints::AttributeLintKind; use rustc_hir::{AttrPath, RustcVersion, Target}; use rustc_parse::parser::{ForceCollect, Parser, Recovery}; use rustc_parse::{exp, parse_in}; @@ -20,6 +19,7 @@ use thin_vec::ThinVec; use crate::attributes::AttributeSafety; +use crate::attributes::diagnostic::check_cfg; use crate::context::{AcceptContext, ShouldEmit, Stage}; use crate::parser::{ AllowExprMetavar, ArgParser, MetaItemListParser, MetaItemOrLitParser, NameValueParser, @@ -224,14 +224,19 @@ pub(crate) fn parse_name_value( match cx.sess.psess.check_config.expecteds.get(&name) { Some(ExpectedValues::Some(values)) if !values.contains(&value.map(|(v, _)| v)) => cx - .emit_lint( + .emit_dyn_lint_with_sess( UNEXPECTED_CFGS, - AttributeLintKind::UnexpectedCfgValue((name, name_span), value), + move |dcx, level, sess| { + check_cfg::unexpected_cfg_value(sess, (name, name_span), value) + .into_diag(dcx, level) + }, span, ), - None if cx.sess.psess.check_config.exhaustive_names => cx.emit_lint( + None if cx.sess.psess.check_config.exhaustive_names => cx.emit_dyn_lint_with_sess( UNEXPECTED_CFGS, - AttributeLintKind::UnexpectedCfgName((name, name_span), value), + move |dcx, level, sess| { + check_cfg::unexpected_cfg_name(sess, (name, name_span), value).into_diag(dcx, level) + }, span, ), _ => { /* not unexpected */ } diff --git a/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/check_cfg.rs similarity index 78% rename from compiler/rustc_lint/src/early/diagnostics/check_cfg.rs rename to compiler/rustc_attr_parsing/src/attributes/diagnostic/check_cfg.rs index 9fcabfa623d8..d6347c457b2b 100644 --- a/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/check_cfg.rs @@ -1,12 +1,10 @@ -use rustc_hir::def_id::LOCAL_CRATE; -use rustc_middle::bug; -use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::config::ExpectedValues; +use rustc_span::def_id::LOCAL_CRATE; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::{ExpnKind, Ident, Span, Symbol, sym}; -use crate::lints; +use crate::errors; const MAX_CHECK_CFG_NAMES_OR_VALUES: usize = 35; @@ -64,57 +62,48 @@ fn to_check_cfg_arg(name: Ident, value: Option, quotes: EscapeQuotes) -> fn cargo_help_sub( sess: &Session, inst: &impl Fn(EscapeQuotes) -> String, -) -> lints::UnexpectedCfgCargoHelp { +) -> errors::UnexpectedCfgCargoHelp { // We don't want to suggest the `build.rs` way to expected cfgs if we are already in a // `build.rs`. We therefor do a best effort check (looking if the `--crate-name` is // `build_script_build`) to try to figure out if we are building a Cargo build script let unescaped = &inst(EscapeQuotes::No); if let Some("build_script_build") = sess.opts.crate_name.as_deref() { - lints::UnexpectedCfgCargoHelp::lint_cfg(unescaped) + errors::UnexpectedCfgCargoHelp::lint_cfg(unescaped) } else { - lints::UnexpectedCfgCargoHelp::lint_cfg_and_build_rs(unescaped, &inst(EscapeQuotes::Yes)) + errors::UnexpectedCfgCargoHelp::lint_cfg_and_build_rs(unescaped, &inst(EscapeQuotes::Yes)) } } -fn rustc_macro_help(span: Span) -> Option { +fn rustc_macro_help(span: Span) -> Option { let oexpn = span.ctxt().outer_expn_data(); if let Some(def_id) = oexpn.macro_def_id && let ExpnKind::Macro(macro_kind, macro_name) = oexpn.kind && def_id.krate != LOCAL_CRATE { - Some(lints::UnexpectedCfgRustcMacroHelp { macro_kind: macro_kind.descr(), macro_name }) + Some(errors::UnexpectedCfgRustcMacroHelp { macro_kind: macro_kind.descr(), macro_name }) } else { None } } -fn cargo_macro_help( - tcx: Option>, - span: Span, -) -> Option { +fn cargo_macro_help(span: Span) -> Option { let oexpn = span.ctxt().outer_expn_data(); if let Some(def_id) = oexpn.macro_def_id - && let ExpnKind::Macro(macro_kind, macro_name) = oexpn.kind && def_id.krate != LOCAL_CRATE - && let Some(tcx) = tcx + && let ExpnKind::Macro(macro_kind, macro_name) = oexpn.kind { - Some(lints::UnexpectedCfgCargoMacroHelp { - macro_kind: macro_kind.descr(), - macro_name, - crate_name: tcx.crate_name(def_id.krate), - }) + Some(errors::UnexpectedCfgCargoMacroHelp { macro_kind: macro_kind.descr(), macro_name }) } else { None } } -pub(super) fn unexpected_cfg_name( +pub(crate) fn unexpected_cfg_name( sess: &Session, - tcx: Option>, (name, name_span): (Symbol, Span), value: Option<(Symbol, Span)>, -) -> lints::UnexpectedCfgName { +) -> errors::UnexpectedCfgName { #[allow(rustc::potential_query_instability)] let possibilities: Vec = sess.psess.check_config.expecteds.keys().copied().collect(); @@ -149,12 +138,12 @@ fn miscapitalized_boolean(name: Symbol) -> Option { } let code_sugg = if is_feature_cfg && is_from_cargo { - lints::unexpected_cfg_name::CodeSuggestion::DefineFeatures + errors::unexpected_cfg_name::CodeSuggestion::DefineFeatures // Suggest correct `version("..")` predicate syntax } else if let Some((_value, value_span)) = value && name == sym::version { - lints::unexpected_cfg_name::CodeSuggestion::VersionSyntax { + errors::unexpected_cfg_name::CodeSuggestion::VersionSyntax { between_name_and_value: name_span.between(value_span), after_value: value_span.shrink_to_hi(), } @@ -169,7 +158,7 @@ fn miscapitalized_boolean(name: Symbol) -> Option { .span_to_snippet(name_span) .map_or(true, |snippet| !snippet.contains("r#")) { - lints::unexpected_cfg_name::CodeSuggestion::BooleanLiteral { + errors::unexpected_cfg_name::CodeSuggestion::BooleanLiteral { span: name_span, literal: boolean, } @@ -189,7 +178,7 @@ fn miscapitalized_boolean(name: Symbol) -> Option { if !possibilities.is_empty() { let possibilities = possibilities.iter().copied().cloned().collect::>().into(); - Some(lints::unexpected_cfg_name::ExpectedValues { best_match, possibilities }) + Some(errors::unexpected_cfg_name::ExpectedValues { best_match, possibilities }) } else { None } @@ -198,37 +187,37 @@ fn miscapitalized_boolean(name: Symbol) -> Option { let best_match = Ident::new(best_match, name_span); if let Some((value, value_span)) = value { if best_match_values.contains(&Some(value)) { - lints::unexpected_cfg_name::CodeSuggestion::SimilarNameAndValue { + errors::unexpected_cfg_name::CodeSuggestion::SimilarNameAndValue { span: name_span, code: best_match.to_string(), } } else if best_match_values.contains(&None) { - lints::unexpected_cfg_name::CodeSuggestion::SimilarNameNoValue { + errors::unexpected_cfg_name::CodeSuggestion::SimilarNameNoValue { span: name_span.to(value_span), code: best_match.to_string(), } } else if let Some(first_value) = possibilities.first() { - lints::unexpected_cfg_name::CodeSuggestion::SimilarNameDifferentValues { + errors::unexpected_cfg_name::CodeSuggestion::SimilarNameDifferentValues { span: name_span.to(value_span), code: format!("{best_match} = \"{first_value}\""), expected: get_possibilities_sub(), } } else { - lints::unexpected_cfg_name::CodeSuggestion::SimilarNameDifferentValues { + errors::unexpected_cfg_name::CodeSuggestion::SimilarNameDifferentValues { span: name_span.to(value_span), code: best_match.to_string(), expected: get_possibilities_sub(), } } } else { - lints::unexpected_cfg_name::CodeSuggestion::SimilarName { + errors::unexpected_cfg_name::CodeSuggestion::SimilarName { span: name_span, code: best_match.to_string(), expected: get_possibilities_sub(), } } } else { - lints::unexpected_cfg_name::CodeSuggestion::SimilarName { + errors::unexpected_cfg_name::CodeSuggestion::SimilarName { span: name_span, code: best_match.to_string(), expected: None, @@ -239,7 +228,7 @@ fn miscapitalized_boolean(name: Symbol) -> Option { names_possibilities.sort(); names_possibilities .iter() - .map(|cfg_name| lints::unexpected_cfg_name::FoundWithSimilarValue { + .map(|cfg_name| errors::unexpected_cfg_name::FoundWithSimilarValue { span: name_span, code: format!("{cfg_name} = \"{name}\""), }) @@ -253,14 +242,14 @@ fn miscapitalized_boolean(name: Symbol) -> Option { let expected_names = if !possibilities.is_empty() { let possibilities: Vec<_> = possibilities.into_iter().map(|s| Ident::new(s, name_span)).collect(); - Some(lints::unexpected_cfg_name::ExpectedNames { + Some(errors::unexpected_cfg_name::ExpectedNames { possibilities: possibilities.into(), and_more, }) } else { None }; - lints::unexpected_cfg_name::CodeSuggestion::SimilarValues { + errors::unexpected_cfg_name::CodeSuggestion::SimilarValues { with_similar_values: similar_values, expected_names, } @@ -276,29 +265,28 @@ fn miscapitalized_boolean(name: Symbol) -> Option { } else { None }; - lints::unexpected_cfg_name::InvocationHelp::Cargo { + errors::unexpected_cfg_name::InvocationHelp::Cargo { help, - macro_help: cargo_macro_help(tcx, name_span), + macro_help: cargo_macro_help(name_span), } } else { - let help = lints::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No)); - lints::unexpected_cfg_name::InvocationHelp::Rustc { + let help = errors::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No)); + errors::unexpected_cfg_name::InvocationHelp::Rustc { help, macro_help: rustc_macro_help(name_span), } }; - lints::UnexpectedCfgName { code_sugg, invocation_help, name } + errors::UnexpectedCfgName { code_sugg, invocation_help, name } } -pub(super) fn unexpected_cfg_value( +pub(crate) fn unexpected_cfg_value( sess: &Session, - tcx: Option>, (name, name_span): (Symbol, Span), value: Option<(Symbol, Span)>, -) -> lints::UnexpectedCfgValue { +) -> errors::UnexpectedCfgValue { let Some(ExpectedValues::Some(values)) = &sess.psess.check_config.expecteds.get(&name) else { - bug!( + panic!( "it shouldn't be possible to have a diagnostic on a value whose name is not in values" ); }; @@ -327,13 +315,13 @@ pub(super) fn unexpected_cfg_value( .iter() .take(max_suggestions) .copied() - .map(|name| lints::unexpected_cfg_value::ChangeNameSuggestion { + .map(|name| errors::unexpected_cfg_value::ChangeNameSuggestion { span: name_span, name, value, }) .collect::>(); - lints::unexpected_cfg_value::CodeSuggestion::ChangeName { suggestions } + errors::unexpected_cfg_value::CodeSuggestion::ChangeName { suggestions } } else if !possibilities.is_empty() { // Show the full list if all possible values for a given name, but don't do it // for names as the possibilities could be very long @@ -343,7 +331,7 @@ pub(super) fn unexpected_cfg_value( possibilities.clone(), FilterWellKnownNames::No, ); - lints::unexpected_cfg_value::ExpectedValues { + errors::unexpected_cfg_value::ExpectedValues { name, have_none_possibility, possibilities: possibilities.into(), @@ -354,7 +342,7 @@ pub(super) fn unexpected_cfg_value( let suggestion = if let Some((value, value_span)) = value { // Suggest the most probable if we found one if let Some(best_match) = find_best_match_for_name(&possibilities, value, None) { - Some(lints::unexpected_cfg_value::ChangeValueSuggestion::SimilarName { + Some(errors::unexpected_cfg_value::ChangeValueSuggestion::SimilarName { span: value_span, best_match, }) @@ -362,7 +350,7 @@ pub(super) fn unexpected_cfg_value( None } } else if let &[first_possibility] = &possibilities[..] { - Some(lints::unexpected_cfg_value::ChangeValueSuggestion::SpecifyValue { + Some(errors::unexpected_cfg_value::ChangeValueSuggestion::SpecifyValue { span: name_span.shrink_to_hi(), first_possibility, }) @@ -370,21 +358,21 @@ pub(super) fn unexpected_cfg_value( None }; - lints::unexpected_cfg_value::CodeSuggestion::ChangeValue { expected_values, suggestion } + errors::unexpected_cfg_value::CodeSuggestion::ChangeValue { expected_values, suggestion } } else if have_none_possibility { let suggestion = - value.map(|(_value, value_span)| lints::unexpected_cfg_value::RemoveValueSuggestion { + value.map(|(_value, value_span)| errors::unexpected_cfg_value::RemoveValueSuggestion { span: name_span.shrink_to_hi().to(value_span), }); - lints::unexpected_cfg_value::CodeSuggestion::RemoveValue { suggestion, name } + errors::unexpected_cfg_value::CodeSuggestion::RemoveValue { suggestion, name } } else { let span = if let Some((_value, value_span)) = value { name_span.to(value_span) } else { name_span }; - let suggestion = lints::unexpected_cfg_value::RemoveConditionSuggestion { span }; - lints::unexpected_cfg_value::CodeSuggestion::RemoveCondition { suggestion, name } + let suggestion = errors::unexpected_cfg_value::RemoveConditionSuggestion { span }; + errors::unexpected_cfg_value::CodeSuggestion::RemoveCondition { suggestion, name } }; // We don't want to encourage people to add values to a well-known names, as these are @@ -405,32 +393,32 @@ pub(super) fn unexpected_cfg_value( let invocation_help = if is_from_cargo { let help = if name == sym::feature && !is_from_external_macro { if let Some((value, _value_span)) = value { - Some(lints::unexpected_cfg_value::CargoHelp::AddFeature { value }) + Some(errors::unexpected_cfg_value::CargoHelp::AddFeature { value }) } else { - Some(lints::unexpected_cfg_value::CargoHelp::DefineFeatures) + Some(errors::unexpected_cfg_value::CargoHelp::DefineFeatures) } } else if can_suggest_adding_value && !is_from_external_macro { - Some(lints::unexpected_cfg_value::CargoHelp::Other(cargo_help_sub(sess, &inst))) + Some(errors::unexpected_cfg_value::CargoHelp::Other(cargo_help_sub(sess, &inst))) } else { None }; - lints::unexpected_cfg_value::InvocationHelp::Cargo { + errors::unexpected_cfg_value::InvocationHelp::Cargo { help, - macro_help: cargo_macro_help(tcx, name_span), + macro_help: cargo_macro_help(name_span), } } else { let help = if can_suggest_adding_value { - Some(lints::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No))) + Some(errors::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No))) } else { None }; - lints::unexpected_cfg_value::InvocationHelp::Rustc { + errors::unexpected_cfg_value::InvocationHelp::Rustc { help, macro_help: rustc_macro_help(name_span), } }; - lints::UnexpectedCfgValue { + errors::UnexpectedCfgValue { code_sugg, invocation_help, has_value: value.is_some(), diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index b215f77c39ad..5f21d29035b0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -23,6 +23,7 @@ }; use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser}; +pub(crate) mod check_cfg; pub(crate) mod do_not_recommend; pub(crate) mod on_const; pub(crate) mod on_move; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 4aa7ebffbd3c..4058fdb7861f 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -11,7 +11,6 @@ use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan}; use rustc_feature::{AttrSuggestionStyle, AttributeTemplate}; use rustc_hir::attrs::AttributeKind; -use rustc_hir::lints::AttributeLintKind; use rustc_hir::{AttrPath, HirId}; use rustc_parse::parser::Recovery; use rustc_session::Session; @@ -460,18 +459,6 @@ pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuarant self.stage.emit_err(&self.sess, diag) } - /// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing - /// must be delayed until after HIR is built. This method will take care of the details of - /// that. - pub(crate) fn emit_lint( - &mut self, - lint: &'static Lint, - kind: AttributeLintKind, - span: impl Into, - ) { - self.emit_lint_inner(lint, EmitAttribute::Static(kind), span); - } - /// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing /// must be delayed until after HIR is built. This method will take care of the details of /// that. @@ -483,7 +470,25 @@ pub(crate) fn emit_dyn_lint< callback: F, span: impl Into, ) { - self.emit_lint_inner(lint, EmitAttribute::Dynamic(Box::new(callback)), span); + self.emit_lint_inner( + lint, + EmitAttribute(Box::new(move |dcx, level, _| callback(dcx, level))), + span, + ); + } + + pub(crate) fn emit_dyn_lint_with_sess< + F: for<'a> Fn(DiagCtxtHandle<'a>, Level, &Session) -> Diag<'a, ()> + + DynSend + + DynSync + + 'static, + >( + &mut self, + lint: &'static Lint, + callback: F, + span: impl Into, + ) { + self.emit_lint_inner(lint, EmitAttribute(Box::new(callback)), span); } fn emit_lint_inner( diff --git a/compiler/rustc_attr_parsing/src/errors.rs b/compiler/rustc_attr_parsing/src/errors.rs index e35f73de35ec..85304241660b 100644 --- a/compiler/rustc_attr_parsing/src/errors.rs +++ b/compiler/rustc_attr_parsing/src/errors.rs @@ -421,3 +421,371 @@ pub(crate) enum FormatWarning { allowed: &'static str, }, } + +#[derive(Subdiagnostic)] +pub(crate) enum UnexpectedCfgCargoHelp { + #[help("consider using a Cargo feature instead")] + #[help( + "or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint:{$cargo_toml_lint_cfg}" + )] + LintCfg { cargo_toml_lint_cfg: String }, + #[help("consider using a Cargo feature instead")] + #[help( + "or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint:{$cargo_toml_lint_cfg}" + )] + #[help("or consider adding `{$build_rs_println}` to the top of the `build.rs`")] + LintCfgAndBuildRs { cargo_toml_lint_cfg: String, build_rs_println: String }, +} + +impl UnexpectedCfgCargoHelp { + fn cargo_toml_lint_cfg(unescaped: &str) -> String { + format!( + "\n [lints.rust]\n unexpected_cfgs = {{ level = \"warn\", check-cfg = ['{unescaped}'] }}" + ) + } + + pub(crate) fn lint_cfg(unescaped: &str) -> Self { + UnexpectedCfgCargoHelp::LintCfg { + cargo_toml_lint_cfg: Self::cargo_toml_lint_cfg(unescaped), + } + } + + pub(crate) fn lint_cfg_and_build_rs(unescaped: &str, escaped: &str) -> Self { + UnexpectedCfgCargoHelp::LintCfgAndBuildRs { + cargo_toml_lint_cfg: Self::cargo_toml_lint_cfg(unescaped), + build_rs_println: format!("println!(\"cargo::rustc-check-cfg={escaped}\");"), + } + } +} + +#[derive(Subdiagnostic)] +#[help("to expect this configuration use `{$cmdline_arg}`")] +pub(crate) struct UnexpectedCfgRustcHelp { + pub cmdline_arg: String, +} + +impl UnexpectedCfgRustcHelp { + pub(crate) fn new(unescaped: &str) -> Self { + Self { cmdline_arg: format!("--check-cfg={unescaped}") } + } +} + +#[derive(Subdiagnostic)] +#[note( + "using a cfg inside a {$macro_kind} will use the cfgs from the destination crate and not the ones from the defining crate" +)] +#[help("try referring to `{$macro_name}` crate for guidance on how handle this unexpected cfg")] +pub(crate) struct UnexpectedCfgRustcMacroHelp { + pub macro_kind: &'static str, + pub macro_name: Symbol, +} + +#[derive(Subdiagnostic)] +#[note( + "using a cfg inside a {$macro_kind} will use the cfgs from the destination crate and not the ones from the defining crate" +)] +#[help("try referring to `{$macro_name}` crate for guidance on how handle this unexpected cfg")] +pub(crate) struct UnexpectedCfgCargoMacroHelp { + pub macro_kind: &'static str, + pub macro_name: Symbol, +} + +#[derive(Diagnostic)] +#[diag("unexpected `cfg` condition name: `{$name}`")] +pub(crate) struct UnexpectedCfgName { + #[subdiagnostic] + pub code_sugg: unexpected_cfg_name::CodeSuggestion, + #[subdiagnostic] + pub invocation_help: unexpected_cfg_name::InvocationHelp, + + pub name: Symbol, +} + +pub(crate) mod unexpected_cfg_name { + use rustc_errors::DiagSymbolList; + use rustc_macros::Subdiagnostic; + use rustc_span::{Ident, Span, Symbol}; + + #[derive(Subdiagnostic)] + pub(crate) enum CodeSuggestion { + #[help("consider defining some features in `Cargo.toml`")] + DefineFeatures, + #[multipart_suggestion( + "there is a similar config predicate: `version(\"..\")`", + applicability = "machine-applicable" + )] + VersionSyntax { + #[suggestion_part(code = "(")] + between_name_and_value: Span, + #[suggestion_part(code = ")")] + after_value: Span, + }, + #[suggestion( + "there is a config with a similar name and value", + applicability = "maybe-incorrect", + code = "{code}" + )] + SimilarNameAndValue { + #[primary_span] + span: Span, + code: String, + }, + #[suggestion( + "there is a config with a similar name and no value", + applicability = "maybe-incorrect", + code = "{code}" + )] + SimilarNameNoValue { + #[primary_span] + span: Span, + code: String, + }, + #[suggestion( + "there is a config with a similar name and different values", + applicability = "maybe-incorrect", + code = "{code}" + )] + SimilarNameDifferentValues { + #[primary_span] + span: Span, + code: String, + #[subdiagnostic] + expected: Option, + }, + #[suggestion( + "there is a config with a similar name", + applicability = "maybe-incorrect", + code = "{code}" + )] + SimilarName { + #[primary_span] + span: Span, + code: String, + #[subdiagnostic] + expected: Option, + }, + SimilarValues { + #[subdiagnostic] + with_similar_values: Vec, + #[subdiagnostic] + expected_names: Option, + }, + #[suggestion( + "you may have meant to use `{$literal}` (notice the capitalization). Doing so makes this predicate evaluate to `{$literal}` unconditionally", + applicability = "machine-applicable", + style = "verbose", + code = "{literal}" + )] + BooleanLiteral { + #[primary_span] + span: Span, + literal: bool, + }, + } + + #[derive(Subdiagnostic)] + #[help("expected values for `{$best_match}` are: {$possibilities}")] + pub(crate) struct ExpectedValues { + pub best_match: Symbol, + pub possibilities: DiagSymbolList, + } + + #[derive(Subdiagnostic)] + #[suggestion( + "found config with similar value", + applicability = "maybe-incorrect", + code = "{code}" + )] + pub(crate) struct FoundWithSimilarValue { + #[primary_span] + pub span: Span, + pub code: String, + } + + #[derive(Subdiagnostic)] + #[help_once( + "expected names are: {$possibilities}{$and_more -> + [0] {\"\"} + *[other] {\" \"}and {$and_more} more + }" + )] + pub(crate) struct ExpectedNames { + pub possibilities: DiagSymbolList, + pub and_more: usize, + } + + #[derive(Subdiagnostic)] + pub(crate) enum InvocationHelp { + #[note( + "see for more information about checking conditional configuration" + )] + Cargo { + #[subdiagnostic] + macro_help: Option, + #[subdiagnostic] + help: Option, + }, + #[note( + "see for more information about checking conditional configuration" + )] + Rustc { + #[subdiagnostic] + macro_help: Option, + #[subdiagnostic] + help: super::UnexpectedCfgRustcHelp, + }, + } +} + +#[derive(Diagnostic)] +#[diag( + "unexpected `cfg` condition value: {$has_value -> + [true] `{$value}` + *[false] (none) + }" +)] +pub(crate) struct UnexpectedCfgValue { + #[subdiagnostic] + pub code_sugg: unexpected_cfg_value::CodeSuggestion, + #[subdiagnostic] + pub invocation_help: unexpected_cfg_value::InvocationHelp, + + pub has_value: bool, + pub value: String, +} + +pub(crate) mod unexpected_cfg_value { + use rustc_errors::DiagSymbolList; + use rustc_macros::Subdiagnostic; + use rustc_span::{Span, Symbol}; + + #[derive(Subdiagnostic)] + pub(crate) enum CodeSuggestion { + ChangeValue { + #[subdiagnostic] + expected_values: ExpectedValues, + #[subdiagnostic] + suggestion: Option, + }, + #[note("no expected value for `{$name}`")] + RemoveValue { + #[subdiagnostic] + suggestion: Option, + + name: Symbol, + }, + #[note("no expected values for `{$name}`")] + RemoveCondition { + #[subdiagnostic] + suggestion: RemoveConditionSuggestion, + + name: Symbol, + }, + ChangeName { + #[subdiagnostic] + suggestions: Vec, + }, + } + + #[derive(Subdiagnostic)] + pub(crate) enum ChangeValueSuggestion { + #[suggestion( + "there is a expected value with a similar name", + code = r#""{best_match}""#, + applicability = "maybe-incorrect" + )] + SimilarName { + #[primary_span] + span: Span, + best_match: Symbol, + }, + #[suggestion( + "specify a config value", + code = r#" = "{first_possibility}""#, + applicability = "maybe-incorrect" + )] + SpecifyValue { + #[primary_span] + span: Span, + first_possibility: Symbol, + }, + } + + #[derive(Subdiagnostic)] + #[suggestion("remove the value", code = "", applicability = "maybe-incorrect")] + pub(crate) struct RemoveValueSuggestion { + #[primary_span] + pub span: Span, + } + + #[derive(Subdiagnostic)] + #[suggestion("remove the condition", code = "", applicability = "maybe-incorrect")] + pub(crate) struct RemoveConditionSuggestion { + #[primary_span] + pub span: Span, + } + + #[derive(Subdiagnostic)] + #[note( + "expected values for `{$name}` are: {$have_none_possibility -> + [true] {\"(none), \"} + *[false] {\"\"} + }{$possibilities}{$and_more -> + [0] {\"\"} + *[other] {\" \"}and {$and_more} more + }" + )] + pub(crate) struct ExpectedValues { + pub name: Symbol, + pub have_none_possibility: bool, + pub possibilities: DiagSymbolList, + pub and_more: usize, + } + + #[derive(Subdiagnostic)] + #[suggestion( + "`{$value}` is an expected value for `{$name}`", + code = "{name}", + applicability = "maybe-incorrect", + style = "verbose" + )] + pub(crate) struct ChangeNameSuggestion { + #[primary_span] + pub span: Span, + pub name: Symbol, + pub value: Symbol, + } + + #[derive(Subdiagnostic)] + pub(crate) enum InvocationHelp { + #[note( + "see for more information about checking conditional configuration" + )] + Cargo { + #[subdiagnostic] + help: Option, + #[subdiagnostic] + macro_help: Option, + }, + #[note( + "see for more information about checking conditional configuration" + )] + Rustc { + #[subdiagnostic] + help: Option, + #[subdiagnostic] + macro_help: Option, + }, + } + + #[derive(Subdiagnostic)] + pub(crate) enum CargoHelp { + #[help("consider adding `{$value}` as a feature in `Cargo.toml`")] + AddFeature { + value: Symbol, + }, + #[help("consider defining some features in `Cargo.toml`")] + DefineFeatures, + Other(#[subdiagnostic] super::UnexpectedCfgCargoHelp), + } +} diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index d350bfee7f34..21f1561d67d7 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -7,7 +7,6 @@ use rustc_errors::{Diag, DiagCtxtHandle, Level, MultiSpan}; 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_session::Session; use rustc_session::lint::LintId; @@ -20,14 +19,14 @@ use crate::session_diagnostics::ParsedDescription; use crate::{Early, Late, OmitDoc, ShouldEmit}; -pub enum EmitAttribute { - Static(AttributeLintKind), - Dynamic( - Box< - dyn for<'a> Fn(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()> + DynSend + DynSync + 'static, - >, - ), -} +pub struct EmitAttribute( + pub Box< + dyn for<'a> Fn(DiagCtxtHandle<'a>, Level, &Session) -> Diag<'a, ()> + + DynSend + + DynSync + + 'static, + >, +); /// Context created once, for example as part of the ast lowering /// context, through which all attributes can be lowered. @@ -127,13 +126,8 @@ pub fn parse_limited_all( target, OmitDoc::Skip, std::convert::identity, - |lint_id, span, kind| match kind { - EmitAttribute::Static(kind) => { - sess.psess.buffer_lint(lint_id.lint, span, target_node_id, kind) - } - EmitAttribute::Dynamic(callback) => { - sess.psess.dyn_buffer_lint(lint_id.lint, span, target_node_id, callback) - } + |lint_id, span, kind| { + sess.psess.dyn_buffer_lint_sess(lint_id.lint, span, target_node_id, kind.0) }, ) } @@ -214,13 +208,8 @@ pub fn parse_single_args( sess, stage: Early { emit_errors }, }; - let mut emit_lint = |lint_id: LintId, span: MultiSpan, kind: EmitAttribute| match kind { - EmitAttribute::Static(kind) => { - sess.psess.buffer_lint(lint_id.lint, span, target_node_id, kind) - } - EmitAttribute::Dynamic(callback) => { - sess.psess.dyn_buffer_lint(lint_id.lint, span, target_node_id, callback) - } + let mut emit_lint = |lint_id: LintId, span: MultiSpan, kind: EmitAttribute| { + sess.psess.dyn_buffer_lint_sess(lint_id.lint, span, target_node_id, kind.0) }; if let Some(safety) = attr_safety { parser.check_attribute_safety( diff --git a/compiler/rustc_attr_parsing/src/safety.rs b/compiler/rustc_attr_parsing/src/safety.rs index 6566aaa55705..78a4e700fdca 100644 --- a/compiler/rustc_attr_parsing/src/safety.rs +++ b/compiler/rustc_attr_parsing/src/safety.rs @@ -79,7 +79,7 @@ pub fn check_attribute_safety( emit_lint( LintId::of(UNSAFE_ATTR_OUTSIDE_UNSAFE), path_span.into(), - EmitAttribute::Dynamic(Box::new(move |dcx, level| { + EmitAttribute(Box::new(move |dcx, level, _| { errors::UnsafeAttrOutsideUnsafeLint { span: path_span, suggestion: not_from_proc_macro diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 8e055c855c4f..1caa0e823090 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -635,7 +635,7 @@ enum ArgRef<'a> { span: Some(arg_name.span.into()), node_id: rustc_ast::CRATE_NODE_ID, lint_id: LintId::of(NAMED_ARGUMENTS_USED_POSITIONALLY), - diagnostic: DecorateDiagCompat::Dynamic(Box::new(move |dcx, level, sess| { + diagnostic: DecorateDiagCompat(Box::new(move |dcx, level, sess| { let (suggestion, name) = if let Some(positional_arg_to_replace) = position_sp_to_replace { let mut name = arg_name.name.to_string(); diff --git a/compiler/rustc_errors/src/decorate_diag.rs b/compiler/rustc_errors/src/decorate_diag.rs index 18c0c571fd1b..3a222fd8a10c 100644 --- a/compiler/rustc_errors/src/decorate_diag.rs +++ b/compiler/rustc_errors/src/decorate_diag.rs @@ -5,25 +5,20 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_error_messages::MultiSpan; -use rustc_lint_defs::{AttributeLintKind, Lint, LintId}; +use rustc_lint_defs::{Lint, LintId}; use crate::{Diag, DiagCtxtHandle, Diagnostic, Level}; -/// We can't implement `Diagnostic` for `AttributeLintKind`, because decorating some of its -/// variants requires types we don't have yet. So, handle that case separately. -pub enum DecorateDiagCompat { +pub struct DecorateDiagCompat( /// The third argument of the closure is a `Session`. However, due to the dependency tree, /// we don't have access to `rustc_session` here, so we downcast it when needed. - Dynamic( - Box< - dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &dyn Any) -> Diag<'a, ()> - + DynSync - + DynSend - + 'static, - >, - ), - Builtin(AttributeLintKind), -} + pub Box< + dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &dyn Any) -> Diag<'a, ()> + + DynSync + + DynSend + + 'static, + >, +); impl std::fmt::Debug for DecorateDiagCompat { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -34,14 +29,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { impl Diagnostic<'a, ()> + DynSync + DynSend + 'static> From for DecorateDiagCompat { #[inline] fn from(d: D) -> Self { - Self::Dynamic(Box::new(|dcx, level, _| d.into_diag(dcx, level))) - } -} - -impl From for DecorateDiagCompat { - #[inline] - fn from(b: AttributeLintKind) -> Self { - Self::Builtin(b) + Self(Box::new(|dcx, level, _| d.into_diag(dcx, level))) } } @@ -106,7 +94,7 @@ pub fn dyn_buffer_lint< lint_id: LintId::of(lint), node_id, span: Some(span.into()), - diagnostic: DecorateDiagCompat::Dynamic(Box::new(|dcx, level, _| callback(dcx, level))), + diagnostic: DecorateDiagCompat(Box::new(|dcx, level, _| callback(dcx, level))), }); } @@ -126,7 +114,7 @@ pub fn dyn_buffer_lint_any< lint_id: LintId::of(lint), node_id, span: Some(span.into()), - diagnostic: DecorateDiagCompat::Dynamic(Box::new(callback)), + diagnostic: DecorateDiagCompat(Box::new(callback)), }); } } diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index ecde304aabfc..377d96b73e9b 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -129,18 +129,6 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { } } -pub struct DiagCallback<'a>( - pub &'a Box< - dyn for<'b> Fn(DiagCtxtHandle<'b>, Level) -> Diag<'b, ()> + DynSend + DynSync + 'static, - >, -); - -impl<'a, 'b> Diagnostic<'a, ()> for DiagCallback<'b> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { - (self.0)(dcx, level) - } -} - /// Type used to emit diagnostic through a closure instead of implementing the `Diagnostic` trait. pub struct DiagDecorator)>(pub F); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index f4874652f6ac..9c51df1a49fe 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -36,8 +36,8 @@ pub use codes::*; pub use decorate_diag::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer}; pub use diagnostic::{ - BugAbort, Diag, DiagCallback, DiagDecorator, DiagInner, DiagLocation, DiagStyledString, - Diagnostic, EmissionGuarantee, FatalAbort, StringPart, Subdiag, Subdiagnostic, + BugAbort, Diag, DiagDecorator, DiagInner, DiagLocation, DiagStyledString, Diagnostic, + EmissionGuarantee, FatalAbort, StringPart, Subdiag, Subdiagnostic, }; pub use diagnostic_impls::{ DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter, diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs index cdafca37a504..0d9fc669bda7 100644 --- a/compiler/rustc_hir/src/lints.rs +++ b/compiler/rustc_hir/src/lints.rs @@ -1,7 +1,6 @@ use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_error_messages::MultiSpan; use rustc_errors::{Diag, DiagCtxtHandle, Level}; -pub use rustc_lint_defs::AttributeLintKind; use rustc_lint_defs::LintId; use crate::HirId; @@ -14,32 +13,21 @@ /// and then there's a gap where no lints can be emitted until HIR is done. /// The variants in this enum represent lints that are temporarily stashed during /// AST lowering to be emitted once HIR is built. -#[derive(Debug)] -pub enum DelayedLint { - AttributeParsing(AttributeLint), - Dynamic(DynAttribute), -} - -#[derive(Debug)] -pub struct AttributeLint { - pub lint_id: LintId, - pub id: HirId, - pub span: MultiSpan, - pub kind: AttributeLintKind, -} - -pub struct DynAttribute { +pub struct DelayedLint { pub lint_id: LintId, pub id: HirId, pub span: MultiSpan, pub callback: Box< - dyn for<'a> Fn(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()> + DynSend + DynSync + 'static, + dyn for<'a> Fn(DiagCtxtHandle<'a>, Level, &dyn std::any::Any) -> Diag<'a, ()> + + DynSend + + DynSync + + 'static, >, } -impl std::fmt::Debug for DynAttribute { +impl std::fmt::Debug for DelayedLint { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("DynAttribute") + f.debug_struct("DelayedLint") .field("lint_id", &self.lint_id) .field("id", &self.id) .field("span", &self.span) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 9c020c35e142..eba38cf24b34 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -11,10 +11,12 @@ use rustc_codegen_ssa::{CompiledModules, CrateInfo}; use rustc_data_structures::indexmap::IndexMap; use rustc_data_structures::steal::Steal; -use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, WorkerLocal, par_fns}; +use rustc_data_structures::sync::{ + AppendOnlyIndexVec, DynSend, DynSync, FreezeLock, WorkerLocal, par_fns, +}; use rustc_data_structures::thousands; -use rustc_errors::DiagCallback; use rustc_errors::timings::TimingSection; +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level}; use rustc_expand::base::{ExtCtxt, LintStoreExpand}; use rustc_feature::Features; use rustc_fs_util::try_canonicalize; @@ -22,12 +24,9 @@ use rustc_hir::def_id::{LOCAL_CRATE, StableCrateId, StableCrateIdMap}; use rustc_hir::definitions::Definitions; use rustc_hir::limit::Limit; -use rustc_hir::lints::DelayedLint; use rustc_hir::{Attribute, MaybeOwner, Target, find_attr}; use rustc_incremental::setup_dep_graph; -use rustc_lint::{ - BufferedEarlyLint, DecorateAttrLint, EarlyCheckNode, LintStore, unerased_lint_store, -}; +use rustc_lint::{BufferedEarlyLint, EarlyCheckNode, LintStore, unerased_lint_store}; use rustc_metadata::EncodedMetadata; use rustc_metadata::creader::CStore; use rustc_middle::arena::Arena; @@ -97,7 +96,6 @@ fn pre_expansion_lint<'a>( || { rustc_lint::check_ast_node( sess, - None, features, true, lint_store, @@ -141,7 +139,7 @@ fn configure_and_expand( let tcx = resolver.tcx(); let sess = tcx.sess; let features = tcx.features(); - let lint_store = unerased_lint_store(tcx.sess); + let lint_store = unerased_lint_store(sess); let crate_name = tcx.crate_name(LOCAL_CRATE); let lint_check_node = (&krate, pre_configured_attrs); pre_expansion_lint( @@ -470,7 +468,6 @@ const fn as_str(self) -> &'static str { let lint_store = unerased_lint_store(tcx.sess); rustc_lint::check_ast_node( sess, - Some(tcx), tcx.features(), false, lint_store, @@ -1028,30 +1025,29 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( ) } +struct DiagCallback<'a, 'tcx> { + callback: &'a Box< + dyn for<'b> Fn(DiagCtxtHandle<'b>, Level, &dyn Any) -> Diag<'b, ()> + DynSend + DynSync, + >, + tcx: TyCtxt<'tcx>, +} + +impl<'a, 'b, 'tcx> Diagnostic<'a, ()> for DiagCallback<'b, 'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + (self.callback)(dcx, level, self.tcx.sess) + } +} + pub fn emit_delayed_lints(tcx: TyCtxt<'_>) { for owner_id in tcx.hir_crate_items(()).delayed_lint_items() { if let Some(delayed_lints) = tcx.opt_ast_lowering_delayed_lints(owner_id) { for lint in delayed_lints { - match lint { - DelayedLint::AttributeParsing(attribute_lint) => { - tcx.emit_node_span_lint( - attribute_lint.lint_id.lint, - attribute_lint.id, - attribute_lint.span.clone(), - DecorateAttrLint { - sess: tcx.sess, - tcx: Some(tcx), - diagnostic: &attribute_lint.kind, - }, - ); - } - DelayedLint::Dynamic(attribute_lint) => tcx.emit_node_span_lint( - attribute_lint.lint_id.lint, - attribute_lint.id, - attribute_lint.span.clone(), - DiagCallback(&attribute_lint.callback), - ), - } + tcx.emit_node_span_lint( + lint.lint_id.lint, + lint.id, + lint.span.clone(), + DiagCallback { callback: &lint.callback, tcx }, + ); } } } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index df5adf694d3a..903e223e9793 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -7,17 +7,17 @@ use rustc_ast::visit::{self as ast_visit, Visitor, walk_list}; use rustc_ast::{self as ast, AttrVec, HasAttrs}; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer}; +use rustc_errors::{BufferedEarlyLint, LintBuffer}; use rustc_feature::Features; -use rustc_middle::ty::{RegisteredTools, TyCtxt}; +use rustc_middle::ty::RegisteredTools; use rustc_session::Session; use rustc_session::lint::LintPass; use rustc_span::{Ident, Span}; use tracing::debug; +use crate::DiagAndSess; use crate::context::{EarlyContext, LintContext, LintStore}; use crate::passes::{EarlyLintPass, EarlyLintPassObject}; -use crate::{DecorateAttrLint, DiagAndSess}; pub(super) mod diagnostics; @@ -27,36 +27,20 @@ macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({ /// Implements the AST traversal for early lint passes. `T` provides the /// `check_*` methods. -pub struct EarlyContextAndPass<'ecx, 'tcx, T: EarlyLintPass> { +pub struct EarlyContextAndPass<'ecx, T: EarlyLintPass> { context: EarlyContext<'ecx>, - tcx: Option>, pass: T, } -impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> { +impl<'ecx, T: EarlyLintPass> EarlyContextAndPass<'ecx, T> { fn check_id(&mut self, id: ast::NodeId) { for early_lint in self.context.buffered.take(id) { let BufferedEarlyLint { span, node_id: _, lint_id, diagnostic } = early_lint; - match diagnostic { - DecorateDiagCompat::Builtin(b) => { - self.context.opt_span_lint( - lint_id.lint, - span, - DecorateAttrLint { - sess: self.context.sess(), - tcx: self.tcx, - diagnostic: &b, - }, - ); - } - DecorateDiagCompat::Dynamic(callback) => { - self.context.opt_span_lint( - lint_id.lint, - span, - DiagAndSess { callback, sess: self.context.sess() }, - ); - } - } + self.context.opt_span_lint( + lint_id.lint, + span, + DiagAndSess { callback: diagnostic.0, sess: self.context.sess() }, + ); } } @@ -80,9 +64,7 @@ fn with_lint_attrs(&mut self, id: ast::NodeId, attrs: &'_ [ast::Attribute], f } } -impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast> - for EarlyContextAndPass<'ecx, 'tcx, T> -{ +impl<'ast, 'ecx, T: EarlyLintPass> ast_visit::Visitor<'ast> for EarlyContextAndPass<'ecx, T> { fn visit_id(&mut self, id: rustc_ast::NodeId) { self.check_id(id); } @@ -297,7 +279,7 @@ impl EarlyLintPass for RuntimeCombinedEarlyLintPass<'_> { pub trait EarlyCheckNode<'a>: Copy { fn id(self) -> ast::NodeId; fn attrs(self) -> &'a [ast::Attribute]; - fn check<'ecx, 'tcx, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'ecx, 'tcx, T>); + fn check<'ecx, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'ecx, T>); } impl<'a> EarlyCheckNode<'a> for (&'a ast::Crate, &'a [ast::Attribute]) { @@ -307,7 +289,7 @@ fn id(self) -> ast::NodeId { fn attrs(self) -> &'a [ast::Attribute] { self.1 } - fn check<'ecx, 'tcx, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'ecx, 'tcx, T>) { + fn check<'ecx, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'ecx, T>) { lint_callback!(cx, check_crate, self.0); ast_visit::walk_crate(cx, self.0); lint_callback!(cx, check_crate_post, self.0); @@ -321,7 +303,7 @@ fn id(self) -> ast::NodeId { fn attrs(self) -> &'a [ast::Attribute] { self.1 } - fn check<'ecx, 'tcx, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'ecx, 'tcx, T>) { + fn check<'ecx, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'ecx, T>) { walk_list!(cx, visit_attribute, self.1); walk_list!(cx, visit_item, self.2); } @@ -329,7 +311,6 @@ fn check<'ecx, 'tcx, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'ecx, pub fn check_ast_node<'a>( sess: &Session, - tcx: Option>, features: &Features, pre_expansion: bool, lint_store: &LintStore, @@ -353,23 +334,22 @@ pub fn check_ast_node<'a>( let passes = if pre_expansion { &lint_store.pre_expansion_passes } else { &lint_store.early_passes }; if passes.is_empty() { - check_ast_node_inner(sess, tcx, check_node, context, builtin_lints); + check_ast_node_inner(sess, check_node, context, builtin_lints); } else { let mut passes: Vec<_> = passes.iter().map(|mk_pass| (mk_pass)()).collect(); passes.push(Box::new(builtin_lints)); let pass = RuntimeCombinedEarlyLintPass { passes: &mut passes[..] }; - check_ast_node_inner(sess, tcx, check_node, context, pass); + check_ast_node_inner(sess, check_node, context, pass); } } fn check_ast_node_inner<'a, T: EarlyLintPass>( sess: &Session, - tcx: Option>, check_node: impl EarlyCheckNode<'a>, context: EarlyContext<'_>, pass: T, ) { - let mut cx = EarlyContextAndPass { context, tcx, pass }; + let mut cx = EarlyContextAndPass { context, pass }; cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx)); diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index a4cb4e532067..91259b010178 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -2,12 +2,8 @@ use rustc_data_structures::sync::DynSend; use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level}; -use rustc_hir::lints::AttributeLintKind; -use rustc_middle::ty::TyCtxt; use rustc_session::Session; -mod check_cfg; - pub struct DiagAndSess<'sess> { pub callback: Box< dyn for<'b> FnOnce(DiagCtxtHandle<'b>, Level, &dyn Any) -> Diag<'b, ()> + DynSend + 'static, @@ -20,26 +16,3 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { (self.callback)(dcx, level, self.sess) } } - -/// This is a diagnostic struct that will decorate a `AttributeLintKind` -/// Directly creating the lint structs is expensive, using this will only decorate the lint structs when needed. -pub struct DecorateAttrLint<'a, 'sess, 'tcx> { - pub sess: &'sess Session, - pub tcx: Option>, - pub diagnostic: &'a AttributeLintKind, -} - -impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { - match self.diagnostic { - &AttributeLintKind::UnexpectedCfgName(name, value) => { - check_cfg::unexpected_cfg_name(self.sess, self.tcx, name, value) - .into_diag(dcx, level) - } - &AttributeLintKind::UnexpectedCfgValue(name, value) => { - check_cfg::unexpected_cfg_value(self.sess, self.tcx, name, value) - .into_diag(dcx, level) - } - } - } -} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 4a5172a237e7..ea0e657f7ede 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -130,7 +130,7 @@ #[rustfmt::skip] pub use builtin::{MissingDoc, SoftLints}; pub use context::{CheckLintNameResult, EarlyContext, LateContext, LintContext, LintStore}; -pub use early::diagnostics::{DecorateAttrLint, DiagAndSess}; +pub use early::diagnostics::DiagAndSess; pub use early::{EarlyCheckNode, check_ast_node}; pub use late::{check_crate, late_lint_mod, unerased_lint_store}; pub use levels::LintLevelsBuilder; diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 05914686705a..1e3ce972a366 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2653,378 +2653,6 @@ pub(crate) enum InvalidAsmLabel { }, } -#[derive(Subdiagnostic)] -pub(crate) enum UnexpectedCfgCargoHelp { - #[help("consider using a Cargo feature instead")] - #[help( - "or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint:{$cargo_toml_lint_cfg}" - )] - LintCfg { cargo_toml_lint_cfg: String }, - #[help("consider using a Cargo feature instead")] - #[help( - "or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint:{$cargo_toml_lint_cfg}" - )] - #[help("or consider adding `{$build_rs_println}` to the top of the `build.rs`")] - LintCfgAndBuildRs { cargo_toml_lint_cfg: String, build_rs_println: String }, -} - -impl UnexpectedCfgCargoHelp { - fn cargo_toml_lint_cfg(unescaped: &str) -> String { - format!( - "\n [lints.rust]\n unexpected_cfgs = {{ level = \"warn\", check-cfg = ['{unescaped}'] }}" - ) - } - - pub(crate) fn lint_cfg(unescaped: &str) -> Self { - UnexpectedCfgCargoHelp::LintCfg { - cargo_toml_lint_cfg: Self::cargo_toml_lint_cfg(unescaped), - } - } - - pub(crate) fn lint_cfg_and_build_rs(unescaped: &str, escaped: &str) -> Self { - UnexpectedCfgCargoHelp::LintCfgAndBuildRs { - cargo_toml_lint_cfg: Self::cargo_toml_lint_cfg(unescaped), - build_rs_println: format!("println!(\"cargo::rustc-check-cfg={escaped}\");"), - } - } -} - -#[derive(Subdiagnostic)] -#[help("to expect this configuration use `{$cmdline_arg}`")] -pub(crate) struct UnexpectedCfgRustcHelp { - pub cmdline_arg: String, -} - -impl UnexpectedCfgRustcHelp { - pub(crate) fn new(unescaped: &str) -> Self { - Self { cmdline_arg: format!("--check-cfg={unescaped}") } - } -} - -#[derive(Subdiagnostic)] -#[note( - "using a cfg inside a {$macro_kind} will use the cfgs from the destination crate and not the ones from the defining crate" -)] -#[help("try referring to `{$macro_name}` crate for guidance on how handle this unexpected cfg")] -pub(crate) struct UnexpectedCfgRustcMacroHelp { - pub macro_kind: &'static str, - pub macro_name: Symbol, -} - -#[derive(Subdiagnostic)] -#[note( - "using a cfg inside a {$macro_kind} will use the cfgs from the destination crate and not the ones from the defining crate" -)] -#[help("try referring to `{$macro_name}` crate for guidance on how handle this unexpected cfg")] -#[help( - "the {$macro_kind} `{$macro_name}` may come from an old version of the `{$crate_name}` crate, try updating your dependency with `cargo update -p {$crate_name}`" -)] -pub(crate) struct UnexpectedCfgCargoMacroHelp { - pub macro_kind: &'static str, - pub macro_name: Symbol, - pub crate_name: Symbol, -} - -#[derive(Diagnostic)] -#[diag("unexpected `cfg` condition name: `{$name}`")] -pub(crate) struct UnexpectedCfgName { - #[subdiagnostic] - pub code_sugg: unexpected_cfg_name::CodeSuggestion, - #[subdiagnostic] - pub invocation_help: unexpected_cfg_name::InvocationHelp, - - pub name: Symbol, -} - -pub(crate) mod unexpected_cfg_name { - use rustc_errors::DiagSymbolList; - use rustc_macros::Subdiagnostic; - use rustc_span::{Ident, Span, Symbol}; - - #[derive(Subdiagnostic)] - pub(crate) enum CodeSuggestion { - #[help("consider defining some features in `Cargo.toml`")] - DefineFeatures, - #[multipart_suggestion( - "there is a similar config predicate: `version(\"..\")`", - applicability = "machine-applicable" - )] - VersionSyntax { - #[suggestion_part(code = "(")] - between_name_and_value: Span, - #[suggestion_part(code = ")")] - after_value: Span, - }, - #[suggestion( - "there is a config with a similar name and value", - applicability = "maybe-incorrect", - code = "{code}" - )] - SimilarNameAndValue { - #[primary_span] - span: Span, - code: String, - }, - #[suggestion( - "there is a config with a similar name and no value", - applicability = "maybe-incorrect", - code = "{code}" - )] - SimilarNameNoValue { - #[primary_span] - span: Span, - code: String, - }, - #[suggestion( - "there is a config with a similar name and different values", - applicability = "maybe-incorrect", - code = "{code}" - )] - SimilarNameDifferentValues { - #[primary_span] - span: Span, - code: String, - #[subdiagnostic] - expected: Option, - }, - #[suggestion( - "there is a config with a similar name", - applicability = "maybe-incorrect", - code = "{code}" - )] - SimilarName { - #[primary_span] - span: Span, - code: String, - #[subdiagnostic] - expected: Option, - }, - SimilarValues { - #[subdiagnostic] - with_similar_values: Vec, - #[subdiagnostic] - expected_names: Option, - }, - #[suggestion( - "you may have meant to use `{$literal}` (notice the capitalization). Doing so makes this predicate evaluate to `{$literal}` unconditionally", - applicability = "machine-applicable", - style = "verbose", - code = "{literal}" - )] - BooleanLiteral { - #[primary_span] - span: Span, - literal: bool, - }, - } - - #[derive(Subdiagnostic)] - #[help("expected values for `{$best_match}` are: {$possibilities}")] - pub(crate) struct ExpectedValues { - pub best_match: Symbol, - pub possibilities: DiagSymbolList, - } - - #[derive(Subdiagnostic)] - #[suggestion( - "found config with similar value", - applicability = "maybe-incorrect", - code = "{code}" - )] - pub(crate) struct FoundWithSimilarValue { - #[primary_span] - pub span: Span, - pub code: String, - } - - #[derive(Subdiagnostic)] - #[help_once( - "expected names are: {$possibilities}{$and_more -> - [0] {\"\"} - *[other] {\" \"}and {$and_more} more - }" - )] - pub(crate) struct ExpectedNames { - pub possibilities: DiagSymbolList, - pub and_more: usize, - } - - #[derive(Subdiagnostic)] - pub(crate) enum InvocationHelp { - #[note( - "see for more information about checking conditional configuration" - )] - Cargo { - #[subdiagnostic] - macro_help: Option, - #[subdiagnostic] - help: Option, - }, - #[note( - "see for more information about checking conditional configuration" - )] - Rustc { - #[subdiagnostic] - macro_help: Option, - #[subdiagnostic] - help: super::UnexpectedCfgRustcHelp, - }, - } -} - -#[derive(Diagnostic)] -#[diag( - "unexpected `cfg` condition value: {$has_value -> - [true] `{$value}` - *[false] (none) - }" -)] -pub(crate) struct UnexpectedCfgValue { - #[subdiagnostic] - pub code_sugg: unexpected_cfg_value::CodeSuggestion, - #[subdiagnostic] - pub invocation_help: unexpected_cfg_value::InvocationHelp, - - pub has_value: bool, - pub value: String, -} - -pub(crate) mod unexpected_cfg_value { - use rustc_errors::DiagSymbolList; - use rustc_macros::Subdiagnostic; - use rustc_span::{Span, Symbol}; - - #[derive(Subdiagnostic)] - pub(crate) enum CodeSuggestion { - ChangeValue { - #[subdiagnostic] - expected_values: ExpectedValues, - #[subdiagnostic] - suggestion: Option, - }, - #[note("no expected value for `{$name}`")] - RemoveValue { - #[subdiagnostic] - suggestion: Option, - - name: Symbol, - }, - #[note("no expected values for `{$name}`")] - RemoveCondition { - #[subdiagnostic] - suggestion: RemoveConditionSuggestion, - - name: Symbol, - }, - ChangeName { - #[subdiagnostic] - suggestions: Vec, - }, - } - - #[derive(Subdiagnostic)] - pub(crate) enum ChangeValueSuggestion { - #[suggestion( - "there is a expected value with a similar name", - code = r#""{best_match}""#, - applicability = "maybe-incorrect" - )] - SimilarName { - #[primary_span] - span: Span, - best_match: Symbol, - }, - #[suggestion( - "specify a config value", - code = r#" = "{first_possibility}""#, - applicability = "maybe-incorrect" - )] - SpecifyValue { - #[primary_span] - span: Span, - first_possibility: Symbol, - }, - } - - #[derive(Subdiagnostic)] - #[suggestion("remove the value", code = "", applicability = "maybe-incorrect")] - pub(crate) struct RemoveValueSuggestion { - #[primary_span] - pub span: Span, - } - - #[derive(Subdiagnostic)] - #[suggestion("remove the condition", code = "", applicability = "maybe-incorrect")] - pub(crate) struct RemoveConditionSuggestion { - #[primary_span] - pub span: Span, - } - - #[derive(Subdiagnostic)] - #[note( - "expected values for `{$name}` are: {$have_none_possibility -> - [true] {\"(none), \"} - *[false] {\"\"} - }{$possibilities}{$and_more -> - [0] {\"\"} - *[other] {\" \"}and {$and_more} more - }" - )] - pub(crate) struct ExpectedValues { - pub name: Symbol, - pub have_none_possibility: bool, - pub possibilities: DiagSymbolList, - pub and_more: usize, - } - - #[derive(Subdiagnostic)] - #[suggestion( - "`{$value}` is an expected value for `{$name}`", - code = "{name}", - applicability = "maybe-incorrect", - style = "verbose" - )] - pub(crate) struct ChangeNameSuggestion { - #[primary_span] - pub span: Span, - pub name: Symbol, - pub value: Symbol, - } - - #[derive(Subdiagnostic)] - pub(crate) enum InvocationHelp { - #[note( - "see for more information about checking conditional configuration" - )] - Cargo { - #[subdiagnostic] - help: Option, - #[subdiagnostic] - macro_help: Option, - }, - #[note( - "see for more information about checking conditional configuration" - )] - Rustc { - #[subdiagnostic] - help: Option, - #[subdiagnostic] - macro_help: Option, - }, - } - - #[derive(Subdiagnostic)] - pub(crate) enum CargoHelp { - #[help("consider adding `{$value}` as a feature in `Cargo.toml`")] - AddFeature { - value: Symbol, - }, - #[help("consider defining some features in `Cargo.toml`")] - DefineFeatures, - Other(#[subdiagnostic] super::UnexpectedCfgCargoHelp), - } -} - #[derive(Diagnostic)] #[diag("creating a {$shared_label}reference to mutable static")] pub(crate) struct RefOfMutStatic<'a> { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 8cbd2456fccd..94f03125ae8e 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -12,7 +12,7 @@ use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::def_id::DefPathHash; pub use rustc_span::edition::Edition; -use rustc_span::{HashStableContext, Ident, Span, Symbol, sym}; +use rustc_span::{HashStableContext, Ident, Symbol, sym}; use serde::{Deserialize, Serialize}; pub use self::Level::*; @@ -652,12 +652,6 @@ pub enum DeprecatedSinceKind { InVersion(String), } -#[derive(Debug)] -pub enum AttributeLintKind { - UnexpectedCfgName((Symbol, Span), Option<(Symbol, Span)>), - UnexpectedCfgValue((Symbol, Span), Option<(Symbol, Span)>), -} - pub type RegisteredTools = FxIndexSet; /// Declares a static item of type `&'static Lint`. diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 8251050b6aea..1f18b178489d 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -344,7 +344,30 @@ pub fn dyn_buffer_lint< lint, Some(span.into()), node_id, - DecorateDiagCompat::Dynamic(Box::new(|dcx, level, _| callback(dcx, level))), + DecorateDiagCompat(Box::new(|dcx, level, _| callback(dcx, level))), + ) + } + + pub fn dyn_buffer_lint_sess< + F: for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &Session) -> Diag<'a, ()> + + DynSync + + DynSend + + 'static, + >( + &self, + lint: &'static Lint, + span: impl Into, + node_id: NodeId, + callback: F, + ) { + self.opt_span_buffer_lint( + lint, + Some(span.into()), + node_id, + DecorateDiagCompat(Box::new(|dcx, level, sess| { + let sess = sess.downcast_ref::().expect("expected a `Session`"); + callback(dcx, level, sess) + })), ) } diff --git a/tests/ui/check-cfg/report-in-external-macros.cargo.stderr b/tests/ui/check-cfg/report-in-external-macros.cargo.stderr index b474322d6afa..18d361ccdbd7 100644 --- a/tests/ui/check-cfg/report-in-external-macros.cargo.stderr +++ b/tests/ui/check-cfg/report-in-external-macros.cargo.stderr @@ -7,7 +7,6 @@ LL | cfg_macro::my_lib_macro!(); = help: expected names are: `feature` and 32 more = note: using a cfg inside a macro will use the cfgs from the destination crate and not the ones from the defining crate = help: try referring to `cfg_macro::my_lib_macro` crate for guidance on how handle this unexpected cfg - = help: the macro `cfg_macro::my_lib_macro` may come from an old version of the `cfg_macro` crate, try updating your dependency with `cargo update -p cfg_macro` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default = note: this warning originates in the macro `cfg_macro::my_lib_macro` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -21,7 +20,6 @@ LL | cfg_macro::my_lib_macro_value!(); = note: expected values for `panic` are: `abort`, `immediate-abort`, and `unwind` = note: using a cfg inside a macro will use the cfgs from the destination crate and not the ones from the defining crate = help: try referring to `cfg_macro::my_lib_macro_value` crate for guidance on how handle this unexpected cfg - = help: the macro `cfg_macro::my_lib_macro_value` may come from an old version of the `cfg_macro` crate, try updating your dependency with `cargo update -p cfg_macro` = note: see for more information about checking conditional configuration = note: this warning originates in the macro `cfg_macro::my_lib_macro_value` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -34,7 +32,6 @@ LL | cfg_macro::my_lib_macro_feature!(); = note: no expected values for `feature` = note: using a cfg inside a macro will use the cfgs from the destination crate and not the ones from the defining crate = help: try referring to `cfg_macro::my_lib_macro_feature` crate for guidance on how handle this unexpected cfg - = help: the macro `cfg_macro::my_lib_macro_feature` may come from an old version of the `cfg_macro` crate, try updating your dependency with `cargo update -p cfg_macro` = note: see for more information about checking conditional configuration = note: this warning originates in the macro `cfg_macro::my_lib_macro_feature` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/triagebot.toml b/triagebot.toml index d22723b42d10..33bd5ce1e597 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1053,7 +1053,7 @@ cc = ["@Nadrieril"] message = "Some changes occurred in cfg and check-cfg configuration" cc = ["@Urgau"] -[mentions."compiler/rustc_lint/src/early/diagnostics/check_cfg.rs"] +[mentions."compiler/rustc_attr_parsing/src/attributes/diagnostic/check_cfg.rs"] message = "Some changes occurred in check-cfg diagnostics" cc = ["@Urgau"]