Auto merge of #120586 - ShE3py:exprkind-err, r=fmease
Add `ErrorGuaranteed` to `ast::ExprKind::Err` See #119967 for context ``` \ \ _~^~^~_ \) / o o \ (/ '_ - _' / '-----' \ ``` r? fmease
This commit is contained in:
commit
b79db437dc
38 changed files with 784 additions and 715 deletions
|
@ -1296,23 +1296,10 @@ impl Expr {
|
||||||
ExprKind::Yeet(..) => ExprPrecedence::Yeet,
|
ExprKind::Yeet(..) => ExprPrecedence::Yeet,
|
||||||
ExprKind::FormatArgs(..) => ExprPrecedence::FormatArgs,
|
ExprKind::FormatArgs(..) => ExprPrecedence::FormatArgs,
|
||||||
ExprKind::Become(..) => ExprPrecedence::Become,
|
ExprKind::Become(..) => ExprPrecedence::Become,
|
||||||
ExprKind::Err => ExprPrecedence::Err,
|
ExprKind::Err(_) | ExprKind::Dummy => ExprPrecedence::Err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take(&mut self) -> Self {
|
|
||||||
mem::replace(
|
|
||||||
self,
|
|
||||||
Expr {
|
|
||||||
id: DUMMY_NODE_ID,
|
|
||||||
kind: ExprKind::Err,
|
|
||||||
span: DUMMY_SP,
|
|
||||||
attrs: AttrVec::new(),
|
|
||||||
tokens: None,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// To a first-order approximation, is this a pattern?
|
/// To a first-order approximation, is this a pattern?
|
||||||
pub fn is_approximately_pattern(&self) -> bool {
|
pub fn is_approximately_pattern(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
|
@ -1531,7 +1518,10 @@ pub enum ExprKind {
|
||||||
FormatArgs(P<FormatArgs>),
|
FormatArgs(P<FormatArgs>),
|
||||||
|
|
||||||
/// Placeholder for an expression that wasn't syntactically well formed in some way.
|
/// Placeholder for an expression that wasn't syntactically well formed in some way.
|
||||||
Err,
|
Err(ErrorGuaranteed),
|
||||||
|
|
||||||
|
/// Acts as a null expression. Lowering it will always emit a bug.
|
||||||
|
Dummy,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to differentiate between `for` loops and `for await` loops.
|
/// Used to differentiate between `for` loops and `for await` loops.
|
||||||
|
|
|
@ -1526,7 +1526,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
|
||||||
}
|
}
|
||||||
ExprKind::Try(expr) => vis.visit_expr(expr),
|
ExprKind::Try(expr) => vis.visit_expr(expr),
|
||||||
ExprKind::TryBlock(body) => vis.visit_block(body),
|
ExprKind::TryBlock(body) => vis.visit_block(body),
|
||||||
ExprKind::Lit(_) | ExprKind::IncludedBytes(..) | ExprKind::Err => {}
|
ExprKind::Lit(_) | ExprKind::IncludedBytes(..) | ExprKind::Err(_) | ExprKind::Dummy => {}
|
||||||
}
|
}
|
||||||
vis.visit_id(id);
|
vis.visit_id(id);
|
||||||
vis.visit_span(span);
|
vis.visit_span(span);
|
||||||
|
@ -1642,7 +1642,7 @@ impl DummyAstNode for Expr {
|
||||||
fn dummy() -> Self {
|
fn dummy() -> Self {
|
||||||
Expr {
|
Expr {
|
||||||
id: DUMMY_NODE_ID,
|
id: DUMMY_NODE_ID,
|
||||||
kind: ExprKind::Err,
|
kind: ExprKind::Dummy,
|
||||||
span: Default::default(),
|
span: Default::default(),
|
||||||
attrs: Default::default(),
|
attrs: Default::default(),
|
||||||
tokens: Default::default(),
|
tokens: Default::default(),
|
||||||
|
|
|
@ -89,7 +89,8 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
|
||||||
| Paren(_)
|
| Paren(_)
|
||||||
| Try(_)
|
| Try(_)
|
||||||
| Yeet(None)
|
| Yeet(None)
|
||||||
| Err => break None,
|
| Err(_)
|
||||||
|
| Dummy => break None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1063,7 +1063,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
|
||||||
}
|
}
|
||||||
ExprKind::Try(subexpression) => try_visit!(visitor.visit_expr(subexpression)),
|
ExprKind::Try(subexpression) => try_visit!(visitor.visit_expr(subexpression)),
|
||||||
ExprKind::TryBlock(body) => try_visit!(visitor.visit_block(body)),
|
ExprKind::TryBlock(body) => try_visit!(visitor.visit_block(body)),
|
||||||
ExprKind::Lit(_) | ExprKind::IncludedBytes(..) | ExprKind::Err => {}
|
ExprKind::Lit(_) | ExprKind::IncludedBytes(..) | ExprKind::Err(_) | ExprKind::Dummy => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
visitor.visit_expr_post(expression)
|
visitor.visit_expr_post(expression)
|
||||||
|
|
|
@ -14,6 +14,7 @@ use rustc_ast::*;
|
||||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::{DefKind, Res};
|
use rustc_hir::def::{DefKind, Res};
|
||||||
|
use rustc_middle::span_bug;
|
||||||
use rustc_session::errors::report_lit_error;
|
use rustc_session::errors::report_lit_error;
|
||||||
use rustc_span::source_map::{respan, Spanned};
|
use rustc_span::source_map::{respan, Spanned};
|
||||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||||
|
@ -328,9 +329,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ExprKind::Yield(opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()),
|
ExprKind::Yield(opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()),
|
||||||
ExprKind::Err => {
|
ExprKind::Err(guar) => hir::ExprKind::Err(*guar),
|
||||||
hir::ExprKind::Err(self.dcx().span_delayed_bug(e.span, "lowered ExprKind::Err"))
|
|
||||||
|
ExprKind::Dummy => {
|
||||||
|
span_bug!(e.span, "lowered ExprKind::Dummy")
|
||||||
}
|
}
|
||||||
|
|
||||||
ExprKind::Try(sub_expr) => self.lower_expr_try(e.span, sub_expr),
|
ExprKind::Try(sub_expr) => self.lower_expr_try(e.span, sub_expr),
|
||||||
|
|
||||||
ExprKind::Paren(_) | ExprKind::ForLoop { .. } => {
|
ExprKind::Paren(_) | ExprKind::ForLoop { .. } => {
|
||||||
|
|
|
@ -331,7 +331,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
ExprKind::Lit(..)
|
ExprKind::Lit(..)
|
||||||
| ExprKind::ConstBlock(..)
|
| ExprKind::ConstBlock(..)
|
||||||
| ExprKind::IncludedBytes(..)
|
| ExprKind::IncludedBytes(..)
|
||||||
| ExprKind::Err => {}
|
| ExprKind::Err(_)
|
||||||
|
| ExprKind::Dummy => {}
|
||||||
ExprKind::Path(..) if allow_paths => {}
|
ExprKind::Path(..) if allow_paths => {}
|
||||||
ExprKind::Unary(UnOp::Neg, inner) if matches!(inner.kind, ExprKind::Lit(_)) => {}
|
ExprKind::Unary(UnOp::Neg, inner) if matches!(inner.kind, ExprKind::Lit(_)) => {}
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
@ -893,11 +893,16 @@ impl<'a> State<'a> {
|
||||||
self.word_nbsp("try");
|
self.word_nbsp("try");
|
||||||
self.print_block_with_attrs(blk, attrs)
|
self.print_block_with_attrs(blk, attrs)
|
||||||
}
|
}
|
||||||
ast::ExprKind::Err => {
|
ast::ExprKind::Err(_) => {
|
||||||
self.popen();
|
self.popen();
|
||||||
self.word("/*ERROR*/");
|
self.word("/*ERROR*/");
|
||||||
self.pclose()
|
self.pclose()
|
||||||
}
|
}
|
||||||
|
ast::ExprKind::Dummy => {
|
||||||
|
self.popen();
|
||||||
|
self.word("/*DUMMY*/");
|
||||||
|
self.pclose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ann.post(self, AnnNode::Expr(expr));
|
self.ann.post(self, AnnNode::Expr(expr));
|
||||||
|
|
|
@ -13,7 +13,7 @@ use rustc_session::lint;
|
||||||
use rustc_session::parse::ParseSess;
|
use rustc_session::parse::ParseSess;
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
use rustc_span::symbol::{kw, sym, Symbol};
|
use rustc_span::symbol::{kw, sym, Symbol};
|
||||||
use rustc_span::{InnerSpan, Span};
|
use rustc_span::{ErrorGuaranteed, InnerSpan, Span};
|
||||||
use rustc_target::asm::InlineAsmArch;
|
use rustc_target::asm::InlineAsmArch;
|
||||||
use smallvec::smallvec;
|
use smallvec::smallvec;
|
||||||
|
|
||||||
|
@ -433,7 +433,10 @@ fn parse_reg<'a>(
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::InlineAsm> {
|
fn expand_preparsed_asm(
|
||||||
|
ecx: &mut ExtCtxt<'_>,
|
||||||
|
args: AsmArgs,
|
||||||
|
) -> Result<ast::InlineAsm, ErrorGuaranteed> {
|
||||||
let mut template = vec![];
|
let mut template = vec![];
|
||||||
// Register operands are implicitly used since they are not allowed to be
|
// Register operands are implicitly used since they are not allowed to be
|
||||||
// referenced in the template string.
|
// referenced in the template string.
|
||||||
|
@ -459,10 +462,10 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
|
||||||
match expr_to_spanned_string(ecx, template_expr, msg) {
|
match expr_to_spanned_string(ecx, template_expr, msg) {
|
||||||
Ok(template_part) => template_part,
|
Ok(template_part) => template_part,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if let Some((err, _)) = err {
|
return Err(match err {
|
||||||
err.emit();
|
Ok((err, _)) => err.emit(),
|
||||||
}
|
Err(guar) => guar,
|
||||||
return None;
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -551,8 +554,8 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
|
||||||
let err_sp = template_span.from_inner(InnerSpan::new(span.start, span.end));
|
let err_sp = template_span.from_inner(InnerSpan::new(span.start, span.end));
|
||||||
e.span_label(err_sp, label);
|
e.span_label(err_sp, label);
|
||||||
}
|
}
|
||||||
e.emit();
|
let guar = e.emit();
|
||||||
return None;
|
return Err(guar);
|
||||||
}
|
}
|
||||||
|
|
||||||
curarg = parser.curarg;
|
curarg = parser.curarg;
|
||||||
|
@ -719,7 +722,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(ast::InlineAsm {
|
Ok(ast::InlineAsm {
|
||||||
template,
|
template,
|
||||||
template_strs: template_strs.into_boxed_slice(),
|
template_strs: template_strs.into_boxed_slice(),
|
||||||
operands: args.operands,
|
operands: args.operands,
|
||||||
|
@ -736,22 +739,21 @@ pub(super) fn expand_asm<'cx>(
|
||||||
) -> Box<dyn base::MacResult + 'cx> {
|
) -> Box<dyn base::MacResult + 'cx> {
|
||||||
match parse_args(ecx, sp, tts, false) {
|
match parse_args(ecx, sp, tts, false) {
|
||||||
Ok(args) => {
|
Ok(args) => {
|
||||||
let expr = if let Some(inline_asm) = expand_preparsed_asm(ecx, args) {
|
let expr = match expand_preparsed_asm(ecx, args) {
|
||||||
P(ast::Expr {
|
Ok(inline_asm) => P(ast::Expr {
|
||||||
id: ast::DUMMY_NODE_ID,
|
id: ast::DUMMY_NODE_ID,
|
||||||
kind: ast::ExprKind::InlineAsm(P(inline_asm)),
|
kind: ast::ExprKind::InlineAsm(P(inline_asm)),
|
||||||
span: sp,
|
span: sp,
|
||||||
attrs: ast::AttrVec::new(),
|
attrs: ast::AttrVec::new(),
|
||||||
tokens: None,
|
tokens: None,
|
||||||
})
|
}),
|
||||||
} else {
|
Err(guar) => DummyResult::raw_expr(sp, Some(guar)),
|
||||||
DummyResult::raw_expr(sp, true)
|
|
||||||
};
|
};
|
||||||
MacEager::expr(expr)
|
MacEager::expr(expr)
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
err.emit();
|
let guar = err.emit();
|
||||||
DummyResult::any(sp)
|
DummyResult::any(sp, guar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -762,28 +764,25 @@ pub(super) fn expand_global_asm<'cx>(
|
||||||
tts: TokenStream,
|
tts: TokenStream,
|
||||||
) -> Box<dyn base::MacResult + 'cx> {
|
) -> Box<dyn base::MacResult + 'cx> {
|
||||||
match parse_args(ecx, sp, tts, true) {
|
match parse_args(ecx, sp, tts, true) {
|
||||||
Ok(args) => {
|
Ok(args) => match expand_preparsed_asm(ecx, args) {
|
||||||
if let Some(inline_asm) = expand_preparsed_asm(ecx, args) {
|
Ok(inline_asm) => MacEager::items(smallvec![P(ast::Item {
|
||||||
MacEager::items(smallvec![P(ast::Item {
|
ident: Ident::empty(),
|
||||||
ident: Ident::empty(),
|
attrs: ast::AttrVec::new(),
|
||||||
attrs: ast::AttrVec::new(),
|
id: ast::DUMMY_NODE_ID,
|
||||||
id: ast::DUMMY_NODE_ID,
|
kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)),
|
||||||
kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)),
|
vis: ast::Visibility {
|
||||||
vis: ast::Visibility {
|
span: sp.shrink_to_lo(),
|
||||||
span: sp.shrink_to_lo(),
|
kind: ast::VisibilityKind::Inherited,
|
||||||
kind: ast::VisibilityKind::Inherited,
|
|
||||||
tokens: None,
|
|
||||||
},
|
|
||||||
span: sp,
|
|
||||||
tokens: None,
|
tokens: None,
|
||||||
})])
|
},
|
||||||
} else {
|
span: sp,
|
||||||
DummyResult::any(sp)
|
tokens: None,
|
||||||
}
|
})]),
|
||||||
}
|
Err(guar) => DummyResult::any(sp, guar),
|
||||||
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
err.emit();
|
let guar = err.emit();
|
||||||
DummyResult::any(sp)
|
DummyResult::any(sp, guar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,8 @@ pub fn expand_assert<'cx>(
|
||||||
let Assert { cond_expr, custom_message } = match parse_assert(cx, span, tts) {
|
let Assert { cond_expr, custom_message } = match parse_assert(cx, span, tts) {
|
||||||
Ok(assert) => assert,
|
Ok(assert) => assert,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
err.emit();
|
let guar = err.emit();
|
||||||
return DummyResult::any(span);
|
return DummyResult::any(span, guar);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -303,7 +303,8 @@ impl<'cx, 'a> Context<'cx, 'a> {
|
||||||
| ExprKind::Closure(_)
|
| ExprKind::Closure(_)
|
||||||
| ExprKind::ConstBlock(_)
|
| ExprKind::ConstBlock(_)
|
||||||
| ExprKind::Continue(_)
|
| ExprKind::Continue(_)
|
||||||
| ExprKind::Err
|
| ExprKind::Dummy
|
||||||
|
| ExprKind::Err(_)
|
||||||
| ExprKind::Field(_, _)
|
| ExprKind::Field(_, _)
|
||||||
| ExprKind::ForLoop { .. }
|
| ExprKind::ForLoop { .. }
|
||||||
| ExprKind::FormatArgs(_)
|
| ExprKind::FormatArgs(_)
|
||||||
|
|
|
@ -8,14 +8,14 @@ use rustc_ast::token;
|
||||||
use rustc_ast::tokenstream::TokenStream;
|
use rustc_ast::tokenstream::TokenStream;
|
||||||
use rustc_attr as attr;
|
use rustc_attr as attr;
|
||||||
use rustc_errors::PResult;
|
use rustc_errors::PResult;
|
||||||
use rustc_expand::base::{self, *};
|
use rustc_expand::base::{DummyResult, ExtCtxt, MacEager, MacResult};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
pub fn expand_cfg(
|
pub fn expand_cfg(
|
||||||
cx: &mut ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
sp: Span,
|
sp: Span,
|
||||||
tts: TokenStream,
|
tts: TokenStream,
|
||||||
) -> Box<dyn base::MacResult + 'static> {
|
) -> Box<dyn MacResult + 'static> {
|
||||||
let sp = cx.with_def_site_ctxt(sp);
|
let sp = cx.with_def_site_ctxt(sp);
|
||||||
|
|
||||||
match parse_cfg(cx, sp, tts) {
|
match parse_cfg(cx, sp, tts) {
|
||||||
|
@ -29,8 +29,8 @@ pub fn expand_cfg(
|
||||||
MacEager::expr(cx.expr_bool(sp, matches_cfg))
|
MacEager::expr(cx.expr_bool(sp, matches_cfg))
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
err.emit();
|
let guar = err.emit();
|
||||||
DummyResult::any(sp)
|
DummyResult::any(sp, guar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,22 @@
|
||||||
// The compiler code necessary to support the compile_error! extension.
|
// The compiler code necessary to support the compile_error! extension.
|
||||||
|
|
||||||
use rustc_ast::tokenstream::TokenStream;
|
use rustc_ast::tokenstream::TokenStream;
|
||||||
use rustc_expand::base::{self, *};
|
use rustc_expand::base::{get_single_str_from_tts, DummyResult, ExtCtxt, MacResult};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
pub fn expand_compile_error<'cx>(
|
pub fn expand_compile_error<'cx>(
|
||||||
cx: &'cx mut ExtCtxt<'_>,
|
cx: &'cx mut ExtCtxt<'_>,
|
||||||
sp: Span,
|
sp: Span,
|
||||||
tts: TokenStream,
|
tts: TokenStream,
|
||||||
) -> Box<dyn base::MacResult + 'cx> {
|
) -> Box<dyn MacResult + 'cx> {
|
||||||
let Some(var) = get_single_str_from_tts(cx, sp, tts, "compile_error!") else {
|
let var = match get_single_str_from_tts(cx, sp, tts, "compile_error!") {
|
||||||
return DummyResult::any(sp);
|
Ok(var) => var,
|
||||||
|
Err(guar) => return DummyResult::any(sp, guar),
|
||||||
};
|
};
|
||||||
|
|
||||||
#[expect(
|
#[expect(rustc::diagnostic_outside_of_impl, reason = "diagnostic message is specified by user")]
|
||||||
rustc::diagnostic_outside_of_impl,
|
|
||||||
reason = "diagnostic message is specified by user"
|
|
||||||
)]
|
|
||||||
#[expect(rustc::untranslatable_diagnostic, reason = "diagnostic message is specified by user")]
|
#[expect(rustc::untranslatable_diagnostic, reason = "diagnostic message is specified by user")]
|
||||||
cx.dcx().span_err(sp, var.to_string());
|
let guar = cx.dcx().span_err(sp, var.to_string());
|
||||||
|
|
||||||
DummyResult::any(sp)
|
DummyResult::any(sp, guar)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,73 +1,69 @@
|
||||||
use rustc_ast as ast;
|
|
||||||
use rustc_ast::tokenstream::TokenStream;
|
use rustc_ast::tokenstream::TokenStream;
|
||||||
use rustc_expand::base::{self, DummyResult};
|
use rustc_ast::{ExprKind, LitKind, UnOp};
|
||||||
|
use rustc_expand::base::{get_exprs_from_tts, DummyResult, ExtCtxt, MacEager, MacResult};
|
||||||
use rustc_session::errors::report_lit_error;
|
use rustc_session::errors::report_lit_error;
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::Symbol;
|
||||||
|
|
||||||
use crate::errors;
|
use crate::errors;
|
||||||
|
|
||||||
pub fn expand_concat(
|
pub fn expand_concat(
|
||||||
cx: &mut base::ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
sp: rustc_span::Span,
|
sp: rustc_span::Span,
|
||||||
tts: TokenStream,
|
tts: TokenStream,
|
||||||
) -> Box<dyn base::MacResult + 'static> {
|
) -> Box<dyn MacResult + 'static> {
|
||||||
let Some(es) = base::get_exprs_from_tts(cx, tts) else {
|
let es = match get_exprs_from_tts(cx, tts) {
|
||||||
return DummyResult::any(sp);
|
Ok(es) => es,
|
||||||
|
Err(guar) => return DummyResult::any(sp, guar),
|
||||||
};
|
};
|
||||||
let mut accumulator = String::new();
|
let mut accumulator = String::new();
|
||||||
let mut missing_literal = vec![];
|
let mut missing_literal = vec![];
|
||||||
let mut has_errors = false;
|
let mut guar = None;
|
||||||
for e in es {
|
for e in es {
|
||||||
match e.kind {
|
match e.kind {
|
||||||
ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) {
|
ExprKind::Lit(token_lit) => match LitKind::from_token_lit(token_lit) {
|
||||||
Ok(ast::LitKind::Str(s, _) | ast::LitKind::Float(s, _)) => {
|
Ok(LitKind::Str(s, _) | LitKind::Float(s, _)) => {
|
||||||
accumulator.push_str(s.as_str());
|
accumulator.push_str(s.as_str());
|
||||||
}
|
}
|
||||||
Ok(ast::LitKind::Char(c)) => {
|
Ok(LitKind::Char(c)) => {
|
||||||
accumulator.push(c);
|
accumulator.push(c);
|
||||||
}
|
}
|
||||||
Ok(ast::LitKind::Int(i, _)) => {
|
Ok(LitKind::Int(i, _)) => {
|
||||||
accumulator.push_str(&i.to_string());
|
accumulator.push_str(&i.to_string());
|
||||||
}
|
}
|
||||||
Ok(ast::LitKind::Bool(b)) => {
|
Ok(LitKind::Bool(b)) => {
|
||||||
accumulator.push_str(&b.to_string());
|
accumulator.push_str(&b.to_string());
|
||||||
}
|
}
|
||||||
Ok(ast::LitKind::CStr(..)) => {
|
Ok(LitKind::CStr(..)) => {
|
||||||
cx.dcx().emit_err(errors::ConcatCStrLit { span: e.span });
|
guar = Some(cx.dcx().emit_err(errors::ConcatCStrLit { span: e.span }));
|
||||||
has_errors = true;
|
|
||||||
}
|
}
|
||||||
Ok(ast::LitKind::Byte(..) | ast::LitKind::ByteStr(..)) => {
|
Ok(LitKind::Byte(..) | LitKind::ByteStr(..)) => {
|
||||||
cx.dcx().emit_err(errors::ConcatBytestr { span: e.span });
|
guar = Some(cx.dcx().emit_err(errors::ConcatBytestr { span: e.span }));
|
||||||
has_errors = true;
|
|
||||||
}
|
}
|
||||||
Ok(ast::LitKind::Err(_)) => {
|
Ok(LitKind::Err(guarantee)) => {
|
||||||
has_errors = true;
|
guar = Some(guarantee);
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
report_lit_error(&cx.sess.parse_sess, err, token_lit, e.span);
|
guar = Some(report_lit_error(&cx.sess.parse_sess, err, token_lit, e.span));
|
||||||
has_errors = true;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// We also want to allow negative numeric literals.
|
// We also want to allow negative numeric literals.
|
||||||
ast::ExprKind::Unary(ast::UnOp::Neg, ref expr)
|
ExprKind::Unary(UnOp::Neg, ref expr) if let ExprKind::Lit(token_lit) = expr.kind => {
|
||||||
if let ast::ExprKind::Lit(token_lit) = expr.kind =>
|
match LitKind::from_token_lit(token_lit) {
|
||||||
{
|
Ok(LitKind::Int(i, _)) => accumulator.push_str(&format!("-{i}")),
|
||||||
match ast::LitKind::from_token_lit(token_lit) {
|
Ok(LitKind::Float(f, _)) => accumulator.push_str(&format!("-{f}")),
|
||||||
Ok(ast::LitKind::Int(i, _)) => accumulator.push_str(&format!("-{i}")),
|
|
||||||
Ok(ast::LitKind::Float(f, _)) => accumulator.push_str(&format!("-{f}")),
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
report_lit_error(&cx.sess.parse_sess, err, token_lit, e.span);
|
guar = Some(report_lit_error(&cx.sess.parse_sess, err, token_lit, e.span));
|
||||||
has_errors = true;
|
|
||||||
}
|
}
|
||||||
_ => missing_literal.push(e.span),
|
_ => missing_literal.push(e.span),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::ExprKind::IncludedBytes(..) => {
|
ExprKind::IncludedBytes(..) => {
|
||||||
cx.dcx().emit_err(errors::ConcatBytestr { span: e.span });
|
cx.dcx().emit_err(errors::ConcatBytestr { span: e.span });
|
||||||
}
|
}
|
||||||
ast::ExprKind::Err => {
|
ExprKind::Err(guarantee) => {
|
||||||
has_errors = true;
|
guar = Some(guarantee);
|
||||||
}
|
}
|
||||||
|
ExprKind::Dummy => cx.dcx().span_bug(e.span, "concatenating `ExprKind::Dummy`"),
|
||||||
_ => {
|
_ => {
|
||||||
missing_literal.push(e.span);
|
missing_literal.push(e.span);
|
||||||
}
|
}
|
||||||
|
@ -75,11 +71,11 @@ pub fn expand_concat(
|
||||||
}
|
}
|
||||||
|
|
||||||
if !missing_literal.is_empty() {
|
if !missing_literal.is_empty() {
|
||||||
cx.dcx().emit_err(errors::ConcatMissingLiteral { spans: missing_literal });
|
let guar = cx.dcx().emit_err(errors::ConcatMissingLiteral { spans: missing_literal });
|
||||||
return DummyResult::any(sp);
|
return DummyResult::any(sp, guar);
|
||||||
} else if has_errors {
|
} else if let Some(guar) = guar {
|
||||||
return DummyResult::any(sp);
|
return DummyResult::any(sp, guar);
|
||||||
}
|
}
|
||||||
let sp = cx.with_def_site_ctxt(sp);
|
let sp = cx.with_def_site_ctxt(sp);
|
||||||
base::MacEager::expr(cx.expr_str(sp, Symbol::intern(&accumulator)))
|
MacEager::expr(cx.expr_str(sp, Symbol::intern(&accumulator)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,192 +1,181 @@
|
||||||
use rustc_ast as ast;
|
use rustc_ast::{ptr::P, token, tokenstream::TokenStream, ExprKind, LitIntType, LitKind, UintTy};
|
||||||
use rustc_ast::{ptr::P, tokenstream::TokenStream};
|
use rustc_expand::base::{get_exprs_from_tts, DummyResult, ExtCtxt, MacEager, MacResult};
|
||||||
use rustc_expand::base::{self, DummyResult};
|
|
||||||
use rustc_session::errors::report_lit_error;
|
use rustc_session::errors::report_lit_error;
|
||||||
use rustc_span::Span;
|
use rustc_span::{ErrorGuaranteed, Span};
|
||||||
|
|
||||||
use crate::errors;
|
use crate::errors;
|
||||||
|
|
||||||
/// Emits errors for literal expressions that are invalid inside and outside of an array.
|
/// Emits errors for literal expressions that are invalid inside and outside of an array.
|
||||||
fn invalid_type_err(
|
fn invalid_type_err(
|
||||||
cx: &mut base::ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
token_lit: ast::token::Lit,
|
token_lit: token::Lit,
|
||||||
span: Span,
|
span: Span,
|
||||||
is_nested: bool,
|
is_nested: bool,
|
||||||
) {
|
) -> ErrorGuaranteed {
|
||||||
use errors::{
|
use errors::{
|
||||||
ConcatBytesInvalid, ConcatBytesInvalidSuggestion, ConcatBytesNonU8, ConcatBytesOob,
|
ConcatBytesInvalid, ConcatBytesInvalidSuggestion, ConcatBytesNonU8, ConcatBytesOob,
|
||||||
};
|
};
|
||||||
let snippet = cx.sess.source_map().span_to_snippet(span).ok();
|
let snippet = cx.sess.source_map().span_to_snippet(span).ok();
|
||||||
let dcx = cx.dcx();
|
let dcx = cx.dcx();
|
||||||
match ast::LitKind::from_token_lit(token_lit) {
|
match LitKind::from_token_lit(token_lit) {
|
||||||
Ok(ast::LitKind::CStr(_, _)) => {
|
Ok(LitKind::CStr(_, _)) => {
|
||||||
// Avoid ambiguity in handling of terminal `NUL` by refusing to
|
// Avoid ambiguity in handling of terminal `NUL` by refusing to
|
||||||
// concatenate C string literals as bytes.
|
// concatenate C string literals as bytes.
|
||||||
dcx.emit_err(errors::ConcatCStrLit { span: span });
|
dcx.emit_err(errors::ConcatCStrLit { span })
|
||||||
}
|
}
|
||||||
Ok(ast::LitKind::Char(_)) => {
|
Ok(LitKind::Char(_)) => {
|
||||||
let sugg =
|
let sugg =
|
||||||
snippet.map(|snippet| ConcatBytesInvalidSuggestion::CharLit { span, snippet });
|
snippet.map(|snippet| ConcatBytesInvalidSuggestion::CharLit { span, snippet });
|
||||||
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "character", sugg });
|
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "character", sugg })
|
||||||
}
|
}
|
||||||
Ok(ast::LitKind::Str(_, _)) => {
|
Ok(LitKind::Str(_, _)) => {
|
||||||
// suggestion would be invalid if we are nested
|
// suggestion would be invalid if we are nested
|
||||||
let sugg = if !is_nested {
|
let sugg = if !is_nested {
|
||||||
snippet.map(|snippet| ConcatBytesInvalidSuggestion::StrLit { span, snippet })
|
snippet.map(|snippet| ConcatBytesInvalidSuggestion::StrLit { span, snippet })
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "string", sugg });
|
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "string", sugg })
|
||||||
}
|
}
|
||||||
Ok(ast::LitKind::Float(_, _)) => {
|
Ok(LitKind::Float(_, _)) => {
|
||||||
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "float", sugg: None });
|
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "float", sugg: None })
|
||||||
}
|
}
|
||||||
Ok(ast::LitKind::Bool(_)) => {
|
Ok(LitKind::Bool(_)) => {
|
||||||
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "boolean", sugg: None });
|
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "boolean", sugg: None })
|
||||||
}
|
}
|
||||||
Ok(ast::LitKind::Err(_)) => {}
|
Ok(LitKind::Int(_, _)) if !is_nested => {
|
||||||
Ok(ast::LitKind::Int(_, _)) if !is_nested => {
|
|
||||||
let sugg =
|
let sugg =
|
||||||
snippet.map(|snippet| ConcatBytesInvalidSuggestion::IntLit { span: span, snippet });
|
snippet.map(|snippet| ConcatBytesInvalidSuggestion::IntLit { span, snippet });
|
||||||
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "numeric", sugg });
|
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "numeric", sugg })
|
||||||
}
|
}
|
||||||
Ok(ast::LitKind::Int(
|
Ok(LitKind::Int(val, LitIntType::Unsuffixed | LitIntType::Unsigned(UintTy::U8))) => {
|
||||||
val,
|
|
||||||
ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8),
|
|
||||||
)) => {
|
|
||||||
assert!(val.get() > u8::MAX.into()); // must be an error
|
assert!(val.get() > u8::MAX.into()); // must be an error
|
||||||
dcx.emit_err(ConcatBytesOob { span });
|
dcx.emit_err(ConcatBytesOob { span })
|
||||||
}
|
|
||||||
Ok(ast::LitKind::Int(_, _)) => {
|
|
||||||
dcx.emit_err(ConcatBytesNonU8 { span });
|
|
||||||
}
|
|
||||||
Ok(ast::LitKind::ByteStr(..) | ast::LitKind::Byte(_)) => unreachable!(),
|
|
||||||
Err(err) => {
|
|
||||||
report_lit_error(&cx.sess.parse_sess, err, token_lit, span);
|
|
||||||
}
|
}
|
||||||
|
Ok(LitKind::Int(_, _)) => dcx.emit_err(ConcatBytesNonU8 { span }),
|
||||||
|
Ok(LitKind::ByteStr(..) | LitKind::Byte(_)) => unreachable!(),
|
||||||
|
Ok(LitKind::Err(guar)) => guar,
|
||||||
|
Err(err) => report_lit_error(&cx.sess.parse_sess, err, token_lit, span),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `expr` as a *single* byte literal if applicable.
|
||||||
|
///
|
||||||
|
/// Otherwise, returns `None`, and either pushes the `expr`'s span to `missing_literals` or
|
||||||
|
/// updates `guar` accordingly.
|
||||||
fn handle_array_element(
|
fn handle_array_element(
|
||||||
cx: &mut base::ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
has_errors: &mut bool,
|
guar: &mut Option<ErrorGuaranteed>,
|
||||||
missing_literals: &mut Vec<rustc_span::Span>,
|
missing_literals: &mut Vec<rustc_span::Span>,
|
||||||
expr: &P<rustc_ast::Expr>,
|
expr: &P<rustc_ast::Expr>,
|
||||||
) -> Option<u8> {
|
) -> Option<u8> {
|
||||||
let dcx = cx.dcx();
|
let dcx = cx.dcx();
|
||||||
match expr.kind {
|
|
||||||
ast::ExprKind::Array(_) | ast::ExprKind::Repeat(_, _) => {
|
|
||||||
if !*has_errors {
|
|
||||||
dcx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: false });
|
|
||||||
}
|
|
||||||
*has_errors = true;
|
|
||||||
None
|
|
||||||
}
|
|
||||||
ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) {
|
|
||||||
Ok(ast::LitKind::Int(
|
|
||||||
val,
|
|
||||||
ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8),
|
|
||||||
)) if val.get() <= u8::MAX.into() => Some(val.get() as u8),
|
|
||||||
|
|
||||||
Ok(ast::LitKind::Byte(val)) => Some(val),
|
match expr.kind {
|
||||||
Ok(ast::LitKind::ByteStr(..)) => {
|
ExprKind::Lit(token_lit) => {
|
||||||
if !*has_errors {
|
match LitKind::from_token_lit(token_lit) {
|
||||||
dcx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: true });
|
Ok(LitKind::Int(
|
||||||
|
val,
|
||||||
|
LitIntType::Unsuffixed | LitIntType::Unsigned(UintTy::U8),
|
||||||
|
)) if let Ok(val) = u8::try_from(val.get()) => {
|
||||||
|
return Some(val);
|
||||||
}
|
}
|
||||||
*has_errors = true;
|
Ok(LitKind::Byte(val)) => return Some(val),
|
||||||
None
|
Ok(LitKind::ByteStr(..)) => {
|
||||||
}
|
guar.get_or_insert_with(|| {
|
||||||
_ => {
|
dcx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: true })
|
||||||
if !*has_errors {
|
});
|
||||||
invalid_type_err(cx, token_lit, expr.span, true);
|
|
||||||
}
|
}
|
||||||
*has_errors = true;
|
_ => {
|
||||||
None
|
guar.get_or_insert_with(|| invalid_type_err(cx, token_lit, expr.span, true));
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
ast::ExprKind::IncludedBytes(..) => {
|
|
||||||
if !*has_errors {
|
|
||||||
dcx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: false });
|
|
||||||
}
|
|
||||||
*has_errors = true;
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
_ => {
|
ExprKind::Array(_) | ExprKind::Repeat(_, _) => {
|
||||||
missing_literals.push(expr.span);
|
guar.get_or_insert_with(|| {
|
||||||
None
|
dcx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: false })
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
ExprKind::IncludedBytes(..) => {
|
||||||
|
guar.get_or_insert_with(|| {
|
||||||
|
dcx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: false })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => missing_literals.push(expr.span),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expand_concat_bytes(
|
pub fn expand_concat_bytes(
|
||||||
cx: &mut base::ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
sp: rustc_span::Span,
|
sp: Span,
|
||||||
tts: TokenStream,
|
tts: TokenStream,
|
||||||
) -> Box<dyn base::MacResult + 'static> {
|
) -> Box<dyn MacResult + 'static> {
|
||||||
let Some(es) = base::get_exprs_from_tts(cx, tts) else {
|
let es = match get_exprs_from_tts(cx, tts) {
|
||||||
return DummyResult::any(sp);
|
Ok(es) => es,
|
||||||
|
Err(guar) => return DummyResult::any(sp, guar),
|
||||||
};
|
};
|
||||||
let mut accumulator = Vec::new();
|
let mut accumulator = Vec::new();
|
||||||
let mut missing_literals = vec![];
|
let mut missing_literals = vec![];
|
||||||
let mut has_errors = false;
|
let mut guar = None;
|
||||||
for e in es {
|
for e in es {
|
||||||
match &e.kind {
|
match &e.kind {
|
||||||
ast::ExprKind::Array(exprs) => {
|
ExprKind::Array(exprs) => {
|
||||||
for expr in exprs {
|
for expr in exprs {
|
||||||
if let Some(elem) =
|
if let Some(elem) =
|
||||||
handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
|
handle_array_element(cx, &mut guar, &mut missing_literals, expr)
|
||||||
{
|
{
|
||||||
accumulator.push(elem);
|
accumulator.push(elem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::ExprKind::Repeat(expr, count) => {
|
ExprKind::Repeat(expr, count) => {
|
||||||
if let ast::ExprKind::Lit(token_lit) = count.value.kind
|
if let ExprKind::Lit(token_lit) = count.value.kind
|
||||||
&& let Ok(ast::LitKind::Int(count_val, _)) =
|
&& let Ok(LitKind::Int(count_val, _)) = LitKind::from_token_lit(token_lit)
|
||||||
ast::LitKind::from_token_lit(token_lit)
|
|
||||||
{
|
{
|
||||||
if let Some(elem) =
|
if let Some(elem) =
|
||||||
handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
|
handle_array_element(cx, &mut guar, &mut missing_literals, expr)
|
||||||
{
|
{
|
||||||
for _ in 0..count_val.get() {
|
for _ in 0..count_val.get() {
|
||||||
accumulator.push(elem);
|
accumulator.push(elem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cx.dcx().emit_err(errors::ConcatBytesBadRepeat { span: count.value.span });
|
guar = Some(
|
||||||
|
cx.dcx().emit_err(errors::ConcatBytesBadRepeat { span: count.value.span }),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) {
|
&ExprKind::Lit(token_lit) => match LitKind::from_token_lit(token_lit) {
|
||||||
Ok(ast::LitKind::Byte(val)) => {
|
Ok(LitKind::Byte(val)) => {
|
||||||
accumulator.push(val);
|
accumulator.push(val);
|
||||||
}
|
}
|
||||||
Ok(ast::LitKind::ByteStr(ref bytes, _)) => {
|
Ok(LitKind::ByteStr(ref bytes, _)) => {
|
||||||
accumulator.extend_from_slice(bytes);
|
accumulator.extend_from_slice(bytes);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if !has_errors {
|
guar.get_or_insert_with(|| invalid_type_err(cx, token_lit, e.span, false));
|
||||||
invalid_type_err(cx, token_lit, e.span, false);
|
|
||||||
}
|
|
||||||
has_errors = true;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ast::ExprKind::IncludedBytes(bytes) => {
|
ExprKind::IncludedBytes(bytes) => {
|
||||||
accumulator.extend_from_slice(bytes);
|
accumulator.extend_from_slice(bytes);
|
||||||
}
|
}
|
||||||
ast::ExprKind::Err => {
|
ExprKind::Err(guarantee) => {
|
||||||
has_errors = true;
|
guar = Some(*guarantee);
|
||||||
}
|
}
|
||||||
|
ExprKind::Dummy => cx.dcx().span_bug(e.span, "concatenating `ExprKind::Dummy`"),
|
||||||
_ => {
|
_ => {
|
||||||
missing_literals.push(e.span);
|
missing_literals.push(e.span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !missing_literals.is_empty() {
|
if !missing_literals.is_empty() {
|
||||||
cx.dcx().emit_err(errors::ConcatBytesMissingLiteral { spans: missing_literals });
|
let guar = cx.dcx().emit_err(errors::ConcatBytesMissingLiteral { spans: missing_literals });
|
||||||
return base::MacEager::expr(DummyResult::raw_expr(sp, true));
|
return MacEager::expr(DummyResult::raw_expr(sp, Some(guar)));
|
||||||
} else if has_errors {
|
} else if let Some(guar) = guar {
|
||||||
return base::MacEager::expr(DummyResult::raw_expr(sp, true));
|
return MacEager::expr(DummyResult::raw_expr(sp, Some(guar)));
|
||||||
}
|
}
|
||||||
let sp = cx.with_def_site_ctxt(sp);
|
let sp = cx.with_def_site_ctxt(sp);
|
||||||
base::MacEager::expr(cx.expr_byte_str(sp, accumulator))
|
MacEager::expr(cx.expr_byte_str(sp, accumulator))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use rustc_ast as ast;
|
|
||||||
use rustc_ast::ptr::P;
|
use rustc_ast::ptr::P;
|
||||||
use rustc_ast::token::{self, Token};
|
use rustc_ast::token::{self, Token};
|
||||||
use rustc_ast::tokenstream::{TokenStream, TokenTree};
|
use rustc_ast::tokenstream::{TokenStream, TokenTree};
|
||||||
use rustc_expand::base::{self, *};
|
use rustc_ast::{AttrVec, Expr, ExprKind, Path, Ty, TyKind, DUMMY_NODE_ID};
|
||||||
|
use rustc_expand::base::{DummyResult, ExtCtxt, MacResult};
|
||||||
use rustc_span::symbol::{Ident, Symbol};
|
use rustc_span::symbol::{Ident, Symbol};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
|
@ -12,10 +12,10 @@ pub fn expand_concat_idents<'cx>(
|
||||||
cx: &'cx mut ExtCtxt<'_>,
|
cx: &'cx mut ExtCtxt<'_>,
|
||||||
sp: Span,
|
sp: Span,
|
||||||
tts: TokenStream,
|
tts: TokenStream,
|
||||||
) -> Box<dyn base::MacResult + 'cx> {
|
) -> Box<dyn MacResult + 'cx> {
|
||||||
if tts.is_empty() {
|
if tts.is_empty() {
|
||||||
cx.dcx().emit_err(errors::ConcatIdentsMissingArgs { span: sp });
|
let guar = cx.dcx().emit_err(errors::ConcatIdentsMissingArgs { span: sp });
|
||||||
return DummyResult::any(sp);
|
return DummyResult::any(sp, guar);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut res_str = String::new();
|
let mut res_str = String::new();
|
||||||
|
@ -24,8 +24,8 @@ pub fn expand_concat_idents<'cx>(
|
||||||
match e {
|
match e {
|
||||||
TokenTree::Token(Token { kind: token::Comma, .. }, _) => {}
|
TokenTree::Token(Token { kind: token::Comma, .. }, _) => {}
|
||||||
_ => {
|
_ => {
|
||||||
cx.dcx().emit_err(errors::ConcatIdentsMissingComma { span: sp });
|
let guar = cx.dcx().emit_err(errors::ConcatIdentsMissingComma { span: sp });
|
||||||
return DummyResult::any(sp);
|
return DummyResult::any(sp, guar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -36,8 +36,8 @@ pub fn expand_concat_idents<'cx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.dcx().emit_err(errors::ConcatIdentsIdentArgs { span: sp });
|
let guar = cx.dcx().emit_err(errors::ConcatIdentsIdentArgs { span: sp });
|
||||||
return DummyResult::any(sp);
|
return DummyResult::any(sp, guar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,21 +47,21 @@ pub fn expand_concat_idents<'cx>(
|
||||||
ident: Ident,
|
ident: Ident,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl base::MacResult for ConcatIdentsResult {
|
impl MacResult for ConcatIdentsResult {
|
||||||
fn make_expr(self: Box<Self>) -> Option<P<ast::Expr>> {
|
fn make_expr(self: Box<Self>) -> Option<P<Expr>> {
|
||||||
Some(P(ast::Expr {
|
Some(P(Expr {
|
||||||
id: ast::DUMMY_NODE_ID,
|
id: DUMMY_NODE_ID,
|
||||||
kind: ast::ExprKind::Path(None, ast::Path::from_ident(self.ident)),
|
kind: ExprKind::Path(None, Path::from_ident(self.ident)),
|
||||||
span: self.ident.span,
|
span: self.ident.span,
|
||||||
attrs: ast::AttrVec::new(),
|
attrs: AttrVec::new(),
|
||||||
tokens: None,
|
tokens: None,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_ty(self: Box<Self>) -> Option<P<ast::Ty>> {
|
fn make_ty(self: Box<Self>) -> Option<P<Ty>> {
|
||||||
Some(P(ast::Ty {
|
Some(P(Ty {
|
||||||
id: ast::DUMMY_NODE_ID,
|
id: DUMMY_NODE_ID,
|
||||||
kind: ast::TyKind::Path(None, ast::Path::from_ident(self.ident)),
|
kind: TyKind::Path(None, Path::from_ident(self.ident)),
|
||||||
span: self.ident.span,
|
span: self.ident.span,
|
||||||
tokens: None,
|
tokens: None,
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -6,7 +6,7 @@ use rustc_ast::{attr, walk_list, EnumDef, VariantData};
|
||||||
use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
|
use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
use rustc_span::symbol::{kw, sym};
|
use rustc_span::symbol::{kw, sym};
|
||||||
use rustc_span::Span;
|
use rustc_span::{ErrorGuaranteed, Span};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use thin_vec::{thin_vec, ThinVec};
|
use thin_vec::{thin_vec, ThinVec};
|
||||||
|
|
||||||
|
@ -83,16 +83,19 @@ fn default_enum_substructure(
|
||||||
trait_span: Span,
|
trait_span: Span,
|
||||||
enum_def: &EnumDef,
|
enum_def: &EnumDef,
|
||||||
) -> BlockOrExpr {
|
) -> BlockOrExpr {
|
||||||
let expr = if let Ok(default_variant) = extract_default_variant(cx, enum_def, trait_span)
|
let expr = match try {
|
||||||
&& let Ok(_) = validate_default_attribute(cx, default_variant)
|
let default_variant = extract_default_variant(cx, enum_def, trait_span)?;
|
||||||
{
|
validate_default_attribute(cx, default_variant)?;
|
||||||
// We now know there is exactly one unit variant with exactly one `#[default]` attribute.
|
default_variant
|
||||||
cx.expr_path(cx.path(
|
} {
|
||||||
default_variant.span,
|
Ok(default_variant) => {
|
||||||
vec![Ident::new(kw::SelfUpper, default_variant.span), default_variant.ident],
|
// We now know there is exactly one unit variant with exactly one `#[default]` attribute.
|
||||||
))
|
cx.expr_path(cx.path(
|
||||||
} else {
|
default_variant.span,
|
||||||
DummyResult::raw_expr(trait_span, true)
|
vec![Ident::new(kw::SelfUpper, default_variant.span), default_variant.ident],
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Err(guar) => DummyResult::raw_expr(trait_span, Some(guar)),
|
||||||
};
|
};
|
||||||
BlockOrExpr::new_expr(expr)
|
BlockOrExpr::new_expr(expr)
|
||||||
}
|
}
|
||||||
|
@ -101,7 +104,7 @@ fn extract_default_variant<'a>(
|
||||||
cx: &mut ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
enum_def: &'a EnumDef,
|
enum_def: &'a EnumDef,
|
||||||
trait_span: Span,
|
trait_span: Span,
|
||||||
) -> Result<&'a rustc_ast::Variant, ()> {
|
) -> Result<&'a rustc_ast::Variant, ErrorGuaranteed> {
|
||||||
let default_variants: SmallVec<[_; 1]> = enum_def
|
let default_variants: SmallVec<[_; 1]> = enum_def
|
||||||
.variants
|
.variants
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -120,9 +123,9 @@ fn extract_default_variant<'a>(
|
||||||
let suggs = possible_defaults
|
let suggs = possible_defaults
|
||||||
.map(|v| errors::NoDefaultVariantSugg { span: v.span, ident: v.ident })
|
.map(|v| errors::NoDefaultVariantSugg { span: v.span, ident: v.ident })
|
||||||
.collect();
|
.collect();
|
||||||
cx.dcx().emit_err(errors::NoDefaultVariant { span: trait_span, suggs });
|
let guar = cx.dcx().emit_err(errors::NoDefaultVariant { span: trait_span, suggs });
|
||||||
|
|
||||||
return Err(());
|
return Err(guar);
|
||||||
}
|
}
|
||||||
[first, rest @ ..] => {
|
[first, rest @ ..] => {
|
||||||
let suggs = default_variants
|
let suggs = default_variants
|
||||||
|
@ -140,28 +143,28 @@ fn extract_default_variant<'a>(
|
||||||
.then_some(errors::MultipleDefaultsSugg { spans, ident: variant.ident })
|
.then_some(errors::MultipleDefaultsSugg { spans, ident: variant.ident })
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
cx.dcx().emit_err(errors::MultipleDefaults {
|
let guar = cx.dcx().emit_err(errors::MultipleDefaults {
|
||||||
span: trait_span,
|
span: trait_span,
|
||||||
first: first.span,
|
first: first.span,
|
||||||
additional: rest.iter().map(|v| v.span).collect(),
|
additional: rest.iter().map(|v| v.span).collect(),
|
||||||
suggs,
|
suggs,
|
||||||
});
|
});
|
||||||
return Err(());
|
return Err(guar);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if !matches!(variant.data, VariantData::Unit(..)) {
|
if !matches!(variant.data, VariantData::Unit(..)) {
|
||||||
cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span });
|
let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span });
|
||||||
return Err(());
|
return Err(guar);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(non_exhaustive_attr) = attr::find_by_name(&variant.attrs, sym::non_exhaustive) {
|
if let Some(non_exhaustive_attr) = attr::find_by_name(&variant.attrs, sym::non_exhaustive) {
|
||||||
cx.dcx().emit_err(errors::NonExhaustiveDefault {
|
let guar = cx.dcx().emit_err(errors::NonExhaustiveDefault {
|
||||||
span: variant.ident.span,
|
span: variant.ident.span,
|
||||||
non_exhaustive: non_exhaustive_attr.span,
|
non_exhaustive: non_exhaustive_attr.span,
|
||||||
});
|
});
|
||||||
|
|
||||||
return Err(());
|
return Err(guar);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(variant)
|
Ok(variant)
|
||||||
|
@ -170,7 +173,7 @@ fn extract_default_variant<'a>(
|
||||||
fn validate_default_attribute(
|
fn validate_default_attribute(
|
||||||
cx: &mut ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
default_variant: &rustc_ast::Variant,
|
default_variant: &rustc_ast::Variant,
|
||||||
) -> Result<(), ()> {
|
) -> Result<(), ErrorGuaranteed> {
|
||||||
let attrs: SmallVec<[_; 1]> =
|
let attrs: SmallVec<[_; 1]> =
|
||||||
attr::filter_by_name(&default_variant.attrs, kw::Default).collect();
|
attr::filter_by_name(&default_variant.attrs, kw::Default).collect();
|
||||||
|
|
||||||
|
@ -183,7 +186,7 @@ fn validate_default_attribute(
|
||||||
let sugg = errors::MultipleDefaultAttrsSugg {
|
let sugg = errors::MultipleDefaultAttrsSugg {
|
||||||
spans: rest.iter().map(|attr| attr.span).collect(),
|
spans: rest.iter().map(|attr| attr.span).collect(),
|
||||||
};
|
};
|
||||||
cx.dcx().emit_err(errors::MultipleDefaultAttrs {
|
let guar = cx.dcx().emit_err(errors::MultipleDefaultAttrs {
|
||||||
span: default_variant.ident.span,
|
span: default_variant.ident.span,
|
||||||
first: first.span,
|
first: first.span,
|
||||||
first_rest: rest[0].span,
|
first_rest: rest[0].span,
|
||||||
|
@ -192,13 +195,13 @@ fn validate_default_attribute(
|
||||||
sugg,
|
sugg,
|
||||||
});
|
});
|
||||||
|
|
||||||
return Err(());
|
return Err(guar);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if !attr.is_word() {
|
if !attr.is_word() {
|
||||||
cx.dcx().emit_err(errors::DefaultHasArg { span: attr.span });
|
let guar = cx.dcx().emit_err(errors::DefaultHasArg { span: attr.span });
|
||||||
|
|
||||||
return Err(());
|
return Err(guar);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,13 @@
|
||||||
// interface.
|
// interface.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
use rustc_ast::token::{self, LitKind};
|
||||||
use rustc_ast::tokenstream::TokenStream;
|
use rustc_ast::tokenstream::TokenStream;
|
||||||
use rustc_ast::{self as ast, AstDeref, GenericArg};
|
use rustc_ast::{AstDeref, ExprKind, GenericArg, Mutability};
|
||||||
use rustc_expand::base::{self, *};
|
use rustc_expand::base::{
|
||||||
|
expr_to_string, get_exprs_from_tts, get_single_str_from_tts, DummyResult, ExtCtxt, MacEager,
|
||||||
|
MacResult,
|
||||||
|
};
|
||||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
@ -27,9 +31,10 @@ pub fn expand_option_env<'cx>(
|
||||||
cx: &'cx mut ExtCtxt<'_>,
|
cx: &'cx mut ExtCtxt<'_>,
|
||||||
sp: Span,
|
sp: Span,
|
||||||
tts: TokenStream,
|
tts: TokenStream,
|
||||||
) -> Box<dyn base::MacResult + 'cx> {
|
) -> Box<dyn MacResult + 'cx> {
|
||||||
let Some(var) = get_single_str_from_tts(cx, sp, tts, "option_env!") else {
|
let var = match get_single_str_from_tts(cx, sp, tts, "option_env!") {
|
||||||
return DummyResult::any(sp);
|
Ok(var) => var,
|
||||||
|
Err(guar) => return DummyResult::any(sp, guar),
|
||||||
};
|
};
|
||||||
|
|
||||||
let sp = cx.with_def_site_ctxt(sp);
|
let sp = cx.with_def_site_ctxt(sp);
|
||||||
|
@ -46,7 +51,7 @@ pub fn expand_option_env<'cx>(
|
||||||
sp,
|
sp,
|
||||||
cx.ty_ident(sp, Ident::new(sym::str, sp)),
|
cx.ty_ident(sp, Ident::new(sym::str, sp)),
|
||||||
Some(lt),
|
Some(lt),
|
||||||
ast::Mutability::Not,
|
Mutability::Not,
|
||||||
))],
|
))],
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -63,26 +68,27 @@ pub fn expand_env<'cx>(
|
||||||
cx: &'cx mut ExtCtxt<'_>,
|
cx: &'cx mut ExtCtxt<'_>,
|
||||||
sp: Span,
|
sp: Span,
|
||||||
tts: TokenStream,
|
tts: TokenStream,
|
||||||
) -> Box<dyn base::MacResult + 'cx> {
|
) -> Box<dyn MacResult + 'cx> {
|
||||||
let mut exprs = match get_exprs_from_tts(cx, tts) {
|
let mut exprs = match get_exprs_from_tts(cx, tts) {
|
||||||
Some(exprs) if exprs.is_empty() || exprs.len() > 2 => {
|
Ok(exprs) if exprs.is_empty() || exprs.len() > 2 => {
|
||||||
cx.dcx().emit_err(errors::EnvTakesArgs { span: sp });
|
let guar = cx.dcx().emit_err(errors::EnvTakesArgs { span: sp });
|
||||||
return DummyResult::any(sp);
|
return DummyResult::any(sp, guar);
|
||||||
}
|
}
|
||||||
None => return DummyResult::any(sp),
|
Err(guar) => return DummyResult::any(sp, guar),
|
||||||
Some(exprs) => exprs.into_iter(),
|
Ok(exprs) => exprs.into_iter(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let var_expr = exprs.next().unwrap();
|
let var_expr = exprs.next().unwrap();
|
||||||
let Some((var, _)) = expr_to_string(cx, var_expr.clone(), "expected string literal") else {
|
let var = match expr_to_string(cx, var_expr.clone(), "expected string literal") {
|
||||||
return DummyResult::any(sp);
|
Ok((var, _)) => var,
|
||||||
|
Err(guar) => return DummyResult::any(sp, guar),
|
||||||
};
|
};
|
||||||
|
|
||||||
let custom_msg = match exprs.next() {
|
let custom_msg = match exprs.next() {
|
||||||
None => None,
|
None => None,
|
||||||
Some(second) => match expr_to_string(cx, second, "expected string literal") {
|
Some(second) => match expr_to_string(cx, second, "expected string literal") {
|
||||||
None => return DummyResult::any(sp),
|
Ok((s, _)) => Some(s),
|
||||||
Some((s, _)) => Some(s),
|
Err(guar) => return DummyResult::any(sp, guar),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -91,32 +97,30 @@ pub fn expand_env<'cx>(
|
||||||
cx.sess.parse_sess.env_depinfo.borrow_mut().insert((var, value));
|
cx.sess.parse_sess.env_depinfo.borrow_mut().insert((var, value));
|
||||||
let e = match value {
|
let e = match value {
|
||||||
None => {
|
None => {
|
||||||
let ast::ExprKind::Lit(ast::token::Lit {
|
let ExprKind::Lit(token::Lit {
|
||||||
kind: ast::token::LitKind::Str | ast::token::LitKind::StrRaw(..),
|
kind: LitKind::Str | LitKind::StrRaw(..), symbol, ..
|
||||||
symbol,
|
|
||||||
..
|
|
||||||
}) = &var_expr.kind
|
}) = &var_expr.kind
|
||||||
else {
|
else {
|
||||||
unreachable!("`expr_to_string` ensures this is a string lit")
|
unreachable!("`expr_to_string` ensures this is a string lit")
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(msg_from_user) = custom_msg {
|
let guar = if let Some(msg_from_user) = custom_msg {
|
||||||
cx.dcx().emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user });
|
cx.dcx().emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user })
|
||||||
} else if is_cargo_env_var(var.as_str()) {
|
} else if is_cargo_env_var(var.as_str()) {
|
||||||
cx.dcx().emit_err(errors::EnvNotDefined::CargoEnvVar {
|
cx.dcx().emit_err(errors::EnvNotDefined::CargoEnvVar {
|
||||||
span,
|
span,
|
||||||
var: *symbol,
|
var: *symbol,
|
||||||
var_expr: var_expr.ast_deref(),
|
var_expr: var_expr.ast_deref(),
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
cx.dcx().emit_err(errors::EnvNotDefined::CustomEnvVar {
|
cx.dcx().emit_err(errors::EnvNotDefined::CustomEnvVar {
|
||||||
span,
|
span,
|
||||||
var: *symbol,
|
var: *symbol,
|
||||||
var_expr: var_expr.ast_deref(),
|
var_expr: var_expr.ast_deref(),
|
||||||
});
|
})
|
||||||
}
|
};
|
||||||
|
|
||||||
return DummyResult::any(sp);
|
return DummyResult::any(sp, guar);
|
||||||
}
|
}
|
||||||
Some(value) => cx.expr_str(span, value),
|
Some(value) => cx.expr_str(span, value),
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,7 +13,7 @@ use rustc_expand::base::{self, *};
|
||||||
use rustc_parse::parser::Recovered;
|
use rustc_parse::parser::Recovered;
|
||||||
use rustc_parse_format as parse;
|
use rustc_parse_format as parse;
|
||||||
use rustc_span::symbol::{Ident, Symbol};
|
use rustc_span::symbol::{Ident, Symbol};
|
||||||
use rustc_span::{BytePos, InnerSpan, Span};
|
use rustc_span::{BytePos, ErrorGuaranteed, InnerSpan, Span};
|
||||||
|
|
||||||
use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY;
|
use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY;
|
||||||
use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, LintId};
|
use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, LintId};
|
||||||
|
@ -160,7 +160,7 @@ fn make_format_args(
|
||||||
ecx: &mut ExtCtxt<'_>,
|
ecx: &mut ExtCtxt<'_>,
|
||||||
input: MacroInput,
|
input: MacroInput,
|
||||||
append_newline: bool,
|
append_newline: bool,
|
||||||
) -> Result<FormatArgs, ()> {
|
) -> Result<FormatArgs, ErrorGuaranteed> {
|
||||||
let msg = "format argument must be a string literal";
|
let msg = "format argument must be a string literal";
|
||||||
let unexpanded_fmt_span = input.fmtstr.span;
|
let unexpanded_fmt_span = input.fmtstr.span;
|
||||||
|
|
||||||
|
@ -173,38 +173,41 @@ fn make_format_args(
|
||||||
}
|
}
|
||||||
Ok(fmt) => fmt,
|
Ok(fmt) => fmt,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if let Some((mut err, suggested)) = err {
|
let guar = match err {
|
||||||
if !suggested {
|
Ok((mut err, suggested)) => {
|
||||||
if let ExprKind::Block(block, None) = &efmt.kind
|
if !suggested {
|
||||||
&& block.stmts.len() == 1
|
if let ExprKind::Block(block, None) = &efmt.kind
|
||||||
&& let StmtKind::Expr(expr) = &block.stmts[0].kind
|
&& block.stmts.len() == 1
|
||||||
&& let ExprKind::Path(None, path) = &expr.kind
|
&& let StmtKind::Expr(expr) = &block.stmts[0].kind
|
||||||
&& path.is_potential_trivial_const_arg()
|
&& let ExprKind::Path(None, path) = &expr.kind
|
||||||
{
|
&& path.is_potential_trivial_const_arg()
|
||||||
err.multipart_suggestion(
|
{
|
||||||
"quote your inlined format argument to use as string literal",
|
err.multipart_suggestion(
|
||||||
vec![
|
"quote your inlined format argument to use as string literal",
|
||||||
(unexpanded_fmt_span.shrink_to_hi(), "\"".to_string()),
|
vec![
|
||||||
(unexpanded_fmt_span.shrink_to_lo(), "\"".to_string()),
|
(unexpanded_fmt_span.shrink_to_hi(), "\"".to_string()),
|
||||||
],
|
(unexpanded_fmt_span.shrink_to_lo(), "\"".to_string()),
|
||||||
Applicability::MaybeIncorrect,
|
],
|
||||||
);
|
Applicability::MaybeIncorrect,
|
||||||
} else {
|
);
|
||||||
let sugg_fmt = match args.explicit_args().len() {
|
} else {
|
||||||
0 => "{}".to_string(),
|
let sugg_fmt = match args.explicit_args().len() {
|
||||||
_ => format!("{}{{}}", "{} ".repeat(args.explicit_args().len())),
|
0 => "{}".to_string(),
|
||||||
};
|
_ => format!("{}{{}}", "{} ".repeat(args.explicit_args().len())),
|
||||||
err.span_suggestion(
|
};
|
||||||
unexpanded_fmt_span.shrink_to_lo(),
|
err.span_suggestion(
|
||||||
"you might be missing a string literal to format with",
|
unexpanded_fmt_span.shrink_to_lo(),
|
||||||
format!("\"{sugg_fmt}\", "),
|
"you might be missing a string literal to format with",
|
||||||
Applicability::MaybeIncorrect,
|
format!("\"{sugg_fmt}\", "),
|
||||||
);
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
err.emit()
|
||||||
}
|
}
|
||||||
err.emit();
|
Err(guar) => guar,
|
||||||
}
|
};
|
||||||
return Err(());
|
return Err(guar);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -293,8 +296,8 @@ fn make_format_args(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ecx.dcx().emit_err(e);
|
let guar = ecx.dcx().emit_err(e);
|
||||||
return Err(());
|
return Err(guar);
|
||||||
}
|
}
|
||||||
|
|
||||||
let to_span = |inner_span: rustc_parse_format::InnerSpan| {
|
let to_span = |inner_span: rustc_parse_format::InnerSpan| {
|
||||||
|
@ -353,9 +356,9 @@ fn make_format_args(
|
||||||
} else {
|
} else {
|
||||||
// For the moment capturing variables from format strings expanded from macros is
|
// For the moment capturing variables from format strings expanded from macros is
|
||||||
// disabled (see RFC #2795)
|
// disabled (see RFC #2795)
|
||||||
ecx.dcx().emit_err(errors::FormatNoArgNamed { span, name });
|
let guar = ecx.dcx().emit_err(errors::FormatNoArgNamed { span, name });
|
||||||
unnamed_arg_after_named_arg = true;
|
unnamed_arg_after_named_arg = true;
|
||||||
DummyResult::raw_expr(span, true)
|
DummyResult::raw_expr(span, Some(guar))
|
||||||
};
|
};
|
||||||
Ok(args.add(FormatArgument { kind: FormatArgumentKind::Captured(ident), expr }))
|
Ok(args.add(FormatArgument { kind: FormatArgumentKind::Captured(ident), expr }))
|
||||||
}
|
}
|
||||||
|
@ -972,16 +975,13 @@ fn expand_format_args_impl<'cx>(
|
||||||
) -> Box<dyn base::MacResult + 'cx> {
|
) -> Box<dyn base::MacResult + 'cx> {
|
||||||
sp = ecx.with_def_site_ctxt(sp);
|
sp = ecx.with_def_site_ctxt(sp);
|
||||||
match parse_args(ecx, sp, tts) {
|
match parse_args(ecx, sp, tts) {
|
||||||
Ok(input) => {
|
Ok(input) => match make_format_args(ecx, input, nl) {
|
||||||
if let Ok(format_args) = make_format_args(ecx, input, nl) {
|
Ok(format_args) => MacEager::expr(ecx.expr(sp, ExprKind::FormatArgs(P(format_args)))),
|
||||||
MacEager::expr(ecx.expr(sp, ExprKind::FormatArgs(P(format_args))))
|
Err(guar) => MacEager::expr(DummyResult::raw_expr(sp, Some(guar))),
|
||||||
} else {
|
},
|
||||||
MacEager::expr(DummyResult::raw_expr(sp, true))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
err.emit();
|
let guar = err.emit();
|
||||||
DummyResult::any(sp)
|
DummyResult::any(sp, guar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#![feature(lint_reasons)]
|
#![feature(lint_reasons)]
|
||||||
#![feature(proc_macro_internals)]
|
#![feature(proc_macro_internals)]
|
||||||
#![feature(proc_macro_quote)]
|
#![feature(proc_macro_quote)]
|
||||||
|
#![feature(try_blocks)]
|
||||||
|
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,10 @@ use rustc_ast::ptr::P;
|
||||||
use rustc_ast::token;
|
use rustc_ast::token;
|
||||||
use rustc_ast::tokenstream::TokenStream;
|
use rustc_ast::tokenstream::TokenStream;
|
||||||
use rustc_ast_pretty::pprust;
|
use rustc_ast_pretty::pprust;
|
||||||
use rustc_expand::base::{self, *};
|
use rustc_expand::base::{
|
||||||
|
check_zero_tts, get_single_str_from_tts, parse_expr, resolve_path, DummyResult, ExtCtxt,
|
||||||
|
MacEager, MacResult,
|
||||||
|
};
|
||||||
use rustc_expand::module::DirOwnership;
|
use rustc_expand::module::DirOwnership;
|
||||||
use rustc_parse::new_parser_from_file;
|
use rustc_parse::new_parser_from_file;
|
||||||
use rustc_parse::parser::{ForceCollect, Parser};
|
use rustc_parse::parser::{ForceCollect, Parser};
|
||||||
|
@ -23,14 +26,14 @@ pub fn expand_line(
|
||||||
cx: &mut ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
sp: Span,
|
sp: Span,
|
||||||
tts: TokenStream,
|
tts: TokenStream,
|
||||||
) -> Box<dyn base::MacResult + 'static> {
|
) -> Box<dyn MacResult + 'static> {
|
||||||
let sp = cx.with_def_site_ctxt(sp);
|
let sp = cx.with_def_site_ctxt(sp);
|
||||||
base::check_zero_tts(cx, sp, tts, "line!");
|
check_zero_tts(cx, sp, tts, "line!");
|
||||||
|
|
||||||
let topmost = cx.expansion_cause().unwrap_or(sp);
|
let topmost = cx.expansion_cause().unwrap_or(sp);
|
||||||
let loc = cx.source_map().lookup_char_pos(topmost.lo());
|
let loc = cx.source_map().lookup_char_pos(topmost.lo());
|
||||||
|
|
||||||
base::MacEager::expr(cx.expr_u32(topmost, loc.line as u32))
|
MacEager::expr(cx.expr_u32(topmost, loc.line as u32))
|
||||||
}
|
}
|
||||||
|
|
||||||
/* column!(): expands to the current column number */
|
/* column!(): expands to the current column number */
|
||||||
|
@ -38,14 +41,14 @@ pub fn expand_column(
|
||||||
cx: &mut ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
sp: Span,
|
sp: Span,
|
||||||
tts: TokenStream,
|
tts: TokenStream,
|
||||||
) -> Box<dyn base::MacResult + 'static> {
|
) -> Box<dyn MacResult + 'static> {
|
||||||
let sp = cx.with_def_site_ctxt(sp);
|
let sp = cx.with_def_site_ctxt(sp);
|
||||||
base::check_zero_tts(cx, sp, tts, "column!");
|
check_zero_tts(cx, sp, tts, "column!");
|
||||||
|
|
||||||
let topmost = cx.expansion_cause().unwrap_or(sp);
|
let topmost = cx.expansion_cause().unwrap_or(sp);
|
||||||
let loc = cx.source_map().lookup_char_pos(topmost.lo());
|
let loc = cx.source_map().lookup_char_pos(topmost.lo());
|
||||||
|
|
||||||
base::MacEager::expr(cx.expr_u32(topmost, loc.col.to_usize() as u32 + 1))
|
MacEager::expr(cx.expr_u32(topmost, loc.col.to_usize() as u32 + 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// file!(): expands to the current filename */
|
/// file!(): expands to the current filename */
|
||||||
|
@ -55,15 +58,15 @@ pub fn expand_file(
|
||||||
cx: &mut ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
sp: Span,
|
sp: Span,
|
||||||
tts: TokenStream,
|
tts: TokenStream,
|
||||||
) -> Box<dyn base::MacResult + 'static> {
|
) -> Box<dyn MacResult + 'static> {
|
||||||
let sp = cx.with_def_site_ctxt(sp);
|
let sp = cx.with_def_site_ctxt(sp);
|
||||||
base::check_zero_tts(cx, sp, tts, "file!");
|
check_zero_tts(cx, sp, tts, "file!");
|
||||||
|
|
||||||
let topmost = cx.expansion_cause().unwrap_or(sp);
|
let topmost = cx.expansion_cause().unwrap_or(sp);
|
||||||
let loc = cx.source_map().lookup_char_pos(topmost.lo());
|
let loc = cx.source_map().lookup_char_pos(topmost.lo());
|
||||||
|
|
||||||
use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt};
|
use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt};
|
||||||
base::MacEager::expr(cx.expr_str(
|
MacEager::expr(cx.expr_str(
|
||||||
topmost,
|
topmost,
|
||||||
Symbol::intern(
|
Symbol::intern(
|
||||||
&loc.file.name.for_scope(cx.sess, RemapPathScopeComponents::MACRO).to_string_lossy(),
|
&loc.file.name.for_scope(cx.sess, RemapPathScopeComponents::MACRO).to_string_lossy(),
|
||||||
|
@ -75,23 +78,23 @@ pub fn expand_stringify(
|
||||||
cx: &mut ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
sp: Span,
|
sp: Span,
|
||||||
tts: TokenStream,
|
tts: TokenStream,
|
||||||
) -> Box<dyn base::MacResult + 'static> {
|
) -> Box<dyn MacResult + 'static> {
|
||||||
let sp = cx.with_def_site_ctxt(sp);
|
let sp = cx.with_def_site_ctxt(sp);
|
||||||
let s = pprust::tts_to_string(&tts);
|
let s = pprust::tts_to_string(&tts);
|
||||||
base::MacEager::expr(cx.expr_str(sp, Symbol::intern(&s)))
|
MacEager::expr(cx.expr_str(sp, Symbol::intern(&s)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expand_mod(
|
pub fn expand_mod(
|
||||||
cx: &mut ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
sp: Span,
|
sp: Span,
|
||||||
tts: TokenStream,
|
tts: TokenStream,
|
||||||
) -> Box<dyn base::MacResult + 'static> {
|
) -> Box<dyn MacResult + 'static> {
|
||||||
let sp = cx.with_def_site_ctxt(sp);
|
let sp = cx.with_def_site_ctxt(sp);
|
||||||
base::check_zero_tts(cx, sp, tts, "module_path!");
|
check_zero_tts(cx, sp, tts, "module_path!");
|
||||||
let mod_path = &cx.current_expansion.module.mod_path;
|
let mod_path = &cx.current_expansion.module.mod_path;
|
||||||
let string = mod_path.iter().map(|x| x.to_string()).collect::<Vec<String>>().join("::");
|
let string = mod_path.iter().map(|x| x.to_string()).collect::<Vec<String>>().join("::");
|
||||||
|
|
||||||
base::MacEager::expr(cx.expr_str(sp, Symbol::intern(&string)))
|
MacEager::expr(cx.expr_str(sp, Symbol::intern(&string)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// include! : parse the given file as an expr
|
/// include! : parse the given file as an expr
|
||||||
|
@ -101,17 +104,18 @@ pub fn expand_include<'cx>(
|
||||||
cx: &'cx mut ExtCtxt<'_>,
|
cx: &'cx mut ExtCtxt<'_>,
|
||||||
sp: Span,
|
sp: Span,
|
||||||
tts: TokenStream,
|
tts: TokenStream,
|
||||||
) -> Box<dyn base::MacResult + 'cx> {
|
) -> Box<dyn MacResult + 'cx> {
|
||||||
let sp = cx.with_def_site_ctxt(sp);
|
let sp = cx.with_def_site_ctxt(sp);
|
||||||
let Some(file) = get_single_str_from_tts(cx, sp, tts, "include!") else {
|
let file = match get_single_str_from_tts(cx, sp, tts, "include!") {
|
||||||
return DummyResult::any(sp);
|
Ok(file) => file,
|
||||||
|
Err(guar) => return DummyResult::any(sp, guar),
|
||||||
};
|
};
|
||||||
// The file will be added to the code map by the parser
|
// The file will be added to the code map by the parser
|
||||||
let file = match resolve_path(&cx.sess, file.as_str(), sp) {
|
let file = match resolve_path(&cx.sess, file.as_str(), sp) {
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
err.emit();
|
let guar = err.emit();
|
||||||
return DummyResult::any(sp);
|
return DummyResult::any(sp, guar);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let p = new_parser_from_file(cx.parse_sess(), &file, Some(sp));
|
let p = new_parser_from_file(cx.parse_sess(), &file, Some(sp));
|
||||||
|
@ -128,9 +132,9 @@ pub fn expand_include<'cx>(
|
||||||
p: Parser<'a>,
|
p: Parser<'a>,
|
||||||
node_id: ast::NodeId,
|
node_id: ast::NodeId,
|
||||||
}
|
}
|
||||||
impl<'a> base::MacResult for ExpandResult<'a> {
|
impl<'a> MacResult for ExpandResult<'a> {
|
||||||
fn make_expr(mut self: Box<ExpandResult<'a>>) -> Option<P<ast::Expr>> {
|
fn make_expr(mut self: Box<ExpandResult<'a>>) -> Option<P<ast::Expr>> {
|
||||||
let r = base::parse_expr(&mut self.p)?;
|
let expr = parse_expr(&mut self.p).ok()?;
|
||||||
if self.p.token != token::Eof {
|
if self.p.token != token::Eof {
|
||||||
self.p.sess.buffer_lint(
|
self.p.sess.buffer_lint(
|
||||||
INCOMPLETE_INCLUDE,
|
INCOMPLETE_INCLUDE,
|
||||||
|
@ -139,7 +143,7 @@ pub fn expand_include<'cx>(
|
||||||
"include macro expected single expression in source",
|
"include macro expected single expression in source",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Some(r)
|
Some(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_items(mut self: Box<ExpandResult<'a>>) -> Option<SmallVec<[P<ast::Item>; 1]>> {
|
fn make_items(mut self: Box<ExpandResult<'a>>) -> Option<SmallVec<[P<ast::Item>; 1]>> {
|
||||||
|
@ -174,32 +178,33 @@ pub fn expand_include_str(
|
||||||
cx: &mut ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
sp: Span,
|
sp: Span,
|
||||||
tts: TokenStream,
|
tts: TokenStream,
|
||||||
) -> Box<dyn base::MacResult + 'static> {
|
) -> Box<dyn MacResult + 'static> {
|
||||||
let sp = cx.with_def_site_ctxt(sp);
|
let sp = cx.with_def_site_ctxt(sp);
|
||||||
let Some(file) = get_single_str_from_tts(cx, sp, tts, "include_str!") else {
|
let file = match get_single_str_from_tts(cx, sp, tts, "include_str!") {
|
||||||
return DummyResult::any(sp);
|
Ok(file) => file,
|
||||||
|
Err(guar) => return DummyResult::any(sp, guar),
|
||||||
};
|
};
|
||||||
let file = match resolve_path(&cx.sess, file.as_str(), sp) {
|
let file = match resolve_path(&cx.sess, file.as_str(), sp) {
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
err.emit();
|
let guar = err.emit();
|
||||||
return DummyResult::any(sp);
|
return DummyResult::any(sp, guar);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match cx.source_map().load_binary_file(&file) {
|
match cx.source_map().load_binary_file(&file) {
|
||||||
Ok(bytes) => match std::str::from_utf8(&bytes) {
|
Ok(bytes) => match std::str::from_utf8(&bytes) {
|
||||||
Ok(src) => {
|
Ok(src) => {
|
||||||
let interned_src = Symbol::intern(src);
|
let interned_src = Symbol::intern(src);
|
||||||
base::MacEager::expr(cx.expr_str(sp, interned_src))
|
MacEager::expr(cx.expr_str(sp, interned_src))
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
cx.dcx().span_err(sp, format!("{} wasn't a utf-8 file", file.display()));
|
let guar = cx.dcx().span_err(sp, format!("{} wasn't a utf-8 file", file.display()));
|
||||||
DummyResult::any(sp)
|
DummyResult::any(sp, guar)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
cx.dcx().span_err(sp, format!("couldn't read {}: {}", file.display(), e));
|
let guar = cx.dcx().span_err(sp, format!("couldn't read {}: {}", file.display(), e));
|
||||||
DummyResult::any(sp)
|
DummyResult::any(sp, guar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,26 +213,27 @@ pub fn expand_include_bytes(
|
||||||
cx: &mut ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
sp: Span,
|
sp: Span,
|
||||||
tts: TokenStream,
|
tts: TokenStream,
|
||||||
) -> Box<dyn base::MacResult + 'static> {
|
) -> Box<dyn MacResult + 'static> {
|
||||||
let sp = cx.with_def_site_ctxt(sp);
|
let sp = cx.with_def_site_ctxt(sp);
|
||||||
let Some(file) = get_single_str_from_tts(cx, sp, tts, "include_bytes!") else {
|
let file = match get_single_str_from_tts(cx, sp, tts, "include_bytes!") {
|
||||||
return DummyResult::any(sp);
|
Ok(file) => file,
|
||||||
|
Err(guar) => return DummyResult::any(sp, guar),
|
||||||
};
|
};
|
||||||
let file = match resolve_path(&cx.sess, file.as_str(), sp) {
|
let file = match resolve_path(&cx.sess, file.as_str(), sp) {
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
err.emit();
|
let guar = err.emit();
|
||||||
return DummyResult::any(sp);
|
return DummyResult::any(sp, guar);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match cx.source_map().load_binary_file(&file) {
|
match cx.source_map().load_binary_file(&file) {
|
||||||
Ok(bytes) => {
|
Ok(bytes) => {
|
||||||
let expr = cx.expr(sp, ast::ExprKind::IncludedBytes(bytes));
|
let expr = cx.expr(sp, ast::ExprKind::IncludedBytes(bytes));
|
||||||
base::MacEager::expr(expr)
|
MacEager::expr(expr)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
cx.dcx().span_err(sp, format!("couldn't read {}: {}", file.display(), e));
|
let guar = cx.dcx().span_err(sp, format!("couldn't read {}: {}", file.display(), e));
|
||||||
DummyResult::any(sp)
|
DummyResult::any(sp, guar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,8 @@ pub fn expand_type_ascribe(
|
||||||
let (expr, ty) = match parse_ascribe(cx, tts) {
|
let (expr, ty) = match parse_ascribe(cx, tts) {
|
||||||
Ok(parsed) => parsed,
|
Ok(parsed) => parsed,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
err.emit();
|
let guar = err.emit();
|
||||||
return DummyResult::any(span);
|
return DummyResult::any(span, guar);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -532,7 +532,7 @@ impl MacResult for MacEager {
|
||||||
/// after hitting errors.
|
/// after hitting errors.
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct DummyResult {
|
pub struct DummyResult {
|
||||||
is_error: bool,
|
guar: Option<ErrorGuaranteed>,
|
||||||
span: Span,
|
span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -541,20 +541,24 @@ impl DummyResult {
|
||||||
///
|
///
|
||||||
/// Use this as a return value after hitting any errors and
|
/// Use this as a return value after hitting any errors and
|
||||||
/// calling `span_err`.
|
/// calling `span_err`.
|
||||||
pub fn any(span: Span) -> Box<dyn MacResult + 'static> {
|
pub fn any(span: Span, guar: ErrorGuaranteed) -> Box<dyn MacResult + 'static> {
|
||||||
Box::new(DummyResult { is_error: true, span })
|
Box::new(DummyResult { guar: Some(guar), span })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as `any`, but must be a valid fragment, not error.
|
/// Same as `any`, but must be a valid fragment, not error.
|
||||||
pub fn any_valid(span: Span) -> Box<dyn MacResult + 'static> {
|
pub fn any_valid(span: Span) -> Box<dyn MacResult + 'static> {
|
||||||
Box::new(DummyResult { is_error: false, span })
|
Box::new(DummyResult { guar: None, span })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A plain dummy expression.
|
/// A plain dummy expression.
|
||||||
pub fn raw_expr(sp: Span, is_error: bool) -> P<ast::Expr> {
|
pub fn raw_expr(sp: Span, guar: Option<ErrorGuaranteed>) -> P<ast::Expr> {
|
||||||
P(ast::Expr {
|
P(ast::Expr {
|
||||||
id: ast::DUMMY_NODE_ID,
|
id: ast::DUMMY_NODE_ID,
|
||||||
kind: if is_error { ast::ExprKind::Err } else { ast::ExprKind::Tup(ThinVec::new()) },
|
kind: if let Some(guar) = guar {
|
||||||
|
ast::ExprKind::Err(guar)
|
||||||
|
} else {
|
||||||
|
ast::ExprKind::Tup(ThinVec::new())
|
||||||
|
},
|
||||||
span: sp,
|
span: sp,
|
||||||
attrs: ast::AttrVec::new(),
|
attrs: ast::AttrVec::new(),
|
||||||
tokens: None,
|
tokens: None,
|
||||||
|
@ -582,7 +586,7 @@ impl DummyResult {
|
||||||
|
|
||||||
impl MacResult for DummyResult {
|
impl MacResult for DummyResult {
|
||||||
fn make_expr(self: Box<DummyResult>) -> Option<P<ast::Expr>> {
|
fn make_expr(self: Box<DummyResult>) -> Option<P<ast::Expr>> {
|
||||||
Some(DummyResult::raw_expr(self.span, self.is_error))
|
Some(DummyResult::raw_expr(self.span, self.guar))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_pat(self: Box<DummyResult>) -> Option<P<ast::Pat>> {
|
fn make_pat(self: Box<DummyResult>) -> Option<P<ast::Pat>> {
|
||||||
|
@ -608,7 +612,7 @@ impl MacResult for DummyResult {
|
||||||
fn make_stmts(self: Box<DummyResult>) -> Option<SmallVec<[ast::Stmt; 1]>> {
|
fn make_stmts(self: Box<DummyResult>) -> Option<SmallVec<[ast::Stmt; 1]>> {
|
||||||
Some(smallvec![ast::Stmt {
|
Some(smallvec![ast::Stmt {
|
||||||
id: ast::DUMMY_NODE_ID,
|
id: ast::DUMMY_NODE_ID,
|
||||||
kind: ast::StmtKind::Expr(DummyResult::raw_expr(self.span, self.is_error)),
|
kind: ast::StmtKind::Expr(DummyResult::raw_expr(self.span, self.guar)),
|
||||||
span: self.span,
|
span: self.span,
|
||||||
}])
|
}])
|
||||||
}
|
}
|
||||||
|
@ -884,17 +888,19 @@ impl SyntaxExtension {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A dummy bang macro `foo!()`.
|
||||||
pub fn dummy_bang(edition: Edition) -> SyntaxExtension {
|
pub fn dummy_bang(edition: Edition) -> SyntaxExtension {
|
||||||
fn expander<'cx>(
|
fn expander<'cx>(
|
||||||
_: &'cx mut ExtCtxt<'_>,
|
cx: &'cx mut ExtCtxt<'_>,
|
||||||
span: Span,
|
span: Span,
|
||||||
_: TokenStream,
|
_: TokenStream,
|
||||||
) -> Box<dyn MacResult + 'cx> {
|
) -> Box<dyn MacResult + 'cx> {
|
||||||
DummyResult::any(span)
|
DummyResult::any(span, cx.dcx().span_delayed_bug(span, "expanded a dummy bang macro"))
|
||||||
}
|
}
|
||||||
SyntaxExtension::default(SyntaxExtensionKind::LegacyBang(Box::new(expander)), edition)
|
SyntaxExtension::default(SyntaxExtensionKind::LegacyBang(Box::new(expander)), edition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A dummy derive macro `#[derive(Foo)]`.
|
||||||
pub fn dummy_derive(edition: Edition) -> SyntaxExtension {
|
pub fn dummy_derive(edition: Edition) -> SyntaxExtension {
|
||||||
fn expander(
|
fn expander(
|
||||||
_: &mut ExtCtxt<'_>,
|
_: &mut ExtCtxt<'_>,
|
||||||
|
@ -1066,7 +1072,7 @@ pub struct ExtCtxt<'a> {
|
||||||
pub sess: &'a Session,
|
pub sess: &'a Session,
|
||||||
pub ecfg: expand::ExpansionConfig<'a>,
|
pub ecfg: expand::ExpansionConfig<'a>,
|
||||||
pub num_standard_library_imports: usize,
|
pub num_standard_library_imports: usize,
|
||||||
pub reduced_recursion_limit: Option<Limit>,
|
pub reduced_recursion_limit: Option<(Limit, ErrorGuaranteed)>,
|
||||||
pub root_path: PathBuf,
|
pub root_path: PathBuf,
|
||||||
pub resolver: &'a mut dyn ResolverExpand,
|
pub resolver: &'a mut dyn ResolverExpand,
|
||||||
pub current_expansion: ExpansionData,
|
pub current_expansion: ExpansionData,
|
||||||
|
@ -1244,7 +1250,7 @@ pub fn resolve_path(
|
||||||
/// Extracts a string literal from the macro expanded version of `expr`,
|
/// Extracts a string literal from the macro expanded version of `expr`,
|
||||||
/// returning a diagnostic error of `err_msg` if `expr` is not a string literal.
|
/// returning a diagnostic error of `err_msg` if `expr` is not a string literal.
|
||||||
/// The returned bool indicates whether an applicable suggestion has already been
|
/// The returned bool indicates whether an applicable suggestion has already been
|
||||||
/// added to the diagnostic to avoid emitting multiple suggestions. `Err(None)`
|
/// added to the diagnostic to avoid emitting multiple suggestions. `Err(Err(ErrorGuaranteed))`
|
||||||
/// indicates that an ast error was encountered.
|
/// indicates that an ast error was encountered.
|
||||||
// FIXME(Nilstrieb) Make this function setup translatable
|
// FIXME(Nilstrieb) Make this function setup translatable
|
||||||
#[allow(rustc::untranslatable_diagnostic)]
|
#[allow(rustc::untranslatable_diagnostic)]
|
||||||
|
@ -1252,7 +1258,10 @@ pub fn expr_to_spanned_string<'a>(
|
||||||
cx: &'a mut ExtCtxt<'_>,
|
cx: &'a mut ExtCtxt<'_>,
|
||||||
expr: P<ast::Expr>,
|
expr: P<ast::Expr>,
|
||||||
err_msg: &'static str,
|
err_msg: &'static str,
|
||||||
) -> Result<(Symbol, ast::StrStyle, Span), Option<(DiagnosticBuilder<'a>, bool)>> {
|
) -> Result<
|
||||||
|
(Symbol, ast::StrStyle, Span),
|
||||||
|
Result<(DiagnosticBuilder<'a>, bool /* has_suggestions */), ErrorGuaranteed>,
|
||||||
|
> {
|
||||||
// Perform eager expansion on the expression.
|
// Perform eager expansion on the expression.
|
||||||
// We want to be able to handle e.g., `concat!("foo", "bar")`.
|
// We want to be able to handle e.g., `concat!("foo", "bar")`.
|
||||||
let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr();
|
let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr();
|
||||||
|
@ -1269,35 +1278,33 @@ pub fn expr_to_spanned_string<'a>(
|
||||||
"",
|
"",
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
Some((err, true))
|
Ok((err, true))
|
||||||
}
|
}
|
||||||
Ok(ast::LitKind::Err(_)) => None,
|
Ok(ast::LitKind::Err(guar)) => Err(guar),
|
||||||
Err(err) => {
|
Err(err) => Err(report_lit_error(&cx.sess.parse_sess, err, token_lit, expr.span)),
|
||||||
report_lit_error(&cx.sess.parse_sess, err, token_lit, expr.span);
|
_ => Ok((cx.dcx().struct_span_err(expr.span, err_msg), false)),
|
||||||
None
|
|
||||||
}
|
|
||||||
_ => Some((cx.dcx().struct_span_err(expr.span, err_msg), false)),
|
|
||||||
},
|
},
|
||||||
ast::ExprKind::Err => None,
|
ast::ExprKind::Err(guar) => Err(guar),
|
||||||
_ => Some((cx.dcx().struct_span_err(expr.span, err_msg), false)),
|
ast::ExprKind::Dummy => {
|
||||||
|
cx.dcx().span_bug(expr.span, "tried to get a string literal from `ExprKind::Dummy`")
|
||||||
|
}
|
||||||
|
_ => Ok((cx.dcx().struct_span_err(expr.span, err_msg), false)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extracts a string literal from the macro expanded version of `expr`,
|
/// Extracts a string literal from the macro expanded version of `expr`,
|
||||||
/// emitting `err_msg` if `expr` is not a string literal. This does not stop
|
/// emitting `err_msg` if `expr` is not a string literal. This does not stop
|
||||||
/// compilation on error, merely emits a non-fatal error and returns `None`.
|
/// compilation on error, merely emits a non-fatal error and returns `Err`.
|
||||||
pub fn expr_to_string(
|
pub fn expr_to_string(
|
||||||
cx: &mut ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
expr: P<ast::Expr>,
|
expr: P<ast::Expr>,
|
||||||
err_msg: &'static str,
|
err_msg: &'static str,
|
||||||
) -> Option<(Symbol, ast::StrStyle)> {
|
) -> Result<(Symbol, ast::StrStyle), ErrorGuaranteed> {
|
||||||
expr_to_spanned_string(cx, expr, err_msg)
|
expr_to_spanned_string(cx, expr, err_msg)
|
||||||
.map_err(|err| {
|
.map_err(|err| match err {
|
||||||
err.map(|(err, _)| {
|
Ok((err, _)) => err.emit(),
|
||||||
err.emit();
|
Err(guar) => guar,
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.ok()
|
|
||||||
.map(|(symbol, style, _)| (symbol, style))
|
.map(|(symbol, style, _)| (symbol, style))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1311,32 +1318,30 @@ pub fn check_zero_tts(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream, name: &str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an expression. On error, emit it, advancing to `Eof`, and return `None`.
|
/// Parse an expression. On error, emit it, advancing to `Eof`, and return `Err`.
|
||||||
pub fn parse_expr(p: &mut parser::Parser<'_>) -> Option<P<ast::Expr>> {
|
pub fn parse_expr(p: &mut parser::Parser<'_>) -> Result<P<ast::Expr>, ErrorGuaranteed> {
|
||||||
match p.parse_expr() {
|
let guar = match p.parse_expr() {
|
||||||
Ok(e) => return Some(e),
|
Ok(expr) => return Ok(expr),
|
||||||
Err(err) => {
|
Err(err) => err.emit(),
|
||||||
err.emit();
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
while p.token != token::Eof {
|
while p.token != token::Eof {
|
||||||
p.bump();
|
p.bump();
|
||||||
}
|
}
|
||||||
None
|
Err(guar)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interpreting `tts` as a comma-separated sequence of expressions,
|
/// Interpreting `tts` as a comma-separated sequence of expressions,
|
||||||
/// expect exactly one string literal, or emit an error and return `None`.
|
/// expect exactly one string literal, or emit an error and return `Err`.
|
||||||
pub fn get_single_str_from_tts(
|
pub fn get_single_str_from_tts(
|
||||||
cx: &mut ExtCtxt<'_>,
|
cx: &mut ExtCtxt<'_>,
|
||||||
span: Span,
|
span: Span,
|
||||||
tts: TokenStream,
|
tts: TokenStream,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> Option<Symbol> {
|
) -> Result<Symbol, ErrorGuaranteed> {
|
||||||
let mut p = cx.new_parser_from_tts(tts);
|
let mut p = cx.new_parser_from_tts(tts);
|
||||||
if p.token == token::Eof {
|
if p.token == token::Eof {
|
||||||
cx.dcx().emit_err(errors::OnlyOneArgument { span, name });
|
let guar = cx.dcx().emit_err(errors::OnlyOneArgument { span, name });
|
||||||
return None;
|
return Err(guar);
|
||||||
}
|
}
|
||||||
let ret = parse_expr(&mut p)?;
|
let ret = parse_expr(&mut p)?;
|
||||||
let _ = p.eat(&token::Comma);
|
let _ = p.eat(&token::Comma);
|
||||||
|
@ -1348,8 +1353,11 @@ pub fn get_single_str_from_tts(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extracts comma-separated expressions from `tts`.
|
/// Extracts comma-separated expressions from `tts`.
|
||||||
/// On error, emit it, and return `None`.
|
/// On error, emit it, and return `Err`.
|
||||||
pub fn get_exprs_from_tts(cx: &mut ExtCtxt<'_>, tts: TokenStream) -> Option<Vec<P<ast::Expr>>> {
|
pub fn get_exprs_from_tts(
|
||||||
|
cx: &mut ExtCtxt<'_>,
|
||||||
|
tts: TokenStream,
|
||||||
|
) -> Result<Vec<P<ast::Expr>>, ErrorGuaranteed> {
|
||||||
let mut p = cx.new_parser_from_tts(tts);
|
let mut p = cx.new_parser_from_tts(tts);
|
||||||
let mut es = Vec::new();
|
let mut es = Vec::new();
|
||||||
while p.token != token::Eof {
|
while p.token != token::Eof {
|
||||||
|
@ -1364,11 +1372,11 @@ pub fn get_exprs_from_tts(cx: &mut ExtCtxt<'_>, tts: TokenStream) -> Option<Vec<
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if p.token != token::Eof {
|
if p.token != token::Eof {
|
||||||
cx.dcx().emit_err(errors::ExpectedCommaInList { span: p.token.span });
|
let guar = cx.dcx().emit_err(errors::ExpectedCommaInList { span: p.token.span });
|
||||||
return None;
|
return Err(guar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(es)
|
Ok(es)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_macro_name_and_helper_attrs(
|
pub fn parse_macro_name_and_helper_attrs(
|
||||||
|
|
|
@ -34,7 +34,7 @@ use rustc_session::lint::BuiltinLintDiagnostics;
|
||||||
use rustc_session::parse::feature_err;
|
use rustc_session::parse::feature_err;
|
||||||
use rustc_session::{Limit, Session};
|
use rustc_session::{Limit, Session};
|
||||||
use rustc_span::symbol::{sym, Ident};
|
use rustc_span::symbol::{sym, Ident};
|
||||||
use rustc_span::{FileName, LocalExpnId, Span};
|
use rustc_span::{ErrorGuaranteed, FileName, LocalExpnId, Span};
|
||||||
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
@ -232,8 +232,8 @@ pub enum SupportsMacroExpansion {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AstFragmentKind {
|
impl AstFragmentKind {
|
||||||
pub(crate) fn dummy(self, span: Span) -> AstFragment {
|
pub(crate) fn dummy(self, span: Span, guar: ErrorGuaranteed) -> AstFragment {
|
||||||
self.make_from(DummyResult::any(span)).expect("couldn't create a dummy AST fragment")
|
self.make_from(DummyResult::any(span, guar)).expect("couldn't create a dummy AST fragment")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn supports_macro_expansion(self) -> SupportsMacroExpansion {
|
pub fn supports_macro_expansion(self) -> SupportsMacroExpansion {
|
||||||
|
@ -604,14 +604,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||||
(fragment, invocations)
|
(fragment, invocations)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error_recursion_limit_reached(&mut self) {
|
fn error_recursion_limit_reached(&mut self) -> ErrorGuaranteed {
|
||||||
let expn_data = self.cx.current_expansion.id.expn_data();
|
let expn_data = self.cx.current_expansion.id.expn_data();
|
||||||
let suggested_limit = match self.cx.ecfg.recursion_limit {
|
let suggested_limit = match self.cx.ecfg.recursion_limit {
|
||||||
Limit(0) => Limit(2),
|
Limit(0) => Limit(2),
|
||||||
limit => limit * 2,
|
limit => limit * 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.cx.dcx().emit_err(RecursionLimitReached {
|
let guar = self.cx.dcx().emit_err(RecursionLimitReached {
|
||||||
span: expn_data.call_site,
|
span: expn_data.call_site,
|
||||||
descr: expn_data.kind.descr(),
|
descr: expn_data.kind.descr(),
|
||||||
suggested_limit,
|
suggested_limit,
|
||||||
|
@ -619,14 +619,21 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||||
});
|
});
|
||||||
|
|
||||||
self.cx.trace_macros_diag();
|
self.cx.trace_macros_diag();
|
||||||
|
guar
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A macro's expansion does not fit in this fragment kind.
|
/// A macro's expansion does not fit in this fragment kind.
|
||||||
/// For example, a non-type macro in a type position.
|
/// For example, a non-type macro in a type position.
|
||||||
fn error_wrong_fragment_kind(&mut self, kind: AstFragmentKind, mac: &ast::MacCall, span: Span) {
|
fn error_wrong_fragment_kind(
|
||||||
self.cx.dcx().emit_err(WrongFragmentKind { span, kind: kind.name(), name: &mac.path });
|
&mut self,
|
||||||
|
kind: AstFragmentKind,
|
||||||
|
mac: &ast::MacCall,
|
||||||
|
span: Span,
|
||||||
|
) -> ErrorGuaranteed {
|
||||||
|
let guar =
|
||||||
|
self.cx.dcx().emit_err(WrongFragmentKind { span, kind: kind.name(), name: &mac.path });
|
||||||
self.cx.trace_macros_diag();
|
self.cx.trace_macros_diag();
|
||||||
|
guar
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expand_invoc(
|
fn expand_invoc(
|
||||||
|
@ -634,36 +641,41 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||||
invoc: Invocation,
|
invoc: Invocation,
|
||||||
ext: &SyntaxExtensionKind,
|
ext: &SyntaxExtensionKind,
|
||||||
) -> ExpandResult<AstFragment, Invocation> {
|
) -> ExpandResult<AstFragment, Invocation> {
|
||||||
let recursion_limit =
|
let recursion_limit = match self.cx.reduced_recursion_limit {
|
||||||
self.cx.reduced_recursion_limit.unwrap_or(self.cx.ecfg.recursion_limit);
|
Some((limit, _)) => limit,
|
||||||
|
None => self.cx.ecfg.recursion_limit,
|
||||||
|
};
|
||||||
|
|
||||||
if !recursion_limit.value_within_limit(self.cx.current_expansion.depth) {
|
if !recursion_limit.value_within_limit(self.cx.current_expansion.depth) {
|
||||||
if self.cx.reduced_recursion_limit.is_none() {
|
let guar = match self.cx.reduced_recursion_limit {
|
||||||
self.error_recursion_limit_reached();
|
Some((_, guar)) => guar,
|
||||||
}
|
None => self.error_recursion_limit_reached(),
|
||||||
|
};
|
||||||
|
|
||||||
// Reduce the recursion limit by half each time it triggers.
|
// Reduce the recursion limit by half each time it triggers.
|
||||||
self.cx.reduced_recursion_limit = Some(recursion_limit / 2);
|
self.cx.reduced_recursion_limit = Some((recursion_limit / 2, guar));
|
||||||
|
|
||||||
return ExpandResult::Ready(invoc.fragment_kind.dummy(invoc.span()));
|
return ExpandResult::Ready(invoc.fragment_kind.dummy(invoc.span(), guar));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (fragment_kind, span) = (invoc.fragment_kind, invoc.span());
|
let (fragment_kind, span) = (invoc.fragment_kind, invoc.span());
|
||||||
ExpandResult::Ready(match invoc.kind {
|
ExpandResult::Ready(match invoc.kind {
|
||||||
InvocationKind::Bang { mac, .. } => match ext {
|
InvocationKind::Bang { mac, .. } => match ext {
|
||||||
SyntaxExtensionKind::Bang(expander) => {
|
SyntaxExtensionKind::Bang(expander) => {
|
||||||
let Ok(tok_result) = expander.expand(self.cx, span, mac.args.tokens.clone())
|
match expander.expand(self.cx, span, mac.args.tokens.clone()) {
|
||||||
else {
|
Ok(tok_result) => {
|
||||||
return ExpandResult::Ready(fragment_kind.dummy(span));
|
self.parse_ast_fragment(tok_result, fragment_kind, &mac.path, span)
|
||||||
};
|
}
|
||||||
self.parse_ast_fragment(tok_result, fragment_kind, &mac.path, span)
|
Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SyntaxExtensionKind::LegacyBang(expander) => {
|
SyntaxExtensionKind::LegacyBang(expander) => {
|
||||||
let tok_result = expander.expand(self.cx, span, mac.args.tokens.clone());
|
let tok_result = expander.expand(self.cx, span, mac.args.tokens.clone());
|
||||||
let result = if let Some(result) = fragment_kind.make_from(tok_result) {
|
let result = if let Some(result) = fragment_kind.make_from(tok_result) {
|
||||||
result
|
result
|
||||||
} else {
|
} else {
|
||||||
self.error_wrong_fragment_kind(fragment_kind, &mac, span);
|
let guar = self.error_wrong_fragment_kind(fragment_kind, &mac, span);
|
||||||
fragment_kind.dummy(span)
|
fragment_kind.dummy(span, guar)
|
||||||
};
|
};
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -705,11 +717,15 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||||
self.cx.dcx().emit_err(UnsupportedKeyValue { span });
|
self.cx.dcx().emit_err(UnsupportedKeyValue { span });
|
||||||
}
|
}
|
||||||
let inner_tokens = attr_item.args.inner_tokens();
|
let inner_tokens = attr_item.args.inner_tokens();
|
||||||
let Ok(tok_result) = expander.expand(self.cx, span, inner_tokens, tokens)
|
match expander.expand(self.cx, span, inner_tokens, tokens) {
|
||||||
else {
|
Ok(tok_result) => self.parse_ast_fragment(
|
||||||
return ExpandResult::Ready(fragment_kind.dummy(span));
|
tok_result,
|
||||||
};
|
fragment_kind,
|
||||||
self.parse_ast_fragment(tok_result, fragment_kind, &attr_item.path, span)
|
&attr_item.path,
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SyntaxExtensionKind::LegacyAttr(expander) => {
|
SyntaxExtensionKind::LegacyAttr(expander) => {
|
||||||
match validate_attr::parse_meta(&self.cx.sess.parse_sess, &attr) {
|
match validate_attr::parse_meta(&self.cx.sess.parse_sess, &attr) {
|
||||||
|
@ -729,15 +745,15 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||||
AstFragmentKind::Expr | AstFragmentKind::MethodReceiverExpr
|
AstFragmentKind::Expr | AstFragmentKind::MethodReceiverExpr
|
||||||
) && items.is_empty()
|
) && items.is_empty()
|
||||||
{
|
{
|
||||||
self.cx.dcx().emit_err(RemoveExprNotSupported { span });
|
let guar = self.cx.dcx().emit_err(RemoveExprNotSupported { span });
|
||||||
fragment_kind.dummy(span)
|
fragment_kind.dummy(span, guar)
|
||||||
} else {
|
} else {
|
||||||
fragment_kind.expect_from_annotatables(items)
|
fragment_kind.expect_from_annotatables(items)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
err.emit();
|
let guar = err.emit();
|
||||||
fragment_kind.dummy(span)
|
fragment_kind.dummy(span, guar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -857,9 +873,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||||
err.span(span);
|
err.span(span);
|
||||||
}
|
}
|
||||||
annotate_err_with_kind(&mut err, kind, span);
|
annotate_err_with_kind(&mut err, kind, span);
|
||||||
err.emit();
|
let guar = err.emit();
|
||||||
self.cx.trace_macros_diag();
|
self.cx.trace_macros_diag();
|
||||||
kind.dummy(span)
|
kind.dummy(span, guar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use rustc_errors::{Applicability, DiagCtxt, DiagnosticBuilder, DiagnosticMessage
|
||||||
use rustc_parse::parser::{Parser, Recovery};
|
use rustc_parse::parser::{Parser, Recovery};
|
||||||
use rustc_span::source_map::SourceMap;
|
use rustc_span::source_map::SourceMap;
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
use rustc_span::Span;
|
use rustc_span::{ErrorGuaranteed, Span};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use super::macro_rules::{parser_from_cx, NoopTracker};
|
use super::macro_rules::{parser_from_cx, NoopTracker};
|
||||||
|
@ -47,7 +47,7 @@ pub(super) fn failed_to_match_macro<'cx>(
|
||||||
|
|
||||||
let Some(BestFailure { token, msg: label, remaining_matcher, .. }) = tracker.best_failure
|
let Some(BestFailure { token, msg: label, remaining_matcher, .. }) = tracker.best_failure
|
||||||
else {
|
else {
|
||||||
return DummyResult::any(sp);
|
return DummyResult::any(sp, cx.dcx().span_delayed_bug(sp, "failed to match a macro"));
|
||||||
};
|
};
|
||||||
|
|
||||||
let span = token.span.substitute_dummy(sp);
|
let span = token.span.substitute_dummy(sp);
|
||||||
|
@ -106,9 +106,9 @@ pub(super) fn failed_to_match_macro<'cx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err.emit();
|
let guar = err.emit();
|
||||||
cx.trace_macros_diag();
|
cx.trace_macros_diag();
|
||||||
DummyResult::any(sp)
|
DummyResult::any(sp, guar)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The tracker used for the slow error path that collects useful info for diagnostics.
|
/// The tracker used for the slow error path that collects useful info for diagnostics.
|
||||||
|
@ -180,10 +180,10 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx,
|
||||||
}
|
}
|
||||||
Error(err_sp, msg) => {
|
Error(err_sp, msg) => {
|
||||||
let span = err_sp.substitute_dummy(self.root_span);
|
let span = err_sp.substitute_dummy(self.root_span);
|
||||||
self.cx.dcx().span_err(span, msg.clone());
|
let guar = self.cx.dcx().span_err(span, msg.clone());
|
||||||
self.result = Some(DummyResult::any(span));
|
self.result = Some(DummyResult::any(span, guar));
|
||||||
}
|
}
|
||||||
ErrorReported(_) => self.result = Some(DummyResult::any(self.root_span)),
|
ErrorReported(guar) => self.result = Some(DummyResult::any(self.root_span, *guar)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +224,7 @@ pub(super) fn emit_frag_parse_err(
|
||||||
site_span: Span,
|
site_span: Span,
|
||||||
arm_span: Span,
|
arm_span: Span,
|
||||||
kind: AstFragmentKind,
|
kind: AstFragmentKind,
|
||||||
) {
|
) -> ErrorGuaranteed {
|
||||||
// FIXME(davidtwco): avoid depending on the error message text
|
// FIXME(davidtwco): avoid depending on the error message text
|
||||||
if parser.token == token::Eof
|
if parser.token == token::Eof
|
||||||
&& let DiagnosticMessage::Str(message) = &e.messages[0].0
|
&& let DiagnosticMessage::Str(message) = &e.messages[0].0
|
||||||
|
@ -282,7 +282,7 @@ pub(super) fn emit_frag_parse_err(
|
||||||
},
|
},
|
||||||
_ => annotate_err_with_kind(&mut e, kind, site_span),
|
_ => annotate_err_with_kind(&mut e, kind, site_span),
|
||||||
};
|
};
|
||||||
e.emit();
|
e.emit()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn annotate_err_with_kind(
|
pub(crate) fn annotate_err_with_kind(
|
||||||
|
|
|
@ -114,7 +114,7 @@ use rustc_errors::{DiagnosticMessage, MultiSpan};
|
||||||
use rustc_session::lint::builtin::{META_VARIABLE_MISUSE, MISSING_FRAGMENT_SPECIFIER};
|
use rustc_session::lint::builtin::{META_VARIABLE_MISUSE, MISSING_FRAGMENT_SPECIFIER};
|
||||||
use rustc_session::parse::ParseSess;
|
use rustc_session::parse::ParseSess;
|
||||||
use rustc_span::symbol::kw;
|
use rustc_span::symbol::kw;
|
||||||
use rustc_span::{symbol::MacroRulesNormalizedIdent, Span};
|
use rustc_span::{symbol::MacroRulesNormalizedIdent, ErrorGuaranteed, Span};
|
||||||
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
@ -203,17 +203,17 @@ pub(super) fn check_meta_variables(
|
||||||
span: Span,
|
span: Span,
|
||||||
lhses: &[TokenTree],
|
lhses: &[TokenTree],
|
||||||
rhses: &[TokenTree],
|
rhses: &[TokenTree],
|
||||||
) -> bool {
|
) -> Result<(), ErrorGuaranteed> {
|
||||||
if lhses.len() != rhses.len() {
|
if lhses.len() != rhses.len() {
|
||||||
sess.dcx.span_bug(span, "length mismatch between LHSes and RHSes")
|
sess.dcx.span_bug(span, "length mismatch between LHSes and RHSes")
|
||||||
}
|
}
|
||||||
let mut valid = true;
|
let mut guar = None;
|
||||||
for (lhs, rhs) in iter::zip(lhses, rhses) {
|
for (lhs, rhs) in iter::zip(lhses, rhses) {
|
||||||
let mut binders = Binders::default();
|
let mut binders = Binders::default();
|
||||||
check_binders(sess, node_id, lhs, &Stack::Empty, &mut binders, &Stack::Empty, &mut valid);
|
check_binders(sess, node_id, lhs, &Stack::Empty, &mut binders, &Stack::Empty, &mut guar);
|
||||||
check_occurrences(sess, node_id, rhs, &Stack::Empty, &binders, &Stack::Empty, &mut valid);
|
check_occurrences(sess, node_id, rhs, &Stack::Empty, &binders, &Stack::Empty, &mut guar);
|
||||||
}
|
}
|
||||||
valid
|
guar.map_or(Ok(()), Err)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks `lhs` as part of the LHS of a macro definition, extends `binders` with new binders, and
|
/// Checks `lhs` as part of the LHS of a macro definition, extends `binders` with new binders, and
|
||||||
|
@ -226,7 +226,7 @@ pub(super) fn check_meta_variables(
|
||||||
/// - `macros` is the stack of possible outer macros
|
/// - `macros` is the stack of possible outer macros
|
||||||
/// - `binders` contains the binders of the LHS
|
/// - `binders` contains the binders of the LHS
|
||||||
/// - `ops` is the stack of Kleene operators from the LHS
|
/// - `ops` is the stack of Kleene operators from the LHS
|
||||||
/// - `valid` is set in case of errors
|
/// - `guar` is set in case of errors
|
||||||
fn check_binders(
|
fn check_binders(
|
||||||
sess: &ParseSess,
|
sess: &ParseSess,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
|
@ -234,7 +234,7 @@ fn check_binders(
|
||||||
macros: &Stack<'_, MacroState<'_>>,
|
macros: &Stack<'_, MacroState<'_>>,
|
||||||
binders: &mut Binders,
|
binders: &mut Binders,
|
||||||
ops: &Stack<'_, KleeneToken>,
|
ops: &Stack<'_, KleeneToken>,
|
||||||
valid: &mut bool,
|
guar: &mut Option<ErrorGuaranteed>,
|
||||||
) {
|
) {
|
||||||
match *lhs {
|
match *lhs {
|
||||||
TokenTree::Token(..) => {}
|
TokenTree::Token(..) => {}
|
||||||
|
@ -258,7 +258,7 @@ fn check_binders(
|
||||||
binders.insert(name, BinderInfo { span, ops: ops.into() });
|
binders.insert(name, BinderInfo { span, ops: ops.into() });
|
||||||
} else {
|
} else {
|
||||||
// 3. The meta-variable is bound: This is an occurrence.
|
// 3. The meta-variable is bound: This is an occurrence.
|
||||||
check_occurrences(sess, node_id, lhs, macros, binders, ops, valid);
|
check_occurrences(sess, node_id, lhs, macros, binders, ops, guar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Similarly, this can only happen when checking a toplevel macro.
|
// Similarly, this can only happen when checking a toplevel macro.
|
||||||
|
@ -281,8 +281,10 @@ fn check_binders(
|
||||||
if let Some(prev_info) = get_binder_info(macros, binders, name) {
|
if let Some(prev_info) = get_binder_info(macros, binders, name) {
|
||||||
// Duplicate binders at the top-level macro definition are errors. The lint is only
|
// Duplicate binders at the top-level macro definition are errors. The lint is only
|
||||||
// for nested macro definitions.
|
// for nested macro definitions.
|
||||||
sess.dcx.emit_err(errors::DuplicateMatcherBinding { span, prev: prev_info.span });
|
*guar = Some(
|
||||||
*valid = false;
|
sess.dcx
|
||||||
|
.emit_err(errors::DuplicateMatcherBinding { span, prev: prev_info.span }),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
binders.insert(name, BinderInfo { span, ops: ops.into() });
|
binders.insert(name, BinderInfo { span, ops: ops.into() });
|
||||||
}
|
}
|
||||||
|
@ -291,13 +293,13 @@ fn check_binders(
|
||||||
TokenTree::MetaVarExpr(..) => {}
|
TokenTree::MetaVarExpr(..) => {}
|
||||||
TokenTree::Delimited(.., ref del) => {
|
TokenTree::Delimited(.., ref del) => {
|
||||||
for tt in &del.tts {
|
for tt in &del.tts {
|
||||||
check_binders(sess, node_id, tt, macros, binders, ops, valid);
|
check_binders(sess, node_id, tt, macros, binders, ops, guar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TokenTree::Sequence(_, ref seq) => {
|
TokenTree::Sequence(_, ref seq) => {
|
||||||
let ops = ops.push(seq.kleene);
|
let ops = ops.push(seq.kleene);
|
||||||
for tt in &seq.tts {
|
for tt in &seq.tts {
|
||||||
check_binders(sess, node_id, tt, macros, binders, &ops, valid);
|
check_binders(sess, node_id, tt, macros, binders, &ops, guar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -327,7 +329,7 @@ fn get_binder_info<'a>(
|
||||||
/// - `macros` is the stack of possible outer macros
|
/// - `macros` is the stack of possible outer macros
|
||||||
/// - `binders` contains the binders of the associated LHS
|
/// - `binders` contains the binders of the associated LHS
|
||||||
/// - `ops` is the stack of Kleene operators from the RHS
|
/// - `ops` is the stack of Kleene operators from the RHS
|
||||||
/// - `valid` is set in case of errors
|
/// - `guar` is set in case of errors
|
||||||
fn check_occurrences(
|
fn check_occurrences(
|
||||||
sess: &ParseSess,
|
sess: &ParseSess,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
|
@ -335,7 +337,7 @@ fn check_occurrences(
|
||||||
macros: &Stack<'_, MacroState<'_>>,
|
macros: &Stack<'_, MacroState<'_>>,
|
||||||
binders: &Binders,
|
binders: &Binders,
|
||||||
ops: &Stack<'_, KleeneToken>,
|
ops: &Stack<'_, KleeneToken>,
|
||||||
valid: &mut bool,
|
guar: &mut Option<ErrorGuaranteed>,
|
||||||
) {
|
) {
|
||||||
match *rhs {
|
match *rhs {
|
||||||
TokenTree::Token(..) => {}
|
TokenTree::Token(..) => {}
|
||||||
|
@ -353,11 +355,11 @@ fn check_occurrences(
|
||||||
check_ops_is_prefix(sess, node_id, macros, binders, ops, dl.entire(), name);
|
check_ops_is_prefix(sess, node_id, macros, binders, ops, dl.entire(), name);
|
||||||
}
|
}
|
||||||
TokenTree::Delimited(.., ref del) => {
|
TokenTree::Delimited(.., ref del) => {
|
||||||
check_nested_occurrences(sess, node_id, &del.tts, macros, binders, ops, valid);
|
check_nested_occurrences(sess, node_id, &del.tts, macros, binders, ops, guar);
|
||||||
}
|
}
|
||||||
TokenTree::Sequence(_, ref seq) => {
|
TokenTree::Sequence(_, ref seq) => {
|
||||||
let ops = ops.push(seq.kleene);
|
let ops = ops.push(seq.kleene);
|
||||||
check_nested_occurrences(sess, node_id, &seq.tts, macros, binders, &ops, valid);
|
check_nested_occurrences(sess, node_id, &seq.tts, macros, binders, &ops, guar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -392,7 +394,7 @@ enum NestedMacroState {
|
||||||
/// - `macros` is the stack of possible outer macros
|
/// - `macros` is the stack of possible outer macros
|
||||||
/// - `binders` contains the binders of the associated LHS
|
/// - `binders` contains the binders of the associated LHS
|
||||||
/// - `ops` is the stack of Kleene operators from the RHS
|
/// - `ops` is the stack of Kleene operators from the RHS
|
||||||
/// - `valid` is set in case of errors
|
/// - `guar` is set in case of errors
|
||||||
fn check_nested_occurrences(
|
fn check_nested_occurrences(
|
||||||
sess: &ParseSess,
|
sess: &ParseSess,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
|
@ -400,7 +402,7 @@ fn check_nested_occurrences(
|
||||||
macros: &Stack<'_, MacroState<'_>>,
|
macros: &Stack<'_, MacroState<'_>>,
|
||||||
binders: &Binders,
|
binders: &Binders,
|
||||||
ops: &Stack<'_, KleeneToken>,
|
ops: &Stack<'_, KleeneToken>,
|
||||||
valid: &mut bool,
|
guar: &mut Option<ErrorGuaranteed>,
|
||||||
) {
|
) {
|
||||||
let mut state = NestedMacroState::Empty;
|
let mut state = NestedMacroState::Empty;
|
||||||
let nested_macros = macros.push(MacroState { binders, ops: ops.into() });
|
let nested_macros = macros.push(MacroState { binders, ops: ops.into() });
|
||||||
|
@ -432,7 +434,7 @@ fn check_nested_occurrences(
|
||||||
(NestedMacroState::MacroRulesNot, &TokenTree::MetaVar(..)) => {
|
(NestedMacroState::MacroRulesNot, &TokenTree::MetaVar(..)) => {
|
||||||
state = NestedMacroState::MacroRulesNotName;
|
state = NestedMacroState::MacroRulesNotName;
|
||||||
// We check that the meta-variable is correctly used.
|
// We check that the meta-variable is correctly used.
|
||||||
check_occurrences(sess, node_id, tt, macros, binders, ops, valid);
|
check_occurrences(sess, node_id, tt, macros, binders, ops, guar);
|
||||||
}
|
}
|
||||||
(NestedMacroState::MacroRulesNotName, TokenTree::Delimited(.., del))
|
(NestedMacroState::MacroRulesNotName, TokenTree::Delimited(.., del))
|
||||||
| (NestedMacroState::MacroName, TokenTree::Delimited(.., del))
|
| (NestedMacroState::MacroName, TokenTree::Delimited(.., del))
|
||||||
|
@ -441,7 +443,7 @@ fn check_nested_occurrences(
|
||||||
let macro_rules = state == NestedMacroState::MacroRulesNotName;
|
let macro_rules = state == NestedMacroState::MacroRulesNotName;
|
||||||
state = NestedMacroState::Empty;
|
state = NestedMacroState::Empty;
|
||||||
let rest =
|
let rest =
|
||||||
check_nested_macro(sess, node_id, macro_rules, &del.tts, &nested_macros, valid);
|
check_nested_macro(sess, node_id, macro_rules, &del.tts, &nested_macros, guar);
|
||||||
// If we did not check the whole macro definition, then check the rest as if outside
|
// If we did not check the whole macro definition, then check the rest as if outside
|
||||||
// the macro definition.
|
// the macro definition.
|
||||||
check_nested_occurrences(
|
check_nested_occurrences(
|
||||||
|
@ -451,7 +453,7 @@ fn check_nested_occurrences(
|
||||||
macros,
|
macros,
|
||||||
binders,
|
binders,
|
||||||
ops,
|
ops,
|
||||||
valid,
|
guar,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
|
@ -463,7 +465,7 @@ fn check_nested_occurrences(
|
||||||
(NestedMacroState::Macro, &TokenTree::MetaVar(..)) => {
|
(NestedMacroState::Macro, &TokenTree::MetaVar(..)) => {
|
||||||
state = NestedMacroState::MacroName;
|
state = NestedMacroState::MacroName;
|
||||||
// We check that the meta-variable is correctly used.
|
// We check that the meta-variable is correctly used.
|
||||||
check_occurrences(sess, node_id, tt, macros, binders, ops, valid);
|
check_occurrences(sess, node_id, tt, macros, binders, ops, guar);
|
||||||
}
|
}
|
||||||
(NestedMacroState::MacroName, TokenTree::Delimited(.., del))
|
(NestedMacroState::MacroName, TokenTree::Delimited(.., del))
|
||||||
if del.delim == Delimiter::Parenthesis =>
|
if del.delim == Delimiter::Parenthesis =>
|
||||||
|
@ -477,7 +479,7 @@ fn check_nested_occurrences(
|
||||||
&nested_macros,
|
&nested_macros,
|
||||||
&mut nested_binders,
|
&mut nested_binders,
|
||||||
&Stack::Empty,
|
&Stack::Empty,
|
||||||
valid,
|
guar,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(NestedMacroState::MacroNameParen, TokenTree::Delimited(.., del))
|
(NestedMacroState::MacroNameParen, TokenTree::Delimited(.., del))
|
||||||
|
@ -491,12 +493,12 @@ fn check_nested_occurrences(
|
||||||
&nested_macros,
|
&nested_macros,
|
||||||
&nested_binders,
|
&nested_binders,
|
||||||
&Stack::Empty,
|
&Stack::Empty,
|
||||||
valid,
|
guar,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(_, tt) => {
|
(_, tt) => {
|
||||||
state = NestedMacroState::Empty;
|
state = NestedMacroState::Empty;
|
||||||
check_occurrences(sess, node_id, tt, macros, binders, ops, valid);
|
check_occurrences(sess, node_id, tt, macros, binders, ops, guar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -515,14 +517,14 @@ fn check_nested_occurrences(
|
||||||
/// - `macro_rules` specifies whether the macro is `macro_rules`
|
/// - `macro_rules` specifies whether the macro is `macro_rules`
|
||||||
/// - `tts` is checked as a list of (LHS) => {RHS}
|
/// - `tts` is checked as a list of (LHS) => {RHS}
|
||||||
/// - `macros` is the stack of outer macros
|
/// - `macros` is the stack of outer macros
|
||||||
/// - `valid` is set in case of errors
|
/// - `guar` is set in case of errors
|
||||||
fn check_nested_macro(
|
fn check_nested_macro(
|
||||||
sess: &ParseSess,
|
sess: &ParseSess,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
macro_rules: bool,
|
macro_rules: bool,
|
||||||
tts: &[TokenTree],
|
tts: &[TokenTree],
|
||||||
macros: &Stack<'_, MacroState<'_>>,
|
macros: &Stack<'_, MacroState<'_>>,
|
||||||
valid: &mut bool,
|
guar: &mut Option<ErrorGuaranteed>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
let n = tts.len();
|
let n = tts.len();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
|
@ -539,8 +541,8 @@ fn check_nested_macro(
|
||||||
let lhs = &tts[i];
|
let lhs = &tts[i];
|
||||||
let rhs = &tts[i + 2];
|
let rhs = &tts[i + 2];
|
||||||
let mut binders = Binders::default();
|
let mut binders = Binders::default();
|
||||||
check_binders(sess, node_id, lhs, macros, &mut binders, &Stack::Empty, valid);
|
check_binders(sess, node_id, lhs, macros, &mut binders, &Stack::Empty, guar);
|
||||||
check_occurrences(sess, node_id, rhs, macros, &binders, &Stack::Empty, valid);
|
check_occurrences(sess, node_id, rhs, macros, &binders, &Stack::Empty, guar);
|
||||||
// Since the last semicolon is optional for `macro_rules` macros and decl_macro are not terminated,
|
// Since the last semicolon is optional for `macro_rules` macros and decl_macro are not terminated,
|
||||||
// we increment our checked position by how many token trees we already checked (the 3
|
// we increment our checked position by how many token trees we already checked (the 3
|
||||||
// above) before checking for the separator.
|
// above) before checking for the separator.
|
||||||
|
|
|
@ -66,8 +66,10 @@ impl<'a> ParserAnyMacro<'a> {
|
||||||
let fragment = match parse_ast_fragment(parser, kind) {
|
let fragment = match parse_ast_fragment(parser, kind) {
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
diagnostics::emit_frag_parse_err(err, parser, snapshot, site_span, arm_span, kind);
|
let guar = diagnostics::emit_frag_parse_err(
|
||||||
return kind.dummy(site_span);
|
err, parser, snapshot, site_span, arm_span, kind,
|
||||||
|
);
|
||||||
|
return kind.dummy(site_span, guar);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -101,7 +103,6 @@ struct MacroRulesMacroExpander {
|
||||||
transparency: Transparency,
|
transparency: Transparency,
|
||||||
lhses: Vec<Vec<MatcherLoc>>,
|
lhses: Vec<Vec<MatcherLoc>>,
|
||||||
rhses: Vec<mbe::TokenTree>,
|
rhses: Vec<mbe::TokenTree>,
|
||||||
valid: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TTMacroExpander for MacroRulesMacroExpander {
|
impl TTMacroExpander for MacroRulesMacroExpander {
|
||||||
|
@ -111,9 +112,6 @@ impl TTMacroExpander for MacroRulesMacroExpander {
|
||||||
sp: Span,
|
sp: Span,
|
||||||
input: TokenStream,
|
input: TokenStream,
|
||||||
) -> Box<dyn MacResult + 'cx> {
|
) -> Box<dyn MacResult + 'cx> {
|
||||||
if !self.valid {
|
|
||||||
return DummyResult::any(sp);
|
|
||||||
}
|
|
||||||
expand_macro(
|
expand_macro(
|
||||||
cx,
|
cx,
|
||||||
sp,
|
sp,
|
||||||
|
@ -128,12 +126,17 @@ impl TTMacroExpander for MacroRulesMacroExpander {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn macro_rules_dummy_expander<'cx>(
|
struct DummyExpander(ErrorGuaranteed);
|
||||||
_: &'cx mut ExtCtxt<'_>,
|
|
||||||
span: Span,
|
impl TTMacroExpander for DummyExpander {
|
||||||
_: TokenStream,
|
fn expand<'cx>(
|
||||||
) -> Box<dyn MacResult + 'cx> {
|
&self,
|
||||||
DummyResult::any(span)
|
_: &'cx mut ExtCtxt<'_>,
|
||||||
|
span: Span,
|
||||||
|
_: TokenStream,
|
||||||
|
) -> Box<dyn MacResult + 'cx> {
|
||||||
|
DummyResult::any(span, self.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace_macros_note(cx_expansions: &mut FxIndexMap<Span, Vec<String>>, sp: Span, message: String) {
|
fn trace_macros_note(cx_expansions: &mut FxIndexMap<Span, Vec<String>>, sp: Span, message: String) {
|
||||||
|
@ -217,8 +220,8 @@ fn expand_macro<'cx>(
|
||||||
let tts = match transcribe(cx, &named_matches, rhs, rhs_span, transparency) {
|
let tts = match transcribe(cx, &named_matches, rhs, rhs_span, transparency) {
|
||||||
Ok(tts) => tts,
|
Ok(tts) => tts,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
err.emit();
|
let guar = err.emit();
|
||||||
return DummyResult::any(arm_span);
|
return DummyResult::any(arm_span, guar);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -249,9 +252,9 @@ fn expand_macro<'cx>(
|
||||||
is_local,
|
is_local,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Err(CanRetry::No(_)) => {
|
Err(CanRetry::No(guar)) => {
|
||||||
debug!("Will not retry matching as an error was emitted already");
|
debug!("Will not retry matching as an error was emitted already");
|
||||||
DummyResult::any(sp)
|
DummyResult::any(sp, guar)
|
||||||
}
|
}
|
||||||
Err(CanRetry::Yes) => {
|
Err(CanRetry::Yes) => {
|
||||||
// Retry and emit a better error.
|
// Retry and emit a better error.
|
||||||
|
@ -371,7 +374,7 @@ pub fn compile_declarative_macro(
|
||||||
def.id != DUMMY_NODE_ID,
|
def.id != DUMMY_NODE_ID,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let dummy_syn_ext = || (mk_syn_ext(Box::new(macro_rules_dummy_expander)), Vec::new());
|
let dummy_syn_ext = |guar| (mk_syn_ext(Box::new(DummyExpander(guar))), Vec::new());
|
||||||
|
|
||||||
let dcx = &sess.parse_sess.dcx;
|
let dcx = &sess.parse_sess.dcx;
|
||||||
let lhs_nm = Ident::new(sym::lhs, def.span);
|
let lhs_nm = Ident::new(sym::lhs, def.span);
|
||||||
|
@ -456,19 +459,20 @@ pub fn compile_declarative_macro(
|
||||||
let mut err = sess.dcx().struct_span_err(sp, s);
|
let mut err = sess.dcx().struct_span_err(sp, s);
|
||||||
err.span_label(sp, msg);
|
err.span_label(sp, msg);
|
||||||
annotate_doc_comment(sess.dcx(), &mut err, sess.source_map(), sp);
|
annotate_doc_comment(sess.dcx(), &mut err, sess.source_map(), sp);
|
||||||
err.emit();
|
let guar = err.emit();
|
||||||
return dummy_syn_ext();
|
return dummy_syn_ext(guar);
|
||||||
}
|
}
|
||||||
Error(sp, msg) => {
|
Error(sp, msg) => {
|
||||||
sess.dcx().span_err(sp.substitute_dummy(def.span), msg);
|
let guar = sess.dcx().span_err(sp.substitute_dummy(def.span), msg);
|
||||||
return dummy_syn_ext();
|
return dummy_syn_ext(guar);
|
||||||
}
|
}
|
||||||
ErrorReported(_) => {
|
ErrorReported(guar) => {
|
||||||
return dummy_syn_ext();
|
return dummy_syn_ext(guar);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut valid = true;
|
let mut guar = None;
|
||||||
|
let mut check_emission = |ret: Result<(), ErrorGuaranteed>| guar = guar.or(ret.err());
|
||||||
|
|
||||||
// Extract the arguments:
|
// Extract the arguments:
|
||||||
let lhses = match &argument_map[&MacroRulesNormalizedIdent::new(lhs_nm)] {
|
let lhses = match &argument_map[&MacroRulesNormalizedIdent::new(lhs_nm)] {
|
||||||
|
@ -488,7 +492,7 @@ pub fn compile_declarative_macro(
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// We don't handle errors here, the driver will abort
|
// We don't handle errors here, the driver will abort
|
||||||
// after parsing/expansion. we can report every error in every macro this way.
|
// after parsing/expansion. we can report every error in every macro this way.
|
||||||
valid &= check_lhs_nt_follows(sess, def, &tt).is_ok();
|
check_emission(check_lhs_nt_follows(sess, def, &tt));
|
||||||
return tt;
|
return tt;
|
||||||
}
|
}
|
||||||
sess.dcx().span_bug(def.span, "wrong-structured lhs")
|
sess.dcx().span_bug(def.span, "wrong-structured lhs")
|
||||||
|
@ -520,15 +524,21 @@ pub fn compile_declarative_macro(
|
||||||
};
|
};
|
||||||
|
|
||||||
for rhs in &rhses {
|
for rhs in &rhses {
|
||||||
valid &= check_rhs(sess, rhs);
|
check_emission(check_rhs(sess, rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't abort iteration early, so that errors for multiple lhses can be reported
|
// don't abort iteration early, so that errors for multiple lhses can be reported
|
||||||
for lhs in &lhses {
|
for lhs in &lhses {
|
||||||
valid &= check_lhs_no_empty_seq(sess, slice::from_ref(lhs));
|
check_emission(check_lhs_no_empty_seq(sess, slice::from_ref(lhs)));
|
||||||
}
|
}
|
||||||
|
|
||||||
valid &= macro_check::check_meta_variables(&sess.parse_sess, def.id, def.span, &lhses, &rhses);
|
check_emission(macro_check::check_meta_variables(
|
||||||
|
&sess.parse_sess,
|
||||||
|
def.id,
|
||||||
|
def.span,
|
||||||
|
&lhses,
|
||||||
|
&rhses,
|
||||||
|
));
|
||||||
|
|
||||||
let (transparency, transparency_error) = attr::find_transparency(&def.attrs, macro_rules);
|
let (transparency, transparency_error) = attr::find_transparency(&def.attrs, macro_rules);
|
||||||
match transparency_error {
|
match transparency_error {
|
||||||
|
@ -541,11 +551,15 @@ pub fn compile_declarative_macro(
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(guar) = guar {
|
||||||
|
// To avoid warning noise, only consider the rules of this
|
||||||
|
// macro for the lint, if all rules are valid.
|
||||||
|
return dummy_syn_ext(guar);
|
||||||
|
}
|
||||||
|
|
||||||
// Compute the spans of the macro rules for unused rule linting.
|
// Compute the spans of the macro rules for unused rule linting.
|
||||||
// To avoid warning noise, only consider the rules of this
|
|
||||||
// macro for the lint, if all rules are valid.
|
|
||||||
// Also, we are only interested in non-foreign macros.
|
// Also, we are only interested in non-foreign macros.
|
||||||
let rule_spans = if valid && def.id != DUMMY_NODE_ID {
|
let rule_spans = if def.id != DUMMY_NODE_ID {
|
||||||
lhses
|
lhses
|
||||||
.iter()
|
.iter()
|
||||||
.zip(rhses.iter())
|
.zip(rhses.iter())
|
||||||
|
@ -562,23 +576,19 @@ pub fn compile_declarative_macro(
|
||||||
};
|
};
|
||||||
|
|
||||||
// Convert the lhses into `MatcherLoc` form, which is better for doing the
|
// Convert the lhses into `MatcherLoc` form, which is better for doing the
|
||||||
// actual matching. Unless the matcher is invalid.
|
// actual matching.
|
||||||
let lhses = if valid {
|
let lhses = lhses
|
||||||
lhses
|
.iter()
|
||||||
.iter()
|
.map(|lhs| {
|
||||||
.map(|lhs| {
|
// Ignore the delimiters around the matcher.
|
||||||
// Ignore the delimiters around the matcher.
|
match lhs {
|
||||||
match lhs {
|
mbe::TokenTree::Delimited(.., delimited) => {
|
||||||
mbe::TokenTree::Delimited(.., delimited) => {
|
mbe::macro_parser::compute_locs(&delimited.tts)
|
||||||
mbe::macro_parser::compute_locs(&delimited.tts)
|
|
||||||
}
|
|
||||||
_ => sess.dcx().span_bug(def.span, "malformed macro lhs"),
|
|
||||||
}
|
}
|
||||||
})
|
_ => sess.dcx().span_bug(def.span, "malformed macro lhs"),
|
||||||
.collect()
|
}
|
||||||
} else {
|
})
|
||||||
vec![]
|
.collect();
|
||||||
};
|
|
||||||
|
|
||||||
let expander = Box::new(MacroRulesMacroExpander {
|
let expander = Box::new(MacroRulesMacroExpander {
|
||||||
name: def.ident,
|
name: def.ident,
|
||||||
|
@ -587,7 +597,6 @@ pub fn compile_declarative_macro(
|
||||||
transparency,
|
transparency,
|
||||||
lhses,
|
lhses,
|
||||||
rhses,
|
rhses,
|
||||||
valid,
|
|
||||||
});
|
});
|
||||||
(mk_syn_ext(expander), rule_spans)
|
(mk_syn_ext(expander), rule_spans)
|
||||||
}
|
}
|
||||||
|
@ -640,7 +649,7 @@ fn is_empty_token_tree(sess: &Session, seq: &mbe::SequenceRepetition) -> bool {
|
||||||
|
|
||||||
/// Checks that the lhs contains no repetition which could match an empty token
|
/// Checks that the lhs contains no repetition which could match an empty token
|
||||||
/// tree, because then the matcher would hang indefinitely.
|
/// tree, because then the matcher would hang indefinitely.
|
||||||
fn check_lhs_no_empty_seq(sess: &Session, tts: &[mbe::TokenTree]) -> bool {
|
fn check_lhs_no_empty_seq(sess: &Session, tts: &[mbe::TokenTree]) -> Result<(), ErrorGuaranteed> {
|
||||||
use mbe::TokenTree;
|
use mbe::TokenTree;
|
||||||
for tt in tts {
|
for tt in tts {
|
||||||
match tt {
|
match tt {
|
||||||
|
@ -648,35 +657,26 @@ fn check_lhs_no_empty_seq(sess: &Session, tts: &[mbe::TokenTree]) -> bool {
|
||||||
| TokenTree::MetaVar(..)
|
| TokenTree::MetaVar(..)
|
||||||
| TokenTree::MetaVarDecl(..)
|
| TokenTree::MetaVarDecl(..)
|
||||||
| TokenTree::MetaVarExpr(..) => (),
|
| TokenTree::MetaVarExpr(..) => (),
|
||||||
TokenTree::Delimited(.., del) => {
|
TokenTree::Delimited(.., del) => check_lhs_no_empty_seq(sess, &del.tts)?,
|
||||||
if !check_lhs_no_empty_seq(sess, &del.tts) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TokenTree::Sequence(span, seq) => {
|
TokenTree::Sequence(span, seq) => {
|
||||||
if is_empty_token_tree(sess, seq) {
|
if is_empty_token_tree(sess, seq) {
|
||||||
let sp = span.entire();
|
let sp = span.entire();
|
||||||
sess.dcx().span_err(sp, "repetition matches empty token tree");
|
let guar = sess.dcx().span_err(sp, "repetition matches empty token tree");
|
||||||
return false;
|
return Err(guar);
|
||||||
}
|
|
||||||
if !check_lhs_no_empty_seq(sess, &seq.tts) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
check_lhs_no_empty_seq(sess, &seq.tts)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_rhs(sess: &Session, rhs: &mbe::TokenTree) -> bool {
|
fn check_rhs(sess: &Session, rhs: &mbe::TokenTree) -> Result<(), ErrorGuaranteed> {
|
||||||
match *rhs {
|
match *rhs {
|
||||||
mbe::TokenTree::Delimited(..) => return true,
|
mbe::TokenTree::Delimited(..) => Ok(()),
|
||||||
_ => {
|
_ => Err(sess.dcx().span_err(rhs.span(), "macro rhs must be delimited")),
|
||||||
sess.dcx().span_err(rhs.span(), "macro rhs must be delimited");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_matcher(
|
fn check_matcher(
|
||||||
|
|
|
@ -758,7 +758,7 @@ trait UnusedDelimLint {
|
||||||
}
|
}
|
||||||
impl<'ast> Visitor<'ast> for ErrExprVisitor {
|
impl<'ast> Visitor<'ast> for ErrExprVisitor {
|
||||||
fn visit_expr(&mut self, expr: &'ast ast::Expr) {
|
fn visit_expr(&mut self, expr: &'ast ast::Expr) {
|
||||||
if let ExprKind::Err = expr.kind {
|
if let ExprKind::Err(_) = expr.kind {
|
||||||
self.has_error = true;
|
self.has_error = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -744,7 +744,8 @@ impl<'a> Parser<'a> {
|
||||||
Err(err)
|
Err(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn attr_on_non_tail_expr(&self, expr: &Expr) {
|
/// The user has written `#[attr] expr` which is unsupported. (#106020)
|
||||||
|
pub(super) fn attr_on_non_tail_expr(&self, expr: &Expr) -> ErrorGuaranteed {
|
||||||
// Missing semicolon typo error.
|
// Missing semicolon typo error.
|
||||||
let span = self.prev_token.span.shrink_to_hi();
|
let span = self.prev_token.span.shrink_to_hi();
|
||||||
let mut err = self.dcx().create_err(ExpectedSemi {
|
let mut err = self.dcx().create_err(ExpectedSemi {
|
||||||
|
@ -787,6 +788,8 @@ impl<'a> Parser<'a> {
|
||||||
],
|
],
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Special handling for `#[cfg(...)]` chains
|
||||||
let mut snapshot = self.create_snapshot_for_diagnostic();
|
let mut snapshot = self.create_snapshot_for_diagnostic();
|
||||||
if let [attr] = &expr.attrs[..]
|
if let [attr] = &expr.attrs[..]
|
||||||
&& let ast::AttrKind::Normal(attr_kind) = &attr.kind
|
&& let ast::AttrKind::Normal(attr_kind) = &attr.kind
|
||||||
|
@ -799,7 +802,7 @@ impl<'a> Parser<'a> {
|
||||||
Err(inner_err) => {
|
Err(inner_err) => {
|
||||||
err.cancel();
|
err.cancel();
|
||||||
inner_err.cancel();
|
inner_err.cancel();
|
||||||
return;
|
return self.dcx().span_delayed_bug(expr.span, "not a tail expression");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&& let ast::AttrKind::Normal(next_attr_kind) = next_attr.kind
|
&& let ast::AttrKind::Normal(next_attr_kind) = next_attr.kind
|
||||||
|
@ -812,7 +815,7 @@ impl<'a> Parser<'a> {
|
||||||
Err(inner_err) => {
|
Err(inner_err) => {
|
||||||
err.cancel();
|
err.cancel();
|
||||||
inner_err.cancel();
|
inner_err.cancel();
|
||||||
return;
|
return self.dcx().span_delayed_bug(expr.span, "not a tail expression");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// We have for sure
|
// We have for sure
|
||||||
|
@ -845,7 +848,7 @@ impl<'a> Parser<'a> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err.emit();
|
err.emit()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_too_many_raw_str_terminators(&mut self, err: &mut DiagnosticBuilder<'_>) -> bool {
|
fn check_too_many_raw_str_terminators(&mut self, err: &mut DiagnosticBuilder<'_>) -> bool {
|
||||||
|
@ -921,10 +924,10 @@ impl<'a> Parser<'a> {
|
||||||
// fn foo() -> Foo { Path {
|
// fn foo() -> Foo { Path {
|
||||||
// field: value,
|
// field: value,
|
||||||
// } }
|
// } }
|
||||||
err.delay_as_bug();
|
let guar = err.delay_as_bug();
|
||||||
self.restore_snapshot(snapshot);
|
self.restore_snapshot(snapshot);
|
||||||
let mut tail = self.mk_block(
|
let mut tail = self.mk_block(
|
||||||
thin_vec![self.mk_stmt_err(expr.span)],
|
thin_vec![self.mk_stmt_err(expr.span, guar)],
|
||||||
s,
|
s,
|
||||||
lo.to(self.prev_token.span),
|
lo.to(self.prev_token.span),
|
||||||
);
|
);
|
||||||
|
@ -990,7 +993,7 @@ impl<'a> Parser<'a> {
|
||||||
decl_hi: Span,
|
decl_hi: Span,
|
||||||
) -> PResult<'a, P<Expr>> {
|
) -> PResult<'a, P<Expr>> {
|
||||||
err.span_label(lo.to(decl_hi), "while parsing the body of this closure");
|
err.span_label(lo.to(decl_hi), "while parsing the body of this closure");
|
||||||
match before.kind {
|
let guar = match before.kind {
|
||||||
token::OpenDelim(Delimiter::Brace)
|
token::OpenDelim(Delimiter::Brace)
|
||||||
if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) =>
|
if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) =>
|
||||||
{
|
{
|
||||||
|
@ -1004,8 +1007,9 @@ impl<'a> Parser<'a> {
|
||||||
],
|
],
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
err.emit();
|
let guar = err.emit();
|
||||||
self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Brace)]);
|
self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Brace)]);
|
||||||
|
guar
|
||||||
}
|
}
|
||||||
token::OpenDelim(Delimiter::Parenthesis)
|
token::OpenDelim(Delimiter::Parenthesis)
|
||||||
if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) =>
|
if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) =>
|
||||||
|
@ -1022,7 +1026,7 @@ impl<'a> Parser<'a> {
|
||||||
],
|
],
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
err.emit();
|
err.emit()
|
||||||
}
|
}
|
||||||
_ if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) => {
|
_ if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) => {
|
||||||
// We don't have a heuristic to correctly identify where the block
|
// We don't have a heuristic to correctly identify where the block
|
||||||
|
@ -1035,8 +1039,8 @@ impl<'a> Parser<'a> {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
_ => return Err(err),
|
_ => return Err(err),
|
||||||
}
|
};
|
||||||
Ok(self.mk_expr_err(lo.to(self.token.span)))
|
Ok(self.mk_expr_err(lo.to(self.token.span), guar))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Eats and discards tokens until one of `kets` is encountered. Respects token trees,
|
/// Eats and discards tokens until one of `kets` is encountered. Respects token trees,
|
||||||
|
@ -1214,7 +1218,7 @@ impl<'a> Parser<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
mut e: DiagnosticBuilder<'a>,
|
mut e: DiagnosticBuilder<'a>,
|
||||||
expr: &mut P<Expr>,
|
expr: &mut P<Expr>,
|
||||||
) -> PResult<'a, ()> {
|
) -> PResult<'a, ErrorGuaranteed> {
|
||||||
if let ExprKind::Binary(binop, _, _) = &expr.kind
|
if let ExprKind::Binary(binop, _, _) = &expr.kind
|
||||||
&& let ast::BinOpKind::Lt = binop.node
|
&& let ast::BinOpKind::Lt = binop.node
|
||||||
&& self.eat(&token::Comma)
|
&& self.eat(&token::Comma)
|
||||||
|
@ -1239,9 +1243,9 @@ impl<'a> Parser<'a> {
|
||||||
// The subsequent expression is valid. Mark
|
// The subsequent expression is valid. Mark
|
||||||
// `expr` as erroneous and emit `e` now, but
|
// `expr` as erroneous and emit `e` now, but
|
||||||
// return `Ok` so parsing can continue.
|
// return `Ok` so parsing can continue.
|
||||||
e.emit();
|
let guar = e.emit();
|
||||||
*expr = self.mk_expr_err(expr.span.to(self.prev_token.span));
|
*expr = self.mk_expr_err(expr.span.to(self.prev_token.span), guar);
|
||||||
return Ok(());
|
return Ok(guar);
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
err.cancel();
|
err.cancel();
|
||||||
|
@ -1393,7 +1397,8 @@ impl<'a> Parser<'a> {
|
||||||
outer_op.node,
|
outer_op.node,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mk_err_expr = |this: &Self, span| Ok(Some(this.mk_expr(span, ExprKind::Err)));
|
let mk_err_expr =
|
||||||
|
|this: &Self, span, guar| Ok(Some(this.mk_expr(span, ExprKind::Err(guar))));
|
||||||
|
|
||||||
match &inner_op.kind {
|
match &inner_op.kind {
|
||||||
ExprKind::Binary(op, l1, r1) if op.node.is_comparison() => {
|
ExprKind::Binary(op, l1, r1) if op.node.is_comparison() => {
|
||||||
|
@ -1443,11 +1448,11 @@ impl<'a> Parser<'a> {
|
||||||
match self.parse_expr() {
|
match self.parse_expr() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// 99% certain that the suggestion is correct, continue parsing.
|
// 99% certain that the suggestion is correct, continue parsing.
|
||||||
self.dcx().emit_err(err);
|
let guar = self.dcx().emit_err(err);
|
||||||
// FIXME: actually check that the two expressions in the binop are
|
// FIXME: actually check that the two expressions in the binop are
|
||||||
// paths and resynthesize new fn call expression instead of using
|
// paths and resynthesize new fn call expression instead of using
|
||||||
// `ExprKind::Err` placeholder.
|
// `ExprKind::Err` placeholder.
|
||||||
mk_err_expr(self, inner_op.span.to(self.prev_token.span))
|
mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar)
|
||||||
}
|
}
|
||||||
Err(expr_err) => {
|
Err(expr_err) => {
|
||||||
expr_err.cancel();
|
expr_err.cancel();
|
||||||
|
@ -1471,11 +1476,11 @@ impl<'a> Parser<'a> {
|
||||||
match self.consume_fn_args() {
|
match self.consume_fn_args() {
|
||||||
Err(()) => Err(self.dcx().create_err(err)),
|
Err(()) => Err(self.dcx().create_err(err)),
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
self.dcx().emit_err(err);
|
let guar = self.dcx().emit_err(err);
|
||||||
// FIXME: actually check that the two expressions in the binop are
|
// FIXME: actually check that the two expressions in the binop are
|
||||||
// paths and resynthesize new fn call expression instead of using
|
// paths and resynthesize new fn call expression instead of using
|
||||||
// `ExprKind::Err` placeholder.
|
// `ExprKind::Err` placeholder.
|
||||||
mk_err_expr(self, inner_op.span.to(self.prev_token.span))
|
mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1492,8 +1497,8 @@ impl<'a> Parser<'a> {
|
||||||
let recovered = self
|
let recovered = self
|
||||||
.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op);
|
.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op);
|
||||||
if matches!(recovered, Recovered::Yes) {
|
if matches!(recovered, Recovered::Yes) {
|
||||||
self.dcx().emit_err(err);
|
let guar = self.dcx().emit_err(err);
|
||||||
mk_err_expr(self, inner_op.span.to(self.prev_token.span))
|
mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar)
|
||||||
} else {
|
} else {
|
||||||
// These cases cause too many knock-down errors, bail out (#61329).
|
// These cases cause too many knock-down errors, bail out (#61329).
|
||||||
Err(self.dcx().create_err(err))
|
Err(self.dcx().create_err(err))
|
||||||
|
@ -1502,9 +1507,9 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
let recover =
|
let recover =
|
||||||
self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op);
|
self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op);
|
||||||
self.dcx().emit_err(err);
|
let guar = self.dcx().emit_err(err);
|
||||||
if matches!(recover, Recovered::Yes) {
|
if matches!(recover, Recovered::Yes) {
|
||||||
return mk_err_expr(self, inner_op.span.to(self.prev_token.span));
|
return mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -1925,8 +1930,8 @@ impl<'a> Parser<'a> {
|
||||||
} else {
|
} else {
|
||||||
self.recover_await_prefix(await_sp)?
|
self.recover_await_prefix(await_sp)?
|
||||||
};
|
};
|
||||||
let sp = self.error_on_incorrect_await(lo, hi, &expr, is_question);
|
let (sp, guar) = self.error_on_incorrect_await(lo, hi, &expr, is_question);
|
||||||
let expr = self.mk_expr(lo.to(sp), ExprKind::Err);
|
let expr = self.mk_expr_err(lo.to(sp), guar);
|
||||||
self.maybe_recover_from_bad_qpath(expr)
|
self.maybe_recover_from_bad_qpath(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1955,21 +1960,27 @@ impl<'a> Parser<'a> {
|
||||||
Ok((expr.span, expr, is_question))
|
Ok((expr.span, expr, is_question))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error_on_incorrect_await(&self, lo: Span, hi: Span, expr: &Expr, is_question: bool) -> Span {
|
fn error_on_incorrect_await(
|
||||||
|
&self,
|
||||||
|
lo: Span,
|
||||||
|
hi: Span,
|
||||||
|
expr: &Expr,
|
||||||
|
is_question: bool,
|
||||||
|
) -> (Span, ErrorGuaranteed) {
|
||||||
let span = lo.to(hi);
|
let span = lo.to(hi);
|
||||||
let applicability = match expr.kind {
|
let applicability = match expr.kind {
|
||||||
ExprKind::Try(_) => Applicability::MaybeIncorrect, // `await <expr>?`
|
ExprKind::Try(_) => Applicability::MaybeIncorrect, // `await <expr>?`
|
||||||
_ => Applicability::MachineApplicable,
|
_ => Applicability::MachineApplicable,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.dcx().emit_err(IncorrectAwait {
|
let guar = self.dcx().emit_err(IncorrectAwait {
|
||||||
span,
|
span,
|
||||||
sugg_span: (span, applicability),
|
sugg_span: (span, applicability),
|
||||||
expr: self.span_to_snippet(expr.span).unwrap_or_else(|_| pprust::expr_to_string(expr)),
|
expr: self.span_to_snippet(expr.span).unwrap_or_else(|_| pprust::expr_to_string(expr)),
|
||||||
question_mark: if is_question { "?" } else { "" },
|
question_mark: if is_question { "?" } else { "" },
|
||||||
});
|
});
|
||||||
|
|
||||||
span
|
(span, guar)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If encountering `future.await()`, consumes and emits an error.
|
/// If encountering `future.await()`, consumes and emits an error.
|
||||||
|
@ -2013,8 +2024,8 @@ impl<'a> Parser<'a> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
err.span_suggestion(lo.shrink_to_lo(), format!("{prefix}you can still access the deprecated `try!()` macro using the \"raw identifier\" syntax"), "r#", Applicability::MachineApplicable);
|
err.span_suggestion(lo.shrink_to_lo(), format!("{prefix}you can still access the deprecated `try!()` macro using the \"raw identifier\" syntax"), "r#", Applicability::MachineApplicable);
|
||||||
err.emit();
|
let guar = err.emit();
|
||||||
Ok(self.mk_expr_err(lo.to(hi)))
|
Ok(self.mk_expr_err(lo.to(hi), guar))
|
||||||
} else {
|
} else {
|
||||||
Err(self.expected_expression_found()) // The user isn't trying to invoke the try! macro
|
Err(self.expected_expression_found()) // The user isn't trying to invoke the try! macro
|
||||||
}
|
}
|
||||||
|
@ -2059,10 +2070,10 @@ impl<'a> Parser<'a> {
|
||||||
lo: Span,
|
lo: Span,
|
||||||
err: PErr<'a>,
|
err: PErr<'a>,
|
||||||
) -> P<Expr> {
|
) -> P<Expr> {
|
||||||
err.emit();
|
let guar = err.emit();
|
||||||
// Recover from parse error, callers expect the closing delim to be consumed.
|
// Recover from parse error, callers expect the closing delim to be consumed.
|
||||||
self.consume_block(delim, ConsumeClosingDelim::Yes);
|
self.consume_block(delim, ConsumeClosingDelim::Yes);
|
||||||
self.mk_expr(lo.to(self.prev_token.span), ExprKind::Err)
|
self.mk_expr(lo.to(self.prev_token.span), ExprKind::Err(guar))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Eats tokens until we can be relatively sure we reached the end of the
|
/// Eats tokens until we can be relatively sure we reached the end of the
|
||||||
|
@ -2549,9 +2560,10 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
self.dcx().emit_err(UnexpectedConstParamDeclaration { span: param.span(), sugg });
|
let guar =
|
||||||
|
self.dcx().emit_err(UnexpectedConstParamDeclaration { span: param.span(), sugg });
|
||||||
|
|
||||||
let value = self.mk_expr_err(param.span());
|
let value = self.mk_expr_err(param.span(), guar);
|
||||||
Some(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }))
|
Some(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2630,8 +2642,8 @@ impl<'a> Parser<'a> {
|
||||||
"=",
|
"=",
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
let value = self.mk_expr_err(start.to(expr.span));
|
let guar = err.emit();
|
||||||
err.emit();
|
let value = self.mk_expr_err(start.to(expr.span), guar);
|
||||||
return Ok(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }));
|
return Ok(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }));
|
||||||
} else if token::Colon == snapshot.token.kind
|
} else if token::Colon == snapshot.token.kind
|
||||||
&& expr.span.lo() == snapshot.token.span.hi()
|
&& expr.span.lo() == snapshot.token.span.hi()
|
||||||
|
@ -2701,8 +2713,8 @@ impl<'a> Parser<'a> {
|
||||||
vec![(span.shrink_to_lo(), "{ ".to_string()), (span.shrink_to_hi(), " }".to_string())],
|
vec![(span.shrink_to_lo(), "{ ".to_string()), (span.shrink_to_hi(), " }".to_string())],
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
let value = self.mk_expr_err(span);
|
let guar = err.emit();
|
||||||
err.emit();
|
let value = self.mk_expr_err(span, guar);
|
||||||
GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })
|
GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
|
||||||
use rustc_session::lint::BuiltinLintDiagnostics;
|
use rustc_session::lint::BuiltinLintDiagnostics;
|
||||||
use rustc_span::source_map::{self, Spanned};
|
use rustc_span::source_map::{self, Spanned};
|
||||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||||
use rustc_span::{BytePos, Pos, Span};
|
use rustc_span::{BytePos, ErrorGuaranteed, Pos, Span};
|
||||||
use thin_vec::{thin_vec, ThinVec};
|
use thin_vec::{thin_vec, ThinVec};
|
||||||
|
|
||||||
/// Possibly accepts an `token::Interpolated` expression (a pre-parsed expression
|
/// Possibly accepts an `token::Interpolated` expression (a pre-parsed expression
|
||||||
|
@ -131,9 +131,9 @@ impl<'a> Parser<'a> {
|
||||||
if self.may_recover() && self.look_ahead(1, |t| t == &token::Comma) =>
|
if self.may_recover() && self.look_ahead(1, |t| t == &token::Comma) =>
|
||||||
{
|
{
|
||||||
// Special-case handling of `foo(_, _, _)`
|
// Special-case handling of `foo(_, _, _)`
|
||||||
err.emit();
|
let guar = err.emit();
|
||||||
self.bump();
|
self.bump();
|
||||||
Ok(self.mk_expr(self.prev_token.span, ExprKind::Err))
|
Ok(self.mk_expr(self.prev_token.span, ExprKind::Err(guar)))
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
},
|
},
|
||||||
|
@ -667,8 +667,8 @@ impl<'a> Parser<'a> {
|
||||||
let (span, _) = self.parse_expr_prefix_common(box_kw)?;
|
let (span, _) = self.parse_expr_prefix_common(box_kw)?;
|
||||||
let inner_span = span.with_lo(box_kw.hi());
|
let inner_span = span.with_lo(box_kw.hi());
|
||||||
let code = self.sess.source_map().span_to_snippet(inner_span).unwrap();
|
let code = self.sess.source_map().span_to_snippet(inner_span).unwrap();
|
||||||
self.dcx().emit_err(errors::BoxSyntaxRemoved { span: span, code: code.trim() });
|
let guar = self.dcx().emit_err(errors::BoxSyntaxRemoved { span: span, code: code.trim() });
|
||||||
Ok((span, ExprKind::Err))
|
Ok((span, ExprKind::Err(guar)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_mistaken_not_ident_negation(&self) -> bool {
|
fn is_mistaken_not_ident_negation(&self) -> bool {
|
||||||
|
@ -860,7 +860,7 @@ impl<'a> Parser<'a> {
|
||||||
ExprKind::MethodCall(_) => "a method call",
|
ExprKind::MethodCall(_) => "a method call",
|
||||||
ExprKind::Call(_, _) => "a function call",
|
ExprKind::Call(_, _) => "a function call",
|
||||||
ExprKind::Await(_, _) => "`.await`",
|
ExprKind::Await(_, _) => "`.await`",
|
||||||
ExprKind::Err => return Ok(with_postfix),
|
ExprKind::Err(_) => return Ok(with_postfix),
|
||||||
_ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"),
|
_ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -1315,7 +1315,7 @@ impl<'a> Parser<'a> {
|
||||||
let fields: Vec<_> =
|
let fields: Vec<_> =
|
||||||
fields.into_iter().filter(|field| !field.is_shorthand).collect();
|
fields.into_iter().filter(|field| !field.is_shorthand).collect();
|
||||||
|
|
||||||
if !fields.is_empty() &&
|
let guar = if !fields.is_empty() &&
|
||||||
// `token.kind` should not be compared here.
|
// `token.kind` should not be compared here.
|
||||||
// This is because the `snapshot.token.kind` is treated as the same as
|
// This is because the `snapshot.token.kind` is treated as the same as
|
||||||
// that of the open delim in `TokenTreesReader::parse_token_tree`, even
|
// that of the open delim in `TokenTreesReader::parse_token_tree`, even
|
||||||
|
@ -1338,11 +1338,11 @@ impl<'a> Parser<'a> {
|
||||||
.collect(),
|
.collect(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.emit();
|
.emit()
|
||||||
} else {
|
} else {
|
||||||
err.emit();
|
err.emit()
|
||||||
}
|
};
|
||||||
Ok(self.mk_expr_err(span))
|
Ok(self.mk_expr_err(span, guar))
|
||||||
}
|
}
|
||||||
Ok(_) => Err(err),
|
Ok(_) => Err(err),
|
||||||
Err(err2) => {
|
Err(err2) => {
|
||||||
|
@ -1684,13 +1684,13 @@ impl<'a> Parser<'a> {
|
||||||
&& (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt))
|
&& (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt))
|
||||||
{
|
{
|
||||||
// We're probably inside of a `Path<'a>` that needs a turbofish
|
// We're probably inside of a `Path<'a>` that needs a turbofish
|
||||||
self.dcx().emit_err(errors::UnexpectedTokenAfterLabel {
|
let guar = self.dcx().emit_err(errors::UnexpectedTokenAfterLabel {
|
||||||
span: self.token.span,
|
span: self.token.span,
|
||||||
remove_label: None,
|
remove_label: None,
|
||||||
enclose_in_block: None,
|
enclose_in_block: None,
|
||||||
});
|
});
|
||||||
consume_colon = false;
|
consume_colon = false;
|
||||||
Ok(self.mk_expr_err(lo))
|
Ok(self.mk_expr_err(lo, guar))
|
||||||
} else {
|
} else {
|
||||||
let mut err = errors::UnexpectedTokenAfterLabel {
|
let mut err = errors::UnexpectedTokenAfterLabel {
|
||||||
span: self.token.span,
|
span: self.token.span,
|
||||||
|
@ -2039,7 +2039,7 @@ impl<'a> Parser<'a> {
|
||||||
) -> PResult<'a, L> {
|
) -> PResult<'a, L> {
|
||||||
if let token::Interpolated(nt) = &self.token.kind
|
if let token::Interpolated(nt) = &self.token.kind
|
||||||
&& let token::NtExpr(e) | token::NtLiteral(e) = &nt.0
|
&& let token::NtExpr(e) | token::NtLiteral(e) = &nt.0
|
||||||
&& matches!(e.kind, ExprKind::Err)
|
&& matches!(e.kind, ExprKind::Err(_))
|
||||||
{
|
{
|
||||||
let mut err = self
|
let mut err = self
|
||||||
.dcx()
|
.dcx()
|
||||||
|
@ -2207,7 +2207,7 @@ impl<'a> Parser<'a> {
|
||||||
let mut snapshot = self.create_snapshot_for_diagnostic();
|
let mut snapshot = self.create_snapshot_for_diagnostic();
|
||||||
match snapshot.parse_expr_array_or_repeat(Delimiter::Brace) {
|
match snapshot.parse_expr_array_or_repeat(Delimiter::Brace) {
|
||||||
Ok(arr) => {
|
Ok(arr) => {
|
||||||
self.dcx().emit_err(errors::ArrayBracketsInsteadOfSpaces {
|
let guar = self.dcx().emit_err(errors::ArrayBracketsInsteadOfSpaces {
|
||||||
span: arr.span,
|
span: arr.span,
|
||||||
sub: errors::ArrayBracketsInsteadOfSpacesSugg {
|
sub: errors::ArrayBracketsInsteadOfSpacesSugg {
|
||||||
left: lo,
|
left: lo,
|
||||||
|
@ -2216,7 +2216,7 @@ impl<'a> Parser<'a> {
|
||||||
});
|
});
|
||||||
|
|
||||||
self.restore_snapshot(snapshot);
|
self.restore_snapshot(snapshot);
|
||||||
Some(self.mk_expr_err(arr.span))
|
Some(self.mk_expr_err(arr.span, guar))
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
e.cancel();
|
e.cancel();
|
||||||
|
@ -2370,7 +2370,10 @@ impl<'a> Parser<'a> {
|
||||||
// It is likely that the closure body is a block but where the
|
// It is likely that the closure body is a block but where the
|
||||||
// braces have been removed. We will recover and eat the next
|
// braces have been removed. We will recover and eat the next
|
||||||
// statements later in the parsing process.
|
// statements later in the parsing process.
|
||||||
body = self.mk_expr_err(body.span);
|
body = self.mk_expr_err(
|
||||||
|
body.span,
|
||||||
|
self.dcx().span_delayed_bug(body.span, "recovered a closure body as a block"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let body_span = body.span;
|
let body_span = body.span;
|
||||||
|
@ -2485,7 +2488,7 @@ impl<'a> Parser<'a> {
|
||||||
ExprKind::Binary(Spanned { span: binop_span, .. }, _, right)
|
ExprKind::Binary(Spanned { span: binop_span, .. }, _, right)
|
||||||
if let ExprKind::Block(_, None) = right.kind =>
|
if let ExprKind::Block(_, None) = right.kind =>
|
||||||
{
|
{
|
||||||
this.dcx().emit_err(errors::IfExpressionMissingThenBlock {
|
let guar = this.dcx().emit_err(errors::IfExpressionMissingThenBlock {
|
||||||
if_span: lo,
|
if_span: lo,
|
||||||
missing_then_block_sub:
|
missing_then_block_sub:
|
||||||
errors::IfExpressionMissingThenBlockSub::UnfinishedCondition(
|
errors::IfExpressionMissingThenBlockSub::UnfinishedCondition(
|
||||||
|
@ -2493,14 +2496,14 @@ impl<'a> Parser<'a> {
|
||||||
),
|
),
|
||||||
let_else_sub: None,
|
let_else_sub: None,
|
||||||
});
|
});
|
||||||
std::mem::replace(right, this.mk_expr_err(binop_span.shrink_to_hi()))
|
std::mem::replace(right, this.mk_expr_err(binop_span.shrink_to_hi(), guar))
|
||||||
}
|
}
|
||||||
ExprKind::Block(_, None) => {
|
ExprKind::Block(_, None) => {
|
||||||
this.dcx().emit_err(errors::IfExpressionMissingCondition {
|
let guar = this.dcx().emit_err(errors::IfExpressionMissingCondition {
|
||||||
if_span: lo.with_neighbor(cond.span).shrink_to_hi(),
|
if_span: lo.with_neighbor(cond.span).shrink_to_hi(),
|
||||||
block_span: self.sess.source_map().start_point(cond_span),
|
block_span: self.sess.source_map().start_point(cond_span),
|
||||||
});
|
});
|
||||||
std::mem::replace(&mut cond, this.mk_expr_err(cond_span.shrink_to_hi()))
|
std::mem::replace(&mut cond, this.mk_expr_err(cond_span.shrink_to_hi(), guar))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return None;
|
return None;
|
||||||
|
@ -2520,14 +2523,14 @@ impl<'a> Parser<'a> {
|
||||||
let let_else_sub = matches!(cond.kind, ExprKind::Let(..))
|
let let_else_sub = matches!(cond.kind, ExprKind::Let(..))
|
||||||
.then(|| errors::IfExpressionLetSomeSub { if_span: lo.until(cond_span) });
|
.then(|| errors::IfExpressionLetSomeSub { if_span: lo.until(cond_span) });
|
||||||
|
|
||||||
self.dcx().emit_err(errors::IfExpressionMissingThenBlock {
|
let guar = self.dcx().emit_err(errors::IfExpressionMissingThenBlock {
|
||||||
if_span: lo,
|
if_span: lo,
|
||||||
missing_then_block_sub: errors::IfExpressionMissingThenBlockSub::AddThenBlock(
|
missing_then_block_sub: errors::IfExpressionMissingThenBlockSub::AddThenBlock(
|
||||||
cond_span.shrink_to_hi(),
|
cond_span.shrink_to_hi(),
|
||||||
),
|
),
|
||||||
let_else_sub,
|
let_else_sub,
|
||||||
});
|
});
|
||||||
self.mk_block_err(cond_span.shrink_to_hi())
|
self.mk_block_err(cond_span.shrink_to_hi(), guar)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let attrs = self.parse_outer_attributes()?; // For recovery.
|
let attrs = self.parse_outer_attributes()?; // For recovery.
|
||||||
|
@ -2797,9 +2800,10 @@ impl<'a> Parser<'a> {
|
||||||
&& !matches!(self.token.kind, token::OpenDelim(Delimiter::Brace))
|
&& !matches!(self.token.kind, token::OpenDelim(Delimiter::Brace))
|
||||||
&& self.may_recover()
|
&& self.may_recover()
|
||||||
{
|
{
|
||||||
self.dcx()
|
let guar = self
|
||||||
|
.dcx()
|
||||||
.emit_err(errors::MissingExpressionInForLoop { span: expr.span.shrink_to_lo() });
|
.emit_err(errors::MissingExpressionInForLoop { span: expr.span.shrink_to_lo() });
|
||||||
let err_expr = self.mk_expr(expr.span, ExprKind::Err);
|
let err_expr = self.mk_expr(expr.span, ExprKind::Err(guar));
|
||||||
let block = self.mk_block(thin_vec![], BlockCheckMode::Default, self.prev_token.span);
|
let block = self.mk_block(thin_vec![], BlockCheckMode::Default, self.prev_token.span);
|
||||||
return Ok(self.mk_expr(
|
return Ok(self.mk_expr(
|
||||||
lo.to(self.prev_token.span),
|
lo.to(self.prev_token.span),
|
||||||
|
@ -2924,7 +2928,7 @@ impl<'a> Parser<'a> {
|
||||||
attrs: Default::default(),
|
attrs: Default::default(),
|
||||||
pat: self.mk_pat(span, ast::PatKind::Err(guar)),
|
pat: self.mk_pat(span, ast::PatKind::Err(guar)),
|
||||||
guard: None,
|
guard: None,
|
||||||
body: Some(self.mk_expr_err(span)),
|
body: Some(self.mk_expr_err(span, guar)),
|
||||||
span,
|
span,
|
||||||
id: DUMMY_NODE_ID,
|
id: DUMMY_NODE_ID,
|
||||||
is_placeholder: false,
|
is_placeholder: false,
|
||||||
|
@ -2959,7 +2963,7 @@ impl<'a> Parser<'a> {
|
||||||
let err = |this: &Parser<'_>, stmts: Vec<ast::Stmt>| {
|
let err = |this: &Parser<'_>, stmts: Vec<ast::Stmt>| {
|
||||||
let span = stmts[0].span.to(stmts[stmts.len() - 1].span);
|
let span = stmts[0].span.to(stmts[stmts.len() - 1].span);
|
||||||
|
|
||||||
this.dcx().emit_err(errors::MatchArmBodyWithoutBraces {
|
let guar = this.dcx().emit_err(errors::MatchArmBodyWithoutBraces {
|
||||||
statements: span,
|
statements: span,
|
||||||
arrow: arrow_span,
|
arrow: arrow_span,
|
||||||
num_statements: stmts.len(),
|
num_statements: stmts.len(),
|
||||||
|
@ -2972,7 +2976,7 @@ impl<'a> Parser<'a> {
|
||||||
errors::MatchArmBodyWithoutBracesSugg::UseComma { semicolon: semi_sp }
|
errors::MatchArmBodyWithoutBracesSugg::UseComma { semicolon: semi_sp }
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
this.mk_expr_err(span)
|
this.mk_expr_err(span, guar)
|
||||||
};
|
};
|
||||||
// We might have either a `,` -> `;` typo, or a block without braces. We need
|
// We might have either a `,` -> `;` typo, or a block without braces. We need
|
||||||
// a more subtle parsing strategy.
|
// a more subtle parsing strategy.
|
||||||
|
@ -3433,14 +3437,20 @@ impl<'a> Parser<'a> {
|
||||||
pth: ast::Path,
|
pth: ast::Path,
|
||||||
recover: bool,
|
recover: bool,
|
||||||
close_delim: Delimiter,
|
close_delim: Delimiter,
|
||||||
) -> PResult<'a, (ThinVec<ExprField>, ast::StructRest, bool)> {
|
) -> PResult<
|
||||||
|
'a,
|
||||||
|
(
|
||||||
|
ThinVec<ExprField>,
|
||||||
|
ast::StructRest,
|
||||||
|
Option<ErrorGuaranteed>, /* async blocks are forbidden in Rust 2015 */
|
||||||
|
),
|
||||||
|
> {
|
||||||
let mut fields = ThinVec::new();
|
let mut fields = ThinVec::new();
|
||||||
let mut base = ast::StructRest::None;
|
let mut base = ast::StructRest::None;
|
||||||
let mut recover_async = false;
|
let mut recovered_async = None;
|
||||||
let in_if_guard = self.restrictions.contains(Restrictions::IN_IF_GUARD);
|
let in_if_guard = self.restrictions.contains(Restrictions::IN_IF_GUARD);
|
||||||
|
|
||||||
let mut async_block_err = |e: &mut DiagnosticBuilder<'_>, span: Span| {
|
let async_block_err = |e: &mut DiagnosticBuilder<'_>, span: Span| {
|
||||||
recover_async = true;
|
|
||||||
errors::AsyncBlockIn2015 { span }.add_to_diagnostic(e);
|
errors::AsyncBlockIn2015 { span }.add_to_diagnostic(e);
|
||||||
errors::HelpUseLatestEdition::new().add_to_diagnostic(e);
|
errors::HelpUseLatestEdition::new().add_to_diagnostic(e);
|
||||||
};
|
};
|
||||||
|
@ -3465,9 +3475,34 @@ impl<'a> Parser<'a> {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let recovery_field = self.find_struct_error_after_field_looking_code();
|
// Peek the field's ident before parsing its expr in order to emit better diagnostics.
|
||||||
|
let peek = self
|
||||||
|
.token
|
||||||
|
.ident()
|
||||||
|
.filter(|(ident, is_raw)| {
|
||||||
|
(!ident.is_reserved() || matches!(is_raw, IdentIsRaw::Yes))
|
||||||
|
&& self.look_ahead(1, |tok| *tok == token::Colon)
|
||||||
|
})
|
||||||
|
.map(|(ident, _)| ident);
|
||||||
|
|
||||||
|
// We still want a field even if its expr didn't parse.
|
||||||
|
let field_ident = |this: &Self, guar: ErrorGuaranteed| {
|
||||||
|
peek.map(|ident| {
|
||||||
|
let span = ident.span;
|
||||||
|
ExprField {
|
||||||
|
ident,
|
||||||
|
span,
|
||||||
|
expr: this.mk_expr_err(span, guar),
|
||||||
|
is_shorthand: false,
|
||||||
|
attrs: AttrVec::new(),
|
||||||
|
id: DUMMY_NODE_ID,
|
||||||
|
is_placeholder: false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
let parsed_field = match self.parse_expr_field() {
|
let parsed_field = match self.parse_expr_field() {
|
||||||
Ok(f) => Some(f),
|
Ok(f) => Ok(f),
|
||||||
Err(mut e) => {
|
Err(mut e) => {
|
||||||
if pth == kw::Async {
|
if pth == kw::Async {
|
||||||
async_block_err(&mut e, pth.span);
|
async_block_err(&mut e, pth.span);
|
||||||
|
@ -3499,7 +3534,10 @@ impl<'a> Parser<'a> {
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
e.emit();
|
let guar = e.emit();
|
||||||
|
if pth == kw::Async {
|
||||||
|
recovered_async = Some(guar);
|
||||||
|
}
|
||||||
|
|
||||||
// If the next token is a comma, then try to parse
|
// If the next token is a comma, then try to parse
|
||||||
// what comes next as additional fields, rather than
|
// what comes next as additional fields, rather than
|
||||||
|
@ -3511,18 +3549,20 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
Err(guar)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_shorthand = parsed_field.as_ref().is_some_and(|f| f.is_shorthand);
|
let is_shorthand = parsed_field.as_ref().is_ok_and(|f| f.is_shorthand);
|
||||||
// A shorthand field can be turned into a full field with `:`.
|
// A shorthand field can be turned into a full field with `:`.
|
||||||
// We should point this out.
|
// We should point this out.
|
||||||
self.check_or_expected(!is_shorthand, TokenType::Token(token::Colon));
|
self.check_or_expected(!is_shorthand, TokenType::Token(token::Colon));
|
||||||
|
|
||||||
match self.expect_one_of(&[token::Comma], &[token::CloseDelim(close_delim)]) {
|
match self.expect_one_of(&[token::Comma], &[token::CloseDelim(close_delim)]) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
if let Some(f) = parsed_field.or(recovery_field) {
|
if let Some(f) =
|
||||||
|
parsed_field.or_else(|guar| field_ident(self, guar).ok_or(guar)).ok()
|
||||||
|
{
|
||||||
// Only include the field if there's no parse error for the field name.
|
// Only include the field if there's no parse error for the field name.
|
||||||
fields.push(f);
|
fields.push(f);
|
||||||
}
|
}
|
||||||
|
@ -3532,8 +3572,7 @@ impl<'a> Parser<'a> {
|
||||||
async_block_err(&mut e, pth.span);
|
async_block_err(&mut e, pth.span);
|
||||||
} else {
|
} else {
|
||||||
e.span_label(pth.span, "while parsing this struct");
|
e.span_label(pth.span, "while parsing this struct");
|
||||||
if let Some(f) = recovery_field {
|
if peek.is_some() {
|
||||||
fields.push(f);
|
|
||||||
e.span_suggestion(
|
e.span_suggestion(
|
||||||
self.prev_token.span.shrink_to_hi(),
|
self.prev_token.span.shrink_to_hi(),
|
||||||
"try adding a comma",
|
"try adding a comma",
|
||||||
|
@ -3545,13 +3584,18 @@ impl<'a> Parser<'a> {
|
||||||
if !recover {
|
if !recover {
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
e.emit();
|
let guar = e.emit();
|
||||||
|
if pth == kw::Async {
|
||||||
|
recovered_async = Some(guar);
|
||||||
|
} else if let Some(f) = field_ident(self, guar) {
|
||||||
|
fields.push(f);
|
||||||
|
}
|
||||||
self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore);
|
self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore);
|
||||||
self.eat(&token::Comma);
|
self.eat(&token::Comma);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok((fields, base, recover_async))
|
Ok((fields, base, recovered_async))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Precondition: already parsed the '{'.
|
/// Precondition: already parsed the '{'.
|
||||||
|
@ -3562,39 +3606,18 @@ impl<'a> Parser<'a> {
|
||||||
recover: bool,
|
recover: bool,
|
||||||
) -> PResult<'a, P<Expr>> {
|
) -> PResult<'a, P<Expr>> {
|
||||||
let lo = pth.span;
|
let lo = pth.span;
|
||||||
let (fields, base, recover_async) =
|
let (fields, base, recovered_async) =
|
||||||
self.parse_struct_fields(pth.clone(), recover, Delimiter::Brace)?;
|
self.parse_struct_fields(pth.clone(), recover, Delimiter::Brace)?;
|
||||||
let span = lo.to(self.token.span);
|
let span = lo.to(self.token.span);
|
||||||
self.expect(&token::CloseDelim(Delimiter::Brace))?;
|
self.expect(&token::CloseDelim(Delimiter::Brace))?;
|
||||||
let expr = if recover_async {
|
let expr = if let Some(guar) = recovered_async {
|
||||||
ExprKind::Err
|
ExprKind::Err(guar)
|
||||||
} else {
|
} else {
|
||||||
ExprKind::Struct(P(ast::StructExpr { qself, path: pth, fields, rest: base }))
|
ExprKind::Struct(P(ast::StructExpr { qself, path: pth, fields, rest: base }))
|
||||||
};
|
};
|
||||||
Ok(self.mk_expr(span, expr))
|
Ok(self.mk_expr(span, expr))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use in case of error after field-looking code: `S { foo: () with a }`.
|
|
||||||
fn find_struct_error_after_field_looking_code(&self) -> Option<ExprField> {
|
|
||||||
match self.token.ident() {
|
|
||||||
Some((ident, is_raw))
|
|
||||||
if (matches!(is_raw, IdentIsRaw::Yes) || !ident.is_reserved())
|
|
||||||
&& self.look_ahead(1, |t| *t == token::Colon) =>
|
|
||||||
{
|
|
||||||
Some(ast::ExprField {
|
|
||||||
ident,
|
|
||||||
span: self.token.span,
|
|
||||||
expr: self.mk_expr_err(self.token.span),
|
|
||||||
is_shorthand: false,
|
|
||||||
attrs: AttrVec::new(),
|
|
||||||
id: DUMMY_NODE_ID,
|
|
||||||
is_placeholder: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn recover_struct_comma_after_dotdot(&mut self, span: Span) {
|
fn recover_struct_comma_after_dotdot(&mut self, span: Span) {
|
||||||
if self.token != token::Comma {
|
if self.token != token::Comma {
|
||||||
return;
|
return;
|
||||||
|
@ -3718,8 +3741,8 @@ impl<'a> Parser<'a> {
|
||||||
limits: RangeLimits,
|
limits: RangeLimits,
|
||||||
) -> ExprKind {
|
) -> ExprKind {
|
||||||
if end.is_none() && limits == RangeLimits::Closed {
|
if end.is_none() && limits == RangeLimits::Closed {
|
||||||
self.inclusive_range_with_incorrect_end();
|
let guar = self.inclusive_range_with_incorrect_end();
|
||||||
ExprKind::Err
|
ExprKind::Err(guar)
|
||||||
} else {
|
} else {
|
||||||
ExprKind::Range(start, end, limits)
|
ExprKind::Range(start, end, limits)
|
||||||
}
|
}
|
||||||
|
@ -3756,8 +3779,8 @@ impl<'a> Parser<'a> {
|
||||||
self.mk_expr_with_attrs(span, kind, AttrVec::new())
|
self.mk_expr_with_attrs(span, kind, AttrVec::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn mk_expr_err(&self, span: Span) -> P<Expr> {
|
pub(super) fn mk_expr_err(&self, span: Span, guar: ErrorGuaranteed) -> P<Expr> {
|
||||||
self.mk_expr(span, ExprKind::Err)
|
self.mk_expr(span, ExprKind::Err(guar))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create expression span ensuring the span of the parent node
|
/// Create expression span ensuring the span of the parent node
|
||||||
|
@ -3949,7 +3972,8 @@ impl MutVisitor for CondChecker<'_> {
|
||||||
| ExprKind::Become(_)
|
| ExprKind::Become(_)
|
||||||
| ExprKind::IncludedBytes(_)
|
| ExprKind::IncludedBytes(_)
|
||||||
| ExprKind::FormatArgs(_)
|
| ExprKind::FormatArgs(_)
|
||||||
| ExprKind::Err => {
|
| ExprKind::Err(_)
|
||||||
|
| ExprKind::Dummy => {
|
||||||
// These would forbid any let expressions they contain already.
|
// These would forbid any let expressions they contain already.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2328,11 +2328,11 @@ impl<'a> Parser<'a> {
|
||||||
let _ = self.parse_expr()?;
|
let _ = self.parse_expr()?;
|
||||||
self.expect_semi()?; // `;`
|
self.expect_semi()?; // `;`
|
||||||
let span = eq_sp.to(self.prev_token.span);
|
let span = eq_sp.to(self.prev_token.span);
|
||||||
self.dcx().emit_err(errors::FunctionBodyEqualsExpr {
|
let guar = self.dcx().emit_err(errors::FunctionBodyEqualsExpr {
|
||||||
span,
|
span,
|
||||||
sugg: errors::FunctionBodyEqualsExprSugg { eq: eq_sp, semi: self.prev_token.span },
|
sugg: errors::FunctionBodyEqualsExprSugg { eq: eq_sp, semi: self.prev_token.span },
|
||||||
});
|
});
|
||||||
(AttrVec::new(), Some(self.mk_block_err(span)))
|
(AttrVec::new(), Some(self.mk_block_err(span, guar)))
|
||||||
} else {
|
} else {
|
||||||
let expected = if req_body {
|
let expected = if req_body {
|
||||||
&[token::OpenDelim(Delimiter::Brace)][..]
|
&[token::OpenDelim(Delimiter::Brace)][..]
|
||||||
|
|
|
@ -388,7 +388,7 @@ impl<'a> Parser<'a> {
|
||||||
// Parse `?`, `.f`, `(arg0, arg1, ...)` or `[expr]` until they've all been eaten.
|
// Parse `?`, `.f`, `(arg0, arg1, ...)` or `[expr]` until they've all been eaten.
|
||||||
if let Ok(expr) = snapshot
|
if let Ok(expr) = snapshot
|
||||||
.parse_expr_dot_or_call_with(
|
.parse_expr_dot_or_call_with(
|
||||||
self.mk_expr_err(pat_span), // equivalent to transforming the parsed pattern into an `Expr`
|
self.mk_expr(pat_span, ExprKind::Dummy), // equivalent to transforming the parsed pattern into an `Expr`
|
||||||
pat_span,
|
pat_span,
|
||||||
AttrVec::new(),
|
AttrVec::new(),
|
||||||
)
|
)
|
||||||
|
@ -566,7 +566,7 @@ impl<'a> Parser<'a> {
|
||||||
match self.parse_literal_maybe_minus() {
|
match self.parse_literal_maybe_minus() {
|
||||||
Ok(begin) => {
|
Ok(begin) => {
|
||||||
let begin = match self.maybe_recover_trailing_expr(begin.span, false) {
|
let begin = match self.maybe_recover_trailing_expr(begin.span, false) {
|
||||||
Some(_) => self.mk_expr_err(begin.span),
|
Some(guar) => self.mk_expr_err(begin.span, guar),
|
||||||
None => begin,
|
None => begin,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -719,7 +719,7 @@ impl<'a> Parser<'a> {
|
||||||
self.parse_pat_range_begin_with(begin.clone(), form)
|
self.parse_pat_range_begin_with(begin.clone(), form)
|
||||||
}
|
}
|
||||||
// recover ranges with parentheses around the `(start)..`
|
// recover ranges with parentheses around the `(start)..`
|
||||||
PatKind::Err(_)
|
PatKind::Err(guar)
|
||||||
if self.may_recover()
|
if self.may_recover()
|
||||||
&& let Some(form) = self.parse_range_end() =>
|
&& let Some(form) = self.parse_range_end() =>
|
||||||
{
|
{
|
||||||
|
@ -731,7 +731,7 @@ impl<'a> Parser<'a> {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
self.parse_pat_range_begin_with(self.mk_expr(pat.span, ExprKind::Err), form)
|
self.parse_pat_range_begin_with(self.mk_expr_err(pat.span, *guar), form)
|
||||||
}
|
}
|
||||||
|
|
||||||
// (pat) with optional parentheses
|
// (pat) with optional parentheses
|
||||||
|
@ -886,7 +886,7 @@ impl<'a> Parser<'a> {
|
||||||
Ok(PatKind::Range(Some(begin), end, re))
|
Ok(PatKind::Range(Some(begin), end, re))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn inclusive_range_with_incorrect_end(&mut self) {
|
pub(super) fn inclusive_range_with_incorrect_end(&mut self) -> ErrorGuaranteed {
|
||||||
let tok = &self.token;
|
let tok = &self.token;
|
||||||
let span = self.prev_token.span;
|
let span = self.prev_token.span;
|
||||||
// If the user typed "..==" instead of "..=", we want to give them
|
// If the user typed "..==" instead of "..=", we want to give them
|
||||||
|
@ -905,15 +905,13 @@ impl<'a> Parser<'a> {
|
||||||
let _ = self.parse_pat_range_end().map_err(|e| e.cancel());
|
let _ = self.parse_pat_range_end().map_err(|e| e.cancel());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.dcx().emit_err(InclusiveRangeExtraEquals { span: span_with_eq });
|
self.dcx().emit_err(InclusiveRangeExtraEquals { span: span_with_eq })
|
||||||
}
|
}
|
||||||
token::Gt if no_space => {
|
token::Gt if no_space => {
|
||||||
let after_pat = span.with_hi(span.hi() - rustc_span::BytePos(1)).shrink_to_hi();
|
let after_pat = span.with_hi(span.hi() - rustc_span::BytePos(1)).shrink_to_hi();
|
||||||
self.dcx().emit_err(InclusiveRangeMatchArrow { span, arrow: tok.span, after_pat });
|
self.dcx().emit_err(InclusiveRangeMatchArrow { span, arrow: tok.span, after_pat })
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
self.dcx().emit_err(InclusiveRangeNoEnd { span });
|
|
||||||
}
|
}
|
||||||
|
_ => self.dcx().emit_err(InclusiveRangeNoEnd { span }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -987,7 +985,7 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(match recovered {
|
Ok(match recovered {
|
||||||
Some(_) => self.mk_expr_err(bound.span),
|
Some(guar) => self.mk_expr_err(bound.span, guar),
|
||||||
None => bound,
|
None => bound,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Stmt};
|
||||||
use rustc_ast::{StmtKind, DUMMY_NODE_ID};
|
use rustc_ast::{StmtKind, DUMMY_NODE_ID};
|
||||||
use rustc_errors::{Applicability, DiagnosticBuilder, PResult};
|
use rustc_errors::{Applicability, DiagnosticBuilder, PResult};
|
||||||
use rustc_span::symbol::{kw, sym, Ident};
|
use rustc_span::symbol::{kw, sym, Ident};
|
||||||
use rustc_span::{BytePos, Span};
|
use rustc_span::{BytePos, ErrorGuaranteed, Span};
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -610,9 +610,9 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err.emit();
|
let guar = err.emit();
|
||||||
self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
|
self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
|
||||||
Some(self.mk_stmt_err(self.token.span))
|
Some(self.mk_stmt_err(self.token.span, guar))
|
||||||
}
|
}
|
||||||
Ok(stmt) => stmt,
|
Ok(stmt) => stmt,
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
|
@ -651,10 +651,10 @@ impl<'a> Parser<'a> {
|
||||||
.contains(&self.token.kind) =>
|
.contains(&self.token.kind) =>
|
||||||
{
|
{
|
||||||
// The user has written `#[attr] expr` which is unsupported. (#106020)
|
// The user has written `#[attr] expr` which is unsupported. (#106020)
|
||||||
self.attr_on_non_tail_expr(&expr);
|
let guar = self.attr_on_non_tail_expr(&expr);
|
||||||
// We already emitted an error, so don't emit another type error
|
// We already emitted an error, so don't emit another type error
|
||||||
let sp = expr.span.to(self.prev_token.span);
|
let sp = expr.span.to(self.prev_token.span);
|
||||||
*expr = self.mk_expr_err(sp);
|
*expr = self.mk_expr_err(sp, guar);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expression without semicolon.
|
// Expression without semicolon.
|
||||||
|
@ -666,10 +666,18 @@ impl<'a> Parser<'a> {
|
||||||
let expect_result =
|
let expect_result =
|
||||||
self.expect_one_of(&[], &[token::Semi, token::CloseDelim(Delimiter::Brace)]);
|
self.expect_one_of(&[], &[token::Semi, token::CloseDelim(Delimiter::Brace)]);
|
||||||
|
|
||||||
|
// Try to both emit a better diagnostic, and avoid further errors by replacing
|
||||||
|
// the `expr` with `ExprKind::Err`.
|
||||||
let replace_with_err = 'break_recover: {
|
let replace_with_err = 'break_recover: {
|
||||||
match expect_result {
|
match expect_result {
|
||||||
// Recover from parser, skip type error to avoid extra errors.
|
Ok(Recovered::No) => None,
|
||||||
Ok(Recovered::Yes) => true,
|
Ok(Recovered::Yes) => {
|
||||||
|
// Skip type error to avoid extra errors.
|
||||||
|
let guar = self
|
||||||
|
.dcx()
|
||||||
|
.span_delayed_bug(self.prev_token.span, "expected `;` or `}`");
|
||||||
|
Some(guar)
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if self.recover_colon_as_semi() {
|
if self.recover_colon_as_semi() {
|
||||||
// recover_colon_as_semi has already emitted a nicer error.
|
// recover_colon_as_semi has already emitted a nicer error.
|
||||||
|
@ -677,7 +685,7 @@ impl<'a> Parser<'a> {
|
||||||
add_semi_to_stmt = true;
|
add_semi_to_stmt = true;
|
||||||
eat_semi = false;
|
eat_semi = false;
|
||||||
|
|
||||||
break 'break_recover false;
|
break 'break_recover None;
|
||||||
}
|
}
|
||||||
|
|
||||||
match &expr.kind {
|
match &expr.kind {
|
||||||
|
@ -705,13 +713,13 @@ impl<'a> Parser<'a> {
|
||||||
};
|
};
|
||||||
match self.parse_expr_labeled(label, false) {
|
match self.parse_expr_labeled(label, false) {
|
||||||
Ok(labeled_expr) => {
|
Ok(labeled_expr) => {
|
||||||
e.delay_as_bug();
|
e.cancel();
|
||||||
self.dcx().emit_err(MalformedLoopLabel {
|
self.dcx().emit_err(MalformedLoopLabel {
|
||||||
span: label.ident.span,
|
span: label.ident.span,
|
||||||
correct_label: label.ident,
|
correct_label: label.ident,
|
||||||
});
|
});
|
||||||
*expr = labeled_expr;
|
*expr = labeled_expr;
|
||||||
break 'break_recover false;
|
break 'break_recover None;
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
err.cancel();
|
err.cancel();
|
||||||
|
@ -723,26 +731,26 @@ impl<'a> Parser<'a> {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) =
|
let res =
|
||||||
self.check_mistyped_turbofish_with_multiple_type_params(e, expr)
|
self.check_mistyped_turbofish_with_multiple_type_params(e, expr);
|
||||||
{
|
|
||||||
if recover.no() {
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
e.emit();
|
|
||||||
self.recover_stmt();
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
Some(if recover.no() {
|
||||||
|
res?
|
||||||
|
} else {
|
||||||
|
res.unwrap_or_else(|e| {
|
||||||
|
let guar = e.emit();
|
||||||
|
self.recover_stmt();
|
||||||
|
guar
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
Ok(Recovered::No) => false,
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if replace_with_err {
|
if let Some(guar) = replace_with_err {
|
||||||
// We already emitted an error, so don't emit another type error
|
// We already emitted an error, so don't emit another type error
|
||||||
let sp = expr.span.to(self.prev_token.span);
|
let sp = expr.span.to(self.prev_token.span);
|
||||||
*expr = self.mk_expr_err(sp);
|
*expr = self.mk_expr_err(sp, guar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StmtKind::Expr(_) | StmtKind::MacCall(_) => {}
|
StmtKind::Expr(_) | StmtKind::MacCall(_) => {}
|
||||||
|
@ -791,11 +799,11 @@ impl<'a> Parser<'a> {
|
||||||
Stmt { id: DUMMY_NODE_ID, kind, span }
|
Stmt { id: DUMMY_NODE_ID, kind, span }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn mk_stmt_err(&self, span: Span) -> Stmt {
|
pub(super) fn mk_stmt_err(&self, span: Span, guar: ErrorGuaranteed) -> Stmt {
|
||||||
self.mk_stmt(span, StmtKind::Expr(self.mk_expr_err(span)))
|
self.mk_stmt(span, StmtKind::Expr(self.mk_expr_err(span, guar)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn mk_block_err(&self, span: Span) -> P<Block> {
|
pub(super) fn mk_block_err(&self, span: Span, guar: ErrorGuaranteed) -> P<Block> {
|
||||||
self.mk_block(thin_vec![self.mk_stmt_err(span)], BlockCheckMode::Default, span)
|
self.mk_block(thin_vec![self.mk_stmt_err(span, guar)], BlockCheckMode::Default, span)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,7 +90,7 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta
|
||||||
// been reported.
|
// been reported.
|
||||||
let msg = "attribute value must be a literal";
|
let msg = "attribute value must be a literal";
|
||||||
let mut err = sess.dcx.struct_span_err(expr.span, msg);
|
let mut err = sess.dcx.struct_span_err(expr.span, msg);
|
||||||
if let ast::ExprKind::Err = expr.kind {
|
if let ast::ExprKind::Err(_) = expr.kind {
|
||||||
err.downgrade_to_delayed_bug();
|
err.downgrade_to_delayed_bug();
|
||||||
}
|
}
|
||||||
return Err(err);
|
return Err(err);
|
||||||
|
|
|
@ -589,7 +589,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
|
||||||
If, While, ForLoop, Loop, Match, Closure, Block, Await, TryBlock, Assign,
|
If, While, ForLoop, Loop, Match, Closure, Block, Await, TryBlock, Assign,
|
||||||
AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
|
AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
|
||||||
InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet,
|
InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet,
|
||||||
Become, IncludedBytes, Gen, Err
|
Become, IncludedBytes, Gen, Err, Dummy
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
ast_visit::walk_expr(self, e)
|
ast_visit::walk_expr(self, e)
|
||||||
|
|
|
@ -143,7 +143,8 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
|
||||||
match (&l.kind, &r.kind) {
|
match (&l.kind, &r.kind) {
|
||||||
(Paren(l), _) => eq_expr(l, r),
|
(Paren(l), _) => eq_expr(l, r),
|
||||||
(_, Paren(r)) => eq_expr(l, r),
|
(_, Paren(r)) => eq_expr(l, r),
|
||||||
(Err, Err) => true,
|
(Err(_), Err(_)) => true,
|
||||||
|
(Dummy, _) | (_, Dummy) => unreachable!("comparing `ExprKind::Dummy`"),
|
||||||
(Try(l), Try(r)) | (Await(l, _), Await(r, _)) => eq_expr(l, r),
|
(Try(l), Try(r)) | (Await(l, _), Await(r, _)) => eq_expr(l, r),
|
||||||
(Array(l), Array(r)) => over(l, r, |l, r| eq_expr(l, r)),
|
(Array(l), Array(r)) => over(l, r, |l, r| eq_expr(l, r)),
|
||||||
(Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)),
|
(Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)),
|
||||||
|
|
|
@ -222,7 +222,8 @@ impl<'a> Sugg<'a> {
|
||||||
| ast::ExprKind::Array(..)
|
| ast::ExprKind::Array(..)
|
||||||
| ast::ExprKind::While(..)
|
| ast::ExprKind::While(..)
|
||||||
| ast::ExprKind::Await(..)
|
| ast::ExprKind::Await(..)
|
||||||
| ast::ExprKind::Err => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
|
| ast::ExprKind::Err(_)
|
||||||
|
| ast::ExprKind::Dummy => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
|
||||||
ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp(
|
ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp(
|
||||||
AssocOp::DotDot,
|
AssocOp::DotDot,
|
||||||
lhs.as_ref().map_or("".into(), |lhs| {
|
lhs.as_ref().map_or("".into(), |lhs| {
|
||||||
|
|
|
@ -404,7 +404,7 @@ pub(crate) fn format_expr(
|
||||||
// These do not occur in the AST because macros aren't expanded.
|
// These do not occur in the AST because macros aren't expanded.
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
ast::ExprKind::Err => None,
|
ast::ExprKind::Err(_) | ast::ExprKind::Dummy => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
expr_rw
|
expr_rw
|
||||||
|
|
|
@ -497,7 +497,8 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr
|
||||||
| ast::ExprKind::Break(..)
|
| ast::ExprKind::Break(..)
|
||||||
| ast::ExprKind::Cast(..)
|
| ast::ExprKind::Cast(..)
|
||||||
| ast::ExprKind::Continue(..)
|
| ast::ExprKind::Continue(..)
|
||||||
| ast::ExprKind::Err
|
| ast::ExprKind::Dummy
|
||||||
|
| ast::ExprKind::Err(_)
|
||||||
| ast::ExprKind::Field(..)
|
| ast::ExprKind::Field(..)
|
||||||
| ast::ExprKind::IncludedBytes(..)
|
| ast::ExprKind::IncludedBytes(..)
|
||||||
| ast::ExprKind::InlineAsm(..)
|
| ast::ExprKind::InlineAsm(..)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue