From c3c2c23e0d96c76f11a307cf3c4cf14a86fd158b Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Thu, 3 Apr 2025 20:59:05 -0400 Subject: Extend `QueryStability` to handle `IntoIterator` implementations Fix adjacent code Fix duplicate warning; merge test into `tests/ui-fulldeps/internal-lints` Use `rustc_middle::ty::FnSig::inputs` Address two review comments - https://github.com/rust-lang/rust/pull/139345#discussion_r2109006991 - https://github.com/rust-lang/rust/pull/139345#discussion_r2109058588 Use `Instance::try_resolve` Import `rustc_middle::ty::Ty` as `Ty` rather than `MiddleTy` Simplify predicate handling Add more `#[allow(rustc::potential_query_instability)]` following rebase Remove two `#[allow(rustc::potential_query_instability)]` following rebase Address review comment Update compiler/rustc_lint/src/internal.rs Co-authored-by: lcnr --- compiler/rustc_interface/src/interface.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'compiler/rustc_interface/src') diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index c46e879b976..8f131f45bbd 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -285,7 +285,9 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec) -> Ch .expecteds .entry(name.name) .and_modify(|v| match v { - ExpectedValues::Some(v) if !values_any_specified => { + ExpectedValues::Some(v) if !values_any_specified => + { + #[allow(rustc::potential_query_instability)] v.extend(values.clone()) } ExpectedValues::Some(_) => *v = ExpectedValues::Any, -- cgit 1.4.1-3-g733a5 From 1cd7080c3a7f29297675a72a157575ae12717304 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 7 May 2025 07:57:01 +0000 Subject: Add -Zindirect-branch-cs-prefix (from draft PR) --- compiler/rustc_codegen_llvm/src/context.rs | 9 +++++++++ compiler/rustc_interface/src/tests.rs | 1 + compiler/rustc_session/src/options.rs | 2 ++ tests/codegen-llvm/indirect-branch-cs-prefix.rs | 18 ++++++++++++++++++ 4 files changed, 30 insertions(+) create mode 100644 tests/codegen-llvm/indirect-branch-cs-prefix.rs (limited to 'compiler/rustc_interface/src') diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index b0f3494ea68..ea3a21dfdb5 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -462,6 +462,15 @@ pub(crate) unsafe fn create_module<'ll>( } } + if sess.opts.unstable_opts.indirect_branch_cs_prefix { + llvm::add_module_flag_u32( + llmod, + llvm::ModuleFlagMergeBehavior::Override, + "indirect_branch_cs_prefix", + 1, + ); + } + match (sess.opts.unstable_opts.small_data_threshold, sess.target.small_data_threshold_support()) { // Set up the small-data optimization limit for architectures that use diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 0a764808f95..4425877308a 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -807,6 +807,7 @@ fn test_unstable_options_tracking_hash() { tracked!(hint_mostly_unused, true); tracked!(human_readable_cgu_names, true); tracked!(incremental_ignore_spans, true); + tracked!(indirect_branch_cs_prefix, true); tracked!(inline_mir, Some(true)); tracked!(inline_mir_hint_threshold, Some(123)); tracked!(inline_mir_threshold, Some(123)); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 0e112edc733..fce4d18a1d8 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2295,6 +2295,8 @@ options! { - hashes of green query instances - hash collisions of query keys - hash collisions when creating dep-nodes"), + indirect_branch_cs_prefix: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], + "add cs prefix to call and jmp to indirect thunk (default: no)"), inline_llvm: bool = (true, parse_bool, [TRACKED], "enable LLVM inlining (default: yes)"), inline_mir: Option = (None, parse_opt_bool, [TRACKED], diff --git a/tests/codegen-llvm/indirect-branch-cs-prefix.rs b/tests/codegen-llvm/indirect-branch-cs-prefix.rs new file mode 100644 index 00000000000..a00b63a4970 --- /dev/null +++ b/tests/codegen-llvm/indirect-branch-cs-prefix.rs @@ -0,0 +1,18 @@ +// Test that the `indirect_branch_cs_prefix` module attribute is (not) emitted when the +// `-Zindirect-branch-cs-prefix` flag is (not) set. + +//@ add-core-stubs +//@ revisions: unset set +//@ needs-llvm-components: x86 +//@ compile-flags: --target x86_64-unknown-linux-gnu +//@ [set] compile-flags: -Zindirect-branch-cs-prefix + +#![crate_type = "lib"] +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +trait Sized {} + +// unset-NOT: !{{[0-9]+}} = !{i32 4, !"indirect_branch_cs_prefix", i32 1} +// set: !{{[0-9]+}} = !{i32 4, !"indirect_branch_cs_prefix", i32 1} -- cgit 1.4.1-3-g733a5 From 21d31897794ed7fc7990de8e664d3c4ec511da7d Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 29 Jul 2025 16:35:22 +0200 Subject: Move validate_attr to `rustc_attr_parsing` --- Cargo.lock | 3 +- compiler/rustc_ast_passes/Cargo.toml | 1 - compiler/rustc_ast_passes/src/ast_validation.rs | 2 +- compiler/rustc_attr_parsing/Cargo.toml | 1 + compiler/rustc_attr_parsing/messages.ftl | 12 + compiler/rustc_attr_parsing/src/lib.rs | 1 + .../rustc_attr_parsing/src/session_diagnostics.rs | 55 +++- compiler/rustc_attr_parsing/src/validate_attr.rs | 340 ++++++++++++++++++++ .../rustc_builtin_macros/src/cfg_accessible.rs | 2 +- compiler/rustc_builtin_macros/src/derive.rs | 2 +- compiler/rustc_builtin_macros/src/util.rs | 3 +- compiler/rustc_expand/src/config.rs | 4 +- compiler/rustc_expand/src/expand.rs | 3 +- compiler/rustc_expand/src/module.rs | 3 +- compiler/rustc_interface/src/passes.rs | 5 +- compiler/rustc_interface/src/util.rs | 2 +- compiler/rustc_parse/Cargo.toml | 1 - compiler/rustc_parse/messages.ftl | 10 - compiler/rustc_parse/src/errors.rs | 41 --- compiler/rustc_parse/src/lib.rs | 17 +- compiler/rustc_parse/src/parser/attr.rs | 2 +- compiler/rustc_parse/src/validate_attr.rs | 350 --------------------- 22 files changed, 437 insertions(+), 423 deletions(-) create mode 100644 compiler/rustc_attr_parsing/src/validate_attr.rs delete mode 100644 compiler/rustc_parse/src/validate_attr.rs (limited to 'compiler/rustc_interface/src') diff --git a/Cargo.lock b/Cargo.lock index 91528a4135e..8eb77165cf2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3459,7 +3459,6 @@ dependencies = [ "rustc_feature", "rustc_fluent_macro", "rustc_macros", - "rustc_parse", "rustc_session", "rustc_span", "rustc_target", @@ -3490,6 +3489,7 @@ dependencies = [ "rustc_hir", "rustc_lexer", "rustc_macros", + "rustc_parse", "rustc_session", "rustc_span", "thin-vec", @@ -4355,7 +4355,6 @@ dependencies = [ "rustc-literal-escaper", "rustc_ast", "rustc_ast_pretty", - "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_feature", diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml index 1940628b44a..3e04f8b11ec 100644 --- a/compiler/rustc_ast_passes/Cargo.toml +++ b/compiler/rustc_ast_passes/Cargo.toml @@ -15,7 +15,6 @@ rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_macros = { path = "../rustc_macros" } -rustc_parse = { path = "../rustc_parse" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index ef4410566c5..508eb083672 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -25,10 +25,10 @@ use rustc_abi::{CanonAbi, ExternAbi, InterruptKind}; use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, walk_list}; use rustc_ast::*; use rustc_ast_pretty::pprust::{self, State}; +use rustc_attr_parsing::validate_attr; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::DiagCtxtHandle; use rustc_feature::Features; -use rustc_parse::validate_attr; use rustc_session::Session; use rustc_session::lint::builtin::{ DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN, diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml index cec9d62e656..fd8f7ffb2ed 100644 --- a/compiler/rustc_attr_parsing/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -14,6 +14,7 @@ rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } +rustc_parse = { path = "../rustc_parse" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } thin-vec = "0.2.12" diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 067d95a0f48..1a2815c54ec 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -170,3 +170,15 @@ attr_parsing_unused_multiple = -attr_parsing_previously_accepted = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +attr_parsing_meta_bad_delim = wrong meta list delimiters +attr_parsing_meta_bad_delim_suggestion = the delimiters should be `(` and `)` + +attr_parsing_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe + .label = usage of unsafe attribute +attr_parsing_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)` + +attr_parsing_invalid_attr_unsafe = `{$name}` is not an unsafe attribute + .label = this is not an unsafe attribute + .suggestion = remove the `unsafe(...)` + .note = extraneous unsafe is not allowed in attributes diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 99842cd9687..969c24a4f89 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -102,6 +102,7 @@ pub mod parser; mod lints; mod session_diagnostics; mod target_checking; +pub mod validate_attr; pub use attributes::cfg::{CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr}; pub use attributes::cfg_old::*; diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 2993881f717..b877caa143a 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -1,6 +1,6 @@ use std::num::IntErrorKind; -use rustc_ast::{self as ast, AttrStyle}; +use rustc_ast::{self as ast, AttrStyle, Path}; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, @@ -737,3 +737,56 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { diag } } + +#[derive(Diagnostic)] +#[diag(attr_parsing_invalid_attr_unsafe)] +#[note] +pub(crate) struct InvalidAttrUnsafe { + #[primary_span] + #[label] + pub span: Span, + pub name: Path, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_unsafe_attr_outside_unsafe)] +pub(crate) struct UnsafeAttrOutsideUnsafe { + #[primary_span] + #[label] + pub span: Span, + #[subdiagnostic] + pub suggestion: UnsafeAttrOutsideUnsafeSuggestion, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + attr_parsing_unsafe_attr_outside_unsafe_suggestion, + applicability = "machine-applicable" +)] +pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion { + #[suggestion_part(code = "unsafe(")] + pub left: Span, + #[suggestion_part(code = ")")] + pub right: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_meta_bad_delim)] +pub(crate) struct MetaBadDelim { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub sugg: MetaBadDelimSugg, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + attr_parsing_meta_bad_delim_suggestion, + applicability = "machine-applicable" +)] +pub(crate) struct MetaBadDelimSugg { + #[suggestion_part(code = "(")] + pub open: Span, + #[suggestion_part(code = ")")] + pub close: Span, +} diff --git a/compiler/rustc_attr_parsing/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs new file mode 100644 index 00000000000..da3a1cbe016 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/validate_attr.rs @@ -0,0 +1,340 @@ +//! Meta-syntax validation logic of attributes for post-expansion. + +use std::slice; + +use rustc_ast::token::Delimiter; +use rustc_ast::tokenstream::DelimSpan; +use rustc_ast::{ + self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, NodeId, + Path, Safety, +}; +use rustc_errors::{Applicability, DiagCtxtHandle, FatalError, PResult}; +use rustc_feature::{AttributeSafety, AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; +use rustc_parse::parse_in; +use rustc_session::errors::report_lit_error; +use rustc_session::lint::BuiltinLintDiag; +use rustc_session::lint::builtin::{ILL_FORMED_ATTRIBUTE_INPUT, UNSAFE_ATTR_OUTSIDE_UNSAFE}; +use rustc_session::parse::ParseSess; +use rustc_span::{Span, Symbol, sym}; + +use crate::{AttributeParser, Late, session_diagnostics as errors}; + +pub fn check_attr(psess: &ParseSess, attr: &Attribute, id: NodeId) { + if attr.is_doc_comment() || attr.has_name(sym::cfg_trace) || attr.has_name(sym::cfg_attr_trace) + { + return; + } + + let builtin_attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)); + + let builtin_attr_safety = builtin_attr_info.map(|x| x.safety); + check_attribute_safety(psess, builtin_attr_safety, attr, id); + + // Check input tokens for built-in and key-value attributes. + match builtin_attr_info { + // `rustc_dummy` doesn't have any restrictions specific to built-in attributes. + Some(BuiltinAttribute { name, template, .. }) if *name != sym::rustc_dummy => { + match parse_meta(psess, attr) { + // Don't check safety again, we just did that + Ok(meta) => { + check_builtin_meta_item(psess, &meta, attr.style, *name, *template, false) + } + Err(err) => { + err.emit(); + } + } + } + _ => { + let attr_item = attr.get_normal_item(); + if let AttrArgs::Eq { .. } = attr_item.args { + // All key-value attributes are restricted to meta-item syntax. + match parse_meta(psess, attr) { + Ok(_) => {} + Err(err) => { + err.emit(); + } + } + } + } + } +} + +pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, MetaItem> { + let item = attr.get_normal_item(); + Ok(MetaItem { + unsafety: item.unsafety, + span: attr.span, + path: item.path.clone(), + kind: match &item.args { + AttrArgs::Empty => MetaItemKind::Word, + AttrArgs::Delimited(DelimArgs { dspan, delim, tokens }) => { + check_meta_bad_delim(psess, *dspan, *delim); + let nmis = + parse_in(psess, tokens.clone(), "meta list", |p| p.parse_meta_seq_top())?; + MetaItemKind::List(nmis) + } + AttrArgs::Eq { expr, .. } => { + if let ast::ExprKind::Lit(token_lit) = expr.kind { + let res = ast::MetaItemLit::from_token_lit(token_lit, expr.span); + let res = match res { + Ok(lit) => { + if token_lit.suffix.is_some() { + let mut err = psess.dcx().struct_span_err( + expr.span, + "suffixed literals are not allowed in attributes", + ); + err.help( + "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \ + use an unsuffixed version (`1`, `1.0`, etc.)", + ); + return Err(err); + } else { + MetaItemKind::NameValue(lit) + } + } + Err(err) => { + let guar = report_lit_error(psess, err, token_lit, expr.span); + let lit = ast::MetaItemLit { + symbol: token_lit.symbol, + suffix: token_lit.suffix, + kind: ast::LitKind::Err(guar), + span: expr.span, + }; + MetaItemKind::NameValue(lit) + } + }; + res + } else { + // Example cases: + // - `#[foo = 1+1]`: results in `ast::ExprKind::Binary`. + // - `#[foo = include_str!("nonexistent-file.rs")]`: + // results in `ast::ExprKind::Err`. In that case we delay + // the error because an earlier error will have already + // been reported. + let msg = "attribute value must be a literal"; + let mut err = psess.dcx().struct_span_err(expr.span, msg); + if let ast::ExprKind::Err(_) = expr.kind { + err.downgrade_to_delayed_bug(); + } + return Err(err); + } + } + }, + }) +} + +fn check_meta_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) { + if let Delimiter::Parenthesis = delim { + return; + } + psess.dcx().emit_err(errors::MetaBadDelim { + span: span.entire(), + sugg: errors::MetaBadDelimSugg { open: span.open, close: span.close }, + }); +} + +/// Checks that the given meta-item is compatible with this `AttributeTemplate`. +fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaItemKind) -> bool { + let is_one_allowed_subword = |items: &[MetaItemInner]| match items { + [item] => item.is_word() && template.one_of.iter().any(|&word| item.has_name(word)), + _ => false, + }; + match meta { + MetaItemKind::Word => template.word, + MetaItemKind::List(items) => template.list.is_some() || is_one_allowed_subword(items), + MetaItemKind::NameValue(lit) if lit.kind.is_str() => template.name_value_str.is_some(), + MetaItemKind::NameValue(..) => false, + } +} + +pub fn check_attribute_safety( + psess: &ParseSess, + builtin_attr_safety: Option, + attr: &Attribute, + id: NodeId, +) { + let attr_item = attr.get_normal_item(); + match (builtin_attr_safety, attr_item.unsafety) { + // - Unsafe builtin attribute + // - User wrote `#[unsafe(..)]`, which is permitted on any edition + (Some(AttributeSafety::Unsafe { .. }), Safety::Unsafe(..)) => { + // OK + } + + // - Unsafe builtin attribute + // - User did not write `#[unsafe(..)]` + (Some(AttributeSafety::Unsafe { unsafe_since }), Safety::Default) => { + let path_span = attr_item.path.span; + + // If the `attr_item`'s span is not from a macro, then just suggest + // wrapping it in `unsafe(...)`. Otherwise, we suggest putting the + // `unsafe(`, `)` right after and right before the opening and closing + // square bracket respectively. + let diag_span = attr_item.span(); + + // Attributes can be safe in earlier editions, and become unsafe in later ones. + // + // Use the span of the attribute's name to determine the edition: the span of the + // attribute as a whole may be inaccurate if it was emitted by a macro. + // + // See https://github.com/rust-lang/rust/issues/142182. + let emit_error = match unsafe_since { + None => true, + Some(unsafe_since) => path_span.edition() >= unsafe_since, + }; + + if emit_error { + psess.dcx().emit_err(errors::UnsafeAttrOutsideUnsafe { + span: path_span, + suggestion: errors::UnsafeAttrOutsideUnsafeSuggestion { + left: diag_span.shrink_to_lo(), + right: diag_span.shrink_to_hi(), + }, + }); + } else { + psess.buffer_lint( + UNSAFE_ATTR_OUTSIDE_UNSAFE, + path_span, + id, + BuiltinLintDiag::UnsafeAttrOutsideUnsafe { + attribute_name_span: path_span, + sugg_spans: (diag_span.shrink_to_lo(), diag_span.shrink_to_hi()), + }, + ); + } + } + + // - Normal builtin attribute, or any non-builtin attribute + // - All non-builtin attributes are currently considered safe; writing `#[unsafe(..)]` is + // not permitted on non-builtin attributes or normal builtin attributes + (Some(AttributeSafety::Normal) | None, Safety::Unsafe(unsafe_span)) => { + psess.dcx().emit_err(errors::InvalidAttrUnsafe { + span: unsafe_span, + name: attr_item.path.clone(), + }); + } + + // - Normal builtin attribute + // - No explicit `#[unsafe(..)]` written. + (Some(AttributeSafety::Normal), Safety::Default) => { + // OK + } + + // - Non-builtin attribute + // - No explicit `#[unsafe(..)]` written. + (None, Safety::Default) => { + // OK + } + + ( + Some(AttributeSafety::Unsafe { .. } | AttributeSafety::Normal) | None, + Safety::Safe(..), + ) => { + psess.dcx().span_delayed_bug( + attr_item.span(), + "`check_attribute_safety` does not expect `Safety::Safe` on attributes", + ); + } + } +} + +// Called by `check_builtin_meta_item` and code that manually denies +// `unsafe(...)` in `cfg` +pub fn deny_builtin_meta_unsafety(diag: DiagCtxtHandle<'_>, unsafety: Safety, name: &Path) { + // This only supports denying unsafety right now - making builtin attributes + // support unsafety will requite us to thread the actual `Attribute` through + // for the nice diagnostics. + if let Safety::Unsafe(unsafe_span) = unsafety { + diag.emit_err(errors::InvalidAttrUnsafe { span: unsafe_span, name: name.clone() }); + } +} + +pub fn check_builtin_meta_item( + psess: &ParseSess, + meta: &MetaItem, + style: ast::AttrStyle, + name: Symbol, + template: AttributeTemplate, + deny_unsafety: bool, +) { + if !is_attr_template_compatible(&template, &meta.kind) { + // attrs with new parsers are locally validated so excluded here + if AttributeParser::::is_parsed_attribute(slice::from_ref(&name)) { + return; + } + emit_malformed_attribute(psess, style, meta.span, name, template); + } + + if deny_unsafety { + deny_builtin_meta_unsafety(psess.dcx(), meta.unsafety, &meta.path); + } +} + +fn emit_malformed_attribute( + psess: &ParseSess, + style: ast::AttrStyle, + span: Span, + name: Symbol, + template: AttributeTemplate, +) { + // Some of previously accepted forms were used in practice, + // report them as warnings for now. + let should_warn = |name| matches!(name, sym::doc | sym::link | sym::test | sym::bench); + + let error_msg = format!("malformed `{name}` attribute input"); + let mut suggestions = vec![]; + let inner = if style == ast::AttrStyle::Inner { "!" } else { "" }; + if template.word { + suggestions.push(format!("#{inner}[{name}]")); + } + if let Some(descr) = template.list { + for descr in descr { + suggestions.push(format!("#{inner}[{name}({descr})]")); + } + } + suggestions.extend(template.one_of.iter().map(|&word| format!("#{inner}[{name}({word})]"))); + if let Some(descr) = template.name_value_str { + for descr in descr { + suggestions.push(format!("#{inner}[{name} = \"{descr}\"]")); + } + } + if should_warn(name) { + psess.buffer_lint( + ILL_FORMED_ATTRIBUTE_INPUT, + span, + ast::CRATE_NODE_ID, + BuiltinLintDiag::IllFormedAttributeInput { + suggestions: suggestions.clone(), + docs: template.docs, + }, + ); + } else { + suggestions.sort(); + let mut err = psess.dcx().struct_span_err(span, error_msg).with_span_suggestions( + span, + if suggestions.len() == 1 { + "must be of the form" + } else { + "the following are the possible correct uses" + }, + suggestions, + Applicability::HasPlaceholders, + ); + if let Some(link) = template.docs { + err.note(format!("for more information, visit <{link}>")); + } + err.emit(); + } +} + +pub fn emit_fatal_malformed_builtin_attribute( + psess: &ParseSess, + attr: &Attribute, + name: Symbol, +) -> ! { + let template = BUILTIN_ATTRIBUTE_MAP.get(&name).expect("builtin attr defined").template; + emit_malformed_attribute(psess, attr.style, attr.span, name, template); + // This is fatal, otherwise it will likely cause a cascade of other errors + // (and an error here is expected to be very rare). + FatalError.raise() +} diff --git a/compiler/rustc_builtin_macros/src/cfg_accessible.rs b/compiler/rustc_builtin_macros/src/cfg_accessible.rs index f7d8f4aa783..48d80004cdd 100644 --- a/compiler/rustc_builtin_macros/src/cfg_accessible.rs +++ b/compiler/rustc_builtin_macros/src/cfg_accessible.rs @@ -1,9 +1,9 @@ //! Implementation of the `#[cfg_accessible(path)]` attribute macro. use rustc_ast as ast; +use rustc_attr_parsing::validate_attr; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier}; use rustc_feature::AttributeTemplate; -use rustc_parse::validate_attr; use rustc_span::{Span, sym}; use crate::errors; diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index a33eca43de5..09d827b0635 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -1,10 +1,10 @@ use rustc_ast as ast; use rustc_ast::{GenericParamKind, ItemKind, MetaItemInner, MetaItemKind, StmtKind}; +use rustc_attr_parsing::validate_attr; use rustc_expand::base::{ Annotatable, DeriveResolution, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier, }; use rustc_feature::AttributeTemplate; -use rustc_parse::validate_attr; use rustc_session::Session; use rustc_span::{ErrorGuaranteed, Ident, Span, sym}; diff --git a/compiler/rustc_builtin_macros/src/util.rs b/compiler/rustc_builtin_macros/src/util.rs index f00c170e485..3a4585d5be9 100644 --- a/compiler/rustc_builtin_macros/src/util.rs +++ b/compiler/rustc_builtin_macros/src/util.rs @@ -1,12 +1,13 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::{self as ast, AttrStyle, Attribute, MetaItem, attr, token}; +use rustc_attr_parsing::validate_attr; use rustc_errors::{Applicability, Diag, ErrorGuaranteed}; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt}; use rustc_expand::expand::AstFragment; use rustc_feature::AttributeTemplate; use rustc_lint_defs::BuiltinLintDiag; use rustc_lint_defs::builtin::DUPLICATE_MACRO_ATTRIBUTES; -use rustc_parse::{exp, parser, validate_attr}; +use rustc_parse::{exp, parser}; use rustc_session::errors::report_lit_error; use rustc_span::{BytePos, Span, Symbol}; diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 83a8d601afe..db5b45ff3d5 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -11,8 +11,10 @@ use rustc_ast::{ NodeId, NormalAttr, }; use rustc_attr_parsing as attr; +use rustc_attr_parsing::validate_attr::deny_builtin_meta_unsafety; use rustc_attr_parsing::{ AttributeParser, CFG_TEMPLATE, EvalConfigResult, ShouldEmit, eval_config_entry, parse_cfg_attr, + validate_attr, }; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_feature::{ @@ -20,8 +22,6 @@ use rustc_feature::{ REMOVED_LANG_FEATURES, UNSTABLE_LANG_FEATURES, }; use rustc_lint_defs::BuiltinLintDiag; -use rustc_parse::validate_attr; -use rustc_parse::validate_attr::deny_builtin_meta_unsafety; use rustc_session::Session; use rustc_session::parse::feature_err; use rustc_span::{STDLIB_STABLE_CRATES, Span, Symbol, sym}; diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 755275d3cda..7bc380f0939 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -12,7 +12,7 @@ use rustc_ast::{ MetaItemKind, ModKind, NodeId, PatKind, StmtKind, TyKind, token, }; use rustc_ast_pretty::pprust; -use rustc_attr_parsing::{EvalConfigResult, ShouldEmit}; +use rustc_attr_parsing::{EvalConfigResult, ShouldEmit, validate_attr}; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_errors::PResult; use rustc_feature::Features; @@ -21,7 +21,6 @@ use rustc_parse::parser::{ AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, RecoverColon, RecoverComma, token_descr, }; -use rustc_parse::validate_attr; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS}; use rustc_session::parse::feature_err; diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs index 6666ea33cd3..19f3cdbc549 100644 --- a/compiler/rustc_expand/src/module.rs +++ b/compiler/rustc_expand/src/module.rs @@ -2,8 +2,9 @@ use std::iter::once; use std::path::{self, Path, PathBuf}; use rustc_ast::{AttrVec, Attribute, Inline, Item, ModSpans}; +use rustc_attr_parsing::validate_attr; use rustc_errors::{Diag, ErrorGuaranteed}; -use rustc_parse::{exp, new_parser_from_file, unwrap_or_emit_fatal, validate_attr}; +use rustc_parse::{exp, new_parser_from_file, unwrap_or_emit_fatal}; use rustc_session::Session; use rustc_session::parse::ParseSess; use rustc_span::{Ident, Span, sym}; diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 3ba224723e3..424cba2dae8 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -6,6 +6,7 @@ use std::sync::{Arc, LazyLock, OnceLock}; use std::{env, fs, iter}; use rustc_ast as ast; +use rustc_attr_parsing::validate_attr; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::steal::Steal; @@ -25,9 +26,7 @@ use rustc_middle::arena::Arena; use rustc_middle::dep_graph::DepsType; use rustc_middle::ty::{self, CurrentGcx, GlobalCtxt, RegisteredTools, TyCtxt}; use rustc_middle::util::Providers; -use rustc_parse::{ - new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal, validate_attr, -}; +use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_passes::{abi_test, input_stats, layout_test}; use rustc_resolve::{Resolver, ResolverOutputs}; use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 0ca4fcc66ca..49ac3b7a10d 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -5,12 +5,12 @@ use std::sync::{Arc, OnceLock}; use std::{env, thread}; use rustc_ast as ast; +use rustc_attr_parsing::validate_attr; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::sync; use rustc_metadata::{DylibError, load_symbol_from_dylib}; use rustc_middle::ty::CurrentGcx; -use rustc_parse::validate_attr; use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple}; use rustc_session::lint::{self, BuiltinLintDiag, LintBuffer}; use rustc_session::output::{CRATE_TYPES, categorize_crate_type}; diff --git a/compiler/rustc_parse/Cargo.toml b/compiler/rustc_parse/Cargo.toml index 0ae0b613fa2..6d738a10371 100644 --- a/compiler/rustc_parse/Cargo.toml +++ b/compiler/rustc_parse/Cargo.toml @@ -9,7 +9,6 @@ bitflags = "2.4.1" rustc-literal-escaper = "0.0.5" rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index a107a682184..1c09cab53b8 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -436,11 +436,6 @@ parse_inner_doc_comment_not_permitted = expected outer doc comment .label_does_not_annotate_this = the inner doc comment doesn't annotate this {$item} .sugg_change_inner_to_outer = to annotate the {$item}, change the doc comment from inner to outer style -parse_invalid_attr_unsafe = `{$name}` is not an unsafe attribute - .label = this is not an unsafe attribute - .suggestion = remove the `unsafe(...)` - .note = extraneous unsafe is not allowed in attributes - parse_invalid_block_macro_segment = cannot use a `block` macro fragment here .label = the `block` fragment is within this context .suggestion = wrap this in another block @@ -601,7 +596,6 @@ parse_maybe_report_ambiguous_plus = ambiguous `+` in a type .suggestion = use parentheses to disambiguate -parse_meta_bad_delim = wrong meta list delimiters parse_meta_bad_delim_suggestion = the delimiters should be `(` and `)` parse_mismatched_closing_delimiter = mismatched closing delimiter: `{$delimiter}` @@ -990,10 +984,6 @@ parse_unmatched_angle_brackets = {$num_extra_brackets -> *[other] remove extra angle brackets } -parse_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe - .label = usage of unsafe attribute -parse_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)` - parse_unskipped_whitespace = whitespace symbol '{$ch}' is not skipped .label = {parse_unskipped_whitespace} diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 2c046329e33..dfd4f38cf03 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3345,15 +3345,6 @@ pub(crate) struct KwBadCase<'a> { pub kw: &'a str, } -#[derive(Diagnostic)] -#[diag(parse_meta_bad_delim)] -pub(crate) struct MetaBadDelim { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub sugg: MetaBadDelimSugg, -} - #[derive(Diagnostic)] #[diag(parse_cfg_attr_bad_delim)] pub(crate) struct CfgAttrBadDelim { @@ -3493,38 +3484,6 @@ pub(crate) struct DotDotRangeAttribute { pub span: Span, } -#[derive(Diagnostic)] -#[diag(parse_invalid_attr_unsafe)] -#[note] -pub(crate) struct InvalidAttrUnsafe { - #[primary_span] - #[label] - pub span: Span, - pub name: Path, -} - -#[derive(Diagnostic)] -#[diag(parse_unsafe_attr_outside_unsafe)] -pub(crate) struct UnsafeAttrOutsideUnsafe { - #[primary_span] - #[label] - pub span: Span, - #[subdiagnostic] - pub suggestion: UnsafeAttrOutsideUnsafeSuggestion, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion( - parse_unsafe_attr_outside_unsafe_suggestion, - applicability = "machine-applicable" -)] -pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion { - #[suggestion_part(code = "unsafe(")] - pub left: Span, - #[suggestion_part(code = ")")] - pub right: Span, -} - #[derive(Diagnostic)] #[diag(parse_binder_before_modifiers)] pub(crate) struct BinderBeforeModifiers { diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index adad5751871..48289b2e8ab 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -16,7 +16,7 @@ use std::str::Utf8Error; use std::sync::Arc; use rustc_ast as ast; -use rustc_ast::tokenstream::TokenStream; +use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::{AttrItem, Attribute, MetaItemInner, token}; use rustc_ast_pretty::pprust; use rustc_errors::{Diag, EmissionGuarantee, FatalError, PResult, pluralize}; @@ -31,8 +31,9 @@ pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments"); #[macro_use] pub mod parser; use parser::Parser; +use rustc_ast::token::Delimiter; + pub mod lexer; -pub mod validate_attr; mod errors; @@ -235,7 +236,7 @@ pub fn parse_cfg_attr( ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, ref tokens }) if !tokens.is_empty() => { - crate::validate_attr::check_cfg_attr_bad_delim(psess, dspan, delim); + check_cfg_attr_bad_delim(psess, dspan, delim); match parse_in(psess, tokens.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) { Ok(r) => return Some(r), Err(e) => { @@ -254,3 +255,13 @@ pub fn parse_cfg_attr( } None } + +fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) { + if let Delimiter::Parenthesis = delim { + return; + } + psess.dcx().emit_err(errors::CfgAttrBadDelim { + span: span.entire(), + sugg: errors::MetaBadDelimSugg { open: span.open, close: span.close }, + }); +} diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 7f6afeba28c..acd338156ce 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -399,7 +399,7 @@ impl<'a> Parser<'a> { } /// Matches `COMMASEP(meta_item_inner)`. - pub(crate) fn parse_meta_seq_top(&mut self) -> PResult<'a, ThinVec> { + pub fn parse_meta_seq_top(&mut self) -> PResult<'a, ThinVec> { // Presumably, the majority of the time there will only be one attr. let mut nmis = ThinVec::with_capacity(1); while self.token != token::Eof { diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs deleted file mode 100644 index 68ef6d6f32c..00000000000 --- a/compiler/rustc_parse/src/validate_attr.rs +++ /dev/null @@ -1,350 +0,0 @@ -//! Meta-syntax validation logic of attributes for post-expansion. - -use std::slice; - -use rustc_ast::token::Delimiter; -use rustc_ast::tokenstream::DelimSpan; -use rustc_ast::{ - self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, NodeId, - Path, Safety, -}; -use rustc_attr_parsing::{AttributeParser, Late}; -use rustc_errors::{Applicability, DiagCtxtHandle, FatalError, PResult}; -use rustc_feature::{AttributeSafety, AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; -use rustc_session::errors::report_lit_error; -use rustc_session::lint::BuiltinLintDiag; -use rustc_session::lint::builtin::{ILL_FORMED_ATTRIBUTE_INPUT, UNSAFE_ATTR_OUTSIDE_UNSAFE}; -use rustc_session::parse::ParseSess; -use rustc_span::{Span, Symbol, sym}; - -use crate::{errors, parse_in}; - -pub fn check_attr(psess: &ParseSess, attr: &Attribute, id: NodeId) { - if attr.is_doc_comment() || attr.has_name(sym::cfg_trace) || attr.has_name(sym::cfg_attr_trace) - { - return; - } - - let builtin_attr_info = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)); - - let builtin_attr_safety = builtin_attr_info.map(|x| x.safety); - check_attribute_safety(psess, builtin_attr_safety, attr, id); - - // Check input tokens for built-in and key-value attributes. - match builtin_attr_info { - // `rustc_dummy` doesn't have any restrictions specific to built-in attributes. - Some(BuiltinAttribute { name, template, .. }) if *name != sym::rustc_dummy => { - match parse_meta(psess, attr) { - // Don't check safety again, we just did that - Ok(meta) => { - check_builtin_meta_item(psess, &meta, attr.style, *name, *template, false) - } - Err(err) => { - err.emit(); - } - } - } - _ => { - let attr_item = attr.get_normal_item(); - if let AttrArgs::Eq { .. } = attr_item.args { - // All key-value attributes are restricted to meta-item syntax. - match parse_meta(psess, attr) { - Ok(_) => {} - Err(err) => { - err.emit(); - } - } - } - } - } -} - -pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, MetaItem> { - let item = attr.get_normal_item(); - Ok(MetaItem { - unsafety: item.unsafety, - span: attr.span, - path: item.path.clone(), - kind: match &item.args { - AttrArgs::Empty => MetaItemKind::Word, - AttrArgs::Delimited(DelimArgs { dspan, delim, tokens }) => { - check_meta_bad_delim(psess, *dspan, *delim); - let nmis = - parse_in(psess, tokens.clone(), "meta list", |p| p.parse_meta_seq_top())?; - MetaItemKind::List(nmis) - } - AttrArgs::Eq { expr, .. } => { - if let ast::ExprKind::Lit(token_lit) = expr.kind { - let res = ast::MetaItemLit::from_token_lit(token_lit, expr.span); - let res = match res { - Ok(lit) => { - if token_lit.suffix.is_some() { - let mut err = psess.dcx().struct_span_err( - expr.span, - "suffixed literals are not allowed in attributes", - ); - err.help( - "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \ - use an unsuffixed version (`1`, `1.0`, etc.)", - ); - return Err(err); - } else { - MetaItemKind::NameValue(lit) - } - } - Err(err) => { - let guar = report_lit_error(psess, err, token_lit, expr.span); - let lit = ast::MetaItemLit { - symbol: token_lit.symbol, - suffix: token_lit.suffix, - kind: ast::LitKind::Err(guar), - span: expr.span, - }; - MetaItemKind::NameValue(lit) - } - }; - res - } else { - // Example cases: - // - `#[foo = 1+1]`: results in `ast::ExprKind::Binary`. - // - `#[foo = include_str!("nonexistent-file.rs")]`: - // results in `ast::ExprKind::Err`. In that case we delay - // the error because an earlier error will have already - // been reported. - let msg = "attribute value must be a literal"; - let mut err = psess.dcx().struct_span_err(expr.span, msg); - if let ast::ExprKind::Err(_) = expr.kind { - err.downgrade_to_delayed_bug(); - } - return Err(err); - } - } - }, - }) -} - -fn check_meta_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) { - if let Delimiter::Parenthesis = delim { - return; - } - psess.dcx().emit_err(errors::MetaBadDelim { - span: span.entire(), - sugg: errors::MetaBadDelimSugg { open: span.open, close: span.close }, - }); -} - -pub(super) fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) { - if let Delimiter::Parenthesis = delim { - return; - } - psess.dcx().emit_err(errors::CfgAttrBadDelim { - span: span.entire(), - sugg: errors::MetaBadDelimSugg { open: span.open, close: span.close }, - }); -} - -/// Checks that the given meta-item is compatible with this `AttributeTemplate`. -fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaItemKind) -> bool { - let is_one_allowed_subword = |items: &[MetaItemInner]| match items { - [item] => item.is_word() && template.one_of.iter().any(|&word| item.has_name(word)), - _ => false, - }; - match meta { - MetaItemKind::Word => template.word, - MetaItemKind::List(items) => template.list.is_some() || is_one_allowed_subword(items), - MetaItemKind::NameValue(lit) if lit.kind.is_str() => template.name_value_str.is_some(), - MetaItemKind::NameValue(..) => false, - } -} - -pub fn check_attribute_safety( - psess: &ParseSess, - builtin_attr_safety: Option, - attr: &Attribute, - id: NodeId, -) { - let attr_item = attr.get_normal_item(); - match (builtin_attr_safety, attr_item.unsafety) { - // - Unsafe builtin attribute - // - User wrote `#[unsafe(..)]`, which is permitted on any edition - (Some(AttributeSafety::Unsafe { .. }), Safety::Unsafe(..)) => { - // OK - } - - // - Unsafe builtin attribute - // - User did not write `#[unsafe(..)]` - (Some(AttributeSafety::Unsafe { unsafe_since }), Safety::Default) => { - let path_span = attr_item.path.span; - - // If the `attr_item`'s span is not from a macro, then just suggest - // wrapping it in `unsafe(...)`. Otherwise, we suggest putting the - // `unsafe(`, `)` right after and right before the opening and closing - // square bracket respectively. - let diag_span = attr_item.span(); - - // Attributes can be safe in earlier editions, and become unsafe in later ones. - // - // Use the span of the attribute's name to determine the edition: the span of the - // attribute as a whole may be inaccurate if it was emitted by a macro. - // - // See https://github.com/rust-lang/rust/issues/142182. - let emit_error = match unsafe_since { - None => true, - Some(unsafe_since) => path_span.edition() >= unsafe_since, - }; - - if emit_error { - psess.dcx().emit_err(errors::UnsafeAttrOutsideUnsafe { - span: path_span, - suggestion: errors::UnsafeAttrOutsideUnsafeSuggestion { - left: diag_span.shrink_to_lo(), - right: diag_span.shrink_to_hi(), - }, - }); - } else { - psess.buffer_lint( - UNSAFE_ATTR_OUTSIDE_UNSAFE, - path_span, - id, - BuiltinLintDiag::UnsafeAttrOutsideUnsafe { - attribute_name_span: path_span, - sugg_spans: (diag_span.shrink_to_lo(), diag_span.shrink_to_hi()), - }, - ); - } - } - - // - Normal builtin attribute, or any non-builtin attribute - // - All non-builtin attributes are currently considered safe; writing `#[unsafe(..)]` is - // not permitted on non-builtin attributes or normal builtin attributes - (Some(AttributeSafety::Normal) | None, Safety::Unsafe(unsafe_span)) => { - psess.dcx().emit_err(errors::InvalidAttrUnsafe { - span: unsafe_span, - name: attr_item.path.clone(), - }); - } - - // - Normal builtin attribute - // - No explicit `#[unsafe(..)]` written. - (Some(AttributeSafety::Normal), Safety::Default) => { - // OK - } - - // - Non-builtin attribute - // - No explicit `#[unsafe(..)]` written. - (None, Safety::Default) => { - // OK - } - - ( - Some(AttributeSafety::Unsafe { .. } | AttributeSafety::Normal) | None, - Safety::Safe(..), - ) => { - psess.dcx().span_delayed_bug( - attr_item.span(), - "`check_attribute_safety` does not expect `Safety::Safe` on attributes", - ); - } - } -} - -// Called by `check_builtin_meta_item` and code that manually denies -// `unsafe(...)` in `cfg` -pub fn deny_builtin_meta_unsafety(diag: DiagCtxtHandle<'_>, unsafety: Safety, name: &Path) { - // This only supports denying unsafety right now - making builtin attributes - // support unsafety will requite us to thread the actual `Attribute` through - // for the nice diagnostics. - if let Safety::Unsafe(unsafe_span) = unsafety { - diag.emit_err(errors::InvalidAttrUnsafe { span: unsafe_span, name: name.clone() }); - } -} - -pub fn check_builtin_meta_item( - psess: &ParseSess, - meta: &MetaItem, - style: ast::AttrStyle, - name: Symbol, - template: AttributeTemplate, - deny_unsafety: bool, -) { - if !is_attr_template_compatible(&template, &meta.kind) { - // attrs with new parsers are locally validated so excluded here - if AttributeParser::::is_parsed_attribute(slice::from_ref(&name)) { - return; - } - emit_malformed_attribute(psess, style, meta.span, name, template); - } - - if deny_unsafety { - deny_builtin_meta_unsafety(psess.dcx(), meta.unsafety, &meta.path); - } -} - -fn emit_malformed_attribute( - psess: &ParseSess, - style: ast::AttrStyle, - span: Span, - name: Symbol, - template: AttributeTemplate, -) { - // Some of previously accepted forms were used in practice, - // report them as warnings for now. - let should_warn = |name| matches!(name, sym::doc | sym::link | sym::test | sym::bench); - - let error_msg = format!("malformed `{name}` attribute input"); - let mut suggestions = vec![]; - let inner = if style == ast::AttrStyle::Inner { "!" } else { "" }; - if template.word { - suggestions.push(format!("#{inner}[{name}]")); - } - if let Some(descr) = template.list { - for descr in descr { - suggestions.push(format!("#{inner}[{name}({descr})]")); - } - } - suggestions.extend(template.one_of.iter().map(|&word| format!("#{inner}[{name}({word})]"))); - if let Some(descr) = template.name_value_str { - for descr in descr { - suggestions.push(format!("#{inner}[{name} = \"{descr}\"]")); - } - } - if should_warn(name) { - psess.buffer_lint( - ILL_FORMED_ATTRIBUTE_INPUT, - span, - ast::CRATE_NODE_ID, - BuiltinLintDiag::IllFormedAttributeInput { - suggestions: suggestions.clone(), - docs: template.docs, - }, - ); - } else { - suggestions.sort(); - let mut err = psess.dcx().struct_span_err(span, error_msg).with_span_suggestions( - span, - if suggestions.len() == 1 { - "must be of the form" - } else { - "the following are the possible correct uses" - }, - suggestions, - Applicability::HasPlaceholders, - ); - if let Some(link) = template.docs { - err.note(format!("for more information, visit <{link}>")); - } - err.emit(); - } -} - -pub fn emit_fatal_malformed_builtin_attribute( - psess: &ParseSess, - attr: &Attribute, - name: Symbol, -) -> ! { - let template = BUILTIN_ATTRIBUTE_MAP.get(&name).expect("builtin attr defined").template; - emit_malformed_attribute(psess, attr.style, attr.span, name, template); - // This is fatal, otherwise it will likely cause a cascade of other errors - // (and an error here is expected to be very rare). - FatalError.raise() -} -- cgit 1.4.1-3-g733a5 From c99320156dfeea98836fe7dad97d16f67c4d879e Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 21 Aug 2025 20:30:28 -0700 Subject: Refactor lint buffering to avoid requiring a giant enum Lint buffering currently relies on a giant enum `BuiltinLintDiag` containing all the lints that might potentially get buffered. In addition to being an unwieldy enum in a central crate, this also makes `rustc_lint_defs` a build bottleneck: it depends on various types from various crates (with a steady pressure to add more), and many crates depend on it. Having all of these variants in a separate crate also prevents detecting when a variant becomes unused, which we can do with a dedicated type defined and used in the same crate. Refactor this to use a dyn trait, to allow using `LintDiagnostic` types directly. This requires boxing, but all of this is already on the slow path (emitting an error). Because the existing `BuiltinLintDiag` requires some additional types in order to decorate some variants, which are only available later in `rustc_lint`, use an enum `DecorateDiagCompat` to handle both the `dyn LintDiagnostic` case and the `BuiltinLintDiag` case. --- Cargo.lock | 1 + compiler/rustc_ast_passes/src/ast_validation.rs | 4 +- compiler/rustc_builtin_macros/src/format.rs | 8 ++- compiler/rustc_errors/Cargo.toml | 1 + compiler/rustc_errors/src/decorate_diag.rs | 85 +++++++++++++++++++++++++ compiler/rustc_errors/src/diagnostic.rs | 12 +++- compiler/rustc_errors/src/lib.rs | 4 +- compiler/rustc_expand/src/base.rs | 4 +- compiler/rustc_interface/src/util.rs | 3 +- compiler/rustc_lint/src/context.rs | 4 +- compiler/rustc_lint/src/early.rs | 10 ++- compiler/rustc_lint/src/lib.rs | 5 +- compiler/rustc_lint_defs/src/lib.rs | 52 +-------------- compiler/rustc_middle/src/middle/stability.rs | 4 +- compiler/rustc_middle/src/ty/mod.rs | 3 +- compiler/rustc_resolve/src/lib.rs | 4 +- compiler/rustc_session/src/config/cfg.rs | 2 +- compiler/rustc_session/src/parse.rs | 14 ++-- 18 files changed, 137 insertions(+), 83 deletions(-) create mode 100644 compiler/rustc_errors/src/decorate_diag.rs (limited to 'compiler/rustc_interface/src') diff --git a/Cargo.lock b/Cargo.lock index 6f7a309894c..01f012d5edc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3798,6 +3798,7 @@ dependencies = [ "annotate-snippets 0.11.5", "derive_setters", "rustc_abi", + "rustc_ast", "rustc_data_structures", "rustc_error_codes", "rustc_error_messages", diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index ef4410566c5..4bcb126dfc2 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -26,15 +26,15 @@ use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, walk_list} use rustc_ast::*; use rustc_ast_pretty::pprust::{self, State}; use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::DiagCtxtHandle; +use rustc_errors::{DiagCtxtHandle, LintBuffer}; use rustc_feature::Features; use rustc_parse::validate_attr; use rustc_session::Session; +use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN, PATTERNS_IN_FNS_WITHOUT_BODY, }; -use rustc_session::lint::{BuiltinLintDiag, LintBuffer}; use rustc_span::{Ident, Span, kw, sym}; use rustc_target::spec::{AbiMap, AbiMapping}; use thin_vec::thin_vec; diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index ec613b7b710..6415e55e0b0 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -10,11 +10,12 @@ use rustc_ast::{ }; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ - Applicability, Diag, MultiSpan, PResult, SingleLabelManySpans, listify, pluralize, + Applicability, BufferedEarlyLint, Diag, MultiSpan, PResult, SingleLabelManySpans, listify, + pluralize, }; use rustc_expand::base::*; use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY; -use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiag, LintId}; +use rustc_lint_defs::{BuiltinLintDiag, LintId}; use rustc_parse::exp; use rustc_parse_format as parse; use rustc_span::{BytePos, ErrorGuaranteed, Ident, InnerSpan, Span, Symbol}; @@ -595,7 +596,8 @@ fn make_format_args( named_arg_sp: arg_name.span, named_arg_name: arg_name.name.to_string(), is_formatting_arg: matches!(used_as, Width | Precision), - }, + } + .into(), }); } } diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index ad6d29e21fc..f37b6fb748f 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -8,6 +8,7 @@ edition = "2024" annotate-snippets = "0.11" derive_setters = "0.1.6" rustc_abi = { path = "../rustc_abi" } +rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_error_codes = { path = "../rustc_error_codes" } rustc_error_messages = { path = "../rustc_error_messages" } diff --git a/compiler/rustc_errors/src/decorate_diag.rs b/compiler/rustc_errors/src/decorate_diag.rs new file mode 100644 index 00000000000..5aef26ccf97 --- /dev/null +++ b/compiler/rustc_errors/src/decorate_diag.rs @@ -0,0 +1,85 @@ +/// This module provides types and traits for buffering lints until later in compilation. +use rustc_ast::node_id::NodeId; +use rustc_data_structures::fx::FxIndexMap; +use rustc_error_messages::MultiSpan; +use rustc_lint_defs::{BuiltinLintDiag, Lint, LintId}; + +use crate::{DynSend, LintDiagnostic, LintDiagnosticBox}; + +/// We can't implement `LintDiagnostic` for `BuiltinLintDiag`, because decorating some of its +/// variants requires types we don't have yet. So, handle that case separately. +pub enum DecorateDiagCompat { + Dynamic(Box LintDiagnosticBox<'a, ()> + DynSend + 'static>), + Builtin(BuiltinLintDiag), +} + +impl std::fmt::Debug for DecorateDiagCompat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DecorateDiagCompat").finish() + } +} + +impl !LintDiagnostic<'_, ()> for BuiltinLintDiag {} + +impl LintDiagnostic<'a, ()> + DynSend + 'static> From for DecorateDiagCompat { + #[inline] + fn from(d: D) -> Self { + Self::Dynamic(Box::new(d)) + } +} + +impl From for DecorateDiagCompat { + #[inline] + fn from(b: BuiltinLintDiag) -> Self { + Self::Builtin(b) + } +} + +/// Lints that are buffered up early on in the `Session` before the +/// `LintLevels` is calculated. +#[derive(Debug)] +pub struct BufferedEarlyLint { + /// The span of code that we are linting on. + pub span: Option, + + /// The `NodeId` of the AST node that generated the lint. + pub node_id: NodeId, + + /// A lint Id that can be passed to + /// `rustc_lint::early::EarlyContextAndPass::check_id`. + pub lint_id: LintId, + + /// Customization of the `Diag<'_>` for the lint. + pub diagnostic: DecorateDiagCompat, +} + +#[derive(Default, Debug)] +pub struct LintBuffer { + pub map: FxIndexMap>, +} + +impl LintBuffer { + pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) { + self.map.entry(early_lint.node_id).or_default().push(early_lint); + } + + pub fn take(&mut self, id: NodeId) -> Vec { + // FIXME(#120456) - is `swap_remove` correct? + self.map.swap_remove(&id).unwrap_or_default() + } + + pub fn buffer_lint( + &mut self, + lint: &'static Lint, + node_id: NodeId, + span: impl Into, + decorate: impl Into, + ) { + self.add_early_lint(BufferedEarlyLint { + lint_id: LintId::of(lint), + node_id, + span: Some(span.into()), + diagnostic: decorate.into(), + }); + } +} diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 183dceddd2c..43ce886975c 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -138,10 +138,20 @@ where /// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic]. #[rustc_diagnostic_item = "LintDiagnostic"] pub trait LintDiagnostic<'a, G: EmissionGuarantee> { - /// Decorate and emit a lint. + /// Decorate a lint with the information from this type. fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>); } +pub trait LintDiagnosticBox<'a, G: EmissionGuarantee> { + fn decorate_lint_box<'b>(self: Box, diag: &'b mut Diag<'a, G>); +} + +impl<'a, G: EmissionGuarantee, D: LintDiagnostic<'a, G>> LintDiagnosticBox<'a, G> for D { + fn decorate_lint_box<'b>(self: Box, diag: &'b mut Diag<'a, G>) { + self.decorate_lint(diag); + } +} + #[derive(Clone, Debug, Encodable, Decodable)] pub(crate) struct DiagLocation { file: Cow<'static, str>, diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index a775b70dbee..38c5716348f 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -40,9 +40,10 @@ use std::{fmt, panic}; use Level::*; pub use codes::*; +pub use decorate_diag::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer}; pub use diagnostic::{ BugAbort, Diag, DiagArgMap, DiagInner, DiagStyledString, Diagnostic, EmissionGuarantee, - FatalAbort, LintDiagnostic, StringPart, Subdiag, Subdiagnostic, + FatalAbort, LintDiagnostic, LintDiagnosticBox, StringPart, Subdiag, Subdiagnostic, }; pub use diagnostic_impls::{ DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter, @@ -80,6 +81,7 @@ use crate::timings::TimingRecord; pub mod annotate_snippet_emitter_writer; pub mod codes; +mod decorate_diag; mod diagnostic; mod diagnostic_impls; pub mod emitter; diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index f2c15071532..8ff21509f4a 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -13,13 +13,13 @@ use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::sync; -use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult}; +use rustc_errors::{BufferedEarlyLint, DiagCtxtHandle, ErrorGuaranteed, PResult}; use rustc_feature::Features; use rustc_hir as hir; use rustc_hir::attrs::{AttributeKind, CfgEntry, Deprecation}; use rustc_hir::def::MacroKinds; use rustc_hir::{Stability, find_attr}; -use rustc_lint_defs::{BufferedEarlyLint, RegisteredTools}; +use rustc_lint_defs::RegisteredTools; use rustc_parse::MACRO_ARGUMENTS; use rustc_parse::parser::{ForceCollect, Parser}; use rustc_session::config::CollapseMacroDebuginfo; diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 0ca4fcc66ca..925da03f4c2 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -8,11 +8,12 @@ use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::sync; +use rustc_errors::LintBuffer; use rustc_metadata::{DylibError, load_symbol_from_dylib}; use rustc_middle::ty::CurrentGcx; use rustc_parse::validate_attr; use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple}; -use rustc_session::lint::{self, BuiltinLintDiag, LintBuffer}; +use rustc_session::lint::{self, BuiltinLintDiag}; use rustc_session::output::{CRATE_TYPES, categorize_crate_type}; use rustc_session::{EarlyDiagCtxt, Session, filesearch}; use rustc_span::edit_distance::find_best_match_for_name; diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index e9bd9dccdf1..0669da1a025 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -11,7 +11,7 @@ use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync; use rustc_data_structures::unord::UnordMap; -use rustc_errors::{Diag, LintDiagnostic, MultiSpan}; +use rustc_errors::{Diag, LintBuffer, LintDiagnostic, MultiSpan}; use rustc_feature::Features; use rustc_hir::def::Res; use rustc_hir::def_id::{CrateNum, DefId}; @@ -23,7 +23,7 @@ use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths}; use rustc_middle::ty::{self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode}; -use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintBuffer, LintExpectationId, LintId}; +use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintExpectationId, LintId}; use rustc_session::{DynLintStore, Session}; use rustc_span::edit_distance::find_best_match_for_names; use rustc_span::{Ident, Span, Symbol, sym}; diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 58205087def..dff1fc43670 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -7,10 +7,11 @@ 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::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer}; use rustc_feature::Features; use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::Session; -use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; +use rustc_session::lint::LintPass; use rustc_span::{Ident, Span}; use tracing::debug; @@ -36,8 +37,11 @@ impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> { fn check_id(&mut self, id: ast::NodeId) { for early_lint in self.context.buffered.take(id) { let BufferedEarlyLint { span, node_id: _, lint_id, diagnostic } = early_lint; - self.context.opt_span_lint(lint_id.lint, span, |diag| { - diagnostics::decorate_builtin_lint(self.context.sess(), self.tcx, diagnostic, diag); + self.context.opt_span_lint(lint_id.lint, span, |diag| match diagnostic { + DecorateDiagCompat::Builtin(b) => { + diagnostics::decorate_builtin_lint(self.context.sess(), self.tcx, b, diag); + } + DecorateDiagCompat::Dynamic(d) => d.decorate_lint_box(diag), }); } } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index f06757b3c23..bdbac7fc4d1 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -133,10 +133,9 @@ pub use early::{EarlyCheckNode, check_ast_node}; pub use late::{check_crate, late_lint_mod, unerased_lint_store}; pub use levels::LintLevelsBuilder; pub use passes::{EarlyLintPass, LateLintPass}; +pub use rustc_errors::BufferedEarlyLint; pub use rustc_session::lint::Level::{self, *}; -pub use rustc_session::lint::{ - BufferedEarlyLint, FutureIncompatibleInfo, Lint, LintId, LintPass, LintVec, -}; +pub use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintId, LintPass, LintVec}; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index d1f5cc21277..c1eb31cfa17 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -3,8 +3,7 @@ use std::borrow::Cow; use rustc_abi::ExternAbi; use rustc_ast::AttrId; use rustc_ast::attr::AttributeExt; -use rustc_ast::node_id::NodeId; -use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::stable_hasher::{ HashStable, StableCompare, StableHasher, ToStableHashKey, }; @@ -818,55 +817,6 @@ pub enum BuiltinLintDiag { }, } -/// Lints that are buffered up early on in the `Session` before the -/// `LintLevels` is calculated. -#[derive(Debug)] -pub struct BufferedEarlyLint { - /// The span of code that we are linting on. - pub span: Option, - - /// The `NodeId` of the AST node that generated the lint. - pub node_id: NodeId, - - /// A lint Id that can be passed to - /// `rustc_lint::early::EarlyContextAndPass::check_id`. - pub lint_id: LintId, - - /// Customization of the `Diag<'_>` for the lint. - pub diagnostic: BuiltinLintDiag, -} - -#[derive(Default, Debug)] -pub struct LintBuffer { - pub map: FxIndexMap>, -} - -impl LintBuffer { - pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) { - self.map.entry(early_lint.node_id).or_default().push(early_lint); - } - - pub fn take(&mut self, id: NodeId) -> Vec { - // FIXME(#120456) - is `swap_remove` correct? - self.map.swap_remove(&id).unwrap_or_default() - } - - pub fn buffer_lint( - &mut self, - lint: &'static Lint, - node_id: NodeId, - span: impl Into, - diagnostic: BuiltinLintDiag, - ) { - self.add_early_lint(BufferedEarlyLint { - lint_id: LintId::of(lint), - node_id, - span: Some(span.into()), - diagnostic, - }); - } -} - pub type RegisteredTools = FxIndexSet; /// Declares a static item of type `&'static Lint`. diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 4a19cf1563c..18520089e3e 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -4,7 +4,7 @@ use std::num::NonZero; use rustc_ast::NodeId; -use rustc_errors::{Applicability, Diag, EmissionGuarantee}; +use rustc_errors::{Applicability, Diag, EmissionGuarantee, LintBuffer}; use rustc_feature::GateIssue; use rustc_hir::attrs::{DeprecatedSince, Deprecation}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -12,7 +12,7 @@ use rustc_hir::{self as hir, ConstStability, DefaultBodyStability, HirId, Stabil use rustc_macros::{Decodable, Encodable, HashStable, Subdiagnostic}; use rustc_session::Session; use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE}; -use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint, LintBuffer}; +use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint}; use rustc_session::parse::feature_err_issue; use rustc_span::{Span, Symbol, sym}; use tracing::debug; diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index e70c98ab704..a7298af502e 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -32,7 +32,7 @@ use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::unord::{UnordMap, UnordSet}; -use rustc_errors::{Diag, ErrorGuaranteed}; +use rustc_errors::{Diag, ErrorGuaranteed, LintBuffer}; use rustc_hir::attrs::{AttributeKind, StrippedCfgItem}; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; @@ -46,7 +46,6 @@ use rustc_macros::{ }; use rustc_query_system::ich::StableHashingContext; use rustc_serialize::{Decodable, Encodable}; -use rustc_session::lint::LintBuffer; pub use rustc_session::lint::RegisteredTools; use rustc_span::hygiene::MacroKind; use rustc_span::{DUMMY_SP, ExpnId, ExpnKind, Ident, Span, Symbol, sym}; diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 2063c46124c..9407ce9d398 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -49,7 +49,7 @@ use rustc_data_structures::intern::Interned; use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{FreezeReadGuard, FreezeWriteGuard}; use rustc_data_structures::unord::{UnordMap, UnordSet}; -use rustc_errors::{Applicability, Diag, ErrCode, ErrorGuaranteed}; +use rustc_errors::{Applicability, Diag, ErrCode, ErrorGuaranteed, LintBuffer}; use rustc_expand::base::{DeriveResolution, SyntaxExtension, SyntaxExtensionKind}; use rustc_feature::BUILTIN_ATTRIBUTES; use rustc_hir::attrs::StrippedCfgItem; @@ -72,8 +72,8 @@ use rustc_middle::ty::{ ResolverGlobalCtxt, TyCtxt, TyCtxtFeed, Visibility, }; use rustc_query_system::ich::StableHashingContext; +use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::PRIVATE_MACRO_USE; -use rustc_session::lint::{BuiltinLintDiag, LintBuffer}; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency}; use rustc_span::{DUMMY_SP, Ident, Macros20NormalizedIdent, Span, Symbol, kw, sym}; use smallvec::{SmallVec, smallvec}; diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index 62891eb4f26..852ae414157 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -99,7 +99,7 @@ pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) { EXPLICIT_BUILTIN_CFGS_IN_FLAGS, None, ast::CRATE_NODE_ID, - BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }, + BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.into(), ) }; diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 426480f0dba..9048c51d5b4 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -11,8 +11,8 @@ use rustc_data_structures::sync::{AppendOnlyVec, Lock}; use rustc_errors::emitter::{FatalOnlyEmitter, HumanEmitter, stderr_destination}; use rustc_errors::translation::Translator; use rustc_errors::{ - ColorConfig, Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, EmissionGuarantee, MultiSpan, - StashKey, + BufferedEarlyLint, ColorConfig, DecorateDiagCompat, Diag, DiagCtxt, DiagCtxtHandle, + DiagMessage, EmissionGuarantee, MultiSpan, StashKey, }; use rustc_feature::{GateIssue, UnstableFeatures, find_feature_issue}; use rustc_span::edition::Edition; @@ -27,7 +27,7 @@ use crate::errors::{ FeatureDiagnosticSuggestion, FeatureGateError, SuggestUpgradeCompiler, }; use crate::lint::builtin::UNSTABLE_SYNTAX_PRE_EXPANSION; -use crate::lint::{BufferedEarlyLint, BuiltinLintDiag, Lint, LintId}; +use crate::lint::{Lint, LintId}; /// Collected spans during parsing for places where a certain feature was /// used and should be feature gated accordingly in `check_crate`. @@ -342,17 +342,17 @@ impl ParseSess { lint: &'static Lint, span: impl Into, node_id: NodeId, - diagnostic: BuiltinLintDiag, + diagnostic: impl Into, ) { - self.opt_span_buffer_lint(lint, Some(span.into()), node_id, diagnostic) + self.opt_span_buffer_lint(lint, Some(span.into()), node_id, diagnostic.into()) } - pub fn opt_span_buffer_lint( + pub(crate) fn opt_span_buffer_lint( &self, lint: &'static Lint, span: Option, node_id: NodeId, - diagnostic: BuiltinLintDiag, + diagnostic: DecorateDiagCompat, ) { self.buffered_lints.with_lock(|buffered_lints| { buffered_lints.push(BufferedEarlyLint { -- cgit 1.4.1-3-g733a5 From a3c878f813dd9c7c788cbe8d817699f2ef927e4e Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Thu, 14 Aug 2025 22:54:50 +0000 Subject: Separate transmute checking from typeck. --- compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 8 - compiler/rustc_hir_typeck/src/intrinsicck.rs | 207 ++++++++++----------- compiler/rustc_hir_typeck/src/lib.rs | 5 +- compiler/rustc_hir_typeck/src/writeback.rs | 13 ++ compiler/rustc_interface/src/passes.rs | 3 +- compiler/rustc_middle/src/query/mod.rs | 5 + compiler/rustc_middle/src/ty/typeck_results.rs | 6 + tests/ui/const-generics/transmute-fail.stderr | 16 +- .../transmute-size-mismatch-before-typeck.rs | 7 +- .../transmute-size-mismatch-before-typeck.stderr | 13 +- tests/ui/impl-trait/transmute/in-defining-scope.rs | 9 +- .../impl-trait/transmute/in-defining-scope.stderr | 56 +----- tests/ui/layout/base-layout-is-sized-ice-123078.rs | 4 +- .../layout/base-layout-is-sized-ice-123078.stderr | 12 +- tests/ui/layout/normalization-failure.rs | 4 +- tests/ui/layout/normalization-failure.stderr | 2 +- tests/ui/layout/transmute-to-tail-with-err.rs | 1 + tests/ui/layout/transmute-to-tail-with-err.stderr | 14 +- tests/ui/type-alias-impl-trait/issue-53092-2.rs | 2 +- .../ui/type-alias-impl-trait/issue-53092-2.stderr | 64 ++----- .../no_inferrable_concrete_type.rs | 1 + .../no_inferrable_concrete_type.stderr | 12 +- tests/ui/type/pattern_types/or_patterns_invalid.rs | 4 + .../type/pattern_types/or_patterns_invalid.stderr | 39 +++- 24 files changed, 253 insertions(+), 254 deletions(-) (limited to 'compiler/rustc_interface/src') diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index b80a2af3100..5aec50c8b53 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -83,14 +83,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { *self.deferred_cast_checks.borrow_mut() = deferred_cast_checks; } - pub(in super::super) fn check_transmutes(&self) { - let mut deferred_transmute_checks = self.deferred_transmute_checks.borrow_mut(); - debug!("FnCtxt::check_transmutes: {} deferred checks", deferred_transmute_checks.len()); - for (from, to, hir_id) in deferred_transmute_checks.drain(..) { - self.check_transmute(from, to, hir_id); - } - } - pub(in super::super) fn check_asms(&self) { let mut deferred_asm_checks = self.deferred_asm_checks.borrow_mut(); debug!("FnCtxt::check_asm: {} deferred checks", deferred_asm_checks.len()); diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index 194e420b606..fffff7e6f8c 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -8,10 +8,9 @@ use rustc_index::Idx; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutError, SizeSkeleton}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; +use rustc_span::def_id::LocalDefId; use tracing::trace; -use super::FnCtxt; - /// If the type is `Option`, it will return `T`, otherwise /// the type itself. Works on most `Option`-like types. fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { @@ -39,119 +38,115 @@ fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { ty } -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - /// FIXME: Move this check out of typeck, since it'll easily cycle when revealing opaques, - /// and we shouldn't need to check anything here if the typeck results are tainted. - pub(crate) fn check_transmute(&self, from: Ty<'tcx>, to: Ty<'tcx>, hir_id: HirId) { - let tcx = self.tcx; - let dl = &tcx.data_layout; - let span = tcx.hir_span(hir_id); - let normalize = |ty| { - let ty = self.resolve_vars_if_possible(ty); - if let Ok(ty) = - self.tcx.try_normalize_erasing_regions(self.typing_env(self.param_env), ty) - { - ty - } else { - Ty::new_error_with_message( - tcx, - span, - "tried to normalize non-wf type in check_transmute", - ) - } - }; - let from = normalize(from); - let to = normalize(to); - trace!(?from, ?to); - if from.has_non_region_infer() || to.has_non_region_infer() { - // Note: this path is currently not reached in any test, so any - // example that triggers this would be worth minimizing and - // converting into a test. - self.dcx().span_bug(span, "argument to transmute has inference variables"); - } - // Transmutes that are only changing lifetimes are always ok. - if from == to { - return; +fn check_transmute<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + from: Ty<'tcx>, + to: Ty<'tcx>, + hir_id: HirId, +) { + let dl = &tcx.data_layout; + let span = tcx.hir_span(hir_id); + let normalize = |ty| { + if let Ok(ty) = tcx.try_normalize_erasing_regions(typing_env, ty) { + ty + } else { + Ty::new_error_with_message( + tcx, + span, + format!("tried to normalize non-wf type {ty:#?} in check_transmute"), + ) } + }; + let from = normalize(from); + let to = normalize(to); + trace!(?from, ?to); + if from.has_non_region_infer() || to.has_non_region_infer() { + // Note: this path is currently not reached in any test, so any + // example that triggers this would be worth minimizing and + // converting into a test. + tcx.sess.dcx().span_bug(span, "argument to transmute has inference variables"); + } + // Transmutes that are only changing lifetimes are always ok. + if from == to { + return; + } - let skel = |ty| SizeSkeleton::compute(ty, tcx, self.typing_env(self.param_env)); - let sk_from = skel(from); - let sk_to = skel(to); - trace!(?sk_from, ?sk_to); + let skel = |ty| SizeSkeleton::compute(ty, tcx, typing_env); + let sk_from = skel(from); + let sk_to = skel(to); + trace!(?sk_from, ?sk_to); - // Check for same size using the skeletons. - if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) { - if sk_from.same_size(sk_to) { - return; - } + // Check for same size using the skeletons. + if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) { + if sk_from.same_size(sk_to) { + return; + } - // Special-case transmuting from `typeof(function)` and - // `Option` to present a clearer error. - let from = unpack_option_like(tcx, from); - if let (&ty::FnDef(..), SizeSkeleton::Known(size_to, _)) = (from.kind(), sk_to) - && size_to == Pointer(dl.instruction_address_space).size(&tcx) - { - struct_span_code_err!(self.dcx(), span, E0591, "can't transmute zero-sized type") - .with_note(format!("source type: {from}")) - .with_note(format!("target type: {to}")) - .with_help("cast with `as` to a pointer instead") - .emit(); - return; - } + // Special-case transmuting from `typeof(function)` and + // `Option` to present a clearer error. + let from = unpack_option_like(tcx, from); + if let (&ty::FnDef(..), SizeSkeleton::Known(size_to, _)) = (from.kind(), sk_to) + && size_to == Pointer(dl.instruction_address_space).size(&tcx) + { + struct_span_code_err!(tcx.sess.dcx(), span, E0591, "can't transmute zero-sized type") + .with_note(format!("source type: {from}")) + .with_note(format!("target type: {to}")) + .with_help("cast with `as` to a pointer instead") + .emit(); + return; } + } - // Try to display a sensible error with as much information as possible. - let skeleton_string = |ty: Ty<'tcx>, sk: Result<_, &_>| match sk { - Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"), - Ok(SizeSkeleton::Known(size, _)) => { - if let Some(v) = u128::from(size.bytes()).checked_mul(8) { - format!("{v} bits") - } else { - // `u128` should definitely be able to hold the size of different architectures - // larger sizes should be reported as error `are too big for the target architecture` - // otherwise we have a bug somewhere - bug!("{:?} overflow for u128", size) - } - } - Ok(SizeSkeleton::Generic(size)) => { - if let Some(size) = - self.try_structurally_resolve_const(span, size).try_to_target_usize(tcx) - { - format!("{size} bytes") - } else { - format!("generic size {size}") - } - } - Err(LayoutError::TooGeneric(bad)) => { - if *bad == ty { - "this type does not have a fixed size".to_owned() - } else { - format!("size can vary because of {bad}") - } + // Try to display a sensible error with as much information as possible. + let skeleton_string = |ty: Ty<'tcx>, sk: Result<_, &_>| match sk { + Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"), + Ok(SizeSkeleton::Known(size, _)) => { + if let Some(v) = u128::from(size.bytes()).checked_mul(8) { + format!("{v} bits") + } else { + // `u128` should definitely be able to hold the size of different architectures + // larger sizes should be reported as error `are too big for the target architecture` + // otherwise we have a bug somewhere + bug!("{:?} overflow for u128", size) } - Err(err) => err.to_string(), - }; - - let mut err = struct_span_code_err!( - self.dcx(), - span, - E0512, - "cannot transmute between types of different sizes, \ - or dependently-sized types" - ); - if from == to { - err.note(format!("`{from}` does not have a fixed size")); - err.emit(); - } else { - err.note(format!("source type: `{}` ({})", from, skeleton_string(from, sk_from))) - .note(format!("target type: `{}` ({})", to, skeleton_string(to, sk_to))); - if let Err(LayoutError::ReferencesError(_)) = sk_from { - err.delay_as_bug(); - } else if let Err(LayoutError::ReferencesError(_)) = sk_to { - err.delay_as_bug(); + } + Ok(SizeSkeleton::Generic(size)) => { + format!("generic size {size}") + } + Err(LayoutError::TooGeneric(bad)) => { + if *bad == ty { + "this type does not have a fixed size".to_owned() } else { - err.emit(); + format!("size can vary because of {bad}") } } + Err(err) => err.to_string(), + }; + + let mut err = struct_span_code_err!( + tcx.sess.dcx(), + span, + E0512, + "cannot transmute between types of different sizes, or dependently-sized types" + ); + if from == to { + err.note(format!("`{from}` does not have a fixed size")); + err.emit(); + } else { + err.note(format!("source type: `{}` ({})", from, skeleton_string(from, sk_from))); + err.note(format!("target type: `{}` ({})", to, skeleton_string(to, sk_to))); + err.emit(); + } +} + +pub(crate) fn check_transmutes(tcx: TyCtxt<'_>, owner: LocalDefId) { + assert!(!tcx.is_typeck_child(owner.to_def_id())); + let typeck_results = tcx.typeck(owner); + let None = typeck_results.tainted_by_errors else { return }; + + let typing_env = ty::TypingEnv::post_analysis(tcx, owner); + for &(from, to, hir_id) in &typeck_results.transmutes_to_check { + check_transmute(tcx, typing_env, from, to, hir_id); } } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index aae870f7ee3..ab4181f5293 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -251,10 +251,6 @@ fn typeck_with_inspect<'tcx>( fcx.report_ambiguity_errors(); } - if let None = fcx.infcx.tainted_by_errors() { - fcx.check_transmutes(); - } - fcx.check_asms(); let typeck_results = fcx.resolve_type_vars_in_body(body); @@ -555,6 +551,7 @@ pub fn provide(providers: &mut Providers) { method_autoderef_steps: method::probe::method_autoderef_steps, typeck, used_trait_imports, + check_transmutes: intrinsicck::check_transmutes, ..*providers }; } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 093de950d63..42736a07b2a 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -74,6 +74,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { wbcx.visit_user_provided_tys(); wbcx.visit_user_provided_sigs(); wbcx.visit_coroutine_interior(); + wbcx.visit_transmutes(); wbcx.visit_offset_of_container_types(); wbcx.typeck_results.rvalue_scopes = @@ -532,6 +533,18 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } + fn visit_transmutes(&mut self) { + let tcx = self.tcx(); + let fcx_typeck_results = self.fcx.typeck_results.borrow(); + assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); + for &(from, to, hir_id) in self.fcx.deferred_transmute_checks.borrow().iter() { + let span = tcx.hir_span(hir_id); + let from = self.resolve(from, &span); + let to = self.resolve(to, &span); + self.typeck_results.transmutes_to_check.push((from, to, hir_id)); + } + } + #[instrument(skip(self), level = "debug")] fn visit_opaque_types(&mut self) { let tcx = self.tcx(); diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 424cba2dae8..90f7ae76387 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1080,7 +1080,8 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { if !tcx.is_typeck_child(def_id.to_def_id()) { // Child unsafety and borrowck happens together with the parent tcx.ensure_ok().check_unsafety(def_id); - tcx.ensure_ok().mir_borrowck(def_id) + tcx.ensure_ok().mir_borrowck(def_id); + tcx.ensure_ok().check_transmutes(def_id); } tcx.ensure_ok().has_ffi_unwind_calls(def_id); diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 3bb8353f49e..d4f88c458a8 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1115,6 +1115,11 @@ rustc_queries! { desc { |tcx| "collecting all inherent impls for `{:?}`", key } } + /// Unsafety-check this `LocalDefId`. + query check_transmutes(key: LocalDefId) { + desc { |tcx| "check transmute calls inside `{}`", tcx.def_path_str(key) } + } + /// Unsafety-check this `LocalDefId`. query check_unsafety(key: LocalDefId) { desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) } diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 6b187c5325a..f42dbbd2ac3 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -210,6 +210,11 @@ pub struct TypeckResults<'tcx> { /// on closure size. pub closure_size_eval: LocalDefIdMap>, + /// Stores the types involved in calls to `transmute` intrinsic. These are meant to be checked + /// outside of typeck and borrowck to avoid cycles with opaque types and coroutine layout + /// computation. + pub transmutes_to_check: Vec<(Ty<'tcx>, Ty<'tcx>, HirId)>, + /// Container types and field indices of `offset_of!` expressions offset_of_data: ItemLocalMap<(Ty<'tcx>, Vec<(VariantIdx, FieldIdx)>)>, } @@ -241,6 +246,7 @@ impl<'tcx> TypeckResults<'tcx> { rvalue_scopes: Default::default(), coroutine_stalled_predicates: Default::default(), closure_size_eval: Default::default(), + transmutes_to_check: Default::default(), offset_of_data: Default::default(), } } diff --git a/tests/ui/const-generics/transmute-fail.stderr b/tests/ui/const-generics/transmute-fail.stderr index 0e26daa3a0f..953119a8c34 100644 --- a/tests/ui/const-generics/transmute-fail.stderr +++ b/tests/ui/const-generics/transmute-fail.stderr @@ -6,6 +6,14 @@ LL | fn bar(v: [[u32; H]; W]) -> [[u32; W]; H] { | = note: the length of array `[[u32; H]; W]` must be type `usize` +error: the constant `W` is not of type `usize` + --> $DIR/transmute-fail.rs:19:9 + | +LL | std::mem::transmute(v) + | ^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` + | + = note: the length of array `[[u32; H]; W]` must be type `usize` + error[E0512]: cannot transmute between types of different sizes, or dependently-sized types --> $DIR/transmute-fail.rs:11:9 | @@ -15,14 +23,6 @@ LL | std::mem::transmute(v) = note: source type: `[[u32; H + 1]; W]` (size can vary because of [u32; H + 1]) = note: target type: `[[u32; W + 1]; H]` (size can vary because of [u32; W + 1]) -error: the constant `W` is not of type `usize` - --> $DIR/transmute-fail.rs:19:9 - | -LL | std::mem::transmute(v) - | ^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` - | - = note: the length of array `[[u32; H]; W]` must be type `usize` - error[E0512]: cannot transmute between types of different sizes, or dependently-sized types --> $DIR/transmute-fail.rs:26:9 | diff --git a/tests/ui/consts/transmute-size-mismatch-before-typeck.rs b/tests/ui/consts/transmute-size-mismatch-before-typeck.rs index ffb143da2d4..17b343d2b9b 100644 --- a/tests/ui/consts/transmute-size-mismatch-before-typeck.rs +++ b/tests/ui/consts/transmute-size-mismatch-before-typeck.rs @@ -1,5 +1,9 @@ +//@ normalize-stderr-64bit: "8 byte" -> "word size" +//@ normalize-stderr-32bit: "4 byte" -> "word size" //@ normalize-stderr-64bit: "64 bits" -> "word size" //@ normalize-stderr-32bit: "32 bits" -> "word size" +//@ normalize-stderr-64bit: "16 byte" -> "2 * word size" +//@ normalize-stderr-32bit: "8 byte" -> "2 * word size" //@ normalize-stderr-64bit: "128 bits" -> "2 * word size" //@ normalize-stderr-32bit: "64 bits" -> "2 * word size" @@ -10,4 +14,5 @@ fn main() { } const ZST: &[u8] = unsafe { std::mem::transmute(1usize) }; -//~^ ERROR cannot transmute between types of different sizes +//~^ ERROR transmuting from +//~| ERROR cannot transmute between types of different sizes diff --git a/tests/ui/consts/transmute-size-mismatch-before-typeck.stderr b/tests/ui/consts/transmute-size-mismatch-before-typeck.stderr index 6bc7e7203aa..388cebc6884 100644 --- a/tests/ui/consts/transmute-size-mismatch-before-typeck.stderr +++ b/tests/ui/consts/transmute-size-mismatch-before-typeck.stderr @@ -1,5 +1,11 @@ +error[E0080]: transmuting from 8-byte type to 16-byte type: `usize` -> `&[u8]` + --> $DIR/transmute-size-mismatch-before-typeck.rs:16:29 + | +LL | const ZST: &[u8] = unsafe { std::mem::transmute(1usize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `ZST` failed here + error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-size-mismatch-before-typeck.rs:12:29 + --> $DIR/transmute-size-mismatch-before-typeck.rs:16:29 | LL | const ZST: &[u8] = unsafe { std::mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^ @@ -7,6 +13,7 @@ LL | const ZST: &[u8] = unsafe { std::mem::transmute(1usize) }; = note: source type: `usize` (word size) = note: target type: `&[u8]` (2 * word size) -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0512`. +Some errors have detailed explanations: E0080, E0512. +For more information about an error, try `rustc --explain E0080`. diff --git a/tests/ui/impl-trait/transmute/in-defining-scope.rs b/tests/ui/impl-trait/transmute/in-defining-scope.rs index 4c8e1852a91..d9eafcc553c 100644 --- a/tests/ui/impl-trait/transmute/in-defining-scope.rs +++ b/tests/ui/impl-trait/transmute/in-defining-scope.rs @@ -1,11 +1,12 @@ -// This causes a query cycle due to using `TypingEnv::PostAnalysis`, +// Used to cause a query cycle due to using `TypingEnv::PostAnalysis`, // in #119821 const eval was changed to always use this mode. // -// See that PR for more details. +//@ check-pass + use std::mem::transmute; + fn foo() -> impl Sized { - //~^ ERROR cycle detected when computing type of - //~| WARN function cannot return without recursing + //~^ WARN function cannot return without recursing unsafe { transmute::<_, u8>(foo()); } diff --git a/tests/ui/impl-trait/transmute/in-defining-scope.stderr b/tests/ui/impl-trait/transmute/in-defining-scope.stderr index 31535695178..015a39d6670 100644 --- a/tests/ui/impl-trait/transmute/in-defining-scope.stderr +++ b/tests/ui/impl-trait/transmute/in-defining-scope.stderr @@ -1,56 +1,5 @@ -error[E0391]: cycle detected when computing type of `foo::{opaque#0}` - --> $DIR/in-defining-scope.rs:6:13 - | -LL | fn foo() -> impl Sized { - | ^^^^^^^^^^ - | -note: ...which requires computing type of opaque `foo::{opaque#0}`... - --> $DIR/in-defining-scope.rs:6:13 - | -LL | fn foo() -> impl Sized { - | ^^^^^^^^^^ -note: ...which requires borrow-checking `foo`... - --> $DIR/in-defining-scope.rs:6:1 - | -LL | fn foo() -> impl Sized { - | ^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires promoting constants in MIR for `foo`... - --> $DIR/in-defining-scope.rs:6:1 - | -LL | fn foo() -> impl Sized { - | ^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires checking if `foo` contains FFI-unwind calls... - --> $DIR/in-defining-scope.rs:6:1 - | -LL | fn foo() -> impl Sized { - | ^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires building MIR for `foo`... - --> $DIR/in-defining-scope.rs:6:1 - | -LL | fn foo() -> impl Sized { - | ^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires match-checking `foo`... - --> $DIR/in-defining-scope.rs:6:1 - | -LL | fn foo() -> impl Sized { - | ^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires type-checking `foo`... - --> $DIR/in-defining-scope.rs:6:1 - | -LL | fn foo() -> impl Sized { - | ^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires computing layout of `foo::{opaque#0}`... - = note: ...which requires normalizing `foo::{opaque#0}`... - = note: ...which again requires computing type of `foo::{opaque#0}`, completing the cycle -note: cycle used when checking that `foo::{opaque#0}` is well-formed - --> $DIR/in-defining-scope.rs:6:13 - | -LL | fn foo() -> impl Sized { - | ^^^^^^^^^^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - warning: function cannot return without recursing - --> $DIR/in-defining-scope.rs:6:1 + --> $DIR/in-defining-scope.rs:8:1 | LL | fn foo() -> impl Sized { | ^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -61,6 +10,5 @@ LL | transmute::<_, u8>(foo()); = help: a `loop` may express intention better if this is on purpose = note: `#[warn(unconditional_recursion)]` on by default -error: aborting due to 1 previous error; 1 warning emitted +warning: 1 warning emitted -For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/layout/base-layout-is-sized-ice-123078.rs b/tests/ui/layout/base-layout-is-sized-ice-123078.rs index b1c33e15075..bbe32b2022a 100644 --- a/tests/ui/layout/base-layout-is-sized-ice-123078.rs +++ b/tests/ui/layout/base-layout-is-sized-ice-123078.rs @@ -8,7 +8,9 @@ struct S { } const C: S = unsafe { std::mem::transmute(()) }; -//~^ ERROR cannot transmute between types of different sizes, or dependently-sized types +//~^ ERROR the type `S` has an unknown layout +//~| ERROR cannot transmute between types of different sizes, or dependently-sized types + const _: [(); { C; 0 diff --git a/tests/ui/layout/base-layout-is-sized-ice-123078.stderr b/tests/ui/layout/base-layout-is-sized-ice-123078.stderr index d8743d4e6d6..d6cebd3e7ae 100644 --- a/tests/ui/layout/base-layout-is-sized-ice-123078.stderr +++ b/tests/ui/layout/base-layout-is-sized-ice-123078.stderr @@ -16,6 +16,12 @@ help: the `Box` type always has a statically known size and allocates its conten LL | a: Box<[u8]>, | ++++ + +error[E0080]: the type `S` has an unknown layout + --> $DIR/base-layout-is-sized-ice-123078.rs:10:1 + | +LL | const C: S = unsafe { std::mem::transmute(()) }; + | ^^^^^^^^^^ evaluation of `C` failed here + error[E0512]: cannot transmute between types of different sizes, or dependently-sized types --> $DIR/base-layout-is-sized-ice-123078.rs:10:23 | @@ -25,7 +31,7 @@ LL | const C: S = unsafe { std::mem::transmute(()) }; = note: source type: `()` (0 bits) = note: target type: `S` (the type `S` has an unknown layout) -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0277, E0512. -For more information about an error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0080, E0277, E0512. +For more information about an error, try `rustc --explain E0080`. diff --git a/tests/ui/layout/normalization-failure.rs b/tests/ui/layout/normalization-failure.rs index c0f8710c03c..a0195224d8c 100644 --- a/tests/ui/layout/normalization-failure.rs +++ b/tests/ui/layout/normalization-failure.rs @@ -49,8 +49,8 @@ fn check() { unsafe { std::mem::transmute::<_, ()>(opaque::().get()); //~^ ERROR: cannot transmute - //~| NOTE: (unable to determine layout for `::Assoc2` because `::Assoc2` cannot be normalized) - //~| NOTE: (0 bits) + //~| NOTE: source type: `{type error}` (the type has an unknown layout) + //~| NOTE: target type: `()` (0 bits) } } diff --git a/tests/ui/layout/normalization-failure.stderr b/tests/ui/layout/normalization-failure.stderr index 5fe38d4403a..1c78fc6ac41 100644 --- a/tests/ui/layout/normalization-failure.stderr +++ b/tests/ui/layout/normalization-failure.stderr @@ -4,7 +4,7 @@ error[E0512]: cannot transmute between types of different sizes, or dependently- LL | std::mem::transmute::<_, ()>(opaque::().get()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: source type: `::Assoc2` (unable to determine layout for `::Assoc2` because `::Assoc2` cannot be normalized) + = note: source type: `{type error}` (the type has an unknown layout) = note: target type: `()` (0 bits) error: aborting due to 1 previous error diff --git a/tests/ui/layout/transmute-to-tail-with-err.rs b/tests/ui/layout/transmute-to-tail-with-err.rs index 6753ce15ed1..614c1ac756e 100644 --- a/tests/ui/layout/transmute-to-tail-with-err.rs +++ b/tests/ui/layout/transmute-to-tail-with-err.rs @@ -5,4 +5,5 @@ struct Bar(Box>); fn main() { let x: Bar = unsafe { std::mem::transmute(()) }; + //~^ ERROR cannot transmute between types of different size } diff --git a/tests/ui/layout/transmute-to-tail-with-err.stderr b/tests/ui/layout/transmute-to-tail-with-err.stderr index 433c6b38d0b..cff40812717 100644 --- a/tests/ui/layout/transmute-to-tail-with-err.stderr +++ b/tests/ui/layout/transmute-to-tail-with-err.stderr @@ -9,6 +9,16 @@ help: you might be missing a type parameter LL | struct Bar(Box>); | +++ -error: aborting due to 1 previous error +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-to-tail-with-err.rs:7:27 + | +LL | let x: Bar = unsafe { std::mem::transmute(()) }; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `()` (0 bits) + = note: target type: `Bar` (the type has an unknown layout) + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0412`. +Some errors have detailed explanations: E0412, E0512. +For more information about an error, try `rustc --explain E0412`. diff --git a/tests/ui/type-alias-impl-trait/issue-53092-2.rs b/tests/ui/type-alias-impl-trait/issue-53092-2.rs index 1a530d27971..4ddb06e40ff 100644 --- a/tests/ui/type-alias-impl-trait/issue-53092-2.rs +++ b/tests/ui/type-alias-impl-trait/issue-53092-2.rs @@ -2,11 +2,11 @@ #![allow(dead_code)] type Bug = impl Fn(T) -> U + Copy; -//~^ ERROR cycle detected when computing type of `Bug::{opaque#0}` #[define_opaque(Bug)] const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; //~^ ERROR item does not constrain `Bug::{opaque#0}` +//~| ERROR: cannot transmute between types of different sizes, or dependently-sized types #[define_opaque(Bug)] fn make_bug>() -> Bug { diff --git a/tests/ui/type-alias-impl-trait/issue-53092-2.stderr b/tests/ui/type-alias-impl-trait/issue-53092-2.stderr index c8db9fdfc57..689f7a733cb 100644 --- a/tests/ui/type-alias-impl-trait/issue-53092-2.stderr +++ b/tests/ui/type-alias-impl-trait/issue-53092-2.stderr @@ -1,56 +1,5 @@ -error[E0391]: cycle detected when computing type of `Bug::{opaque#0}` - --> $DIR/issue-53092-2.rs:4:18 - | -LL | type Bug = impl Fn(T) -> U + Copy; - | ^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...which requires computing type of opaque `Bug::{opaque#0}`... - --> $DIR/issue-53092-2.rs:4:18 - | -LL | type Bug = impl Fn(T) -> U + Copy; - | ^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires borrow-checking `CONST_BUG`... - --> $DIR/issue-53092-2.rs:8:1 - | -LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires promoting constants in MIR for `CONST_BUG`... - --> $DIR/issue-53092-2.rs:8:1 - | -LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires const checking `CONST_BUG`... - --> $DIR/issue-53092-2.rs:8:1 - | -LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires building MIR for `CONST_BUG`... - --> $DIR/issue-53092-2.rs:8:1 - | -LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires match-checking `CONST_BUG`... - --> $DIR/issue-53092-2.rs:8:1 - | -LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires type-checking `CONST_BUG`... - --> $DIR/issue-53092-2.rs:8:1 - | -LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires computing layout of `Bug`... - = note: ...which requires normalizing `Bug`... - = note: ...which again requires computing type of `Bug::{opaque#0}`, completing the cycle -note: cycle used when checking that `Bug::{opaque#0}` is well-formed - --> $DIR/issue-53092-2.rs:4:18 - | -LL | type Bug = impl Fn(T) -> U + Copy; - | ^^^^^^^^^^^^^^^^^^^^^^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - error: item does not constrain `Bug::{opaque#0}` - --> $DIR/issue-53092-2.rs:8:7 + --> $DIR/issue-53092-2.rs:7:7 | LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; | ^^^^^^^^^ @@ -62,6 +11,15 @@ note: this opaque type is supposed to be constrained LL | type Bug = impl Fn(T) -> U + Copy; | ^^^^^^^^^^^^^^^^^^^^^^ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/issue-53092-2.rs:7:41 + | +LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `{closure@$DIR/issue-53092-2.rs:7:61: 7:68}` (0 bits) + = note: target type: `{type error}` (the type has an unknown layout) + error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0391`. +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.rs b/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.rs index 8e5e4719415..bd1d6651859 100644 --- a/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.rs +++ b/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.rs @@ -15,5 +15,6 @@ pub fn bar(x: Foo) -> Foo { fn main() { unsafe { let _: Foo = std::mem::transmute(0u8); + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types } } diff --git a/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.stderr b/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.stderr index a57793d5a77..c9646a4e9a4 100644 --- a/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.stderr +++ b/tests/ui/type-alias-impl-trait/no_inferrable_concrete_type.stderr @@ -11,5 +11,15 @@ note: this opaque type is supposed to be constrained LL | pub type Foo = impl Copy; | ^^^^^^^^^ -error: aborting due to 1 previous error +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/no_inferrable_concrete_type.rs:17:22 + | +LL | let _: Foo = std::mem::transmute(0u8); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `{type error}` (the type has an unknown layout) + +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/type/pattern_types/or_patterns_invalid.rs b/tests/ui/type/pattern_types/or_patterns_invalid.rs index d341927601d..a99667c5412 100644 --- a/tests/ui/type/pattern_types/or_patterns_invalid.rs +++ b/tests/ui/type/pattern_types/or_patterns_invalid.rs @@ -13,14 +13,18 @@ use std::pat::pattern_type; fn main() { //~? ERROR: only non-overlapping pattern type ranges are allowed at present let not_adjacent: pattern_type!(i8 is -127..0 | 1..) = unsafe { std::mem::transmute(0) }; + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types //~? ERROR: one pattern needs to end at `i8::MAX`, but was 29 instead let not_wrapping: pattern_type!(i8 is 10..20 | 20..30) = unsafe { std::mem::transmute(0) }; + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types //~? ERROR: only signed integer base types are allowed for or-pattern pattern types let not_signed: pattern_type!(u8 is 10.. | 0..5) = unsafe { std::mem::transmute(0) }; + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types //~? ERROR: allowed are two range patterns that are directly connected let not_simple_enough_for_mvp: pattern_type!(i8 is ..0 | 1..10 | 10..) = unsafe { std::mem::transmute(0) }; + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types } diff --git a/tests/ui/type/pattern_types/or_patterns_invalid.stderr b/tests/ui/type/pattern_types/or_patterns_invalid.stderr index 6964788a6c2..e229c11386d 100644 --- a/tests/ui/type/pattern_types/or_patterns_invalid.stderr +++ b/tests/ui/type/pattern_types/or_patterns_invalid.stderr @@ -1,10 +1,47 @@ error: only non-overlapping pattern type ranges are allowed at present +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/or_patterns_invalid.rs:15:69 + | +LL | let not_adjacent: pattern_type!(i8 is -127..0 | 1..) = unsafe { std::mem::transmute(0) }; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `i32` (32 bits) + = note: target type: `(i8) is (-127..=-1 | 1..)` (the type has an unknown layout) + error: one pattern needs to end at `i8::MAX`, but was 29 instead +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/or_patterns_invalid.rs:19:71 + | +LL | let not_wrapping: pattern_type!(i8 is 10..20 | 20..30) = unsafe { std::mem::transmute(0) }; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `i32` (32 bits) + = note: target type: `(i8) is (10..=19 | 20..=29)` (the type has an unknown layout) + error: only signed integer base types are allowed for or-pattern pattern types at present +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/or_patterns_invalid.rs:23:65 + | +LL | let not_signed: pattern_type!(u8 is 10.. | 0..5) = unsafe { std::mem::transmute(0) }; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `i32` (32 bits) + = note: target type: `(u8) is (10.. | 0..=4)` (the type has an unknown layout) + error: the only or-pattern types allowed are two range patterns that are directly connected at their overflow site -error: aborting due to 4 previous errors +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/or_patterns_invalid.rs:28:18 + | +LL | unsafe { std::mem::transmute(0) }; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `i32` (32 bits) + = note: target type: `(i8) is (i8::MIN..=-1 | 1..=9 | 10..)` (the type has an unknown layout) + +error: aborting due to 8 previous errors +For more information about this error, try `rustc --explain E0512`. -- cgit 1.4.1-3-g733a5 From 59ceb02d65f13a20d29422f4d923ecde429d4c0c Mon Sep 17 00:00:00 2001 From: Jana Dönszelmann Date: Mon, 11 Aug 2025 11:46:30 +0200 Subject: Port crate name to the new attribute system --- .../src/attributes/crate_level.rs | 33 ++++++ compiler/rustc_attr_parsing/src/attributes/mod.rs | 1 + compiler/rustc_attr_parsing/src/attributes/util.rs | 6 +- compiler/rustc_attr_parsing/src/context.rs | 8 +- compiler/rustc_attr_parsing/src/lib.rs | 4 +- compiler/rustc_hir/src/attrs/data_structures.rs | 3 + compiler/rustc_hir/src/attrs/encode_cross_crate.rs | 6 + compiler/rustc_interface/src/passes.rs | 28 ++++- compiler/rustc_interface/src/util.rs | 12 +- compiler/rustc_lint/src/nonstandard_style.rs | 52 ++++----- compiler/rustc_passes/src/check_attr.rs | 46 ++++++-- tests/ui/attributes/crate-name-empty.stderr | 4 +- tests/ui/attributes/crate-name-macro-call.rs | 2 +- tests/ui/attributes/crate-name-macro-call.stderr | 8 +- tests/ui/attributes/crate-name-mismatch.stderr | 4 +- tests/ui/attributes/malformed-attrs.stderr | 14 +-- ...rint-crate-name-request-malformed-crate-name.rs | 2 +- ...-crate-name-request-malformed-crate-name.stderr | 8 +- ...ile-names-request-malformed-crate-name-1.stderr | 5 +- ...nt-file-names-request-malformed-crate-name-2.rs | 2 +- ...ile-names-request-malformed-crate-name-2.stderr | 8 +- ...rint-file-names-request-malformed-crate-name.rs | 2 +- ...-file-names-request-malformed-crate-name.stderr | 8 +- tests/ui/lint/unused/unused-attr-duplicate.rs | 8 +- tests/ui/lint/unused/unused-attr-duplicate.stderr | 130 ++++++++++++--------- 25 files changed, 248 insertions(+), 156 deletions(-) create mode 100644 compiler/rustc_attr_parsing/src/attributes/crate_level.rs (limited to 'compiler/rustc_interface/src') diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs new file mode 100644 index 00000000000..9175d7479e1 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -0,0 +1,33 @@ +use super::prelude::*; + +pub(crate) struct CrateNameParser; + +impl SingleAttributeParser for CrateNameParser { + const PATH: &[Symbol] = &[sym::crate_name]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); + + // FIXME: crate name is allowed on all targets and ignored, + // even though it should only be valid on crates of course + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let ArgParser::NameValue(n) = args else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + + let Some(name) = n.value_as_str() else { + cx.expected_string_literal(n.value_span, Some(n.value_as_lit())); + return None; + }; + + Some(AttributeKind::CrateName { + name, + name_span: n.value_span, + attr_span: cx.attr_span, + style: cx.attr_style, + }) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index b98678041d7..9dad9c893f0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -35,6 +35,7 @@ pub(crate) mod cfg; pub(crate) mod cfg_old; pub(crate) mod codegen_attrs; pub(crate) mod confusables; +pub(crate) mod crate_level; pub(crate) mod deprecation; pub(crate) mod dummy; pub(crate) mod inline; diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs index ef9701cb7cb..77e8c32e59d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/util.rs +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -1,5 +1,5 @@ use rustc_ast::LitKind; -use rustc_ast::attr::{AttributeExt, first_attr_value_str_by_name}; +use rustc_ast::attr::AttributeExt; use rustc_feature::is_builtin_attr_name; use rustc_hir::RustcVersion; use rustc_span::{Symbol, sym}; @@ -27,10 +27,6 @@ pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool { attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name)) } -pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option { - first_attr_value_str_by_name(attrs, sym::crate_name) -} - pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>( attrs: impl Iterator, symbol: Symbol, diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index f30edbfaaa1..128ecd67d2a 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -24,6 +24,7 @@ use crate::attributes::codegen_attrs::{ UsedParser, }; use crate::attributes::confusables::ConfusablesParser; +use crate::attributes::crate_level::CrateNameParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::dummy::DummyParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; @@ -166,6 +167,7 @@ attribute_parsers!( // tidy-alphabetical-start Single, + Single, Single, Single, Single, @@ -310,7 +312,9 @@ pub struct AcceptContext<'f, 'sess, S: Stage> { /// The span of the attribute currently being parsed pub(crate) attr_span: Span, + /// Whether it is an inner or outer attribute pub(crate) attr_style: AttrStyle, + /// The expected structure of the attribute. /// /// Used in reporting errors to give a hint to users what the attribute *should* look like. @@ -683,7 +687,9 @@ impl ShouldEmit { ShouldEmit::ErrorsAndLints => { diag.emit(); } - ShouldEmit::Nothing => {} + ShouldEmit::Nothing => { + diag.cancel(); + } } } } diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 969c24a4f89..4dd908cdc40 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -106,9 +106,7 @@ pub mod validate_attr; pub use attributes::cfg::{CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr}; pub use attributes::cfg_old::*; -pub use attributes::util::{ - find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version, -}; +pub use attributes::util::{is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version}; pub use context::{Early, Late, OmitDoc, ShouldEmit}; pub use interface::AttributeParser; pub use lints::emit_attribute_lint; diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 1e73bb6f5fd..09da5772d23 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -367,6 +367,9 @@ pub enum AttributeKind { /// Represents `#[coverage(..)]`. Coverage(Span, CoverageAttrKind), + /// Represents `#[crate_name = ...]` + CrateName { name: Symbol, name_span: Span, attr_span: Span, style: AttrStyle }, + /// Represents `#[custom_mir]`. CustomMir(Option<(MirDialect, Span)>, Option<(MirPhase, Span)>, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 77c6faa7acd..e5329c104bb 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -7,6 +7,11 @@ pub enum EncodeCrossCrate { } impl AttributeKind { + /// Whether this attribute should be encoded in metadata files. + /// + /// If this is "Yes", then another crate can do `tcx.get_all_attrs(did)` for a did in this crate, and get the attribute. + /// When this is No, the attribute is filtered out while encoding and other crate won't be able to observe it. + /// This can be unexpectedly good for performance, so unless necessary for cross-crate compilation, prefer No. pub fn encode_cross_crate(&self) -> EncodeCrossCrate { use AttributeKind::*; use EncodeCrossCrate::*; @@ -31,6 +36,7 @@ impl AttributeKind { ConstTrait(..) => No, Coroutine(..) => No, Coverage(..) => No, + CrateName { .. } => No, CustomMir(_, _, _) => Yes, DenyExplicitImpl(..) => No, Deprecation { .. } => Yes, diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 90f7ae76387..bc5ef04079e 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -6,7 +6,7 @@ use std::sync::{Arc, LazyLock, OnceLock}; use std::{env, fs, iter}; use rustc_ast as ast; -use rustc_attr_parsing::validate_attr; +use rustc_attr_parsing::{AttributeParser, ShouldEmit, validate_attr}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::steal::Steal; @@ -16,6 +16,7 @@ use rustc_errors::timings::TimingSection; use rustc_expand::base::{ExtCtxt, LintStoreExpand}; use rustc_feature::Features; use rustc_fs_util::try_canonicalize; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{LOCAL_CRATE, StableCrateId, StableCrateIdMap}; use rustc_hir::definitions::Definitions; use rustc_incremental::setup_dep_graph; @@ -1244,8 +1245,7 @@ pub fn get_crate_name(sess: &Session, krate_attrs: &[ast::Attribute]) -> Symbol // in all code paths that require the crate name very early on, namely before // macro expansion. - let attr_crate_name = - validate_and_find_value_str_builtin_attr(sym::crate_name, sess, krate_attrs); + let attr_crate_name = parse_crate_name(sess, krate_attrs, ShouldEmit::EarlyFatal); let validate = |name, span| { rustc_session::output::validate_crate_name(sess, name, span); @@ -1283,6 +1283,28 @@ pub fn get_crate_name(sess: &Session, krate_attrs: &[ast::Attribute]) -> Symbol sym::rust_out } +pub(crate) fn parse_crate_name( + sess: &Session, + attrs: &[ast::Attribute], + emit_errors: ShouldEmit, +) -> Option<(Symbol, Span)> { + let rustc_hir::Attribute::Parsed(AttributeKind::CrateName { name, name_span, .. }) = + AttributeParser::parse_limited_should_emit( + sess, + &attrs, + sym::crate_name, + DUMMY_SP, + rustc_ast::node_id::CRATE_NODE_ID, + None, + emit_errors, + )? + else { + unreachable!("crate_name is the only attr we could've parsed here"); + }; + + Some((name, name_span)) +} + fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit { // We don't permit macro calls inside of the attribute (e.g., #![recursion_limit = `expand!()`]) // because that would require expanding this while in the middle of expansion, which needs to diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 9a432a60819..04006f3e446 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -5,7 +5,7 @@ use std::sync::{Arc, OnceLock}; use std::{env, thread}; use rustc_ast as ast; -use rustc_attr_parsing::validate_attr; +use rustc_attr_parsing::{ShouldEmit, validate_attr}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::sync; @@ -24,6 +24,7 @@ use rustc_target::spec::Target; use tracing::info; use crate::errors; +use crate::passes::parse_crate_name; /// Function pointer type that constructs a new CodegenBackend. type MakeBackendFn = fn() -> Box; @@ -520,11 +521,10 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu sess.dcx().emit_fatal(errors::MultipleOutputTypesToStdout); } - let crate_name = sess - .opts - .crate_name - .clone() - .or_else(|| rustc_attr_parsing::find_crate_name(attrs).map(|n| n.to_string())); + let crate_name = + sess.opts.crate_name.clone().or_else(|| { + parse_crate_name(sess, attrs, ShouldEmit::Nothing).map(|i| i.0.to_string()) + }); match sess.io.output_file { None => { diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 8fafaa33d0c..65075cfecfa 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -5,7 +5,7 @@ use rustc_hir::attrs::{AttributeKind, ReprAttr}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{FnKind, Visitor}; -use rustc_hir::{AttrArgs, AttrItem, Attribute, GenericParamKind, PatExprKind, PatKind, find_attr}; +use rustc_hir::{Attribute, GenericParamKind, PatExprKind, PatKind, find_attr}; use rustc_middle::hir::nested_filter::All; use rustc_middle::ty; use rustc_session::config::CrateType; @@ -343,35 +343,27 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { let crate_ident = if let Some(name) = &cx.tcx.sess.opts.crate_name { Some(Ident::from_str(name)) } else { - ast::attr::find_by_name(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), sym::crate_name).and_then( - |attr| { - if let Attribute::Unparsed(n) = attr - && let AttrItem { args: AttrArgs::Eq { eq_span: _, expr: lit }, .. } = - n.as_ref() - && let ast::LitKind::Str(name, ..) = lit.kind - { - // Discard the double quotes surrounding the literal. - let sp = cx - .sess() - .source_map() - .span_to_snippet(lit.span) - .ok() - .and_then(|snippet| { - let left = snippet.find('"')?; - let right = snippet.rfind('"').map(|pos| snippet.len() - pos)?; - - Some( - lit.span - .with_lo(lit.span.lo() + BytePos(left as u32 + 1)) - .with_hi(lit.span.hi() - BytePos(right as u32)), - ) - }) - .unwrap_or(lit.span); - - Some(Ident::new(name, sp)) - } else { - None - } + find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::CrateName{name, name_span,..} => (name, name_span)).map( + |(&name, &span)| { + // Discard the double quotes surrounding the literal. + let sp = cx + .sess() + .source_map() + .span_to_snippet(span) + .ok() + .and_then(|snippet| { + let left = snippet.find('"')?; + let right = snippet.rfind('"').map(|pos| snippet.len() - pos)?; + + Some( + span + .with_lo(span.lo() + BytePos(left as u32 + 1)) + .with_hi(span.hi() - BytePos(right as u32)), + ) + }) + .unwrap_or(span); + + Ident::new(name, sp) }, ) }; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index c610ce4fc85..7aef60b7b91 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -250,7 +250,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::ShouldPanic { .. } | AttributeKind::Coroutine(..) | AttributeKind::Linkage(..) - | AttributeKind::MustUse { .. }, + | AttributeKind::MustUse { .. } + | AttributeKind::CrateName { .. } ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); @@ -367,22 +368,49 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let builtin = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)); if hir_id != CRATE_HIR_ID { - if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) = - attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)) - { - match style { - Some(ast::AttrStyle::Outer) => self.tcx.emit_node_span_lint( + match attr { + // FIXME(jdonszelmann) move to attribute parsing when validation gets better there + &Attribute::Parsed(AttributeKind::CrateName { + attr_span: span, style, .. + }) => match style { + ast::AttrStyle::Outer => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + span, errors::OuterCrateLevelAttr, ), - Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint( + ast::AttrStyle::Inner => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + span, errors::InnerCrateLevelAttr, ), + }, + Attribute::Parsed(_) => { /* not crate-level */ } + Attribute::Unparsed(attr) => { + // FIXME(jdonszelmann): remove once all crate-level attrs are parsed and caught by + // the above + if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) = + attr.path + .segments + .first() + .and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)) + { + match attr.style { + ast::AttrStyle::Outer => self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::OuterCrateLevelAttr, + ), + ast::AttrStyle::Inner => self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::InnerCrateLevelAttr, + ), + } + } } } } diff --git a/tests/ui/attributes/crate-name-empty.stderr b/tests/ui/attributes/crate-name-empty.stderr index 509a42d05f7..31d9dd0e948 100644 --- a/tests/ui/attributes/crate-name-empty.stderr +++ b/tests/ui/attributes/crate-name-empty.stderr @@ -1,8 +1,8 @@ error: crate name must not be empty - --> $DIR/crate-name-empty.rs:3:1 + --> $DIR/crate-name-empty.rs:3:17 | LL | #![crate_name = ""] - | ^^^^^^^^^^^^^^^^^^^ + | ^^ error: aborting due to 1 previous error diff --git a/tests/ui/attributes/crate-name-macro-call.rs b/tests/ui/attributes/crate-name-macro-call.rs index 1aae2e506e2..61076349cc3 100644 --- a/tests/ui/attributes/crate-name-macro-call.rs +++ b/tests/ui/attributes/crate-name-macro-call.rs @@ -1,6 +1,6 @@ // issue: rust-lang/rust#122001 // Ensure we reject macro calls inside `#![crate_name]` as their result wouldn't get honored anyway. -#![crate_name = concat!("my", "crate")] //~ ERROR malformed `crate_name` attribute input +#![crate_name = concat!("my", "crate")] //~ ERROR attribute value must be a literal fn main() {} diff --git a/tests/ui/attributes/crate-name-macro-call.stderr b/tests/ui/attributes/crate-name-macro-call.stderr index 56827aa11a4..e988b8461d7 100644 --- a/tests/ui/attributes/crate-name-macro-call.stderr +++ b/tests/ui/attributes/crate-name-macro-call.stderr @@ -1,10 +1,8 @@ -error: malformed `crate_name` attribute input - --> $DIR/crate-name-macro-call.rs:4:1 +error: attribute value must be a literal + --> $DIR/crate-name-macro-call.rs:4:17 | LL | #![crate_name = concat!("my", "crate")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]` - | - = note: for more information, visit + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/attributes/crate-name-mismatch.stderr b/tests/ui/attributes/crate-name-mismatch.stderr index 4021fbe7c18..6416ad2d41a 100644 --- a/tests/ui/attributes/crate-name-mismatch.stderr +++ b/tests/ui/attributes/crate-name-mismatch.stderr @@ -1,8 +1,8 @@ error: `--crate-name` and `#[crate_name]` are required to match, but `foo` != `bar` - --> $DIR/crate-name-mismatch.rs:5:1 + --> $DIR/crate-name-mismatch.rs:5:17 | LL | #![crate_name = "bar"] - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 11e01ac29be..69f9a18c179 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -41,14 +41,6 @@ LL | #![windows_subsystem = "console"] LL | #![windows_subsystem = "windows"] | +++++++++++ -error: malformed `crate_name` attribute input - --> $DIR/malformed-attrs.rs:74:1 - | -LL | #[crate_name] - | ^^^^^^^^^^^^^ help: must be of the form: `#[crate_name = "name"]` - | - = note: for more information, visit - error: malformed `instruction_set` attribute input --> $DIR/malformed-attrs.rs:106:1 | @@ -478,6 +470,12 @@ LL | #[used()] | = help: `#[used]` can only be applied to statics +error[E0539]: malformed `crate_name` attribute input + --> $DIR/malformed-attrs.rs:74:1 + | +LL | #[crate_name] + | ^^^^^^^^^^^^^ help: must be of the form: `#[crate_name = "name"]` + error[E0539]: malformed `target_feature` attribute input --> $DIR/malformed-attrs.rs:79:1 | diff --git a/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.rs b/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.rs index c7f3c2c5403..62f1f6cd09c 100644 --- a/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.rs +++ b/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.rs @@ -2,4 +2,4 @@ // See also . //@ compile-flags: --print=crate-name -#![crate_name = concat!("wrapped")] //~ ERROR malformed `crate_name` attribute input +#![crate_name = concat!("wrapped")] //~ ERROR attribute value must be a literal diff --git a/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.stderr b/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.stderr index b773f7c97aa..fab38dae78b 100644 --- a/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.stderr +++ b/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.stderr @@ -1,10 +1,8 @@ -error: malformed `crate_name` attribute input - --> $DIR/print-crate-name-request-malformed-crate-name.rs:5:1 +error: attribute value must be a literal + --> $DIR/print-crate-name-request-malformed-crate-name.rs:5:17 | LL | #![crate_name = concat!("wrapped")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]` - | - = note: for more information, visit + | ^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.stderr b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.stderr index 64e38ed8533..d3e60948e4c 100644 --- a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.stderr +++ b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.stderr @@ -1,10 +1,9 @@ -error: malformed `crate_name` attribute input +error[E0539]: malformed `crate_name` attribute input --> $DIR/print-file-names-request-malformed-crate-name-1.rs:4:1 | LL | #![crate_name] | ^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]` - | - = note: for more information, visit error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.rs b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.rs index 13c9d1e0027..1ac1208ee44 100644 --- a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.rs +++ b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.rs @@ -4,4 +4,4 @@ //@ compile-flags: --print=file-names #![crate_name = "this_one_is_okay"] -#![crate_name = concat!("this_one_is_not")] //~ ERROR malformed `crate_name` attribute input +#![crate_name = concat!("this_one_is_not")] //~ ERROR attribute value must be a literal diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.stderr b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.stderr index e9a5b58e4f9..1571521ff17 100644 --- a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.stderr +++ b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.stderr @@ -1,10 +1,8 @@ -error: malformed `crate_name` attribute input - --> $DIR/print-file-names-request-malformed-crate-name-2.rs:7:1 +error: attribute value must be a literal + --> $DIR/print-file-names-request-malformed-crate-name-2.rs:7:17 | LL | #![crate_name = concat!("this_one_is_not")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]` - | - = note: for more information, visit + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.rs b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.rs index 5fe8bd7945f..6441017c665 100644 --- a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.rs +++ b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.rs @@ -2,4 +2,4 @@ // See also . //@ compile-flags: --print=file-names -#![crate_name = concat!("wrapped")] //~ ERROR malformed `crate_name` attribute input +#![crate_name = concat!("wrapped")] //~ ERROR attribute value must be a literal diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.stderr b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.stderr index e63525c6a5d..155e2e5a3fa 100644 --- a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.stderr +++ b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.stderr @@ -1,10 +1,8 @@ -error: malformed `crate_name` attribute input - --> $DIR/print-file-names-request-malformed-crate-name.rs:5:1 +error: attribute value must be a literal + --> $DIR/print-file-names-request-malformed-crate-name.rs:5:17 | LL | #![crate_name = concat!("wrapped")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]` - | - = note: for more information, visit + | ^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/lint/unused/unused-attr-duplicate.rs b/tests/ui/lint/unused/unused-attr-duplicate.rs index cfa6c2b4228..a334c49788c 100644 --- a/tests/ui/lint/unused/unused-attr-duplicate.rs +++ b/tests/ui/lint/unused/unused-attr-duplicate.rs @@ -11,8 +11,12 @@ // - no_main: extra setup #![deny(unused_attributes)] #![crate_name = "unused_attr_duplicate"] -#![crate_name = "unused_attr_duplicate2"] //~ ERROR unused attribute -//~^ WARN this was previously accepted +#![crate_name = "unused_attr_duplicate2"] +//~^ ERROR unused attribute +//~| WARN this was previously accepted +//~| ERROR unused attribute +//~| WARN this was previously accepted +// FIXME(jdonszelmann) this error is given twice now. I'll look at this in the future #![recursion_limit = "128"] #![recursion_limit = "256"] //~ ERROR unused attribute //~^ WARN this was previously accepted diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr index 6c44e884ba5..203211d0d56 100644 --- a/tests/ui/lint/unused/unused-attr-duplicate.stderr +++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr @@ -1,14 +1,15 @@ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:33:1 + --> $DIR/unused-attr-duplicate.rs:14:1 | -LL | #[no_link] - | ^^^^^^^^^^ help: remove this attribute +LL | #![crate_name = "unused_attr_duplicate2"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:32:1 + --> $DIR/unused-attr-duplicate.rs:13:1 | -LL | #[no_link] - | ^^^^^^^^^^ +LL | #![crate_name = "unused_attr_duplicate"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! note: the lint level is defined here --> $DIR/unused-attr-duplicate.rs:12:9 | @@ -16,291 +17,304 @@ LL | #![deny(unused_attributes)] | ^^^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:14:1 + --> $DIR/unused-attr-duplicate.rs:37:1 | -LL | #![crate_name = "unused_attr_duplicate2"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute +LL | #[no_link] + | ^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:13:1 + --> $DIR/unused-attr-duplicate.rs:36:1 | -LL | #![crate_name = "unused_attr_duplicate"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +LL | #[no_link] + | ^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:17:1 + --> $DIR/unused-attr-duplicate.rs:21:1 | LL | #![recursion_limit = "256"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:16:1 + --> $DIR/unused-attr-duplicate.rs:20:1 | LL | #![recursion_limit = "128"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:20:1 + --> $DIR/unused-attr-duplicate.rs:24:1 | LL | #![type_length_limit = "1"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:19:1 + --> $DIR/unused-attr-duplicate.rs:23:1 | LL | #![type_length_limit = "1048576"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:23:1 + --> $DIR/unused-attr-duplicate.rs:27:1 | LL | #![no_std] | ^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:22:1 + --> $DIR/unused-attr-duplicate.rs:26:1 | LL | #![no_std] | ^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:27:1 + --> $DIR/unused-attr-duplicate.rs:31:1 | LL | #![windows_subsystem = "windows"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:26:1 + --> $DIR/unused-attr-duplicate.rs:30:1 | LL | #![windows_subsystem = "console"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:30:1 + --> $DIR/unused-attr-duplicate.rs:34:1 | LL | #![no_builtins] | ^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:29:1 + --> $DIR/unused-attr-duplicate.rs:33:1 | LL | #![no_builtins] | ^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:40:5 + --> $DIR/unused-attr-duplicate.rs:44:5 | LL | #[macro_export] | ^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:39:5 + --> $DIR/unused-attr-duplicate.rs:43:5 | LL | #[macro_export] | ^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:37:1 + --> $DIR/unused-attr-duplicate.rs:41:1 | LL | #[macro_use] | ^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:36:1 + --> $DIR/unused-attr-duplicate.rs:40:1 | LL | #[macro_use] | ^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:47:1 + --> $DIR/unused-attr-duplicate.rs:51:1 | LL | #[path = "bar.rs"] | ^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:46:1 + --> $DIR/unused-attr-duplicate.rs:50:1 | LL | #[path = "auxiliary/lint_unused_extern_crate.rs"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:53:1 + --> $DIR/unused-attr-duplicate.rs:57:1 | LL | #[ignore = "some text"] | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:52:1 + --> $DIR/unused-attr-duplicate.rs:56:1 | LL | #[ignore] | ^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:55:1 + --> $DIR/unused-attr-duplicate.rs:59:1 | LL | #[should_panic(expected = "values don't match")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:54:1 + --> $DIR/unused-attr-duplicate.rs:58:1 | LL | #[should_panic] | ^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:60:1 + --> $DIR/unused-attr-duplicate.rs:64:1 | LL | #[must_use = "some message"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:59:1 + --> $DIR/unused-attr-duplicate.rs:63:1 | LL | #[must_use] | ^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:66:1 + --> $DIR/unused-attr-duplicate.rs:70:1 | LL | #[non_exhaustive] | ^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:65:1 + --> $DIR/unused-attr-duplicate.rs:69:1 | LL | #[non_exhaustive] | ^^^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:72:1 + --> $DIR/unused-attr-duplicate.rs:76:1 | LL | #[automatically_derived] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:71:1 + --> $DIR/unused-attr-duplicate.rs:75:1 | LL | #[automatically_derived] | ^^^^^^^^^^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:76:1 + --> $DIR/unused-attr-duplicate.rs:80:1 | LL | #[inline(never)] | ^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:75:1 + --> $DIR/unused-attr-duplicate.rs:79:1 | LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:79:1 + --> $DIR/unused-attr-duplicate.rs:83:1 | LL | #[cold] | ^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:78:1 + --> $DIR/unused-attr-duplicate.rs:82:1 | LL | #[cold] | ^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:81:1 + --> $DIR/unused-attr-duplicate.rs:85:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:80:1 + --> $DIR/unused-attr-duplicate.rs:84:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:88:5 + --> $DIR/unused-attr-duplicate.rs:92:5 | LL | #[link_name = "this_does_not_exist"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:90:5 + --> $DIR/unused-attr-duplicate.rs:94:5 | LL | #[link_name = "rust_dbg_extern_identity_u32"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:94:1 + --> $DIR/unused-attr-duplicate.rs:98:1 | LL | #[export_name = "exported_symbol_name"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:96:1 + --> $DIR/unused-attr-duplicate.rs:100:1 | LL | #[export_name = "exported_symbol_name2"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:100:1 + --> $DIR/unused-attr-duplicate.rs:104:1 | LL | #[no_mangle] | ^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:99:1 + --> $DIR/unused-attr-duplicate.rs:103:1 | LL | #[no_mangle] | ^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:104:1 + --> $DIR/unused-attr-duplicate.rs:108:1 | LL | #[used] | ^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:103:1 + --> $DIR/unused-attr-duplicate.rs:107:1 | LL | #[used] | ^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:107:1 + --> $DIR/unused-attr-duplicate.rs:111:1 | LL | #[link_section = ".text"] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:110:1 + --> $DIR/unused-attr-duplicate.rs:114:1 | LL | #[link_section = ".bss"] | ^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:25:1 + --> $DIR/unused-attr-duplicate.rs:14:1 + | +LL | #![crate_name = "unused_attr_duplicate2"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/unused-attr-duplicate.rs:13:1 + | +LL | #![crate_name = "unused_attr_duplicate"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: unused attribute + --> $DIR/unused-attr-duplicate.rs:29:1 | LL | #![no_implicit_prelude] | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:24:1 + --> $DIR/unused-attr-duplicate.rs:28:1 | LL | #![no_implicit_prelude] | ^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 24 previous errors +error: aborting due to 25 previous errors -- cgit 1.4.1-3-g733a5