Skip to content

Commit a78c9be

Browse files
committed
Auto merge of rust-lang#104702 - Manishearth:rollup-75hagzd, r=Manishearth
Rollup of 7 pull requests Successful merges: - rust-lang#83608 (Add slice methods for indexing via an array of indices.) - rust-lang#95583 (Deprecate the unstable `ptr_to_from_bits` feature) - rust-lang#101655 (Make the Box one-liner more descriptive) - rust-lang#102207 (Constify remaining `Layout` methods) - rust-lang#103193 (mark sys_common::once::generic::Once::new const-stable) - rust-lang#104622 (Use clang for the UEFI targets) - rust-lang#104638 (Move macro_rules diagnostics to diagnostics module) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
2 parents 0f7d817 + 9043dfd commit a78c9be

File tree

22 files changed

+597
-259
lines changed

22 files changed

+597
-259
lines changed

Cargo.lock

+2-2
Original file line numberDiff line numberDiff line change
@@ -789,9 +789,9 @@ dependencies = [
789789

790790
[[package]]
791791
name = "compiler_builtins"
792-
version = "0.1.82"
792+
version = "0.1.84"
793793
source = "registry+https://github.com/rust-lang/crates.io-index"
794-
checksum = "18cd7635fea7bb481ea543b392789844c1ad581299da70184c7175ce3af76603"
794+
checksum = "989b2c1ca6e90ad06fdc69d1d1862fa28d27a977be6d92ae2fa762cf61fe0b10"
795795
dependencies = [
796796
"cc",
797797
"rustc-std-workspace-core",

compiler/rustc_expand/src/expand.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::base::*;
22
use crate::config::StripUnconfigured;
33
use crate::hygiene::SyntaxContext;
4-
use crate::mbe::macro_rules::annotate_err_with_kind;
4+
use crate::mbe::diagnostics::annotate_err_with_kind;
55
use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod};
66
use crate::placeholders::{placeholder, PlaceholderExpander};
77

compiler/rustc_expand/src/mbe.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//! why we call this module `mbe`. For external documentation, prefer the
44
//! official terminology: "declarative macros".
55
6+
pub(crate) mod diagnostics;
67
pub(crate) mod macro_check;
78
pub(crate) mod macro_parser;
89
pub(crate) mod macro_rules;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
use std::borrow::Cow;
2+
3+
use crate::base::{DummyResult, ExtCtxt, MacResult};
4+
use crate::expand::{parse_ast_fragment, AstFragmentKind};
5+
use crate::mbe::{
6+
macro_parser::{MatcherLoc, NamedParseResult, ParseResult::*, TtParser},
7+
macro_rules::{try_match_macro, Tracker},
8+
};
9+
use rustc_ast::token::{self, Token};
10+
use rustc_ast::tokenstream::TokenStream;
11+
use rustc_ast_pretty::pprust;
12+
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage};
13+
use rustc_parse::parser::{Parser, Recovery};
14+
use rustc_span::source_map::SourceMap;
15+
use rustc_span::symbol::Ident;
16+
use rustc_span::Span;
17+
18+
use super::macro_rules::{parser_from_cx, NoopTracker};
19+
20+
pub(super) fn failed_to_match_macro<'cx>(
21+
cx: &'cx mut ExtCtxt<'_>,
22+
sp: Span,
23+
def_span: Span,
24+
name: Ident,
25+
arg: TokenStream,
26+
lhses: &[Vec<MatcherLoc>],
27+
) -> Box<dyn MacResult + 'cx> {
28+
let sess = &cx.sess.parse_sess;
29+
30+
// An error occurred, try the expansion again, tracking the expansion closely for better diagnostics.
31+
let mut tracker = CollectTrackerAndEmitter::new(cx, sp);
32+
33+
let try_success_result = try_match_macro(sess, name, &arg, lhses, &mut tracker);
34+
35+
if try_success_result.is_ok() {
36+
// Nonterminal parser recovery might turn failed matches into successful ones,
37+
// but for that it must have emitted an error already
38+
tracker.cx.sess.delay_span_bug(sp, "Macro matching returned a success on the second try");
39+
}
40+
41+
if let Some(result) = tracker.result {
42+
// An irrecoverable error occurred and has been emitted.
43+
return result;
44+
}
45+
46+
let Some((token, label, remaining_matcher)) = tracker.best_failure else {
47+
return DummyResult::any(sp);
48+
};
49+
50+
let span = token.span.substitute_dummy(sp);
51+
52+
let mut err = cx.struct_span_err(span, &parse_failure_msg(&token));
53+
err.span_label(span, label);
54+
if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) {
55+
err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro");
56+
}
57+
58+
annotate_doc_comment(&mut err, sess.source_map(), span);
59+
60+
if let Some(span) = remaining_matcher.span() {
61+
err.span_note(span, format!("while trying to match {remaining_matcher}"));
62+
} else {
63+
err.note(format!("while trying to match {remaining_matcher}"));
64+
}
65+
66+
// Check whether there's a missing comma in this macro call, like `println!("{}" a);`
67+
if let Some((arg, comma_span)) = arg.add_comma() {
68+
for lhs in lhses {
69+
let parser = parser_from_cx(sess, arg.clone(), Recovery::Allowed);
70+
let mut tt_parser = TtParser::new(name);
71+
72+
if let Success(_) =
73+
tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs, &mut NoopTracker)
74+
{
75+
if comma_span.is_dummy() {
76+
err.note("you might be missing a comma");
77+
} else {
78+
err.span_suggestion_short(
79+
comma_span,
80+
"missing comma here",
81+
", ",
82+
Applicability::MachineApplicable,
83+
);
84+
}
85+
}
86+
}
87+
}
88+
err.emit();
89+
cx.trace_macros_diag();
90+
DummyResult::any(sp)
91+
}
92+
93+
/// The tracker used for the slow error path that collects useful info for diagnostics.
94+
struct CollectTrackerAndEmitter<'a, 'cx, 'matcher> {
95+
cx: &'a mut ExtCtxt<'cx>,
96+
remaining_matcher: Option<&'matcher MatcherLoc>,
97+
/// Which arm's failure should we report? (the one furthest along)
98+
best_failure: Option<(Token, &'static str, MatcherLoc)>,
99+
root_span: Span,
100+
result: Option<Box<dyn MacResult + 'cx>>,
101+
}
102+
103+
impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, 'matcher> {
104+
fn before_match_loc(&mut self, parser: &TtParser, matcher: &'matcher MatcherLoc) {
105+
if self.remaining_matcher.is_none()
106+
|| (parser.has_no_remaining_items_for_step() && *matcher != MatcherLoc::Eof)
107+
{
108+
self.remaining_matcher = Some(matcher);
109+
}
110+
}
111+
112+
fn after_arm(&mut self, result: &NamedParseResult) {
113+
match result {
114+
Success(_) => {
115+
// Nonterminal parser recovery might turn failed matches into successful ones,
116+
// but for that it must have emitted an error already
117+
self.cx.sess.delay_span_bug(
118+
self.root_span,
119+
"should not collect detailed info for successful macro match",
120+
);
121+
}
122+
Failure(token, msg) => match self.best_failure {
123+
Some((ref best_token, _, _)) if best_token.span.lo() >= token.span.lo() => {}
124+
_ => {
125+
self.best_failure = Some((
126+
token.clone(),
127+
msg,
128+
self.remaining_matcher
129+
.expect("must have collected matcher already")
130+
.clone(),
131+
))
132+
}
133+
},
134+
Error(err_sp, msg) => {
135+
let span = err_sp.substitute_dummy(self.root_span);
136+
self.cx.struct_span_err(span, msg).emit();
137+
self.result = Some(DummyResult::any(span));
138+
}
139+
ErrorReported(_) => self.result = Some(DummyResult::any(self.root_span)),
140+
}
141+
}
142+
143+
fn description() -> &'static str {
144+
"detailed"
145+
}
146+
147+
fn recovery() -> Recovery {
148+
Recovery::Allowed
149+
}
150+
}
151+
152+
impl<'a, 'cx> CollectTrackerAndEmitter<'a, 'cx, '_> {
153+
fn new(cx: &'a mut ExtCtxt<'cx>, root_span: Span) -> Self {
154+
Self { cx, remaining_matcher: None, best_failure: None, root_span, result: None }
155+
}
156+
}
157+
158+
pub(super) fn emit_frag_parse_err(
159+
mut e: DiagnosticBuilder<'_, rustc_errors::ErrorGuaranteed>,
160+
parser: &Parser<'_>,
161+
orig_parser: &mut Parser<'_>,
162+
site_span: Span,
163+
arm_span: Span,
164+
kind: AstFragmentKind,
165+
) {
166+
// FIXME(davidtwco): avoid depending on the error message text
167+
if parser.token == token::Eof
168+
&& let DiagnosticMessage::Str(message) = &e.message[0].0
169+
&& message.ends_with(", found `<eof>`")
170+
{
171+
let msg = &e.message[0];
172+
e.message[0] = (
173+
DiagnosticMessage::Str(format!(
174+
"macro expansion ends with an incomplete expression: {}",
175+
message.replace(", found `<eof>`", ""),
176+
)),
177+
msg.1,
178+
);
179+
if !e.span.is_dummy() {
180+
// early end of macro arm (#52866)
181+
e.replace_span_with(parser.token.span.shrink_to_hi());
182+
}
183+
}
184+
if e.span.is_dummy() {
185+
// Get around lack of span in error (#30128)
186+
e.replace_span_with(site_span);
187+
if !parser.sess.source_map().is_imported(arm_span) {
188+
e.span_label(arm_span, "in this macro arm");
189+
}
190+
} else if parser.sess.source_map().is_imported(parser.token.span) {
191+
e.span_label(site_span, "in this macro invocation");
192+
}
193+
match kind {
194+
// Try a statement if an expression is wanted but failed and suggest adding `;` to call.
195+
AstFragmentKind::Expr => match parse_ast_fragment(orig_parser, AstFragmentKind::Stmts) {
196+
Err(err) => err.cancel(),
197+
Ok(_) => {
198+
e.note(
199+
"the macro call doesn't expand to an expression, but it can expand to a statement",
200+
);
201+
e.span_suggestion_verbose(
202+
site_span.shrink_to_hi(),
203+
"add `;` to interpret the expansion as a statement",
204+
";",
205+
Applicability::MaybeIncorrect,
206+
);
207+
}
208+
},
209+
_ => annotate_err_with_kind(&mut e, kind, site_span),
210+
};
211+
e.emit();
212+
}
213+
214+
pub(crate) fn annotate_err_with_kind(err: &mut Diagnostic, kind: AstFragmentKind, span: Span) {
215+
match kind {
216+
AstFragmentKind::Ty => {
217+
err.span_label(span, "this macro call doesn't expand to a type");
218+
}
219+
AstFragmentKind::Pat => {
220+
err.span_label(span, "this macro call doesn't expand to a pattern");
221+
}
222+
_ => {}
223+
};
224+
}
225+
226+
#[derive(Subdiagnostic)]
227+
enum ExplainDocComment {
228+
#[label(expand_explain_doc_comment_inner)]
229+
Inner {
230+
#[primary_span]
231+
span: Span,
232+
},
233+
#[label(expand_explain_doc_comment_outer)]
234+
Outer {
235+
#[primary_span]
236+
span: Span,
237+
},
238+
}
239+
240+
pub(super) fn annotate_doc_comment(err: &mut Diagnostic, sm: &SourceMap, span: Span) {
241+
if let Ok(src) = sm.span_to_snippet(span) {
242+
if src.starts_with("///") || src.starts_with("/**") {
243+
err.subdiagnostic(ExplainDocComment::Outer { span });
244+
} else if src.starts_with("//!") || src.starts_with("/*!") {
245+
err.subdiagnostic(ExplainDocComment::Inner { span });
246+
}
247+
}
248+
}
249+
250+
/// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For
251+
/// other tokens, this is "unexpected token...".
252+
pub(super) fn parse_failure_msg(tok: &Token) -> String {
253+
match tok.kind {
254+
token::Eof => "unexpected end of macro invocation".to_string(),
255+
_ => format!("no rules expected the token `{}`", pprust::token_to_string(tok),),
256+
}
257+
}

0 commit comments

Comments
 (0)