diff options
| author | León Orell Valerian Liehr <me@fmease.dev> | 2024-07-10 17:38:52 +0200 | 
|---|---|---|
| committer | León Orell Valerian Liehr <me@fmease.dev> | 2025-02-15 16:47:30 +0100 | 
| commit | 9b6fd35738965ef3f246018fddc743b5e5cd8d2c (patch) | |
| tree | 955710be2b114b379217abfe9c79362d99b37bbe /compiler/rustc_interface/src/passes.rs | |
| parent | 46d53a68aa1b10833ec25178171f34e29e71f1f1 (diff) | |
| download | rust-9b6fd35738965ef3f246018fddc743b5e5cd8d2c.tar.gz rust-9b6fd35738965ef3f246018fddc743b5e5cd8d2c.zip | |
Reject macro calls inside of `#![crate_name]`
Diffstat (limited to 'compiler/rustc_interface/src/passes.rs')
| -rw-r--r-- | compiler/rustc_interface/src/passes.rs | 105 | 
1 files changed, 84 insertions, 21 deletions
| diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index e5adcdb244f..fcebca3ecc9 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -28,10 +28,12 @@ use rustc_passes::{abi_test, input_stats, layout_test}; use rustc_resolve::Resolver; use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; use rustc_session::cstore::Untracked; -use rustc_session::output::{collect_crate_types, filename_for_input, find_crate_name}; +use rustc_session::output::{collect_crate_types, filename_for_input}; use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; -use rustc_span::{ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm, Symbol, sym}; +use rustc_span::{ + ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym, +}; use rustc_target::spec::PanicStrategy; use rustc_trait_selection::traits; use tracing::{info, instrument}; @@ -725,8 +727,7 @@ pub fn create_and_enter_global_ctxt<T, F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> T>( let pre_configured_attrs = rustc_expand::config::pre_configure_attrs(sess, &krate.attrs); - // parse `#[crate_name]` even if `--crate-name` was passed, to make sure it matches. - let crate_name = find_crate_name(sess, &pre_configured_attrs); + let crate_name = get_crate_name(sess, &pre_configured_attrs); let crate_types = collect_crate_types(sess, &pre_configured_attrs); let stable_crate_id = StableCrateId::new( crate_name, @@ -735,7 +736,7 @@ pub fn create_and_enter_global_ctxt<T, F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> T>( sess.cfg_version, ); let outputs = util::build_output_filenames(&pre_configured_attrs, sess); - let dep_graph = setup_dep_graph(sess); + let dep_graph = setup_dep_graph(sess, crate_name); let cstore = FreezeLock::new(Box::new(CStore::new(compiler.codegen_backend.metadata_loader())) as _); @@ -1080,23 +1081,85 @@ pub(crate) fn start_codegen<'tcx>( codegen } -fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit { - if let Some(attr) = krate_attrs - .iter() - .find(|attr| attr.has_name(sym::recursion_limit) && attr.value_str().is_none()) +/// Compute and validate the crate name. +pub fn get_crate_name(sess: &Session, krate_attrs: &[ast::Attribute]) -> Symbol { + // We validate *all* occurrences of `#![crate_name]`, pick the first find and + // if a crate name was passed on the command line via `--crate-name` we enforce + // that they match. + // We perform the validation step here instead of later to ensure it gets run + // 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 validate = |name, span| { + rustc_session::output::validate_crate_name(sess, name, span); + name + }; + + if let Some(crate_name) = &sess.opts.crate_name { + let crate_name = Symbol::intern(crate_name); + if let Some((attr_crate_name, span)) = attr_crate_name + && attr_crate_name != crate_name + { + sess.dcx().emit_err(errors::CrateNameDoesNotMatch { + span, + crate_name, + attr_crate_name, + }); + } + return validate(crate_name, None); + } + + if let Some((crate_name, span)) = attr_crate_name { + return validate(crate_name, Some(span)); + } + + if let Input::File(ref path) = sess.io.input + && let Some(file_stem) = path.file_stem().and_then(|s| s.to_str()) { - // This is here mainly to check for using a macro, such as - // #![recursion_limit = foo!()]. That is not supported since that - // would require expanding this while in the middle of expansion, - // which needs to know the limit before expanding. Otherwise, - // validation would normally be caught in AstValidator (via - // `check_builtin_attribute`), but by the time that runs the macro - // is expanded, and it doesn't give an error. - validate_attr::emit_fatal_malformed_builtin_attribute( - &sess.psess, - attr, - sym::recursion_limit, - ); + if file_stem.starts_with('-') { + sess.dcx().emit_err(errors::CrateNameInvalid { crate_name: file_stem }); + } else { + return validate(Symbol::intern(&file_stem.replace('-', "_")), None); + } } + + sym::rust_out +} + +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 + // know the limit before expanding. + let _ = validate_and_find_value_str_builtin_attr(sym::recursion_limit, sess, krate_attrs); rustc_middle::middle::limits::get_recursion_limit(krate_attrs, sess) } + +/// Validate *all* occurrences of the given "[value-str]" built-in attribute and return the first find. +/// +/// This validator is intended for built-in attributes whose value needs to be known very early +/// during compilation (namely, before macro expansion) and it mainly exists to reject macro calls +/// inside of the attributes, such as in `#![name = expand!()]`. Normal attribute validation happens +/// during semantic analysis via [`TyCtxt::check_mod_attrs`] which happens *after* macro expansion +/// when such macro calls (here: `expand`) have already been expanded and we can no longer check for +/// their presence. +/// +/// [value-str]: ast::Attribute::value_str +fn validate_and_find_value_str_builtin_attr( + name: Symbol, + sess: &Session, + krate_attrs: &[ast::Attribute], +) -> Option<(Symbol, Span)> { + let mut result = None; + // Validate *all* relevant attributes, not just the first occurrence. + for attr in ast::attr::filter_by_name(krate_attrs, name) { + let Some(value) = attr.value_str() else { + validate_attr::emit_fatal_malformed_builtin_attribute(&sess.psess, attr, name) + }; + // Choose the first occurrence as our result. + result.get_or_insert((value, attr.span)); + } + result +} | 
