Skip to content

Commit

Permalink
Migrate parts of rustc_expand to session diagnostics
Browse files Browse the repository at this point in the history
This migrates everything but the `mbe` and `proc_macro` modules. It also
contains a few cleanups and drive-by/accidental diagnostic improvements
which can be seen in the diff for the UI tests.
  • Loading branch information
Nilstrieb committed Dec 10, 2022
1 parent a000811 commit 2f9f097
Show file tree
Hide file tree
Showing 19 changed files with 640 additions and 236 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/concat.rs
Expand Up @@ -11,7 +11,7 @@ pub fn expand_concat(
sp: rustc_span::Span,
tts: TokenStream,
) -> Box<dyn base::MacResult + 'static> {
let Some(es) = base::get_exprs_from_tts(cx, sp, tts) else {
let Some(es) = base::get_exprs_from_tts(cx, tts) else {
return DummyResult::any(sp);
};
let mut accumulator = String::new();
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/concat_bytes.rs
Expand Up @@ -137,7 +137,7 @@ pub fn expand_concat_bytes(
sp: rustc_span::Span,
tts: TokenStream,
) -> Box<dyn base::MacResult + 'static> {
let Some(es) = base::get_exprs_from_tts(cx, sp, tts) else {
let Some(es) = base::get_exprs_from_tts(cx, tts) else {
return DummyResult::any(sp);
};
let mut accumulator = Vec::new();
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/env.rs
Expand Up @@ -52,7 +52,7 @@ pub fn expand_env<'cx>(
sp: Span,
tts: TokenStream,
) -> Box<dyn base::MacResult + 'cx> {
let mut exprs = match get_exprs_from_tts(cx, sp, tts) {
let mut exprs = match get_exprs_from_tts(cx, tts) {
Some(exprs) if exprs.is_empty() => {
cx.span_err(sp, "env! takes 1 or 2 arguments");
return DummyResult::any(sp);
Expand Down
107 changes: 107 additions & 0 deletions compiler/rustc_error_messages/locales/en-US/expand.ftl
Expand Up @@ -20,3 +20,110 @@ expand_var_still_repeating =
variable '{$ident}' is still repeating at this depth
expand_meta_var_dif_seq_matchers = {$msg}
expand_macro_const_stability =
macros cannot have const stability attributes
.label = invalid const stability attribute
.label2 = const stability attribute affects this macro
expand_macro_body_stability =
macros cannot have body stability attributes
.label = invalid body stability attribute
.label2 = body stability attribute affects this macro
expand_resolve_relative_path =
cannot resolve relative path in non-file source `{$path}`
expand_attr_no_arguments =
attribute must have either one or two arguments
expand_not_a_meta_item =
not a meta item
expand_only_one_word =
must only be one word
expand_cannot_be_name_of_macro =
`{$trait_ident}` cannot be a name of {$macro_type} macro
expand_arg_not_attributes =
second argument must be `attributes`
expand_attributes_wrong_form =
attribute must be of form: `attributes(foo, bar)`
expand_attribute_meta_item =
attribute must be a meta item, not a literal
expand_attribute_single_word =
attribute must only be a single word
expand_helper_attribute_name_invalid =
`{$name}` cannot be a name of derive helper attribute
expand_expected_comma_in_list =
expected token: `,`
expand_only_one_argument =
{$name} takes 1 argument
expand_takes_no_arguments =
{$name} takes no arguments
expand_feature_included_in_edition =
the feature `{$feature}` is included in the Rust {$edition} edition
expand_feature_removed =
feature has been removed
.label = feature has been removed
.reason = {$reason}
expand_feature_not_allowed =
the feature `{$name}` is not in the list of allowed features
expand_recursion_limit_reached =
recursion limit reached while expanding `{$descr}`
.help = consider increasing the recursion limit by adding a `#![recursion_limit = "{$suggested_limit}"]` attribute to your crate (`{$crate_name}`)
expand_malformed_feature_attribute =
malformed `feature` attribute input
.expected = expected just one word
expand_remove_expr_not_supported =
removing an expression is not supported in this position
expand_invalid_cfg_no_parens = `cfg` is not followed by parentheses
expand_invalid_cfg_no_predicate = `cfg` predicate is not specified
expand_invalid_cfg_multiple_predicates = multiple `cfg` predicates are specified
expand_invalid_cfg_predicate_literal = `cfg` predicate key cannot be a literal
expand_invalid_cfg_expected_syntax = expected syntax is
expand_wrong_fragment_kind =
non-{$kind} macro in {$kind} position: {$name}
expand_unsupported_key_value =
key-value macro attributes are not supported
expand_incomplete_parse =
macro expansion ignores token `{$token}` and any following
.label = caused by the macro expansion here
.note = the usage of `{$macro_path}!` is likely invalid in {$kind_name} context
.suggestion_add_semi = you might be missing a semicolon here
expand_remove_node_not_supported =
removing {$descr} is not supported in this position
expand_module_circular =
circular modules: {$modules}
expand_module_in_block =
cannot declare a non-inline module inside a block unless it has a path attribute
.note = maybe `use` the module `{$name}` instead of redeclaring it
expand_module_file_not_found =
file not found for module `{$name}`
.help = to create the module `{$name}`, create file "{$default_path}" or "{$secondary_path}"
expand_module_multiple_candidates =
file for module `{$name}` found at both "{$default_path}" and "{$secondary_path}"
.help = delete or rename one of them to remove the ambiguity
6 changes: 6 additions & 0 deletions compiler/rustc_errors/src/diagnostic_impls.rs
Expand Up @@ -152,6 +152,12 @@ impl IntoDiagnosticArg for ast::Path {
}
}

impl IntoDiagnosticArg for &ast::Path {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(pprust::path_to_string(self)))
}
}

impl IntoDiagnosticArg for ast::token::Token {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(pprust::token_to_string(&self))
Expand Down
90 changes: 39 additions & 51 deletions compiler/rustc_expand/src/base.rs
@@ -1,3 +1,11 @@
#![deny(rustc::untranslatable_diagnostic)]

use crate::errors::{
ArgumentNotAttributes, AttrNoArguments, AttributeMetaItem, AttributeSingleWord,
AttributesWrongForm, CannotBeNameOfMacro, ExpectedCommaInList, HelperAttributeNameInvalid,
MacroBodyStability, MacroConstStability, NotAMetaItem, OnlyOneArgument, OnlyOneWord,
ResolveRelativePath, TakesNoArguments,
};
use crate::expand::{self, AstFragment, Invocation};
use crate::module::DirOwnership;

Expand Down Expand Up @@ -789,26 +797,16 @@ impl SyntaxExtension {
.unwrap_or_else(|| (None, helper_attrs));
let (stability, const_stability, body_stability) = attr::find_stability(&sess, attrs, span);
if let Some((_, sp)) = const_stability {
sess.parse_sess
.span_diagnostic
.struct_span_err(sp, "macros cannot have const stability attributes")
.span_label(sp, "invalid const stability attribute")
.span_label(
sess.source_map().guess_head_span(span),
"const stability attribute affects this macro",
)
.emit();
sess.emit_err(MacroConstStability {
span: sp,
head_span: sess.source_map().guess_head_span(span),
});
}
if let Some((_, sp)) = body_stability {
sess.parse_sess
.span_diagnostic
.struct_span_err(sp, "macros cannot have body stability attributes")
.span_label(sp, "invalid body stability attribute")
.span_label(
sess.source_map().guess_head_span(span),
"body stability attribute affects this macro",
)
.emit();
sess.emit_err(MacroBodyStability {
span: sp,
head_span: sess.source_map().guess_head_span(span),
});
}

SyntaxExtension {
Expand Down Expand Up @@ -1200,13 +1198,11 @@ pub fn resolve_path(
.expect("attempting to resolve a file path in an external file"),
FileName::DocTest(path, _) => path,
other => {
return Err(parse_sess.span_diagnostic.struct_span_err(
return Err(ResolveRelativePath {
span,
&format!(
"cannot resolve relative path in non-file source `{}`",
parse_sess.source_map().filename_for_diagnostics(&other)
),
));
path: parse_sess.source_map().filename_for_diagnostics(&other).to_string(),
}
.into_diagnostic(&parse_sess.span_diagnostic));
}
};
result.pop();
Expand All @@ -1222,6 +1218,8 @@ pub fn resolve_path(
/// The returned bool indicates whether an applicable suggestion has already been
/// added to the diagnostic to avoid emitting multiple suggestions. `Err(None)`
/// indicates that an ast error was encountered.
// FIXME(Nilstrieb) Make this function setup translatable
#[allow(rustc::untranslatable_diagnostic)]
pub fn expr_to_spanned_string<'a>(
cx: &'a mut ExtCtxt<'_>,
expr: P<ast::Expr>,
Expand Down Expand Up @@ -1280,9 +1278,9 @@ pub fn expr_to_string(
/// compilation should call
/// `cx.parse_sess.span_diagnostic.abort_if_errors()` (this should be
/// done as rarely as possible).
pub fn check_zero_tts(cx: &ExtCtxt<'_>, sp: Span, tts: TokenStream, name: &str) {
pub fn check_zero_tts(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream, name: &str) {
if !tts.is_empty() {
cx.span_err(sp, &format!("{} takes no arguments", name));
cx.emit_err(TakesNoArguments { span, name });
}
}

Expand All @@ -1304,31 +1302,27 @@ pub fn parse_expr(p: &mut parser::Parser<'_>) -> Option<P<ast::Expr>> {
/// expect exactly one string literal, or emit an error and return `None`.
pub fn get_single_str_from_tts(
cx: &mut ExtCtxt<'_>,
sp: Span,
span: Span,
tts: TokenStream,
name: &str,
) -> Option<Symbol> {
let mut p = cx.new_parser_from_tts(tts);
if p.token == token::Eof {
cx.span_err(sp, &format!("{} takes 1 argument", name));
cx.emit_err(OnlyOneArgument { span, name });
return None;
}
let ret = parse_expr(&mut p)?;
let _ = p.eat(&token::Comma);

if p.token != token::Eof {
cx.span_err(sp, &format!("{} takes 1 argument", name));
cx.emit_err(OnlyOneArgument { span, name });
}
expr_to_string(cx, ret, "argument must be a string literal").map(|(s, _)| s)
}

/// Extracts comma-separated expressions from `tts`.
/// On error, emit it, and return `None`.
pub fn get_exprs_from_tts(
cx: &mut ExtCtxt<'_>,
sp: Span,
tts: TokenStream,
) -> Option<Vec<P<ast::Expr>>> {
pub fn get_exprs_from_tts(cx: &mut ExtCtxt<'_>, tts: TokenStream) -> Option<Vec<P<ast::Expr>>> {
let mut p = cx.new_parser_from_tts(tts);
let mut es = Vec::new();
while p.token != token::Eof {
Expand All @@ -1343,7 +1337,7 @@ pub fn get_exprs_from_tts(
continue;
}
if p.token != token::Eof {
cx.span_err(sp, "expected token: `,`");
cx.emit_err(ExpectedCommaInList { span: p.token.span });
return None;
}
}
Expand All @@ -1353,64 +1347,58 @@ pub fn get_exprs_from_tts(
pub fn parse_macro_name_and_helper_attrs(
diag: &rustc_errors::Handler,
attr: &Attribute,
descr: &str,
macro_type: &str,
) -> Option<(Symbol, Vec<Symbol>)> {
// Once we've located the `#[proc_macro_derive]` attribute, verify
// that it's of the form `#[proc_macro_derive(Foo)]` or
// `#[proc_macro_derive(Foo, attributes(A, ..))]`
let list = attr.meta_item_list()?;
if list.len() != 1 && list.len() != 2 {
diag.span_err(attr.span, "attribute must have either one or two arguments");
diag.emit_err(AttrNoArguments { span: attr.span });
return None;
}
let Some(trait_attr) = list[0].meta_item() else {
diag.span_err(list[0].span(), "not a meta item");
diag.emit_err(NotAMetaItem {span: list[0].span()});
return None;
};
let trait_ident = match trait_attr.ident() {
Some(trait_ident) if trait_attr.is_word() => trait_ident,
_ => {
diag.span_err(trait_attr.span, "must only be one word");
diag.emit_err(OnlyOneWord { span: trait_attr.span });
return None;
}
};

if !trait_ident.name.can_be_raw() {
diag.span_err(
trait_attr.span,
&format!("`{}` cannot be a name of {} macro", trait_ident, descr),
);
diag.emit_err(CannotBeNameOfMacro { span: trait_attr.span, trait_ident, macro_type });
}

let attributes_attr = list.get(1);
let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr {
if !attr.has_name(sym::attributes) {
diag.span_err(attr.span(), "second argument must be `attributes`");
diag.emit_err(ArgumentNotAttributes { span: attr.span() });
}
attr.meta_item_list()
.unwrap_or_else(|| {
diag.span_err(attr.span(), "attribute must be of form: `attributes(foo, bar)`");
diag.emit_err(AttributesWrongForm { span: attr.span() });
&[]
})
.iter()
.filter_map(|attr| {
let Some(attr) = attr.meta_item() else {
diag.span_err(attr.span(), "not a meta item");
diag.emit_err(AttributeMetaItem { span: attr.span() });
return None;
};

let ident = match attr.ident() {
Some(ident) if attr.is_word() => ident,
_ => {
diag.span_err(attr.span, "must only be one word");
diag.emit_err(AttributeSingleWord { span: attr.span });
return None;
}
};
if !ident.name.can_be_raw() {
diag.span_err(
attr.span,
&format!("`{}` cannot be a name of derive helper attribute", ident),
);
diag.emit_err(HelperAttributeNameInvalid { span: attr.span, name: ident });
}

Some(ident.name)
Expand Down

0 comments on commit 2f9f097

Please sign in to comment.