From ab780fa48aa49463301620ec825da3376c0171a3 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sat, 14 Dec 2024 17:33:57 +0100 Subject: Access `TyCtxt` from early diagnostic decoration --- compiler/rustc_interface/src/passes.rs | 2 + compiler/rustc_lint/src/context.rs | 36 +- compiler/rustc_lint/src/context/diagnostics.rs | 462 -------------------- .../src/context/diagnostics/check_cfg.rs | 362 ---------------- compiler/rustc_lint/src/early.rs | 65 ++- compiler/rustc_lint/src/early/diagnostics.rs | 468 +++++++++++++++++++++ .../rustc_lint/src/early/diagnostics/check_cfg.rs | 362 ++++++++++++++++ 7 files changed, 885 insertions(+), 872 deletions(-) delete mode 100644 compiler/rustc_lint/src/context/diagnostics.rs delete mode 100644 compiler/rustc_lint/src/context/diagnostics/check_cfg.rs create mode 100644 compiler/rustc_lint/src/early/diagnostics.rs create mode 100644 compiler/rustc_lint/src/early/diagnostics/check_cfg.rs diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 430bc7db077..f8351a81bec 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -76,6 +76,7 @@ fn pre_expansion_lint<'a>( || { rustc_lint::check_ast_node( sess, + None, features, true, lint_store, @@ -310,6 +311,7 @@ fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) { let lint_store = unerased_lint_store(tcx.sess); rustc_lint::check_ast_node( sess, + Some(tcx), tcx.features(), false, lint_store, diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 44c7888a530..d8d901698d6 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -20,7 +20,7 @@ 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::{ - BuiltinLintDiag, FutureIncompatibleInfo, Level, Lint, LintBuffer, LintExpectationId, LintId, + FutureIncompatibleInfo, Level, Lint, LintBuffer, LintExpectationId, LintId, }; use rustc_session::{LintStoreMarker, Session}; use rustc_span::Span; @@ -33,8 +33,6 @@ use self::TargetLint::*; use crate::levels::LintLevelsBuilder; use crate::passes::{EarlyLintPassObject, LateLintPassObject}; -mod diagnostics; - type EarlyLintPassFactory = dyn Fn() -> EarlyLintPassObject + sync::DynSend + sync::DynSync; type LateLintPassFactory = dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync; @@ -511,38 +509,6 @@ pub struct EarlyContext<'a> { pub buffered: LintBuffer, } -impl EarlyContext<'_> { - /// Emit a lint at the appropriate level, with an associated span and an existing - /// diagnostic. - /// - /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature - #[rustc_lint_diagnostics] - pub fn span_lint_with_diagnostics( - &self, - lint: &'static Lint, - span: MultiSpan, - diagnostic: BuiltinLintDiag, - ) { - self.opt_span_lint_with_diagnostics(lint, Some(span), diagnostic); - } - - /// Emit a lint at the appropriate level, with an optional associated span and an existing - /// diagnostic. - /// - /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature - #[rustc_lint_diagnostics] - pub fn opt_span_lint_with_diagnostics( - &self, - lint: &'static Lint, - span: Option, - diagnostic: BuiltinLintDiag, - ) { - self.opt_span_lint(lint, span, |diag| { - diagnostics::decorate_lint(self.sess(), diagnostic, diag); - }); - } -} - pub trait LintContext { fn sess(&self) -> &Session; diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs deleted file mode 100644 index a3731e31c2b..00000000000 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ /dev/null @@ -1,462 +0,0 @@ -#![allow(rustc::diagnostic_outside_of_impl)] -#![allow(rustc::untranslatable_diagnostic)] - -use std::borrow::Cow; - -use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; -use rustc_errors::{ - Applicability, Diag, DiagArgValue, LintDiagnostic, elided_lifetime_in_path_suggestion, -}; -use rustc_middle::middle::stability; -use rustc_session::Session; -use rustc_session::lint::{BuiltinLintDiag, ElidedLifetimeResolution}; -use rustc_span::BytePos; -use rustc_span::symbol::kw; -use tracing::debug; - -use crate::lints::{self, ElidedNamedLifetime}; - -mod check_cfg; - -pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Diag<'_, ()>) { - match diagnostic { - BuiltinLintDiag::UnicodeTextFlow(comment_span, content) => { - let spans: Vec<_> = content - .char_indices() - .filter_map(|(i, c)| { - TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| { - let lo = comment_span.lo() + BytePos(2 + i as u32); - (c, comment_span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32))) - }) - }) - .collect(); - let characters = spans - .iter() - .map(|&(c, span)| lints::UnicodeCharNoteSub { span, c_debug: format!("{c:?}") }) - .collect(); - let suggestions = (!spans.is_empty()).then_some(lints::UnicodeTextFlowSuggestion { - spans: spans.iter().map(|(_c, span)| *span).collect(), - }); - - lints::UnicodeTextFlow { - comment_span, - characters, - suggestions, - num_codepoints: spans.len(), - } - .decorate_lint(diag); - } - BuiltinLintDiag::AbsPathWithModule(mod_span) => { - let (replacement, applicability) = match sess.source_map().span_to_snippet(mod_span) { - Ok(ref s) => { - // FIXME(Manishearth) ideally the emitting code - // can tell us whether or not this is global - let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" }; - - (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable) - } - Err(_) => ("crate::".to_string(), Applicability::HasPlaceholders), - }; - lints::AbsPathWithModule { - sugg: lints::AbsPathWithModuleSugg { span: mod_span, applicability, replacement }, - } - .decorate_lint(diag); - } - BuiltinLintDiag::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident } => { - lints::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident } - .decorate_lint(diag) - } - BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => { - lints::MacroExpandedMacroExportsAccessedByAbsolutePaths { definition: span_def } - .decorate_lint(diag) - } - - BuiltinLintDiag::ElidedLifetimesInPaths(n, path_span, incl_angl_brckt, insertion_span) => { - lints::ElidedLifetimesInPaths { - subdiag: elided_lifetime_in_path_suggestion( - sess.source_map(), - n, - path_span, - incl_angl_brckt, - insertion_span, - ), - } - .decorate_lint(diag); - } - BuiltinLintDiag::UnknownCrateTypes { span, candidate } => { - let sugg = candidate.map(|candidate| lints::UnknownCrateTypesSub { span, candidate }); - lints::UnknownCrateTypes { sugg }.decorate_lint(diag); - } - BuiltinLintDiag::UnusedImports { - remove_whole_use, - num_to_remove, - remove_spans, - test_module_span, - span_snippets, - } => { - let sugg = if remove_whole_use { - lints::UnusedImportsSugg::RemoveWholeUse { span: remove_spans[0] } - } else { - lints::UnusedImportsSugg::RemoveImports { remove_spans, num_to_remove } - }; - let test_module_span = - test_module_span.map(|span| sess.source_map().guess_head_span(span)); - - lints::UnusedImports { - sugg, - test_module_span, - num_snippets: span_snippets.len(), - span_snippets: DiagArgValue::StrListSepByAnd( - span_snippets.into_iter().map(Cow::Owned).collect(), - ), - } - .decorate_lint(diag); - } - BuiltinLintDiag::RedundantImport(spans, ident) => { - let subs = spans - .into_iter() - .map(|(span, is_imported)| { - (match (span.is_dummy(), is_imported) { - (false, true) => lints::RedundantImportSub::ImportedHere, - (false, false) => lints::RedundantImportSub::DefinedHere, - (true, true) => lints::RedundantImportSub::ImportedPrelude, - (true, false) => lints::RedundantImportSub::DefinedPrelude, - })(span) - }) - .collect(); - lints::RedundantImport { subs, ident }.decorate_lint(diag); - } - BuiltinLintDiag::DeprecatedMacro { - suggestion, - suggestion_span, - note, - path, - since_kind, - } => { - let sub = suggestion.map(|suggestion| stability::DeprecationSuggestion { - span: suggestion_span, - kind: "macro".to_owned(), - suggestion, - }); - - stability::Deprecated { sub, kind: "macro".to_owned(), path, note, since_kind } - .decorate_lint(diag); - } - BuiltinLintDiag::UnusedDocComment(attr_span) => { - lints::UnusedDocComment { span: attr_span }.decorate_lint(diag); - } - BuiltinLintDiag::PatternsInFnsWithoutBody { span: remove_span, ident, is_foreign } => { - let sub = lints::PatternsInFnsWithoutBodySub { ident, span: remove_span }; - if is_foreign { - lints::PatternsInFnsWithoutBody::Foreign { sub } - } else { - lints::PatternsInFnsWithoutBody::Bodiless { sub } - } - .decorate_lint(diag); - } - BuiltinLintDiag::MissingAbi(label_span, default_abi) => { - lints::MissingAbi { span: label_span, default_abi: default_abi.name() } - .decorate_lint(diag); - } - BuiltinLintDiag::LegacyDeriveHelpers(label_span) => { - lints::LegacyDeriveHelpers { span: label_span }.decorate_lint(diag); - } - BuiltinLintDiag::OrPatternsBackCompat(suggestion_span, suggestion) => { - lints::OrPatternsBackCompat { span: suggestion_span, suggestion }.decorate_lint(diag); - } - BuiltinLintDiag::ReservedPrefix(label_span, prefix) => { - lints::ReservedPrefix { - label: label_span, - suggestion: label_span.shrink_to_hi(), - prefix, - } - .decorate_lint(diag); - } - BuiltinLintDiag::RawPrefix(label_span) => { - lints::RawPrefix { label: label_span, suggestion: label_span.shrink_to_hi() } - .decorate_lint(diag); - } - BuiltinLintDiag::ReservedString { is_string, suggestion } => { - if is_string { - lints::ReservedString { suggestion }.decorate_lint(diag); - } else { - lints::ReservedMultihash { suggestion }.decorate_lint(diag); - } - } - BuiltinLintDiag::UnusedBuiltinAttribute { attr_name, macro_name, invoc_span } => { - lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name }.decorate_lint(diag); - } - BuiltinLintDiag::TrailingMacro(is_trailing, name) => { - lints::TrailingMacro { is_trailing, name }.decorate_lint(diag); - } - BuiltinLintDiag::BreakWithLabelAndLoop(sugg_span) => { - lints::BreakWithLabelAndLoop { - sub: lints::BreakWithLabelAndLoopSub { - left: sugg_span.shrink_to_lo(), - right: sugg_span.shrink_to_hi(), - }, - } - .decorate_lint(diag); - } - BuiltinLintDiag::UnexpectedCfgName(name, value) => { - check_cfg::unexpected_cfg_name(sess, name, value).decorate_lint(diag); - } - BuiltinLintDiag::UnexpectedCfgValue(name, value) => { - check_cfg::unexpected_cfg_value(sess, name, value).decorate_lint(diag); - } - BuiltinLintDiag::DeprecatedWhereclauseLocation(left_sp, sugg) => { - let suggestion = match sugg { - Some((right_sp, sugg)) => lints::DeprecatedWhereClauseLocationSugg::MoveToEnd { - left: left_sp, - right: right_sp, - sugg, - }, - None => lints::DeprecatedWhereClauseLocationSugg::RemoveWhere { span: left_sp }, - }; - lints::DeprecatedWhereClauseLocation { suggestion }.decorate_lint(diag); - } - BuiltinLintDiag::MissingUnsafeOnExtern { suggestion } => { - lints::MissingUnsafeOnExtern { suggestion }.decorate_lint(diag); - } - BuiltinLintDiag::SingleUseLifetime { - param_span, - use_span: Some((use_span, elide)), - deletion_span, - ident, - } => { - debug!(?param_span, ?use_span, ?deletion_span); - let suggestion = if let Some(deletion_span) = deletion_span { - let (use_span, replace_lt) = if elide { - let use_span = sess.source_map().span_extend_while_whitespace(use_span); - (use_span, String::new()) - } else { - (use_span, "'_".to_owned()) - }; - debug!(?deletion_span, ?use_span); - - // issue 107998 for the case such as a wrong function pointer type - // `deletion_span` is empty and there is no need to report lifetime uses here - let deletion_span = - if deletion_span.is_empty() { None } else { Some(deletion_span) }; - Some(lints::SingleUseLifetimeSugg { deletion_span, use_span, replace_lt }) - } else { - None - }; - - lints::SingleUseLifetime { suggestion, param_span, use_span, ident } - .decorate_lint(diag); - } - BuiltinLintDiag::SingleUseLifetime { use_span: None, deletion_span, ident, .. } => { - debug!(?deletion_span); - lints::UnusedLifetime { deletion_span, ident }.decorate_lint(diag); - } - BuiltinLintDiag::NamedArgumentUsedPositionally { - position_sp_to_replace, - position_sp_for_msg, - named_arg_sp, - named_arg_name, - is_formatting_arg, - } => { - let (suggestion, name) = if let Some(positional_arg_to_replace) = position_sp_to_replace - { - let mut name = named_arg_name.clone(); - if is_formatting_arg { - name.push('$') - }; - let span_to_replace = if let Ok(positional_arg_content) = - sess.source_map().span_to_snippet(positional_arg_to_replace) - && positional_arg_content.starts_with(':') - { - positional_arg_to_replace.shrink_to_lo() - } else { - positional_arg_to_replace - }; - (Some(span_to_replace), name) - } else { - (None, String::new()) - }; - - lints::NamedArgumentUsedPositionally { - named_arg_sp, - position_label_sp: position_sp_for_msg, - suggestion, - name, - named_arg_name, - } - .decorate_lint(diag); - } - BuiltinLintDiag::ByteSliceInPackedStructWithDerive { ty } => { - lints::ByteSliceInPackedStructWithDerive { ty }.decorate_lint(diag); - } - BuiltinLintDiag::UnusedExternCrate { removal_span } => { - lints::UnusedExternCrate { removal_span }.decorate_lint(diag); - } - BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span } => { - let suggestion_span = vis_span.between(ident_span); - let code = if vis_span.is_empty() { "use " } else { " use " }; - - lints::ExternCrateNotIdiomatic { span: suggestion_span, code }.decorate_lint(diag); - } - BuiltinLintDiag::AmbiguousGlobImports { diag: ambiguity } => { - lints::AmbiguousGlobImports { ambiguity }.decorate_lint(diag); - } - BuiltinLintDiag::AmbiguousGlobReexports { - name, - namespace, - first_reexport_span, - duplicate_reexport_span, - } => { - lints::AmbiguousGlobReexports { - first_reexport: first_reexport_span, - duplicate_reexport: duplicate_reexport_span, - name, - namespace, - } - .decorate_lint(diag); - } - BuiltinLintDiag::HiddenGlobReexports { - name, - namespace, - glob_reexport_span, - private_item_span, - } => { - lints::HiddenGlobReexports { - glob_reexport: glob_reexport_span, - private_item: private_item_span, - - name, - namespace, - } - .decorate_lint(diag); - } - BuiltinLintDiag::UnusedQualifications { removal_span } => { - lints::UnusedQualifications { removal_span }.decorate_lint(diag); - } - BuiltinLintDiag::UnsafeAttrOutsideUnsafe { - attribute_name_span, - sugg_spans: (left, right), - } => { - lints::UnsafeAttrOutsideUnsafe { - span: attribute_name_span, - suggestion: lints::UnsafeAttrOutsideUnsafeSuggestion { left, right }, - } - .decorate_lint(diag); - } - BuiltinLintDiag::AssociatedConstElidedLifetime { - elided, - span: lt_span, - lifetimes_in_scope, - } => { - let lt_span = if elided { lt_span.shrink_to_hi() } else { lt_span }; - let code = if elided { "'static " } else { "'static" }; - lints::AssociatedConstElidedLifetime { - span: lt_span, - code, - elided, - lifetimes_in_scope, - } - .decorate_lint(diag); - } - BuiltinLintDiag::RedundantImportVisibility { max_vis, span: vis_span, import_vis } => { - lints::RedundantImportVisibility { span: vis_span, help: (), max_vis, import_vis } - .decorate_lint(diag); - } - BuiltinLintDiag::UnknownDiagnosticAttribute { span: typo_span, typo_name } => { - let typo = typo_name.map(|typo_name| lints::UnknownDiagnosticAttributeTypoSugg { - span: typo_span, - typo_name, - }); - lints::UnknownDiagnosticAttribute { typo }.decorate_lint(diag); - } - BuiltinLintDiag::MacroUseDeprecated => { - lints::MacroUseDeprecated.decorate_lint(diag); - } - BuiltinLintDiag::UnusedMacroUse => lints::UnusedMacroUse.decorate_lint(diag), - BuiltinLintDiag::PrivateExternCrateReexport { source: ident, extern_crate_span } => { - lints::PrivateExternCrateReexport { ident, sugg: extern_crate_span.shrink_to_lo() } - .decorate_lint(diag); - } - BuiltinLintDiag::UnusedLabel => lints::UnusedLabel.decorate_lint(diag), - BuiltinLintDiag::MacroIsPrivate(ident) => { - lints::MacroIsPrivate { ident }.decorate_lint(diag); - } - BuiltinLintDiag::UnusedMacroDefinition(name) => { - lints::UnusedMacroDefinition { name }.decorate_lint(diag); - } - BuiltinLintDiag::MacroRuleNeverUsed(n, name) => { - lints::MacroRuleNeverUsed { n: n + 1, name }.decorate_lint(diag); - } - BuiltinLintDiag::UnstableFeature(msg) => { - lints::UnstableFeature { msg }.decorate_lint(diag); - } - BuiltinLintDiag::AvoidUsingIntelSyntax => { - lints::AvoidIntelSyntax.decorate_lint(diag); - } - BuiltinLintDiag::AvoidUsingAttSyntax => { - lints::AvoidAttSyntax.decorate_lint(diag); - } - BuiltinLintDiag::IncompleteInclude => { - lints::IncompleteInclude.decorate_lint(diag); - } - BuiltinLintDiag::UnnameableTestItems => { - lints::UnnameableTestItems.decorate_lint(diag); - } - BuiltinLintDiag::DuplicateMacroAttribute => { - lints::DuplicateMacroAttribute.decorate_lint(diag); - } - BuiltinLintDiag::CfgAttrNoAttributes => { - lints::CfgAttrNoAttributes.decorate_lint(diag); - } - BuiltinLintDiag::MissingFragmentSpecifier => { - lints::MissingFragmentSpecifier.decorate_lint(diag); - } - BuiltinLintDiag::MetaVariableStillRepeating(name) => { - lints::MetaVariableStillRepeating { name }.decorate_lint(diag); - } - BuiltinLintDiag::MetaVariableWrongOperator => { - lints::MetaVariableWrongOperator.decorate_lint(diag); - } - BuiltinLintDiag::DuplicateMatcherBinding => { - lints::DuplicateMatcherBinding.decorate_lint(diag); - } - BuiltinLintDiag::UnknownMacroVariable(name) => { - lints::UnknownMacroVariable { name }.decorate_lint(diag); - } - BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => { - lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag) - } - BuiltinLintDiag::WasmCAbi => lints::WasmCAbi.decorate_lint(diag), - BuiltinLintDiag::IllFormedAttributeInput { suggestions } => { - lints::IllFormedAttributeInput { - num_suggestions: suggestions.len(), - suggestions: DiagArgValue::StrListSepByAnd( - suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), - ), - } - .decorate_lint(diag) - } - BuiltinLintDiag::InnerAttributeUnstable { is_macro } => if is_macro { - lints::InnerAttributeUnstable::InnerMacroAttribute - } else { - lints::InnerAttributeUnstable::CustomInnerAttribute - } - .decorate_lint(diag), - BuiltinLintDiag::OutOfScopeMacroCalls { path } => { - lints::OutOfScopeMacroCalls { path }.decorate_lint(diag) - } - BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by } => { - lints::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.decorate_lint(diag) - } - BuiltinLintDiag::ElidedNamedLifetimes { elided: (span, kind), resolution } => { - match resolution { - ElidedLifetimeResolution::Static => { - ElidedNamedLifetime { span, kind, name: kw::StaticLifetime, declaration: None } - } - ElidedLifetimeResolution::Param(name, declaration) => { - ElidedNamedLifetime { span, kind, name, declaration: Some(declaration) } - } - } - .decorate_lint(diag) - } - } -} diff --git a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs b/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs deleted file mode 100644 index 63a722f6067..00000000000 --- a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs +++ /dev/null @@ -1,362 +0,0 @@ -use rustc_hir::def_id::LOCAL_CRATE; -use rustc_middle::bug; -use rustc_session::Session; -use rustc_session::config::ExpectedValues; -use rustc_span::edit_distance::find_best_match_for_name; -use rustc_span::symbol::Ident; -use rustc_span::{ExpnKind, Span, Symbol, sym}; - -use crate::lints; - -const MAX_CHECK_CFG_NAMES_OR_VALUES: usize = 35; - -fn sort_and_truncate_possibilities( - sess: &Session, - mut possibilities: Vec, -) -> (Vec, usize) { - let n_possibilities = if sess.opts.unstable_opts.check_cfg_all_expected { - possibilities.len() - } else { - std::cmp::min(possibilities.len(), MAX_CHECK_CFG_NAMES_OR_VALUES) - }; - - possibilities.sort_by(|s1, s2| s1.as_str().cmp(s2.as_str())); - - let and_more = possibilities.len().saturating_sub(n_possibilities); - possibilities.truncate(n_possibilities); - (possibilities, and_more) -} - -enum EscapeQuotes { - Yes, - No, -} - -fn to_check_cfg_arg(name: Ident, value: Option, quotes: EscapeQuotes) -> String { - if let Some(value) = value { - let value = str::escape_debug(value.as_str()).to_string(); - let values = match quotes { - EscapeQuotes::Yes => format!("\\\"{}\\\"", value.replace("\"", "\\\\\\\\\"")), - EscapeQuotes::No => format!("\"{value}\""), - }; - format!("cfg({name}, values({values}))") - } else { - format!("cfg({name})") - } -} - -fn cargo_help_sub( - sess: &Session, - inst: &impl Fn(EscapeQuotes) -> String, -) -> lints::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 matches!(&sess.opts.crate_name, Some(crate_name) if crate_name == "build_script_build") { - lints::UnexpectedCfgCargoHelp::lint_cfg(unescaped) - } else { - lints::UnexpectedCfgCargoHelp::lint_cfg_and_build_rs(unescaped, &inst(EscapeQuotes::Yes)) - } -} - -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 }) - } else { - None - } -} - -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 - { - Some(lints::UnexpectedCfgCargoMacroHelp { - macro_kind: macro_kind.descr(), - macro_name, - // FIXME: Get access to a `TyCtxt` from an `EarlyContext` - // crate_name: cx.tcx.crate_name(def_id.krate), - }) - } else { - None - } -} - -pub(super) fn unexpected_cfg_name( - sess: &Session, - (name, name_span): (Symbol, Span), - value: Option<(Symbol, Span)>, -) -> lints::UnexpectedCfgName { - #[allow(rustc::potential_query_instability)] - let possibilities: Vec = sess.psess.check_config.expecteds.keys().copied().collect(); - - let mut names_possibilities: Vec<_> = if value.is_none() { - // We later sort and display all the possibilities, so the order here does not matter. - #[allow(rustc::potential_query_instability)] - sess.psess - .check_config - .expecteds - .iter() - .filter_map(|(k, v)| match v { - ExpectedValues::Some(v) if v.contains(&Some(name)) => Some(k), - _ => None, - }) - .collect() - } else { - Vec::new() - }; - - let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); - let is_from_external_macro = rustc_middle::lint::in_external_macro(sess, name_span); - let mut is_feature_cfg = name == sym::feature; - - let code_sugg = if is_feature_cfg && is_from_cargo { - lints::unexpected_cfg_name::CodeSuggestion::DefineFeatures - // Suggest the most probable if we found one - } else if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) { - is_feature_cfg |= best_match == sym::feature; - - if let Some(ExpectedValues::Some(best_match_values)) = - sess.psess.check_config.expecteds.get(&best_match) - { - // We will soon sort, so the initial order does not matter. - #[allow(rustc::potential_query_instability)] - let mut possibilities = best_match_values.iter().flatten().collect::>(); - possibilities.sort_by_key(|s| s.as_str()); - - let get_possibilities_sub = || { - if !possibilities.is_empty() { - let possibilities = - possibilities.iter().copied().cloned().collect::>().into(); - Some(lints::unexpected_cfg_name::ExpectedValues { best_match, possibilities }) - } else { - None - } - }; - - 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 { - span: name_span, - code: best_match.to_string(), - } - } else if best_match_values.contains(&None) { - lints::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 { - span: name_span.to(value_span), - code: format!("{best_match} = \"{first_value}\""), - expected: get_possibilities_sub(), - } - } else { - lints::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 { - span: name_span, - code: best_match.to_string(), - expected: get_possibilities_sub(), - } - } - } else { - lints::unexpected_cfg_name::CodeSuggestion::SimilarName { - span: name_span, - code: best_match.to_string(), - expected: None, - } - } - } else { - let similar_values = if !names_possibilities.is_empty() && names_possibilities.len() <= 3 { - names_possibilities.sort(); - names_possibilities - .iter() - .map(|cfg_name| lints::unexpected_cfg_name::FoundWithSimilarValue { - span: name_span, - code: format!("{cfg_name} = \"{name}\""), - }) - .collect() - } else { - vec![] - }; - let expected_names = if !possibilities.is_empty() { - let (possibilities, and_more) = sort_and_truncate_possibilities(sess, possibilities); - let possibilities: Vec<_> = - possibilities.into_iter().map(|s| Ident::new(s, name_span)).collect(); - Some(lints::unexpected_cfg_name::ExpectedNames { - possibilities: possibilities.into(), - and_more, - }) - } else { - None - }; - lints::unexpected_cfg_name::CodeSuggestion::SimilarValues { - with_similar_values: similar_values, - expected_names, - } - }; - - let inst = |escape_quotes| { - to_check_cfg_arg(Ident::new(name, name_span), value.map(|(v, _s)| v), escape_quotes) - }; - - let invocation_help = if is_from_cargo { - let help = if !is_feature_cfg && !is_from_external_macro { - Some(cargo_help_sub(sess, &inst)) - } else { - None - }; - lints::unexpected_cfg_name::InvocationHelp::Cargo { - help, - macro_help: cargo_macro_help(name_span), - } - } else { - let help = lints::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No)); - lints::unexpected_cfg_name::InvocationHelp::Rustc { - help, - macro_help: rustc_macro_help(name_span), - } - }; - - lints::UnexpectedCfgName { code_sugg, invocation_help, name } -} - -pub(super) fn unexpected_cfg_value( - sess: &Session, - (name, name_span): (Symbol, Span), - value: Option<(Symbol, Span)>, -) -> lints::UnexpectedCfgValue { - let Some(ExpectedValues::Some(values)) = &sess.psess.check_config.expecteds.get(&name) else { - bug!( - "it shouldn't be possible to have a diagnostic on a value whose name is not in values" - ); - }; - let mut have_none_possibility = false; - // We later sort possibilities if it is not empty, so the - // order here does not matter. - #[allow(rustc::potential_query_instability)] - let possibilities: Vec = values - .iter() - .inspect(|a| have_none_possibility |= a.is_none()) - .copied() - .flatten() - .collect(); - - let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); - let is_from_external_macro = rustc_middle::lint::in_external_macro(sess, name_span); - - // 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 - let code_sugg = if !possibilities.is_empty() { - let expected_values = { - let (possibilities, and_more) = - sort_and_truncate_possibilities(sess, possibilities.clone()); - lints::unexpected_cfg_value::ExpectedValues { - name, - have_none_possibility, - possibilities: possibilities.into(), - and_more, - } - }; - - 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 { - span: value_span, - best_match, - }) - } else { - None - } - } else if let &[first_possibility] = &possibilities[..] { - Some(lints::unexpected_cfg_value::ChangeValueSuggestion::SpecifyValue { - span: name_span.shrink_to_hi(), - first_possibility, - }) - } else { - None - }; - - lints::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 { - span: name_span.shrink_to_hi().to(value_span), - }); - lints::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 } - }; - - // We don't want to encourage people to add values to a well-known names, as these are - // defined by rustc/Rust itself. Users can still do this if they wish, but should not be - // encouraged to do so. - let can_suggest_adding_value = !sess.psess.check_config.well_known_names.contains(&name) - // Except when working on rustc or the standard library itself, in which case we want to - // suggest adding these cfgs to the "normal" place because of bootstrapping reasons. As a - // basic heuristic, we use the "cheat" unstable feature enable method and the - // non-ui-testing enabled option. - || (matches!(sess.psess.unstable_features, rustc_feature::UnstableFeatures::Cheat) - && !sess.opts.unstable_opts.ui_testing); - - let inst = |escape_quotes| { - to_check_cfg_arg(Ident::new(name, name_span), value.map(|(v, _s)| v), escape_quotes) - }; - - 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 }) - } else { - Some(lints::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))) - } else { - None - }; - lints::unexpected_cfg_value::InvocationHelp::Cargo { - help, - macro_help: cargo_macro_help(name_span), - } - } else { - let help = if can_suggest_adding_value { - Some(lints::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No))) - } else { - None - }; - lints::unexpected_cfg_value::InvocationHelp::Rustc { - help, - macro_help: rustc_macro_help(name_span), - } - }; - - lints::UnexpectedCfgValue { - code_sugg, - invocation_help, - has_value: value.is_some(), - value: value.map_or_else(String::new, |(v, _span)| v.to_string()), - } -} diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index a68a2a7f983..6d1081700d7 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -8,36 +8,73 @@ use rustc_ast::ptr::P; use rustc_ast::visit::{self as ast_visit, Visitor, walk_list}; use rustc_ast::{self as ast, HasAttrs}; use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_errors::MultiSpan; use rustc_feature::Features; -use rustc_middle::ty::RegisteredTools; +use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::Session; -use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; +use rustc_session::lint::{BufferedEarlyLint, BuiltinLintDiag, LintBuffer, LintPass}; use rustc_span::Span; use rustc_span::symbol::Ident; use tracing::debug; -use crate::context::{EarlyContext, LintStore}; +use crate::Lint; +use crate::context::{EarlyContext, LintContext, LintStore}; use crate::passes::{EarlyLintPass, EarlyLintPassObject}; +mod diagnostics; + macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({ $cx.pass.$f(&$cx.context, $($args),*); }) } /// Implements the AST traversal for early lint passes. `T` provides the /// `check_*` methods. -pub struct EarlyContextAndPass<'a, T: EarlyLintPass> { +pub struct EarlyContextAndPass<'a, 'b, T: EarlyLintPass> { context: EarlyContext<'a>, + tcx: Option>, pass: T, } -impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { +impl EarlyContextAndPass<'_, '_, T> { + /// Emit a lint at the appropriate level, with an associated span and an existing + /// diagnostic. + /// + /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature + #[rustc_lint_diagnostics] + pub fn span_lint_with_diagnostics( + &self, + lint: &'static Lint, + span: MultiSpan, + diagnostic: BuiltinLintDiag, + ) { + self.opt_span_lint_with_diagnostics(lint, Some(span), diagnostic); + } + + /// Emit a lint at the appropriate level, with an optional associated span and an existing + /// diagnostic. + /// + /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature + #[rustc_lint_diagnostics] + pub fn opt_span_lint_with_diagnostics( + &self, + lint: &'static Lint, + span: Option, + diagnostic: BuiltinLintDiag, + ) { + self.context.opt_span_lint(lint, span, |diag| { + diagnostics::decorate_lint(self.context.sess(), self.tcx, diagnostic, diag); + }); + } +} + +impl<'a, 'b, T: EarlyLintPass> EarlyContextAndPass<'a, 'b, T> { // This always-inlined function is for the hot call site. #[inline(always)] #[allow(rustc::diagnostic_outside_of_impl)] fn inlined_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; - self.context.opt_span_lint_with_diagnostics(lint_id.lint, span, diagnostic); + self.opt_span_lint_with_diagnostics(lint_id.lint, span, diagnostic); } } @@ -67,7 +104,7 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { } } -impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> { +impl<'a, 'b, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, 'b, T> { fn visit_coroutine_kind(&mut self, coroutine_kind: &'a ast::CoroutineKind) -> Self::Result { self.check_id(coroutine_kind.closure_id()); } @@ -313,7 +350,7 @@ pub trait EarlyCheckNode<'a>: Copy { fn attrs<'b>(self) -> &'b [ast::Attribute] where 'a: 'b; - fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>) + fn check<'b, 'c, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, 'c, T>) where 'a: 'b; } @@ -328,7 +365,7 @@ impl<'a> EarlyCheckNode<'a> for (&'a ast::Crate, &'a [ast::Attribute]) { { self.1 } - fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>) + fn check<'b, 'c, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, 'c, T>) where 'a: 'b, { @@ -348,7 +385,7 @@ impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [P(self, cx: &mut EarlyContextAndPass<'b, T>) + fn check<'b, 'c, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, 'c, T>) where 'a: 'b, { @@ -359,6 +396,7 @@ impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [P( sess: &Session, + tcx: Option>, features: &Features, pre_expansion: bool, lint_store: &LintStore, @@ -382,22 +420,23 @@ 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, check_node, context, builtin_lints); + check_ast_node_inner(sess, tcx, 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, check_node, context, pass); + check_ast_node_inner(sess, tcx, 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, pass }; + let mut cx = EarlyContextAndPass { context, tcx, 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 new file mode 100644 index 00000000000..7f136672a47 --- /dev/null +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -0,0 +1,468 @@ +#![allow(rustc::diagnostic_outside_of_impl)] +#![allow(rustc::untranslatable_diagnostic)] + +use std::borrow::Cow; + +use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; +use rustc_errors::{ + Applicability, Diag, DiagArgValue, LintDiagnostic, elided_lifetime_in_path_suggestion, +}; +use rustc_middle::middle::stability; +use rustc_middle::ty::TyCtxt; +use rustc_session::Session; +use rustc_session::lint::{BuiltinLintDiag, ElidedLifetimeResolution}; +use rustc_span::BytePos; +use rustc_span::symbol::kw; +use tracing::debug; + +use crate::lints::{self, ElidedNamedLifetime}; + +mod check_cfg; + +pub(super) fn decorate_lint( + sess: &Session, + _tcx: Option>, + diagnostic: BuiltinLintDiag, + diag: &mut Diag<'_, ()>, +) { + match diagnostic { + BuiltinLintDiag::UnicodeTextFlow(comment_span, content) => { + let spans: Vec<_> = content + .char_indices() + .filter_map(|(i, c)| { + TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| { + let lo = comment_span.lo() + BytePos(2 + i as u32); + (c, comment_span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32))) + }) + }) + .collect(); + let characters = spans + .iter() + .map(|&(c, span)| lints::UnicodeCharNoteSub { span, c_debug: format!("{c:?}") }) + .collect(); + let suggestions = (!spans.is_empty()).then_some(lints::UnicodeTextFlowSuggestion { + spans: spans.iter().map(|(_c, span)| *span).collect(), + }); + + lints::UnicodeTextFlow { + comment_span, + characters, + suggestions, + num_codepoints: spans.len(), + } + .decorate_lint(diag); + } + BuiltinLintDiag::AbsPathWithModule(mod_span) => { + let (replacement, applicability) = match sess.source_map().span_to_snippet(mod_span) { + Ok(ref s) => { + // FIXME(Manishearth) ideally the emitting code + // can tell us whether or not this is global + let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" }; + + (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable) + } + Err(_) => ("crate::".to_string(), Applicability::HasPlaceholders), + }; + lints::AbsPathWithModule { + sugg: lints::AbsPathWithModuleSugg { span: mod_span, applicability, replacement }, + } + .decorate_lint(diag); + } + BuiltinLintDiag::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident } => { + lints::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident } + .decorate_lint(diag) + } + BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => { + lints::MacroExpandedMacroExportsAccessedByAbsolutePaths { definition: span_def } + .decorate_lint(diag) + } + + BuiltinLintDiag::ElidedLifetimesInPaths(n, path_span, incl_angl_brckt, insertion_span) => { + lints::ElidedLifetimesInPaths { + subdiag: elided_lifetime_in_path_suggestion( + sess.source_map(), + n, + path_span, + incl_angl_brckt, + insertion_span, + ), + } + .decorate_lint(diag); + } + BuiltinLintDiag::UnknownCrateTypes { span, candidate } => { + let sugg = candidate.map(|candidate| lints::UnknownCrateTypesSub { span, candidate }); + lints::UnknownCrateTypes { sugg }.decorate_lint(diag); + } + BuiltinLintDiag::UnusedImports { + remove_whole_use, + num_to_remove, + remove_spans, + test_module_span, + span_snippets, + } => { + let sugg = if remove_whole_use { + lints::UnusedImportsSugg::RemoveWholeUse { span: remove_spans[0] } + } else { + lints::UnusedImportsSugg::RemoveImports { remove_spans, num_to_remove } + }; + let test_module_span = + test_module_span.map(|span| sess.source_map().guess_head_span(span)); + + lints::UnusedImports { + sugg, + test_module_span, + num_snippets: span_snippets.len(), + span_snippets: DiagArgValue::StrListSepByAnd( + span_snippets.into_iter().map(Cow::Owned).collect(), + ), + } + .decorate_lint(diag); + } + BuiltinLintDiag::RedundantImport(spans, ident) => { + let subs = spans + .into_iter() + .map(|(span, is_imported)| { + (match (span.is_dummy(), is_imported) { + (false, true) => lints::RedundantImportSub::ImportedHere, + (false, false) => lints::RedundantImportSub::DefinedHere, + (true, true) => lints::RedundantImportSub::ImportedPrelude, + (true, false) => lints::RedundantImportSub::DefinedPrelude, + })(span) + }) + .collect(); + lints::RedundantImport { subs, ident }.decorate_lint(diag); + } + BuiltinLintDiag::DeprecatedMacro { + suggestion, + suggestion_span, + note, + path, + since_kind, + } => { + let sub = suggestion.map(|suggestion| stability::DeprecationSuggestion { + span: suggestion_span, + kind: "macro".to_owned(), + suggestion, + }); + + stability::Deprecated { sub, kind: "macro".to_owned(), path, note, since_kind } + .decorate_lint(diag); + } + BuiltinLintDiag::UnusedDocComment(attr_span) => { + lints::UnusedDocComment { span: attr_span }.decorate_lint(diag); + } + BuiltinLintDiag::PatternsInFnsWithoutBody { span: remove_span, ident, is_foreign } => { + let sub = lints::PatternsInFnsWithoutBodySub { ident, span: remove_span }; + if is_foreign { + lints::PatternsInFnsWithoutBody::Foreign { sub } + } else { + lints::PatternsInFnsWithoutBody::Bodiless { sub } + } + .decorate_lint(diag); + } + BuiltinLintDiag::MissingAbi(label_span, default_abi) => { + lints::MissingAbi { span: label_span, default_abi: default_abi.name() } + .decorate_lint(diag); + } + BuiltinLintDiag::LegacyDeriveHelpers(label_span) => { + lints::LegacyDeriveHelpers { span: label_span }.decorate_lint(diag); + } + BuiltinLintDiag::OrPatternsBackCompat(suggestion_span, suggestion) => { + lints::OrPatternsBackCompat { span: suggestion_span, suggestion }.decorate_lint(diag); + } + BuiltinLintDiag::ReservedPrefix(label_span, prefix) => { + lints::ReservedPrefix { + label: label_span, + suggestion: label_span.shrink_to_hi(), + prefix, + } + .decorate_lint(diag); + } + BuiltinLintDiag::RawPrefix(label_span) => { + lints::RawPrefix { label: label_span, suggestion: label_span.shrink_to_hi() } + .decorate_lint(diag); + } + BuiltinLintDiag::ReservedString { is_string, suggestion } => { + if is_string { + lints::ReservedString { suggestion }.decorate_lint(diag); + } else { + lints::ReservedMultihash { suggestion }.decorate_lint(diag); + } + } + BuiltinLintDiag::UnusedBuiltinAttribute { attr_name, macro_name, invoc_span } => { + lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name }.decorate_lint(diag); + } + BuiltinLintDiag::TrailingMacro(is_trailing, name) => { + lints::TrailingMacro { is_trailing, name }.decorate_lint(diag); + } + BuiltinLintDiag::BreakWithLabelAndLoop(sugg_span) => { + lints::BreakWithLabelAndLoop { + sub: lints::BreakWithLabelAndLoopSub { + left: sugg_span.shrink_to_lo(), + right: sugg_span.shrink_to_hi(), + }, + } + .decorate_lint(diag); + } + BuiltinLintDiag::UnexpectedCfgName(name, value) => { + check_cfg::unexpected_cfg_name(sess, name, value).decorate_lint(diag); + } + BuiltinLintDiag::UnexpectedCfgValue(name, value) => { + check_cfg::unexpected_cfg_value(sess, name, value).decorate_lint(diag); + } + BuiltinLintDiag::DeprecatedWhereclauseLocation(left_sp, sugg) => { + let suggestion = match sugg { + Some((right_sp, sugg)) => lints::DeprecatedWhereClauseLocationSugg::MoveToEnd { + left: left_sp, + right: right_sp, + sugg, + }, + None => lints::DeprecatedWhereClauseLocationSugg::RemoveWhere { span: left_sp }, + }; + lints::DeprecatedWhereClauseLocation { suggestion }.decorate_lint(diag); + } + BuiltinLintDiag::MissingUnsafeOnExtern { suggestion } => { + lints::MissingUnsafeOnExtern { suggestion }.decorate_lint(diag); + } + BuiltinLintDiag::SingleUseLifetime { + param_span, + use_span: Some((use_span, elide)), + deletion_span, + ident, + } => { + debug!(?param_span, ?use_span, ?deletion_span); + let suggestion = if let Some(deletion_span) = deletion_span { + let (use_span, replace_lt) = if elide { + let use_span = sess.source_map().span_extend_while_whitespace(use_span); + (use_span, String::new()) + } else { + (use_span, "'_".to_owned()) + }; + debug!(?deletion_span, ?use_span); + + // issue 107998 for the case such as a wrong function pointer type + // `deletion_span` is empty and there is no need to report lifetime uses here + let deletion_span = + if deletion_span.is_empty() { None } else { Some(deletion_span) }; + Some(lints::SingleUseLifetimeSugg { deletion_span, use_span, replace_lt }) + } else { + None + }; + + lints::SingleUseLifetime { suggestion, param_span, use_span, ident } + .decorate_lint(diag); + } + BuiltinLintDiag::SingleUseLifetime { use_span: None, deletion_span, ident, .. } => { + debug!(?deletion_span); + lints::UnusedLifetime { deletion_span, ident }.decorate_lint(diag); + } + BuiltinLintDiag::NamedArgumentUsedPositionally { + position_sp_to_replace, + position_sp_for_msg, + named_arg_sp, + named_arg_name, + is_formatting_arg, + } => { + let (suggestion, name) = if let Some(positional_arg_to_replace) = position_sp_to_replace + { + let mut name = named_arg_name.clone(); + if is_formatting_arg { + name.push('$') + }; + let span_to_replace = if let Ok(positional_arg_content) = + sess.source_map().span_to_snippet(positional_arg_to_replace) + && positional_arg_content.starts_with(':') + { + positional_arg_to_replace.shrink_to_lo() + } else { + positional_arg_to_replace + }; + (Some(span_to_replace), name) + } else { + (None, String::new()) + }; + + lints::NamedArgumentUsedPositionally { + named_arg_sp, + position_label_sp: position_sp_for_msg, + suggestion, + name, + named_arg_name, + } + .decorate_lint(diag); + } + BuiltinLintDiag::ByteSliceInPackedStructWithDerive { ty } => { + lints::ByteSliceInPackedStructWithDerive { ty }.decorate_lint(diag); + } + BuiltinLintDiag::UnusedExternCrate { removal_span } => { + lints::UnusedExternCrate { removal_span }.decorate_lint(diag); + } + BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span } => { + let suggestion_span = vis_span.between(ident_span); + let code = if vis_span.is_empty() { "use " } else { " use " }; + + lints::ExternCrateNotIdiomatic { span: suggestion_span, code }.decorate_lint(diag); + } + BuiltinLintDiag::AmbiguousGlobImports { diag: ambiguity } => { + lints::AmbiguousGlobImports { ambiguity }.decorate_lint(diag); + } + BuiltinLintDiag::AmbiguousGlobReexports { + name, + namespace, + first_reexport_span, + duplicate_reexport_span, + } => { + lints::AmbiguousGlobReexports { + first_reexport: first_reexport_span, + duplicate_reexport: duplicate_reexport_span, + name, + namespace, + } + .decorate_lint(diag); + } + BuiltinLintDiag::HiddenGlobReexports { + name, + namespace, + glob_reexport_span, + private_item_span, + } => { + lints::HiddenGlobReexports { + glob_reexport: glob_reexport_span, + private_item: private_item_span, + + name, + namespace, + } + .decorate_lint(diag); + } + BuiltinLintDiag::UnusedQualifications { removal_span } => { + lints::UnusedQualifications { removal_span }.decorate_lint(diag); + } + BuiltinLintDiag::UnsafeAttrOutsideUnsafe { + attribute_name_span, + sugg_spans: (left, right), + } => { + lints::UnsafeAttrOutsideUnsafe { + span: attribute_name_span, + suggestion: lints::UnsafeAttrOutsideUnsafeSuggestion { left, right }, + } + .decorate_lint(diag); + } + BuiltinLintDiag::AssociatedConstElidedLifetime { + elided, + span: lt_span, + lifetimes_in_scope, + } => { + let lt_span = if elided { lt_span.shrink_to_hi() } else { lt_span }; + let code = if elided { "'static " } else { "'static" }; + lints::AssociatedConstElidedLifetime { + span: lt_span, + code, + elided, + lifetimes_in_scope, + } + .decorate_lint(diag); + } + BuiltinLintDiag::RedundantImportVisibility { max_vis, span: vis_span, import_vis } => { + lints::RedundantImportVisibility { span: vis_span, help: (), max_vis, import_vis } + .decorate_lint(diag); + } + BuiltinLintDiag::UnknownDiagnosticAttribute { span: typo_span, typo_name } => { + let typo = typo_name.map(|typo_name| lints::UnknownDiagnosticAttributeTypoSugg { + span: typo_span, + typo_name, + }); + lints::UnknownDiagnosticAttribute { typo }.decorate_lint(diag); + } + BuiltinLintDiag::MacroUseDeprecated => { + lints::MacroUseDeprecated.decorate_lint(diag); + } + BuiltinLintDiag::UnusedMacroUse => lints::UnusedMacroUse.decorate_lint(diag), + BuiltinLintDiag::PrivateExternCrateReexport { source: ident, extern_crate_span } => { + lints::PrivateExternCrateReexport { ident, sugg: extern_crate_span.shrink_to_lo() } + .decorate_lint(diag); + } + BuiltinLintDiag::UnusedLabel => lints::UnusedLabel.decorate_lint(diag), + BuiltinLintDiag::MacroIsPrivate(ident) => { + lints::MacroIsPrivate { ident }.decorate_lint(diag); + } + BuiltinLintDiag::UnusedMacroDefinition(name) => { + lints::UnusedMacroDefinition { name }.decorate_lint(diag); + } + BuiltinLintDiag::MacroRuleNeverUsed(n, name) => { + lints::MacroRuleNeverUsed { n: n + 1, name }.decorate_lint(diag); + } + BuiltinLintDiag::UnstableFeature(msg) => { + lints::UnstableFeature { msg }.decorate_lint(diag); + } + BuiltinLintDiag::AvoidUsingIntelSyntax => { + lints::AvoidIntelSyntax.decorate_lint(diag); + } + BuiltinLintDiag::AvoidUsingAttSyntax => { + lints::AvoidAttSyntax.decorate_lint(diag); + } + BuiltinLintDiag::IncompleteInclude => { + lints::IncompleteInclude.decorate_lint(diag); + } + BuiltinLintDiag::UnnameableTestItems => { + lints::UnnameableTestItems.decorate_lint(diag); + } + BuiltinLintDiag::DuplicateMacroAttribute => { + lints::DuplicateMacroAttribute.decorate_lint(diag); + } + BuiltinLintDiag::CfgAttrNoAttributes => { + lints::CfgAttrNoAttributes.decorate_lint(diag); + } + BuiltinLintDiag::MissingFragmentSpecifier => { + lints::MissingFragmentSpecifier.decorate_lint(diag); + } + BuiltinLintDiag::MetaVariableStillRepeating(name) => { + lints::MetaVariableStillRepeating { name }.decorate_lint(diag); + } + BuiltinLintDiag::MetaVariableWrongOperator => { + lints::MetaVariableWrongOperator.decorate_lint(diag); + } + BuiltinLintDiag::DuplicateMatcherBinding => { + lints::DuplicateMatcherBinding.decorate_lint(diag); + } + BuiltinLintDiag::UnknownMacroVariable(name) => { + lints::UnknownMacroVariable { name }.decorate_lint(diag); + } + BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => { + lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag) + } + BuiltinLintDiag::WasmCAbi => lints::WasmCAbi.decorate_lint(diag), + BuiltinLintDiag::IllFormedAttributeInput { suggestions } => { + lints::IllFormedAttributeInput { + num_suggestions: suggestions.len(), + suggestions: DiagArgValue::StrListSepByAnd( + suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), + ), + } + .decorate_lint(diag) + } + BuiltinLintDiag::InnerAttributeUnstable { is_macro } => if is_macro { + lints::InnerAttributeUnstable::InnerMacroAttribute + } else { + lints::InnerAttributeUnstable::CustomInnerAttribute + } + .decorate_lint(diag), + BuiltinLintDiag::OutOfScopeMacroCalls { path } => { + lints::OutOfScopeMacroCalls { path }.decorate_lint(diag) + } + BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by } => { + lints::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.decorate_lint(diag) + } + BuiltinLintDiag::ElidedNamedLifetimes { elided: (span, kind), resolution } => { + match resolution { + ElidedLifetimeResolution::Static => { + ElidedNamedLifetime { span, kind, name: kw::StaticLifetime, declaration: None } + } + ElidedLifetimeResolution::Param(name, declaration) => { + ElidedNamedLifetime { span, kind, name, declaration: Some(declaration) } + } + } + .decorate_lint(diag) + } + } +} diff --git a/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs b/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs new file mode 100644 index 00000000000..63a722f6067 --- /dev/null +++ b/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs @@ -0,0 +1,362 @@ +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_middle::bug; +use rustc_session::Session; +use rustc_session::config::ExpectedValues; +use rustc_span::edit_distance::find_best_match_for_name; +use rustc_span::symbol::Ident; +use rustc_span::{ExpnKind, Span, Symbol, sym}; + +use crate::lints; + +const MAX_CHECK_CFG_NAMES_OR_VALUES: usize = 35; + +fn sort_and_truncate_possibilities( + sess: &Session, + mut possibilities: Vec, +) -> (Vec, usize) { + let n_possibilities = if sess.opts.unstable_opts.check_cfg_all_expected { + possibilities.len() + } else { + std::cmp::min(possibilities.len(), MAX_CHECK_CFG_NAMES_OR_VALUES) + }; + + possibilities.sort_by(|s1, s2| s1.as_str().cmp(s2.as_str())); + + let and_more = possibilities.len().saturating_sub(n_possibilities); + possibilities.truncate(n_possibilities); + (possibilities, and_more) +} + +enum EscapeQuotes { + Yes, + No, +} + +fn to_check_cfg_arg(name: Ident, value: Option, quotes: EscapeQuotes) -> String { + if let Some(value) = value { + let value = str::escape_debug(value.as_str()).to_string(); + let values = match quotes { + EscapeQuotes::Yes => format!("\\\"{}\\\"", value.replace("\"", "\\\\\\\\\"")), + EscapeQuotes::No => format!("\"{value}\""), + }; + format!("cfg({name}, values({values}))") + } else { + format!("cfg({name})") + } +} + +fn cargo_help_sub( + sess: &Session, + inst: &impl Fn(EscapeQuotes) -> String, +) -> lints::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 matches!(&sess.opts.crate_name, Some(crate_name) if crate_name == "build_script_build") { + lints::UnexpectedCfgCargoHelp::lint_cfg(unescaped) + } else { + lints::UnexpectedCfgCargoHelp::lint_cfg_and_build_rs(unescaped, &inst(EscapeQuotes::Yes)) + } +} + +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 }) + } else { + None + } +} + +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 + { + Some(lints::UnexpectedCfgCargoMacroHelp { + macro_kind: macro_kind.descr(), + macro_name, + // FIXME: Get access to a `TyCtxt` from an `EarlyContext` + // crate_name: cx.tcx.crate_name(def_id.krate), + }) + } else { + None + } +} + +pub(super) fn unexpected_cfg_name( + sess: &Session, + (name, name_span): (Symbol, Span), + value: Option<(Symbol, Span)>, +) -> lints::UnexpectedCfgName { + #[allow(rustc::potential_query_instability)] + let possibilities: Vec = sess.psess.check_config.expecteds.keys().copied().collect(); + + let mut names_possibilities: Vec<_> = if value.is_none() { + // We later sort and display all the possibilities, so the order here does not matter. + #[allow(rustc::potential_query_instability)] + sess.psess + .check_config + .expecteds + .iter() + .filter_map(|(k, v)| match v { + ExpectedValues::Some(v) if v.contains(&Some(name)) => Some(k), + _ => None, + }) + .collect() + } else { + Vec::new() + }; + + let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); + let is_from_external_macro = rustc_middle::lint::in_external_macro(sess, name_span); + let mut is_feature_cfg = name == sym::feature; + + let code_sugg = if is_feature_cfg && is_from_cargo { + lints::unexpected_cfg_name::CodeSuggestion::DefineFeatures + // Suggest the most probable if we found one + } else if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) { + is_feature_cfg |= best_match == sym::feature; + + if let Some(ExpectedValues::Some(best_match_values)) = + sess.psess.check_config.expecteds.get(&best_match) + { + // We will soon sort, so the initial order does not matter. + #[allow(rustc::potential_query_instability)] + let mut possibilities = best_match_values.iter().flatten().collect::>(); + possibilities.sort_by_key(|s| s.as_str()); + + let get_possibilities_sub = || { + if !possibilities.is_empty() { + let possibilities = + possibilities.iter().copied().cloned().collect::>().into(); + Some(lints::unexpected_cfg_name::ExpectedValues { best_match, possibilities }) + } else { + None + } + }; + + 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 { + span: name_span, + code: best_match.to_string(), + } + } else if best_match_values.contains(&None) { + lints::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 { + span: name_span.to(value_span), + code: format!("{best_match} = \"{first_value}\""), + expected: get_possibilities_sub(), + } + } else { + lints::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 { + span: name_span, + code: best_match.to_string(), + expected: get_possibilities_sub(), + } + } + } else { + lints::unexpected_cfg_name::CodeSuggestion::SimilarName { + span: name_span, + code: best_match.to_string(), + expected: None, + } + } + } else { + let similar_values = if !names_possibilities.is_empty() && names_possibilities.len() <= 3 { + names_possibilities.sort(); + names_possibilities + .iter() + .map(|cfg_name| lints::unexpected_cfg_name::FoundWithSimilarValue { + span: name_span, + code: format!("{cfg_name} = \"{name}\""), + }) + .collect() + } else { + vec![] + }; + let expected_names = if !possibilities.is_empty() { + let (possibilities, and_more) = sort_and_truncate_possibilities(sess, possibilities); + let possibilities: Vec<_> = + possibilities.into_iter().map(|s| Ident::new(s, name_span)).collect(); + Some(lints::unexpected_cfg_name::ExpectedNames { + possibilities: possibilities.into(), + and_more, + }) + } else { + None + }; + lints::unexpected_cfg_name::CodeSuggestion::SimilarValues { + with_similar_values: similar_values, + expected_names, + } + }; + + let inst = |escape_quotes| { + to_check_cfg_arg(Ident::new(name, name_span), value.map(|(v, _s)| v), escape_quotes) + }; + + let invocation_help = if is_from_cargo { + let help = if !is_feature_cfg && !is_from_external_macro { + Some(cargo_help_sub(sess, &inst)) + } else { + None + }; + lints::unexpected_cfg_name::InvocationHelp::Cargo { + help, + macro_help: cargo_macro_help(name_span), + } + } else { + let help = lints::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No)); + lints::unexpected_cfg_name::InvocationHelp::Rustc { + help, + macro_help: rustc_macro_help(name_span), + } + }; + + lints::UnexpectedCfgName { code_sugg, invocation_help, name } +} + +pub(super) fn unexpected_cfg_value( + sess: &Session, + (name, name_span): (Symbol, Span), + value: Option<(Symbol, Span)>, +) -> lints::UnexpectedCfgValue { + let Some(ExpectedValues::Some(values)) = &sess.psess.check_config.expecteds.get(&name) else { + bug!( + "it shouldn't be possible to have a diagnostic on a value whose name is not in values" + ); + }; + let mut have_none_possibility = false; + // We later sort possibilities if it is not empty, so the + // order here does not matter. + #[allow(rustc::potential_query_instability)] + let possibilities: Vec = values + .iter() + .inspect(|a| have_none_possibility |= a.is_none()) + .copied() + .flatten() + .collect(); + + let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); + let is_from_external_macro = rustc_middle::lint::in_external_macro(sess, name_span); + + // 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 + let code_sugg = if !possibilities.is_empty() { + let expected_values = { + let (possibilities, and_more) = + sort_and_truncate_possibilities(sess, possibilities.clone()); + lints::unexpected_cfg_value::ExpectedValues { + name, + have_none_possibility, + possibilities: possibilities.into(), + and_more, + } + }; + + 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 { + span: value_span, + best_match, + }) + } else { + None + } + } else if let &[first_possibility] = &possibilities[..] { + Some(lints::unexpected_cfg_value::ChangeValueSuggestion::SpecifyValue { + span: name_span.shrink_to_hi(), + first_possibility, + }) + } else { + None + }; + + lints::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 { + span: name_span.shrink_to_hi().to(value_span), + }); + lints::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 } + }; + + // We don't want to encourage people to add values to a well-known names, as these are + // defined by rustc/Rust itself. Users can still do this if they wish, but should not be + // encouraged to do so. + let can_suggest_adding_value = !sess.psess.check_config.well_known_names.contains(&name) + // Except when working on rustc or the standard library itself, in which case we want to + // suggest adding these cfgs to the "normal" place because of bootstrapping reasons. As a + // basic heuristic, we use the "cheat" unstable feature enable method and the + // non-ui-testing enabled option. + || (matches!(sess.psess.unstable_features, rustc_feature::UnstableFeatures::Cheat) + && !sess.opts.unstable_opts.ui_testing); + + let inst = |escape_quotes| { + to_check_cfg_arg(Ident::new(name, name_span), value.map(|(v, _s)| v), escape_quotes) + }; + + 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 }) + } else { + Some(lints::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))) + } else { + None + }; + lints::unexpected_cfg_value::InvocationHelp::Cargo { + help, + macro_help: cargo_macro_help(name_span), + } + } else { + let help = if can_suggest_adding_value { + Some(lints::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No))) + } else { + None + }; + lints::unexpected_cfg_value::InvocationHelp::Rustc { + help, + macro_help: rustc_macro_help(name_span), + } + }; + + lints::UnexpectedCfgValue { + code_sugg, + invocation_help, + has_value: value.is_some(), + value: value.map_or_else(String::new, |(v, _span)| v.to_string()), + } +} -- cgit 1.4.1-3-g733a5 From c448e35af57303fc5be1dabc4384c468439ee776 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 15 Dec 2024 00:27:32 +0100 Subject: Simplify `opt_span_lint` call in early diagnostic --- compiler/rustc_lint/src/early.rs | 40 ++++------------------------------------ 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 6d1081700d7..44f724225d9 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -8,16 +8,14 @@ use rustc_ast::ptr::P; use rustc_ast::visit::{self as ast_visit, Visitor, walk_list}; use rustc_ast::{self as ast, HasAttrs}; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::MultiSpan; use rustc_feature::Features; use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::Session; -use rustc_session::lint::{BufferedEarlyLint, BuiltinLintDiag, LintBuffer, LintPass}; +use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; use rustc_span::Span; use rustc_span::symbol::Ident; use tracing::debug; -use crate::Lint; use crate::context::{EarlyContext, LintContext, LintStore}; use crate::passes::{EarlyLintPass, EarlyLintPassObject}; @@ -35,38 +33,6 @@ pub struct EarlyContextAndPass<'a, 'b, T: EarlyLintPass> { pass: T, } -impl EarlyContextAndPass<'_, '_, T> { - /// Emit a lint at the appropriate level, with an associated span and an existing - /// diagnostic. - /// - /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature - #[rustc_lint_diagnostics] - pub fn span_lint_with_diagnostics( - &self, - lint: &'static Lint, - span: MultiSpan, - diagnostic: BuiltinLintDiag, - ) { - self.opt_span_lint_with_diagnostics(lint, Some(span), diagnostic); - } - - /// Emit a lint at the appropriate level, with an optional associated span and an existing - /// diagnostic. - /// - /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature - #[rustc_lint_diagnostics] - pub fn opt_span_lint_with_diagnostics( - &self, - lint: &'static Lint, - span: Option, - diagnostic: BuiltinLintDiag, - ) { - self.context.opt_span_lint(lint, span, |diag| { - diagnostics::decorate_lint(self.context.sess(), self.tcx, diagnostic, diag); - }); - } -} - impl<'a, 'b, T: EarlyLintPass> EarlyContextAndPass<'a, 'b, T> { // This always-inlined function is for the hot call site. #[inline(always)] @@ -74,7 +40,9 @@ impl<'a, 'b, T: EarlyLintPass> EarlyContextAndPass<'a, 'b, T> { fn inlined_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; - self.opt_span_lint_with_diagnostics(lint_id.lint, span, diagnostic); + self.context.opt_span_lint(lint_id.lint, span, |diag| { + diagnostics::decorate_lint(self.context.sess(), self.tcx, diagnostic, diag); + }); } } -- cgit 1.4.1-3-g733a5 From da535b9155fdab852dfd18006a5585612e50a70c Mon Sep 17 00:00:00 2001 From: jyn Date: Tue, 10 Dec 2024 03:03:28 -0500 Subject: Fix `--nocapture` for run-make tests This was confusing because there are three layers of output hiding. 1. libtest shoves all output into a buffer and does not print it unless the test fails or `--nocapture` is passed. 2. compiletest chooses whether to print the output from any given process. 3. run-make-support chooses what output to print. This modifies 2 and 3. - compiletest: Don't require both `--verbose` and `--nocapture` to show the output of run-make tests. - compiletest: Distinguish rustc and rmake stderr by printing the command name (e.g. "--stderr--" to "--rustc stderr--"). - run-make-support: Unconditionally print the needle/haystack being searched. Previously this was only printed on failure. Before: ``` $ x t tests/run-make/linker-warning --force-rerun -- --nocapture running 1 tests . test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 377 filtered out; finished in 281.64ms $ x t tests/run-make/linker-warning --force-rerun -v -- --nocapture 2>&1 | wc -l 1004 $ x t tests/run-make/linker-warning --force-rerun -v -- --nocapture | tail -n40 running 1 tests ------stdout------------------------------ ------stderr------------------------------ warning: unused import: `std::path::Path` --> /home/jyn/src/rust2/tests/run-make/linker-warning/rmake.rs:1:5 | 1 | use std::path::Path; | ^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default warning: unused import: `run_make_support::rfs::remove_file` --> /home/jyn/src/rust2/tests/run-make/linker-warning/rmake.rs:3:5 | 3 | use run_make_support::rfs::remove_file; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: 2 warnings emitted ------------------------------------------ test [run-make] tests/run-make/linker-warning ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 377 filtered out; finished in 285.89ms ``` After: ``` Testing stage1 compiletest suite=run-make mode=run-make (x86_64-unknown-linux-gnu) running 1 tests ------rmake stdout------------------------------ ------rmake stderr------------------------------ assert_contains_regex: === HAYSTACK === error: linking with `./fake-linker` failed: exit status: 1 | = note: LC_ALL="C" PATH="/home/jyn/src/rust2/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-unknown-linux-gnu/bin:...:/bin" VSLANG="1033" "./fake-linker" "-m64" "/tmp/rustcYqdAZT/symbols.o" "main.main.d17f5fbe6225cf88-cgu.0.rcgu.o" "main.2uoctswmurc6ir5rvoay0p9ke.rcgu.o" "-Wl,--as-needed" "-Wl,-Bstatic" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-B/home/jyn/src/rust2/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-unknown-linux-gnu/bin/gcc-ld" "-fuse-ld=lld" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/home/jyn/src/rust2/build/x86_64-unknown-linux-gnu/test/run-make/linker-warning/rmake_out" "-L" "/home/jyn/src/rust2/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "main" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs" "run_make_error" = note: error: baz error: aborting due to 1 previous error === NEEDLE === fake-linker.*run_make_error assert_not_contains_regex: === HAYSTACK === === NEEDLE === fake-linker.*run_make_error ------------------------------------------ . test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 377 filtered out; finished in 314.81ms ``` --- src/tools/compiletest/src/runtest.rs | 38 ++++++++------ src/tools/compiletest/src/runtest/run_make.rs | 13 +++-- .../run-make-support/src/assertion_helpers.rs | 58 +++++++++------------- 3 files changed, 54 insertions(+), 55 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 7b11bf3b121..4a4494a1e23 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -410,7 +410,12 @@ impl<'test> TestCx<'test> { truncated: Truncated::No, cmdline: format!("{cmd:?}"), }; - self.dump_output(&proc_res.stdout, &proc_res.stderr); + self.dump_output( + self.config.verbose, + &cmd.get_program().to_string_lossy(), + &proc_res.stdout, + &proc_res.stderr, + ); proc_res } @@ -1401,7 +1406,12 @@ impl<'test> TestCx<'test> { cmdline, }; - self.dump_output(&result.stdout, &result.stderr); + self.dump_output( + self.config.verbose, + &command.get_program().to_string_lossy(), + &result.stdout, + &result.stderr, + ); result } @@ -1816,12 +1826,22 @@ impl<'test> TestCx<'test> { } } - fn dump_output(&self, out: &str, err: &str) { + fn dump_output(&self, print_output: bool, proc_name: &str, out: &str, err: &str) { let revision = if let Some(r) = self.revision { format!("{}.", r) } else { String::new() }; self.dump_output_file(out, &format!("{}out", revision)); self.dump_output_file(err, &format!("{}err", revision)); - self.maybe_dump_to_stdout(out, err); + + if !print_output { + return; + } + + let proc_name = Path::new(proc_name).file_name().unwrap().to_string_lossy(); + println!("------{proc_name} stdout------------------------------"); + println!("{}", out); + println!("------{proc_name} stderr------------------------------"); + println!("{}", err); + println!("------------------------------------------"); } fn dump_output_file(&self, out: &str, extension: &str) { @@ -1874,16 +1894,6 @@ impl<'test> TestCx<'test> { output_base_name(self.config, self.testpaths, self.safe_revision()) } - fn maybe_dump_to_stdout(&self, out: &str, err: &str) { - if self.config.verbose { - println!("------stdout------------------------------"); - println!("{}", out); - println!("------stderr------------------------------"); - println!("{}", err); - println!("------------------------------------------"); - } - } - fn error(&self, err: &str) { match self.revision { Some(rev) => println!("\nerror in revision `{}`: {}", rev, err), diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs index 04bc2d7787d..85ade5b727a 100644 --- a/src/tools/compiletest/src/runtest/run_make.rs +++ b/src/tools/compiletest/src/runtest/run_make.rs @@ -517,14 +517,13 @@ impl TestCx<'_> { let proc = disable_error_reporting(|| cmd.spawn().expect("failed to spawn `rmake`")); let (Output { stdout, stderr, status }, truncated) = self.read2_abbreviated(proc); + let stdout = String::from_utf8_lossy(&stdout).into_owned(); + let stderr = String::from_utf8_lossy(&stderr).into_owned(); + // This conditions on `status.success()` so we don't print output twice on error. + // NOTE: this code is called from a libtest thread, so it's hidden by default unless --nocapture is passed. + self.dump_output(status.success(), &cmd.get_program().to_string_lossy(), &stdout, &stderr); if !status.success() { - let res = ProcRes { - status, - stdout: String::from_utf8_lossy(&stdout).into_owned(), - stderr: String::from_utf8_lossy(&stderr).into_owned(), - truncated, - cmdline: format!("{:?}", cmd), - }; + let res = ProcRes { status, stdout, stderr, truncated, cmdline: format!("{:?}", cmd) }; self.fatal_proc_rec("rmake recipe failed to complete", &res); } } diff --git a/src/tools/run-make-support/src/assertion_helpers.rs b/src/tools/run-make-support/src/assertion_helpers.rs index b4da65aff4a..e84a3cf633f 100644 --- a/src/tools/run-make-support/src/assertion_helpers.rs +++ b/src/tools/run-make-support/src/assertion_helpers.rs @@ -5,16 +5,31 @@ use std::path::Path; use crate::{fs, regex}; +fn print<'a, 'e, A: AsRef, E: AsRef>( + assertion_kind: &str, + haystack: &'a A, + needle: &'e E, +) -> (&'a str, &'e str) { + let haystack = haystack.as_ref(); + let needle = needle.as_ref(); + eprintln!("{assertion_kind}:"); + eprintln!("=== HAYSTACK ==="); + eprintln!("{}", haystack); + eprintln!("=== NEEDLE ==="); + eprintln!("{}", needle); + (haystack, needle) +} + /// Assert that `actual` is equal to `expected`. #[track_caller] pub fn assert_equals, E: AsRef>(actual: A, expected: E) { let actual = actual.as_ref(); let expected = expected.as_ref(); + eprintln!("=== ACTUAL TEXT ==="); + eprintln!("{}", actual); + eprintln!("=== EXPECTED ==="); + eprintln!("{}", expected); if actual != expected { - eprintln!("=== ACTUAL TEXT ==="); - eprintln!("{}", actual); - eprintln!("=== EXPECTED ==="); - eprintln!("{}", expected); panic!("expected text was not found in actual text"); } } @@ -22,13 +37,8 @@ pub fn assert_equals, E: AsRef>(actual: A, expected: E) { /// Assert that `haystack` contains `needle`. #[track_caller] pub fn assert_contains, N: AsRef>(haystack: H, needle: N) { - let haystack = haystack.as_ref(); - let needle = needle.as_ref(); + let (haystack, needle) = print("assert_contains", &haystack, &needle); if !haystack.contains(needle) { - eprintln!("=== HAYSTACK ==="); - eprintln!("{}", haystack); - eprintln!("=== NEEDLE ==="); - eprintln!("{}", needle); panic!("needle was not found in haystack"); } } @@ -36,13 +46,8 @@ pub fn assert_contains, N: AsRef>(haystack: H, needle: N) { /// Assert that `haystack` does not contain `needle`. #[track_caller] pub fn assert_not_contains, N: AsRef>(haystack: H, needle: N) { - let haystack = haystack.as_ref(); - let needle = needle.as_ref(); + let (haystack, needle) = print("assert_not_contains", &haystack, &needle); if haystack.contains(needle) { - eprintln!("=== HAYSTACK ==="); - eprintln!("{}", haystack); - eprintln!("=== NEEDLE ==="); - eprintln!("{}", needle); panic!("needle was unexpectedly found in haystack"); } } @@ -50,14 +55,9 @@ pub fn assert_not_contains, N: AsRef>(haystack: H, needle: N) /// Assert that `haystack` contains the regex pattern `needle`. #[track_caller] pub fn assert_contains_regex, N: AsRef>(haystack: H, needle: N) { - let haystack = haystack.as_ref(); - let needle = needle.as_ref(); + let (haystack, needle) = print("assert_contains_regex", &haystack, &needle); let re = regex::Regex::new(needle).unwrap(); if !re.is_match(haystack) { - eprintln!("=== HAYSTACK ==="); - eprintln!("{}", haystack); - eprintln!("=== NEEDLE ==="); - eprintln!("{}", needle); panic!("needle was not found in haystack"); } } @@ -65,14 +65,9 @@ pub fn assert_contains_regex, N: AsRef>(haystack: H, needle: /// Assert that `haystack` does not contain the regex pattern `needle`. #[track_caller] pub fn assert_not_contains_regex, N: AsRef>(haystack: H, needle: N) { - let haystack = haystack.as_ref(); - let needle = needle.as_ref(); + let (haystack, needle) = print("assert_not_contains_regex", &haystack, &needle); let re = regex::Regex::new(needle).unwrap(); if re.is_match(haystack) { - eprintln!("=== HAYSTACK ==="); - eprintln!("{}", haystack); - eprintln!("=== NEEDLE ==="); - eprintln!("{}", needle); panic!("needle was unexpectedly found in haystack"); } } @@ -80,13 +75,8 @@ pub fn assert_not_contains_regex, N: AsRef>(haystack: H, need /// Assert that `haystack` contains `needle` a `count` number of times. #[track_caller] pub fn assert_count_is, N: AsRef>(count: usize, haystack: H, needle: N) { - let haystack = haystack.as_ref(); - let needle = needle.as_ref(); + let (haystack, needle) = print("assert_count_is", &haystack, &needle); if count != haystack.matches(needle).count() { - eprintln!("=== HAYSTACK ==="); - eprintln!("{}", haystack); - eprintln!("=== NEEDLE ==="); - eprintln!("{}", needle); panic!("needle did not appear {count} times in haystack"); } } -- cgit 1.4.1-3-g733a5 From 056eb758e7e68df9c1cdcc3a91a07372df4de49c Mon Sep 17 00:00:00 2001 From: jyn Date: Thu, 12 Dec 2024 08:07:59 -0500 Subject: show which test the `rmake` process belongs to --- src/tools/compiletest/src/runtest.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 4a4494a1e23..8af4325e7b1 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; use std::collections::{HashMap, HashSet}; -use std::ffi::OsString; +use std::ffi::{OsStr, OsString}; use std::fs::{self, File, create_dir_all}; use std::hash::{DefaultHasher, Hash, Hasher}; use std::io::prelude::*; @@ -1836,7 +1836,20 @@ impl<'test> TestCx<'test> { return; } - let proc_name = Path::new(proc_name).file_name().unwrap().to_string_lossy(); + let path = Path::new(proc_name); + let proc_name = if path.file_stem().is_some_and(|p| p == "rmake") { + OsString::from_iter( + path.parent() + .unwrap() + .file_name() + .into_iter() + .chain(Some(OsStr::new("/"))) + .chain(path.file_name()), + ) + } else { + path.file_name().unwrap().into() + }; + let proc_name = proc_name.to_string_lossy(); println!("------{proc_name} stdout------------------------------"); println!("{}", out); println!("------{proc_name} stderr------------------------------"); -- cgit 1.4.1-3-g733a5 From 56b8e66c66206cd4ba0f4cf036cdf3dddb219816 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 15 Dec 2024 15:26:50 +0900 Subject: Add m68k_target_feature --- compiler/rustc_feature/src/unstable.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_target/src/target_features.rs | 18 +++++++++++++++++- tests/ui/check-cfg/target_feature.stderr | 8 ++++++++ tests/ui/target-feature/gate.rs | 1 + tests/ui/target-feature/gate.stderr | 2 +- 6 files changed, 29 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 45c63b03fe9..76b9bee4b00 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -332,6 +332,7 @@ declare_features! ( (unstable, hexagon_target_feature, "1.27.0", Some(44839)), (unstable, lahfsahf_target_feature, "1.78.0", Some(44839)), (unstable, loongarch_target_feature, "1.73.0", Some(44839)), + (unstable, m68k_target_feature, "CURRENT_RUSTC_VERSION", Some(134328)), (unstable, mips_target_feature, "1.27.0", Some(44839)), (unstable, powerpc_target_feature, "1.27.0", Some(44839)), (unstable, prfchw_target_feature, "1.78.0", Some(44839)), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e3708896ba9..0b29bd7a0d7 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1190,6 +1190,7 @@ symbols! { loongarch_target_feature, loop_break_value, lt, + m68k_target_feature, macro_at_most_once_rep, macro_attributes_in_derive_output, macro_escape, diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 29d3f826a15..1256e5b4374 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -668,6 +668,20 @@ const SPARC_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ // tidy-alphabetical-end ]; +const M68K_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ + // tidy-alphabetical-start + ("isa-68000", unstable(sym::m68k_target_feature), &[]), + ("isa-68010", unstable(sym::m68k_target_feature), &["isa-68000"]), + ("isa-68020", unstable(sym::m68k_target_feature), &["isa-68010"]), + ("isa-68030", unstable(sym::m68k_target_feature), &["isa-68020"]), + ("isa-68040", unstable(sym::m68k_target_feature), &["isa-68030", "isa-68882"]), + ("isa-68060", unstable(sym::m68k_target_feature), &["isa-68040"]), + // FPU + ("isa-68881", unstable(sym::m68k_target_feature), &[]), + ("isa-68882", unstable(sym::m68k_target_feature), &["isa-68881"]), + // tidy-alphabetical-end +]; + /// When rustdoc is running, provide a list of all known features so that all their respective /// primitives may be documented. /// @@ -687,6 +701,7 @@ pub fn all_rust_features() -> impl Iterator LOONGARCH_FEATURES, "s390x" => IBMZ_FEATURES, "sparc" | "sparc64" => SPARC_FEATURES, + "m68k" => M68K_FEATURES, _ => &[], } } @@ -751,7 +767,7 @@ impl Target { "sparc" | "sparc64" => SPARC_FEATURES_FOR_CORRECT_VECTOR_ABI, "hexagon" => HEXAGON_FEATURES_FOR_CORRECT_VECTOR_ABI, "mips" | "mips32r6" | "mips64" | "mips64r6" => MIPS_FEATURES_FOR_CORRECT_VECTOR_ABI, - "bpf" => &[], // no vector ABI + "bpf" | "m68k" => &[], // no vector ABI "csky" => CSKY_FEATURES_FOR_CORRECT_VECTOR_ABI, // FIXME: for some tier3 targets, we are overly cautious and always give warnings // when passing args in vector registers. diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index e2ceb669482..70fec8a350a 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -118,6 +118,14 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `hvx-length128b` `hwdiv` `i8mm` +`isa-68000` +`isa-68010` +`isa-68020` +`isa-68030` +`isa-68040` +`isa-68060` +`isa-68881` +`isa-68882` `jsconv` `lahfsahf` `lasx` diff --git a/tests/ui/target-feature/gate.rs b/tests/ui/target-feature/gate.rs index 2626685fa0a..14fdad02f56 100644 --- a/tests/ui/target-feature/gate.rs +++ b/tests/ui/target-feature/gate.rs @@ -25,6 +25,7 @@ // gate-test-s390x_target_feature // gate-test-sparc_target_feature // gate-test-x87_target_feature +// gate-test-m68k_target_feature #[target_feature(enable = "avx512bw")] //~^ ERROR: currently unstable diff --git a/tests/ui/target-feature/gate.stderr b/tests/ui/target-feature/gate.stderr index ba5ae79f942..fa876893848 100644 --- a/tests/ui/target-feature/gate.stderr +++ b/tests/ui/target-feature/gate.stderr @@ -1,5 +1,5 @@ error[E0658]: the target feature `avx512bw` is currently unstable - --> $DIR/gate.rs:29:18 + --> $DIR/gate.rs:30:18 | LL | #[target_feature(enable = "avx512bw")] | ^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 4795399ab6f11b648822f4ebdc06499a4d038888 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 15 Dec 2024 09:55:44 +0100 Subject: bootstrap: make ./x test error-index work --- src/bootstrap/src/core/build_steps/test.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 30fdea7e19e..8d9d2b6b6a1 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2442,7 +2442,9 @@ impl Step for ErrorIndex { const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/tools/error_index_generator") + // Also add `error-index` here since that is what appears in the error message + // when this fails. + run.path("src/tools/error_index_generator").alias("error-index") } fn make_run(run: RunConfig<'_>) { -- cgit 1.4.1-3-g733a5 From e3e5bd95cd306160cfa799b3717583fec01c7793 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 15 Dec 2024 17:15:55 +0100 Subject: Cleanup lifetimes around `EarlyContextAndPass` and `EarlyCheckNode` --- compiler/rustc_lint/src/early.rs | 102 +++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 58 deletions(-) diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 44f724225d9..a15aab32aee 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -27,13 +27,13 @@ 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<'a, 'b, T: EarlyLintPass> { - context: EarlyContext<'a>, - tcx: Option>, +pub struct EarlyContextAndPass<'ecx, 'tcx, T: EarlyLintPass> { + context: EarlyContext<'ecx>, + tcx: Option>, pass: T, } -impl<'a, 'b, T: EarlyLintPass> EarlyContextAndPass<'a, 'b, T> { +impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> { // This always-inlined function is for the hot call site. #[inline(always)] #[allow(rustc::diagnostic_outside_of_impl)] @@ -54,7 +54,7 @@ impl<'a, 'b, T: EarlyLintPass> EarlyContextAndPass<'a, 'b, 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: &'a [ast::Attribute], f: F) + fn with_lint_attrs(&mut self, id: ast::NodeId, attrs: &'_ [ast::Attribute], f: F) where F: FnOnce(&mut Self), { @@ -72,19 +72,21 @@ impl<'a, 'b, T: EarlyLintPass> EarlyContextAndPass<'a, 'b, T> { } } -impl<'a, 'b, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, 'b, T> { - fn visit_coroutine_kind(&mut self, coroutine_kind: &'a ast::CoroutineKind) -> Self::Result { +impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast> + for EarlyContextAndPass<'ecx, 'tcx, T> +{ + fn visit_coroutine_kind(&mut self, coroutine_kind: &'ast ast::CoroutineKind) -> Self::Result { self.check_id(coroutine_kind.closure_id()); } - fn visit_param(&mut self, param: &'a ast::Param) { + 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); }); } - fn visit_item(&mut self, it: &'a ast::Item) { + 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); @@ -92,31 +94,31 @@ impl<'a, 'b, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a }) } - fn visit_foreign_item(&mut self, it: &'a ast::ForeignItem) { + 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); }) } - fn visit_pat(&mut self, p: &'a ast::Pat) { + fn visit_pat(&mut self, p: &'ast ast::Pat) { lint_callback!(self, check_pat, p); self.check_id(p.id); ast_visit::walk_pat(self, p); lint_callback!(self, check_pat_post, p); } - fn visit_pat_field(&mut self, field: &'a ast::PatField) { + 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); }); } - fn visit_anon_const(&mut self, c: &'a ast::AnonConst) { + fn visit_anon_const(&mut self, c: &'ast ast::AnonConst) { self.check_id(c.id); ast_visit::walk_anon_const(self, c); } - fn visit_expr(&mut self, e: &'a ast::Expr) { + 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); @@ -124,13 +126,13 @@ impl<'a, 'b, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a }) } - fn visit_expr_field(&mut self, f: &'a ast::ExprField) { + 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); }) } - fn visit_stmt(&mut self, s: &'a ast::Stmt) { + fn visit_stmt(&mut self, s: &'ast ast::Stmt) { // Add the statement's lint attributes to our // current state when checking the statement itself. // This allows us to handle attributes like @@ -150,33 +152,33 @@ impl<'a, 'b, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a ast_visit::walk_stmt(self, s); } - fn visit_fn(&mut self, fk: ast_visit::FnKind<'a>, span: Span, id: ast::NodeId) { + fn visit_fn(&mut self, fk: ast_visit::FnKind<'ast>, span: Span, id: ast::NodeId) { lint_callback!(self, check_fn, fk, span, id); self.check_id(id); ast_visit::walk_fn(self, fk); } - fn visit_variant_data(&mut self, s: &'a ast::VariantData) { + fn visit_variant_data(&mut self, s: &'ast ast::VariantData) { if let Some(ctor_node_id) = s.ctor_node_id() { self.check_id(ctor_node_id); } ast_visit::walk_struct_def(self, s); } - fn visit_field_def(&mut self, s: &'a ast::FieldDef) { + 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); }) } - fn visit_variant(&mut self, v: &'a ast::Variant) { + 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); }) } - fn visit_ty(&mut self, t: &'a ast::Ty) { + fn visit_ty(&mut self, t: &'ast ast::Ty) { lint_callback!(self, check_ty, t); self.check_id(t.id); ast_visit::walk_ty(self, t); @@ -186,55 +188,55 @@ impl<'a, 'b, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a lint_callback!(self, check_ident, ident); } - fn visit_local(&mut self, l: &'a ast::Local) { + 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); }) } - fn visit_block(&mut self, b: &'a ast::Block) { + fn visit_block(&mut self, b: &'ast ast::Block) { lint_callback!(self, check_block, b); self.check_id(b.id); ast_visit::walk_block(self, b); } - fn visit_arm(&mut self, a: &'a ast::Arm) { + 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); }) } - fn visit_generic_arg(&mut self, arg: &'a ast::GenericArg) { + fn visit_generic_arg(&mut self, arg: &'ast ast::GenericArg) { lint_callback!(self, check_generic_arg, arg); ast_visit::walk_generic_arg(self, arg); } - fn visit_generic_param(&mut self, param: &'a ast::GenericParam) { + 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); }); } - fn visit_generics(&mut self, g: &'a ast::Generics) { + fn visit_generics(&mut self, g: &'ast ast::Generics) { lint_callback!(self, check_generics, g); ast_visit::walk_generics(self, g); } - fn visit_where_predicate(&mut self, p: &'a ast::WherePredicate) { + fn visit_where_predicate(&mut self, p: &'ast ast::WherePredicate) { lint_callback!(self, enter_where_predicate, p); ast_visit::walk_where_predicate(self, p); lint_callback!(self, exit_where_predicate, p); } - fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef) { + fn visit_poly_trait_ref(&mut self, t: &'ast ast::PolyTraitRef) { lint_callback!(self, check_poly_trait_ref, t); ast_visit::walk_poly_trait_ref(self, t); } - fn visit_assoc_item(&mut self, item: &'a ast::AssocItem, ctxt: ast_visit::AssocCtxt) { + 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 => { @@ -248,32 +250,32 @@ impl<'a, 'b, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a }); } - fn visit_lifetime(&mut self, lt: &'a ast::Lifetime, _: ast_visit::LifetimeCtxt) { + fn visit_lifetime(&mut self, lt: &'ast ast::Lifetime, _: ast_visit::LifetimeCtxt) { self.check_id(lt.id); ast_visit::walk_lifetime(self, lt); } - fn visit_path(&mut self, p: &'a ast::Path, id: ast::NodeId) { + fn visit_path(&mut self, p: &'ast ast::Path, id: ast::NodeId) { self.check_id(id); ast_visit::walk_path(self, p); } - fn visit_path_segment(&mut self, s: &'a ast::PathSegment) { + fn visit_path_segment(&mut self, s: &'ast ast::PathSegment) { self.check_id(s.id); ast_visit::walk_path_segment(self, s); } - fn visit_attribute(&mut self, attr: &'a ast::Attribute) { + fn visit_attribute(&mut self, attr: &'ast ast::Attribute) { lint_callback!(self, check_attribute, attr); ast_visit::walk_attribute(self, attr); } - fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) { + fn visit_mac_def(&mut self, mac: &'ast ast::MacroDef, id: ast::NodeId) { lint_callback!(self, check_mac_def, mac); self.check_id(id); } - fn visit_mac_call(&mut self, mac: &'a ast::MacCall) { + fn visit_mac_call(&mut self, mac: &'ast ast::MacCall) { lint_callback!(self, check_mac, mac); ast_visit::walk_mac(self, mac); } @@ -315,28 +317,18 @@ crate::early_lint_methods!(impl_early_lint_pass, []); /// This trait generalizes over those nodes. pub trait EarlyCheckNode<'a>: Copy { fn id(self) -> ast::NodeId; - fn attrs<'b>(self) -> &'b [ast::Attribute] - where - 'a: 'b; - fn check<'b, 'c, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, 'c, T>) - where - 'a: 'b; + fn attrs(self) -> &'a [ast::Attribute]; + fn check<'ecx, 'tcx, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'ecx, 'tcx, T>); } impl<'a> EarlyCheckNode<'a> for (&'a ast::Crate, &'a [ast::Attribute]) { fn id(self) -> ast::NodeId { ast::CRATE_NODE_ID } - fn attrs<'b>(self) -> &'b [ast::Attribute] - where - 'a: 'b, - { + fn attrs(self) -> &'a [ast::Attribute] { self.1 } - fn check<'b, 'c, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, 'c, T>) - where - 'a: 'b, - { + fn check<'ecx, 'tcx, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'ecx, 'tcx, T>) { lint_callback!(cx, check_crate, self.0); ast_visit::walk_crate(cx, self.0); lint_callback!(cx, check_crate_post, self.0); @@ -347,16 +339,10 @@ impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [P ast::NodeId { self.0 } - fn attrs<'b>(self) -> &'b [ast::Attribute] - where - 'a: 'b, - { + fn attrs(self) -> &'a [ast::Attribute] { self.1 } - fn check<'b, 'c, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, 'c, T>) - where - 'a: 'b, - { + 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); } -- cgit 1.4.1-3-g733a5 From 291c519c6966bb18e13d66a4283380ced339c49f Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 15 Dec 2024 12:52:04 +0100 Subject: Improve check-cfg Cargo macro diagnostic with crate name --- compiler/rustc_lint/messages.ftl | 2 +- compiler/rustc_lint/src/early/diagnostics.rs | 6 +++--- compiler/rustc_lint/src/early/diagnostics/check_cfg.rs | 16 +++++++++++----- compiler/rustc_lint/src/lints.rs | 3 +-- .../ui/check-cfg/report-in-external-macros.cargo.stderr | 6 +++--- 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 01d9ac20fae..48d6d6bbf67 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -806,7 +806,7 @@ lint_unexpected_cfg_add_build_rs_println = or consider adding `{$build_rs_printl lint_unexpected_cfg_add_cargo_feature = consider using a Cargo feature instead lint_unexpected_cfg_add_cargo_toml_lint_cfg = or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint:{$cargo_toml_lint_cfg} lint_unexpected_cfg_add_cmdline_arg = to expect this configuration use `{$cmdline_arg}` -lint_unexpected_cfg_cargo_update = the {$macro_kind} `{$macro_name}` may come from an old version of it's defining crate, try updating your dependencies with `cargo update` +lint_unexpected_cfg_cargo_update = 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}` lint_unexpected_cfg_define_features = consider defining some features in `Cargo.toml` lint_unexpected_cfg_doc_cargo = see for more information about checking conditional configuration diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 7f136672a47..b0fb1e4af76 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -21,7 +21,7 @@ mod check_cfg; pub(super) fn decorate_lint( sess: &Session, - _tcx: Option>, + tcx: Option>, diagnostic: BuiltinLintDiag, diag: &mut Diag<'_, ()>, ) { @@ -205,10 +205,10 @@ pub(super) fn decorate_lint( .decorate_lint(diag); } BuiltinLintDiag::UnexpectedCfgName(name, value) => { - check_cfg::unexpected_cfg_name(sess, name, value).decorate_lint(diag); + check_cfg::unexpected_cfg_name(sess, tcx, name, value).decorate_lint(diag); } BuiltinLintDiag::UnexpectedCfgValue(name, value) => { - check_cfg::unexpected_cfg_value(sess, name, value).decorate_lint(diag); + check_cfg::unexpected_cfg_value(sess, tcx, name, value).decorate_lint(diag); } BuiltinLintDiag::DeprecatedWhereclauseLocation(left_sp, sugg) => { let suggestion = match sugg { diff --git a/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs b/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs index 63a722f6067..033c0fa4c88 100644 --- a/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs +++ b/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs @@ -1,5 +1,6 @@ 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::edit_distance::find_best_match_for_name; @@ -73,17 +74,20 @@ fn rustc_macro_help(span: Span) -> Option { } } -fn cargo_macro_help(span: Span) -> Option { +fn cargo_macro_help( + tcx: Option>, + 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 { Some(lints::UnexpectedCfgCargoMacroHelp { macro_kind: macro_kind.descr(), macro_name, - // FIXME: Get access to a `TyCtxt` from an `EarlyContext` - // crate_name: cx.tcx.crate_name(def_id.krate), + crate_name: tcx.crate_name(def_id.krate), }) } else { None @@ -92,6 +96,7 @@ fn cargo_macro_help(span: Span) -> Option { pub(super) fn unexpected_cfg_name( sess: &Session, + tcx: Option>, (name, name_span): (Symbol, Span), value: Option<(Symbol, Span)>, ) -> lints::UnexpectedCfgName { @@ -223,7 +228,7 @@ pub(super) fn unexpected_cfg_name( }; lints::unexpected_cfg_name::InvocationHelp::Cargo { help, - macro_help: cargo_macro_help(name_span), + macro_help: cargo_macro_help(tcx, name_span), } } else { let help = lints::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No)); @@ -238,6 +243,7 @@ pub(super) fn unexpected_cfg_name( pub(super) fn unexpected_cfg_value( sess: &Session, + tcx: Option>, (name, name_span): (Symbol, Span), value: Option<(Symbol, Span)>, ) -> lints::UnexpectedCfgValue { @@ -339,7 +345,7 @@ pub(super) fn unexpected_cfg_value( }; lints::unexpected_cfg_value::InvocationHelp::Cargo { help, - macro_help: cargo_macro_help(name_span), + macro_help: cargo_macro_help(tcx, name_span), } } else { let help = if can_suggest_adding_value { diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 5da9f6d2053..4977b3971bd 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2187,8 +2187,7 @@ pub(crate) struct UnexpectedCfgRustcMacroHelp { pub(crate) struct UnexpectedCfgCargoMacroHelp { pub macro_kind: &'static str, pub macro_name: Symbol, - // FIXME: Figure out a way to get the crate name - // crate_name: String, + pub crate_name: Symbol, } #[derive(LintDiagnostic)] 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 6fb397b5529..d70fedf55a3 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,7 @@ LL | cfg_macro::my_lib_macro!(); = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `fmt_debug`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, and `windows` = 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 refering 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 it's defining crate, try updating your dependencies with `cargo update` + = 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 +21,7 @@ LL | cfg_macro::my_lib_macro_value!(); = note: expected values for `panic` are: `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 refering 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 it's defining crate, try updating your dependencies with `cargo update` + = 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 +34,7 @@ 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 refering 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 it's defining crate, try updating your dependencies with `cargo update` + = 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) -- cgit 1.4.1-3-g733a5