Skip to content

Commit 70053f9

Browse files
committed
Reject macro calls inside of #![crate_name]
1 parent de6b815 commit 70053f9

21 files changed

+194
-116
lines changed

Diff for: compiler/rustc_driver_impl/src/lib.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -751,11 +751,12 @@ fn print_crate_info(
751751
return Compilation::Continue;
752752
};
753753
let t_outputs = rustc_interface::util::build_output_filenames(attrs, sess);
754-
let id = rustc_session::output::find_crate_name(sess, attrs);
754+
let crate_name = passes::get_crate_name(sess, attrs);
755755
let crate_types = collect_crate_types(sess, attrs);
756756
for &style in &crate_types {
757-
let fname =
758-
rustc_session::output::filename_for_input(sess, style, id, &t_outputs);
757+
let fname = rustc_session::output::filename_for_input(
758+
sess, style, crate_name, &t_outputs,
759+
);
759760
println_info!("{}", fname.as_path().file_name().unwrap().to_string_lossy());
760761
}
761762
}
@@ -764,8 +765,7 @@ fn print_crate_info(
764765
// no crate attributes, print out an error and exit
765766
return Compilation::Continue;
766767
};
767-
let id = rustc_session::output::find_crate_name(sess, attrs);
768-
println_info!("{id}");
768+
println_info!("{}", passes::get_crate_name(sess, attrs));
769769
}
770770
Cfg => {
771771
let mut cfgs = sess

Diff for: compiler/rustc_expand/src/module.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -180,12 +180,12 @@ pub(crate) fn mod_file_path_from_attr(
180180
let first_path = attrs.iter().find(|at| at.has_name(sym::path))?;
181181
let Some(path_sym) = first_path.value_str() else {
182182
// This check is here mainly to catch attempting to use a macro,
183-
// such as #[path = concat!(...)]. This isn't currently supported
184-
// because otherwise the InvocationCollector would need to defer
185-
// loading a module until the #[path] attribute was expanded, and
186-
// it doesn't support that (and would likely add a bit of
187-
// complexity). Usually bad forms are checked in AstValidator (via
188-
// `check_builtin_attribute`), but by the time that runs the macro
183+
// such as `#[path = concat!(...)]`. This isn't supported because
184+
// otherwise the `InvocationCollector` would need to defer loading
185+
// a module until the `#[path]` attribute was expanded, and it
186+
// doesn't support that (and would likely add a bit of complexity).
187+
// Usually bad forms are checked during semantic analysis via
188+
// `TyCtxt::check_mod_attrs`), but by the time that runs the macro
189189
// is expanded, and it doesn't give an error.
190190
validate_attr::emit_fatal_malformed_builtin_attribute(&sess.psess, first_path, sym::path);
191191
};

Diff for: compiler/rustc_incremental/src/persist/fs.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,9 @@ use rustc_errors::ErrorGuaranteed;
118118
use rustc_fs_util::{LinkOrCopy, link_or_copy, try_canonicalize};
119119
use rustc_middle::bug;
120120
use rustc_session::config::CrateType;
121-
use rustc_session::output::{collect_crate_types, find_crate_name};
121+
use rustc_session::output::collect_crate_types;
122122
use rustc_session::{Session, StableCrateId};
123+
use rustc_span::Symbol;
123124
use tracing::debug;
124125

125126
use crate::errors;
@@ -212,7 +213,10 @@ pub fn in_incr_comp_dir(incr_comp_session_dir: &Path, file_name: &str) -> PathBu
212213
/// The garbage collection will take care of it.
213214
///
214215
/// [`rustc_interface::queries::dep_graph`]: ../../rustc_interface/struct.Queries.html#structfield.dep_graph
215-
pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuaranteed> {
216+
pub(crate) fn prepare_session_directory(
217+
sess: &Session,
218+
crate_name: Symbol,
219+
) -> Result<(), ErrorGuaranteed> {
216220
if sess.opts.incremental.is_none() {
217221
return Ok(());
218222
}
@@ -222,7 +226,7 @@ pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuara
222226
debug!("prepare_session_directory");
223227

224228
// {incr-comp-dir}/{crate-name-and-disambiguator}
225-
let crate_dir = crate_path(sess);
229+
let crate_dir = crate_path(sess, crate_name);
226230
debug!("crate-dir: {}", crate_dir.display());
227231
create_dir(sess, &crate_dir, "crate")?;
228232

@@ -605,10 +609,9 @@ fn string_to_timestamp(s: &str) -> Result<SystemTime, &'static str> {
605609
Ok(UNIX_EPOCH + duration)
606610
}
607611

608-
fn crate_path(sess: &Session) -> PathBuf {
612+
fn crate_path(sess: &Session, crate_name: Symbol) -> PathBuf {
609613
let incr_dir = sess.opts.incremental.as_ref().unwrap().clone();
610614

611-
let crate_name = find_crate_name(sess, &[]);
612615
let crate_types = collect_crate_types(sess, &[]);
613616
let stable_crate_id = StableCrateId::new(
614617
crate_name,

Diff for: compiler/rustc_incremental/src/persist/load.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_serialize::Decodable;
1111
use rustc_serialize::opaque::MemDecoder;
1212
use rustc_session::Session;
1313
use rustc_session::config::IncrementalStateAssertion;
14-
use rustc_span::ErrorGuaranteed;
14+
use rustc_span::{ErrorGuaranteed, Symbol};
1515
use tracing::{debug, warn};
1616

1717
use super::data::*;
@@ -204,9 +204,9 @@ pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache<'_>> {
204204

205205
/// Setups the dependency graph by loading an existing graph from disk and set up streaming of a
206206
/// new graph to an incremental session directory.
207-
pub fn setup_dep_graph(sess: &Session) -> Result<DepGraph, ErrorGuaranteed> {
207+
pub fn setup_dep_graph(sess: &Session, crate_name: Symbol) -> Result<DepGraph, ErrorGuaranteed> {
208208
// `load_dep_graph` can only be called after `prepare_session_directory`.
209-
prepare_session_directory(sess)?;
209+
prepare_session_directory(sess, crate_name)?;
210210

211211
let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess));
212212

Diff for: compiler/rustc_interface/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
interface_cant_emit_mir =
22
could not emit MIR: {$error}
33
4+
interface_crate_name_does_not_match = `--crate-name` and `#[crate_name]` are required to match, but `{$crate_name}` != `{$attr_crate_name}`
5+
6+
interface_crate_name_invalid = crate names cannot start with a `-`, but `{$crate_name}` has a leading hyphen
7+
48
interface_emoji_identifier =
59
identifiers cannot contain emoji: `{$ident}`
610

Diff for: compiler/rustc_interface/src/errors.rs

+15
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,21 @@ use std::path::Path;
44
use rustc_macros::Diagnostic;
55
use rustc_span::{Span, Symbol};
66

7+
#[derive(Diagnostic)]
8+
#[diag(interface_crate_name_does_not_match)]
9+
pub(crate) struct CrateNameDoesNotMatch {
10+
#[primary_span]
11+
pub(crate) span: Span,
12+
pub(crate) crate_name: Symbol,
13+
pub(crate) attr_crate_name: Symbol,
14+
}
15+
16+
#[derive(Diagnostic)]
17+
#[diag(interface_crate_name_invalid)]
18+
pub(crate) struct CrateNameInvalid<'a> {
19+
pub(crate) crate_name: &'a str,
20+
}
21+
722
#[derive(Diagnostic)]
823
#[diag(interface_ferris_identifier)]
924
pub struct FerrisIdentifier {

Diff for: compiler/rustc_interface/src/passes.rs

+80-22
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,10 @@ use rustc_resolve::Resolver;
2929
use rustc_session::code_stats::VTableSizeInfo;
3030
use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType};
3131
use rustc_session::cstore::Untracked;
32-
use rustc_session::output::{collect_crate_types, filename_for_input, find_crate_name};
32+
use rustc_session::output::{collect_crate_types, filename_for_input};
3333
use rustc_session::search_paths::PathKind;
3434
use rustc_session::{Limit, Session};
35-
use rustc_span::FileName;
36-
use rustc_span::symbol::{Symbol, sym};
35+
use rustc_span::{FileName, Span, Symbol, sym};
3736
use rustc_target::spec::PanicStrategy;
3837
use rustc_trait_selection::traits;
3938
use tracing::{info, instrument};
@@ -663,8 +662,7 @@ pub(crate) fn create_global_ctxt<'tcx>(
663662

664663
let pre_configured_attrs = rustc_expand::config::pre_configure_attrs(sess, &krate.attrs);
665664

666-
// parse `#[crate_name]` even if `--crate-name` was passed, to make sure it matches.
667-
let crate_name = find_crate_name(sess, &pre_configured_attrs);
665+
let crate_name = get_crate_name(sess, &pre_configured_attrs);
668666
let crate_types = collect_crate_types(sess, &pre_configured_attrs);
669667
let stable_crate_id = StableCrateId::new(
670668
crate_name,
@@ -673,7 +671,7 @@ pub(crate) fn create_global_ctxt<'tcx>(
673671
sess.cfg_version,
674672
);
675673
let outputs = util::build_output_filenames(&pre_configured_attrs, sess);
676-
let dep_graph = setup_dep_graph(sess)?;
674+
let dep_graph = setup_dep_graph(sess, crate_name)?;
677675

678676
let cstore =
679677
FreezeLock::new(Box::new(CStore::new(compiler.codegen_backend.metadata_loader())) as _);
@@ -1070,23 +1068,83 @@ pub(crate) fn start_codegen<'tcx>(
10701068
Ok(codegen)
10711069
}
10721070

1073-
fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit {
1074-
if let Some(attr) = krate_attrs
1075-
.iter()
1076-
.find(|attr| attr.has_name(sym::recursion_limit) && attr.value_str().is_none())
1071+
/// Compute and validate the crate name.
1072+
pub fn get_crate_name(sess: &Session, krate_attrs: &[ast::Attribute]) -> Symbol {
1073+
// We unconditionally validate all `#![crate_name]`s even if a crate name was
1074+
// set on the command line via `--crate-name` which we prioritize over the
1075+
// crate attributes. We perform the validation here instead of later to ensure
1076+
// it gets run in all code paths requiring the crate name very early on.
1077+
// Namely, print requests (`--print`).
1078+
let attr_crate_name =
1079+
validate_and_find_value_str_builtin_attr(sym::crate_name, sess, krate_attrs);
1080+
1081+
let validate = |name, span| {
1082+
rustc_session::output::validate_crate_name(sess, name, span);
1083+
name
1084+
};
1085+
1086+
if let Some(crate_name) = &sess.opts.crate_name {
1087+
let crate_name = Symbol::intern(crate_name);
1088+
if let Some((attr_crate_name, span)) = attr_crate_name
1089+
&& attr_crate_name != crate_name
1090+
{
1091+
sess.dcx().emit_err(errors::CrateNameDoesNotMatch {
1092+
span,
1093+
crate_name,
1094+
attr_crate_name,
1095+
});
1096+
}
1097+
return validate(crate_name, None);
1098+
}
1099+
1100+
if let Some((crate_name, span)) = attr_crate_name {
1101+
return validate(crate_name, Some(span));
1102+
}
1103+
1104+
if let Input::File(ref path) = sess.io.input
1105+
&& let Some(file_stem) = path.file_stem().and_then(|s| s.to_str())
10771106
{
1078-
// This is here mainly to check for using a macro, such as
1079-
// #![recursion_limit = foo!()]. That is not supported since that
1080-
// would require expanding this while in the middle of expansion,
1081-
// which needs to know the limit before expanding. Otherwise,
1082-
// validation would normally be caught in AstValidator (via
1083-
// `check_builtin_attribute`), but by the time that runs the macro
1084-
// is expanded, and it doesn't give an error.
1085-
validate_attr::emit_fatal_malformed_builtin_attribute(
1086-
&sess.psess,
1087-
attr,
1088-
sym::recursion_limit,
1089-
);
1107+
if file_stem.starts_with('-') {
1108+
sess.dcx().emit_err(errors::CrateNameInvalid { crate_name: file_stem });
1109+
} else {
1110+
return validate(Symbol::intern(&file_stem.replace('-', "_")), None);
1111+
}
10901112
}
1113+
1114+
Symbol::intern("rust_out")
1115+
}
1116+
1117+
fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit {
1118+
// We don't permit macro calls inside of the attribute (e.g., #![recursion_limit = `expand!()`])
1119+
// because that would require expanding this while in the middle of expansion, which needs to
1120+
// know the limit before expanding.
1121+
let _ = validate_and_find_value_str_builtin_attr(sym::recursion_limit, sess, krate_attrs);
10911122
rustc_middle::middle::limits::get_recursion_limit(krate_attrs, sess)
10921123
}
1124+
1125+
/// Validate *all* occurrences of the given "[value-str]" built-in attribute and return the first find.
1126+
///
1127+
/// This validator is intended for built-in attributes whose value needs to be known very early
1128+
/// during compilation (namely, before macro expansion) and it mainly exists to reject macro calls
1129+
/// inside of the attributes, such as in `#![name = expand!()]`. Normal attribute validation happens
1130+
/// during semantic analysis via [`TyCtxt::check_mod_attrs`] which happens *after* macro expansion
1131+
/// when such macro calls (here: `expand`) have already been expanded and we can no longer check for
1132+
/// their presence.
1133+
///
1134+
/// [value-str]: ast::Attribute::value_str
1135+
fn validate_and_find_value_str_builtin_attr(
1136+
name: Symbol,
1137+
sess: &Session,
1138+
krate_attrs: &[ast::Attribute],
1139+
) -> Option<(Symbol, Span)> {
1140+
let mut result = None;
1141+
// Validate *all* relevant attributes, not just the first occurrence.
1142+
for attr in ast::attr::filter_by_name(krate_attrs, name) {
1143+
let Some(value) = attr.value_str() else {
1144+
validate_attr::emit_fatal_malformed_builtin_attribute(&sess.psess, attr, name)
1145+
};
1146+
// Choose the first occurrence as our result.
1147+
result.get_or_insert((value, attr.span));
1148+
}
1149+
result
1150+
}

Diff for: compiler/rustc_interface/src/util.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -415,11 +415,11 @@ pub(crate) fn check_attr_crate_type(
415415
}
416416
} else {
417417
// This is here mainly to check for using a macro, such as
418-
// #![crate_type = foo!()]. That is not supported since the
418+
// `#![crate_type = foo!()]`. That is not supported since the
419419
// crate type needs to be known very early in compilation long
420420
// before expansion. Otherwise, validation would normally be
421-
// caught in AstValidator (via `check_builtin_attribute`), but
422-
// by the time that runs the macro is expanded, and it doesn't
421+
// caught during semantic analysis via `TyCtxt::check_mod_attrs`,
422+
// but by the time that runs the macro is expanded, and it doesn't
423423
// give an error.
424424
validate_attr::emit_fatal_malformed_builtin_attribute(
425425
&sess.psess,

Diff for: compiler/rustc_session/messages.ftl

-4
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,8 @@ session_cannot_mix_and_match_sanitizers = `-Zsanitizer={$first}` is incompatible
88
session_cli_feature_diagnostic_help =
99
add `-Zcrate-attr="feature({$feature})"` to the command-line options to enable
1010
11-
session_crate_name_does_not_match = `--crate-name` and `#[crate_name]` are required to match, but `{$crate_name}` != `{$attr_crate_name}`
12-
1311
session_crate_name_empty = crate name must not be empty
1412
15-
session_crate_name_invalid = crate names cannot start with a `-`, but `{$s}` has a leading hyphen
16-
1713
session_embed_source_insufficient_dwarf_version = `-Zembed-source=y` requires at least `-Z dwarf-version=5` but DWARF version is {$dwarf_version}
1814
1915
session_embed_source_requires_debug_info = `-Zembed-source=y` requires debug information to be enabled

Diff for: compiler/rustc_session/src/errors.rs

-15
Original file line numberDiff line numberDiff line change
@@ -211,21 +211,6 @@ pub(crate) struct FileWriteFail<'a> {
211211
pub(crate) err: String,
212212
}
213213

214-
#[derive(Diagnostic)]
215-
#[diag(session_crate_name_does_not_match)]
216-
pub(crate) struct CrateNameDoesNotMatch {
217-
#[primary_span]
218-
pub(crate) span: Span,
219-
pub(crate) crate_name: Symbol,
220-
pub(crate) attr_crate_name: Symbol,
221-
}
222-
223-
#[derive(Diagnostic)]
224-
#[diag(session_crate_name_invalid)]
225-
pub(crate) struct CrateNameInvalid<'a> {
226-
pub(crate) s: &'a str,
227-
}
228-
229214
#[derive(Diagnostic)]
230215
#[diag(session_crate_name_empty)]
231216
pub(crate) struct CrateNameEmpty {

0 commit comments

Comments
 (0)