use std::num::{NonZero, ParseIntError}; use rustc_ast::token; use rustc_ast::util::literal::LitError; use rustc_errors::codes::*; use rustc_errors::{ Diag, DiagCtxtHandle, DiagMessage, Diagnostic, EmissionGuarantee, ErrorGuaranteed, Level, MultiSpan, StashKey, }; use rustc_feature::{GateIssue, find_feature_issue}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol, sym}; use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTuple}; use crate::Session; use crate::lint::builtin::UNSTABLE_SYNTAX_PRE_EXPANSION; use crate::parse::ParseSess; /// Construct a diagnostic for a language feature error due to the given `span`. /// The `feature`'s `Symbol` is the one you used in `unstable.rs` and `rustc_span::symbol`. #[track_caller] pub fn feature_err( sess: &Session, feature: Symbol, span: impl Into, explain: impl Into, ) -> Diag<'_> { feature_err_issue(sess, feature, span, GateIssue::Language, explain) } /// Construct a diagnostic for a feature gate error. /// /// This variant allows you to control whether it is a library or language feature. /// Almost always, you want to use this for a language feature. If so, prefer `feature_err`. #[track_caller] pub fn feature_err_issue( sess: &Session, feature: Symbol, span: impl Into, issue: GateIssue, explain: impl Into, ) -> Diag<'_> { let span = span.into(); // Cancel an earlier warning for this same error, if it exists. if let Some(span) = span.primary_span() && let Some(err) = sess.dcx().steal_non_err(span, StashKey::EarlySyntaxWarning) { err.cancel() } let mut err = sess.dcx().create_err(FeatureGateError { span, explain: explain.into() }); add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None); err } /// Construct a future incompatibility diagnostic for a feature gate. /// /// This diagnostic is only a warning and *does not cause compilation to fail*. #[track_caller] pub fn feature_warn(sess: &Session, feature: Symbol, span: Span, explain: &'static str) { feature_warn_issue(sess, feature, span, GateIssue::Language, explain); } /// Construct a future incompatibility diagnostic for a feature gate. /// /// This diagnostic is only a warning and *does not cause compilation to fail*. /// /// This variant allows you to control whether it is a library or language feature. /// Almost always, you want to use this for a language feature. If so, prefer `feature_warn`. #[track_caller] pub fn feature_warn_issue( sess: &Session, feature: Symbol, span: Span, issue: GateIssue, explain: &'static str, ) { let mut err = sess.dcx().struct_span_warn(span, explain); add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None); // Decorate this as a future-incompatibility lint as in rustc_middle::lint::lint_level let lint = UNSTABLE_SYNTAX_PRE_EXPANSION; let future_incompatible = lint.future_incompatible.as_ref().unwrap(); err.is_lint(lint.name_lower(), /* has_future_breakage */ false); err.warn(lint.desc); err.note(format!("for more information, see {}", future_incompatible.reason.reference())); // A later feature_err call can steal and cancel this warning. err.stash(span, StashKey::EarlySyntaxWarning); } /// Adds the diagnostics for a feature to an existing error. /// Must be a language feature! pub fn add_feature_diagnostics( err: &mut Diag<'_, G>, sess: &Session, feature: Symbol, ) { add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language, false, None); } /// Adds the diagnostics for a feature to an existing error. /// /// This variant allows you to control whether it is a library or language feature. /// Almost always, you want to use this for a language feature. If so, prefer /// `add_feature_diagnostics`. pub fn add_feature_diagnostics_for_issue( err: &mut Diag<'_, G>, sess: &Session, feature: Symbol, issue: GateIssue, feature_from_cli: bool, inject_span: Option, ) { if let Some(n) = find_feature_issue(feature, issue) { err.subdiagnostic(FeatureDiagnosticForIssue { n }); } // #23973: do not suggest `#![feature(...)]` if we are in beta/stable if sess.unstable_features.is_nightly_build() { if feature_from_cli { err.subdiagnostic(CliFeatureDiagnosticHelp { feature }); } else if let Some(span) = inject_span { err.subdiagnostic(FeatureDiagnosticSuggestion { feature, span }); } else { err.subdiagnostic(FeatureDiagnosticHelp { feature }); } if feature == sym::rustc_attrs { // We're unlikely to stabilize something out of `rustc_attrs` // without at least renaming it, so pointing out how old // the compiler is will do little good. } else if sess.opts.unstable_opts.ui_testing { err.subdiagnostic(SuggestUpgradeCompiler::ui_testing()); } else if let Some(suggestion) = SuggestUpgradeCompiler::new() { err.subdiagnostic(suggestion); } } } /// This is only used by unstable_feature_bound as it does not have issue number information for now. /// This is basically the same as `feature_err_issue` /// but without the feature issue note. If we can do a lookup for issue number from feature name, /// then we should directly use `feature_err_issue` for ambiguity error of /// `#[unstable_feature_bound]`. #[track_caller] pub fn feature_err_unstable_feature_bound( sess: &Session, feature: Symbol, span: impl Into, explain: impl Into, ) -> Diag<'_> { let span = span.into(); // Cancel an earlier warning for this same error, if it exists. if let Some(span) = span.primary_span() { if let Some(err) = sess.dcx().steal_non_err(span, StashKey::EarlySyntaxWarning) { err.cancel() } } let mut err = sess.dcx().create_err(FeatureGateError { span, explain: explain.into() }); // #23973: do not suggest `#![feature(...)]` if we are in beta/stable if sess.unstable_features.is_nightly_build() { err.subdiagnostic(FeatureDiagnosticHelp { feature }); if feature == sym::rustc_attrs { // We're unlikely to stabilize something out of `rustc_attrs` // without at least renaming it, so pointing out how old // the compiler is will do little good. } else if sess.opts.unstable_opts.ui_testing { err.subdiagnostic(SuggestUpgradeCompiler::ui_testing()); } else if let Some(suggestion) = SuggestUpgradeCompiler::new() { err.subdiagnostic(suggestion); } } err } #[derive(Diagnostic)] pub(crate) enum AppleDeploymentTarget { #[diag("failed to parse deployment target specified in {$env_var}: {$error}")] Invalid { env_var: &'static str, error: ParseIntError }, #[diag( "deployment target in {$env_var} was set to {$version}, but the minimum supported by `rustc` is {$os_min}" )] TooLow { env_var: &'static str, version: String, os_min: String }, } pub(crate) struct FeatureGateError { pub(crate) span: MultiSpan, pub(crate) explain: DiagMessage, } impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for FeatureGateError { #[track_caller] fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { Diag::new(dcx, level, self.explain).with_span(self.span).with_code(E0658) } } #[derive(Subdiagnostic)] #[note("see issue #{$n} for more information")] pub(crate) struct FeatureDiagnosticForIssue { pub(crate) n: NonZero, } #[derive(Subdiagnostic)] #[note("this compiler was built on {$date}; consider upgrading it if it is out of date")] pub(crate) struct SuggestUpgradeCompiler { date: &'static str, } impl SuggestUpgradeCompiler { pub(crate) fn ui_testing() -> Self { Self { date: "YYYY-MM-DD" } } pub(crate) fn new() -> Option { let date = option_env!("CFG_VER_DATE")?; Some(Self { date }) } } #[derive(Subdiagnostic)] #[help("add `#![feature({$feature})]` to the crate attributes to enable")] pub(crate) struct FeatureDiagnosticHelp { pub(crate) feature: Symbol, } #[derive(Subdiagnostic)] #[suggestion( "add `#![feature({$feature})]` to the crate attributes to enable", applicability = "maybe-incorrect", code = "#![feature({feature})]\n" )] pub(crate) struct FeatureDiagnosticSuggestion { pub feature: Symbol, #[primary_span] pub span: Span, } #[derive(Subdiagnostic)] #[help("add `-Zcrate-attr=\"feature({$feature})\"` to the command-line options to enable")] pub(crate) struct CliFeatureDiagnosticHelp { pub(crate) feature: Symbol, } #[derive(Diagnostic)] #[diag( "`-Zunleash-the-miri-inside-of-you` may not be used to circumvent feature gates, except when testing error paths in the CTFE engine" )] pub(crate) struct NotCircumventFeature; #[derive(Diagnostic)] #[diag( "linker plugin based LTO is not supported together with `-C prefer-dynamic` when targeting Windows-like targets" )] pub(crate) struct LinkerPluginToWindowsNotSupported; #[derive(Diagnostic)] #[diag("file `{$path}` passed to `-C profile-use` does not exist")] pub(crate) struct ProfileUseFileDoesNotExist<'a> { pub(crate) path: &'a std::path::Path, } #[derive(Diagnostic)] #[diag("file `{$path}` passed to `-C profile-sample-use` does not exist")] pub(crate) struct ProfileSampleUseFileDoesNotExist<'a> { pub(crate) path: &'a std::path::Path, } #[derive(Diagnostic)] #[diag("target requires unwind tables, they cannot be disabled with `-C force-unwind-tables=no`")] pub(crate) struct TargetRequiresUnwindTables; #[derive(Diagnostic)] #[diag("{$us} instrumentation is not supported for this target")] pub(crate) struct InstrumentationNotSupported { pub(crate) us: String, } #[derive(Diagnostic)] #[diag("{$us} sanitizer is not supported for this target")] pub(crate) struct SanitizerNotSupported { pub(crate) us: String, } #[derive(Diagnostic)] #[diag("{$us} sanitizers are not supported for this target")] pub(crate) struct SanitizersNotSupported { pub(crate) us: String, } #[derive(Diagnostic)] #[diag("`-Zsanitizer={$first}` is incompatible with `-Zsanitizer={$second}`")] pub(crate) struct CannotMixAndMatchSanitizers { pub(crate) first: String, pub(crate) second: String, } #[derive(Diagnostic)] #[diag( "sanitizer is incompatible with statically linked libc, disable it using `-C target-feature=-crt-static`" )] pub(crate) struct CannotEnableCrtStaticLinux; #[derive(Diagnostic)] #[diag("`-Zsanitizer=cfi` requires `-Clto` or `-Clinker-plugin-lto`")] pub(crate) struct SanitizerCfiRequiresLto; #[derive(Diagnostic)] #[diag("`-Zsanitizer=cfi` with `-Clto` requires `-Ccodegen-units=1`")] pub(crate) struct SanitizerCfiRequiresSingleCodegenUnit; #[derive(Diagnostic)] #[diag("`-Zsanitizer-cfi-canonical-jump-tables` requires `-Zsanitizer=cfi`")] pub(crate) struct SanitizerCfiCanonicalJumpTablesRequiresCfi; #[derive(Diagnostic)] #[diag("`-Zsanitizer-cfi-generalize-pointers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi`")] pub(crate) struct SanitizerCfiGeneralizePointersRequiresCfi; #[derive(Diagnostic)] #[diag("`-Zsanitizer-cfi-normalize-integers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi`")] pub(crate) struct SanitizerCfiNormalizeIntegersRequiresCfi; #[derive(Diagnostic)] #[diag("`-Zsanitizer-kcfi-arity` requires `-Zsanitizer=kcfi`")] pub(crate) struct SanitizerKcfiArityRequiresKcfi; #[derive(Diagnostic)] #[diag("`-Z sanitizer=kcfi` requires `-C panic=abort`")] pub(crate) struct SanitizerKcfiRequiresPanicAbort; #[derive(Diagnostic)] #[diag("`-Zsplit-lto-unit` requires `-Clto`, `-Clto=thin`, or `-Clinker-plugin-lto`")] pub(crate) struct SplitLtoUnitRequiresLto; #[derive(Diagnostic)] #[diag("`-Zvirtual-function-elimination` requires `-Clto`")] pub(crate) struct UnstableVirtualFunctionElimination; #[derive(Diagnostic)] #[diag("requested DWARF version {$dwarf_version} is not supported")] #[help("supported DWARF versions are 2, 3, 4 and 5")] pub(crate) struct UnsupportedDwarfVersion { pub(crate) dwarf_version: u32, } #[derive(Diagnostic)] #[diag( "`-Zembed-source=y` requires at least `-Z dwarf-version=5` but DWARF version is {$dwarf_version}" )] pub(crate) struct EmbedSourceInsufficientDwarfVersion { pub(crate) dwarf_version: u32, } #[derive(Diagnostic)] #[diag("`-Zembed-source=y` requires debug information to be enabled")] pub(crate) struct EmbedSourceRequiresDebugInfo; #[derive(Diagnostic)] #[diag( "`-Z stack-protector={$stack_protector}` is not supported for target {$target_triple} and will be ignored" )] pub(crate) struct StackProtectorNotSupportedForTarget<'a> { pub(crate) stack_protector: StackProtector, pub(crate) target_triple: &'a TargetTuple, } #[derive(Diagnostic)] #[diag( "`-Z small-data-threshold` is not supported for target {$target_triple} and will be ignored" )] pub(crate) struct SmallDataThresholdNotSupportedForTarget<'a> { pub(crate) target_triple: &'a TargetTuple, } #[derive(Diagnostic)] #[diag("`-Zbranch-protection` is only supported on aarch64")] pub(crate) struct BranchProtectionRequiresAArch64; #[derive(Diagnostic)] #[diag("`-Csplit-debuginfo={$debuginfo}` is unstable on this platform")] pub(crate) struct SplitDebugInfoUnstablePlatform { pub(crate) debuginfo: SplitDebuginfo, } #[derive(Diagnostic)] #[diag("output file {$file} is not writeable -- check its permissions")] pub(crate) struct FileIsNotWriteable<'a> { pub(crate) file: &'a std::path::Path, } #[derive(Diagnostic)] #[diag("failed to write `{$path}` due to error `{$err}`")] pub(crate) struct FileWriteFail<'a> { pub(crate) path: &'a std::path::Path, pub(crate) err: String, } #[derive(Diagnostic)] #[diag("crate name must not be empty")] pub(crate) struct CrateNameEmpty { #[primary_span] pub(crate) span: Option, } #[derive(Diagnostic)] #[diag("invalid character {$character} in crate name: `{$crate_name}`")] pub(crate) struct InvalidCharacterInCrateName { #[primary_span] pub(crate) span: Option, pub(crate) character: char, pub(crate) crate_name: Symbol, } #[derive(Subdiagnostic)] #[multipart_suggestion( "parentheses are required to parse this as an expression", applicability = "machine-applicable" )] pub struct ExprParenthesesNeeded { #[suggestion_part(code = "(")] left: Span, #[suggestion_part(code = ")")] right: Span, } impl ExprParenthesesNeeded { pub fn surrounding(s: Span) -> Self { ExprParenthesesNeeded { left: s.shrink_to_lo(), right: s.shrink_to_hi() } } } #[derive(Diagnostic)] #[diag("skipping const checks")] pub(crate) struct SkippingConstChecks { #[subdiagnostic] pub(crate) unleashed_features: Vec, } #[derive(Subdiagnostic)] pub(crate) enum UnleashedFeatureHelp { #[help("skipping check for `{$gate}` feature")] Named { #[primary_span] span: Span, gate: Symbol, }, #[help("skipping check that does not even have a feature gate")] Unnamed { #[primary_span] span: Span, }, } #[derive(Diagnostic)] #[diag("suffixes on {$kind} literals are invalid")] struct InvalidLiteralSuffix<'a> { #[primary_span] #[label("invalid suffix `{$suffix}`")] span: Span, // FIXME(#100717) kind: &'a str, suffix: Symbol, } #[derive(Diagnostic)] #[diag("invalid width `{$width}` for integer literal")] #[help("valid widths are 8, 16, 32, 64 and 128")] struct InvalidIntLiteralWidth { #[primary_span] span: Span, width: String, } #[derive(Diagnostic)] #[diag("invalid base prefix for number literal")] #[note("base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase")] struct InvalidNumLiteralBasePrefix { #[primary_span] #[suggestion( "try making the prefix lowercase", applicability = "maybe-incorrect", code = "{fixed}" )] span: Span, fixed: String, } #[derive(Diagnostic)] #[diag("invalid suffix `{$suffix}` for number literal")] #[help("the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)")] struct InvalidNumLiteralSuffix { #[primary_span] #[label("invalid suffix `{$suffix}`")] span: Span, suffix: String, } #[derive(Diagnostic)] #[diag("invalid width `{$width}` for float literal")] #[help("valid widths are 32 and 64")] struct InvalidFloatLiteralWidth { #[primary_span] span: Span, width: String, } #[derive(Diagnostic)] #[diag("invalid suffix `{$suffix}` for float literal")] #[help("valid suffixes are `f32` and `f64`")] struct InvalidFloatLiteralSuffix { #[primary_span] #[label("invalid suffix `{$suffix}`")] span: Span, suffix: String, } #[derive(Diagnostic)] #[diag("integer literal is too large")] #[note("value exceeds limit of `{$limit}`")] struct IntLiteralTooLarge { #[primary_span] span: Span, limit: String, } #[derive(Diagnostic)] #[diag("hexadecimal float literal is not supported")] struct HexadecimalFloatLiteralNotSupported { #[primary_span] #[label("not supported")] span: Span, } #[derive(Diagnostic)] #[diag("octal float literal is not supported")] struct OctalFloatLiteralNotSupported { #[primary_span] #[label("not supported")] span: Span, } #[derive(Diagnostic)] #[diag("binary float literal is not supported")] struct BinaryFloatLiteralNotSupported { #[primary_span] #[label("not supported")] span: Span, } pub fn report_lit_error( psess: &ParseSess, err: LitError, lit: token::Lit, span: Span, ) -> ErrorGuaranteed { create_lit_error(psess, err, lit, span).emit() } pub fn create_lit_error(psess: &ParseSess, err: LitError, lit: token::Lit, span: Span) -> Diag<'_> { // Checks if `s` looks like i32 or u1234 etc. fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool { s.len() > 1 && s.starts_with(first_chars) && s[1..].chars().all(|c| c.is_ascii_digit()) } // Try to lowercase the prefix if the prefix and suffix are valid. fn fix_base_capitalisation(prefix: &str, suffix: &str) -> Option { let mut chars = suffix.chars(); let base_char = chars.next().unwrap(); let base = match base_char { 'B' => 2, 'O' => 8, 'X' => 16, _ => return None, }; // check that the suffix contains only base-appropriate characters let valid = prefix == "0" && chars .filter(|c| *c != '_') .take_while(|c| *c != 'i' && *c != 'u') .all(|c| c.to_digit(base).is_some()); valid.then(|| format!("0{}{}", base_char.to_ascii_lowercase(), &suffix[1..])) } let dcx = psess.dcx(); match err { LitError::InvalidSuffix(suffix) => { dcx.create_err(InvalidLiteralSuffix { span, kind: lit.kind.descr(), suffix }) } LitError::InvalidIntSuffix(suffix) => { let suf = suffix.as_str(); if looks_like_width_suffix(&['i', 'u'], suf) { // If it looks like a width, try to be helpful. dcx.create_err(InvalidIntLiteralWidth { span, width: suf[1..].into() }) } else if let Some(fixed) = fix_base_capitalisation(lit.symbol.as_str(), suf) { dcx.create_err(InvalidNumLiteralBasePrefix { span, fixed }) } else { dcx.create_err(InvalidNumLiteralSuffix { span, suffix: suf.to_string() }) } } LitError::InvalidFloatSuffix(suffix) => { let suf = suffix.as_str(); if looks_like_width_suffix(&['f'], suf) { // If it looks like a width, try to be helpful. dcx.create_err(InvalidFloatLiteralWidth { span, width: suf[1..].to_string() }) } else { dcx.create_err(InvalidFloatLiteralSuffix { span, suffix: suf.to_string() }) } } LitError::NonDecimalFloat(base) => match base { 16 => dcx.create_err(HexadecimalFloatLiteralNotSupported { span }), 8 => dcx.create_err(OctalFloatLiteralNotSupported { span }), 2 => dcx.create_err(BinaryFloatLiteralNotSupported { span }), _ => unreachable!(), }, LitError::IntTooLarge(base) => { let max = u128::MAX; let limit = match base { 2 => format!("{max:#b}"), 8 => format!("{max:#o}"), 16 => format!("{max:#x}"), _ => format!("{max}"), }; dcx.create_err(IntLiteralTooLarge { span, limit }) } } } #[derive(Diagnostic)] #[diag("linker flavor `{$flavor}` is incompatible with the current target")] #[note("compatible flavors are: {$compatible_list}")] pub(crate) struct IncompatibleLinkerFlavor { pub(crate) flavor: &'static str, pub(crate) compatible_list: String, } #[derive(Diagnostic)] #[diag("`-Zfunction-return` (except `keep`) is only supported on x86 and x86_64")] pub(crate) struct FunctionReturnRequiresX86OrX8664; #[derive(Diagnostic)] #[diag("`-Zfunction-return=thunk-extern` is only supported on non-large code models")] pub(crate) struct FunctionReturnThunkExternRequiresNonLargeCodeModel; #[derive(Diagnostic)] #[diag("`-Zindirect-branch-cs-prefix` is only supported on x86 and x86_64")] pub(crate) struct IndirectBranchCsPrefixRequiresX86OrX8664; #[derive(Diagnostic)] #[diag("`-Zregparm={$regparm}` is unsupported (valid values 0-3)")] pub(crate) struct UnsupportedRegparm { pub(crate) regparm: u32, } #[derive(Diagnostic)] #[diag("`-Zregparm=N` is only supported on x86")] pub(crate) struct UnsupportedRegparmArch; #[derive(Diagnostic)] #[diag("`-Zreg-struct-return` is only supported on x86")] pub(crate) struct UnsupportedRegStructReturnArch; #[derive(Diagnostic)] #[diag("failed to create profiler: {$err}")] pub(crate) struct FailedToCreateProfiler { pub(crate) err: String, } #[derive(Diagnostic)] #[diag("unexpected `--cfg {$cfg}` flag")] #[note("config `{$cfg_name}` is only supposed to be controlled by `{$controlled_by}`")] #[note("manually setting a built-in cfg can and does create incoherent behaviors")] pub(crate) struct UnexpectedBuiltinCfg { pub(crate) cfg: String, pub(crate) cfg_name: Symbol, pub(crate) controlled_by: &'static str, } #[derive(Diagnostic)] #[diag("ThinLTO is not supported by the codegen backend, using fat LTO instead")] pub(crate) struct ThinLtoNotSupportedByBackend; #[derive(Diagnostic)] #[diag("`-Zpacked-stack` is only supported on s390x")] pub(crate) struct UnsupportedPackedStack;