Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustc_span: More consistent span combination operations #119624

Merged
merged 5 commits into from
Jan 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2489,7 +2489,7 @@ impl<'a> Parser<'a> {
}
ExprKind::Block(_, None) => {
this.dcx().emit_err(errors::IfExpressionMissingCondition {
if_span: lo.shrink_to_hi(),
if_span: lo.with_neighbor(cond.span).shrink_to_hi(),
block_span: self.sess.source_map().start_point(cond_span),
});
std::mem::replace(&mut cond, this.mk_expr_err(cond_span.shrink_to_hi()))
Expand Down Expand Up @@ -3735,7 +3735,7 @@ impl<'a> Parser<'a> {
}

pub(crate) fn mk_expr(&self, span: Span, kind: ExprKind) -> P<Expr> {
P(Expr { kind, span, attrs: AttrVec::new(), id: DUMMY_NODE_ID, tokens: None })
self.mk_expr_with_attrs(span, kind, AttrVec::new())
}

pub(super) fn mk_expr_err(&self, span: Span) -> P<Expr> {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2118,7 +2118,7 @@ impl<'a> Parser<'a> {
Applicability::MaybeIncorrect,
);
err.span_suggestion(
span.shrink_to_hi(),
span.with_neighbor(self.token.span).shrink_to_hi(),
"add a semicolon",
';',
Applicability::MaybeIncorrect,
Expand Down Expand Up @@ -2632,7 +2632,7 @@ impl<'a> Parser<'a> {

let is_name_required = match this.token.kind {
token::DotDotDot => false,
_ => req_name(this.token.span.edition()),
_ => req_name(this.token.span.with_neighbor(this.prev_token.span).edition()),
};
let (pat, ty) = if is_name_required || this.is_named_param() {
debug!("parse_param_general parse_pat (is_name_required:{})", is_name_required);
Expand Down
17 changes: 1 addition & 16 deletions compiler/rustc_span/src/hygiene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -850,21 +850,6 @@ impl fmt::Debug for SyntaxContext {
}

impl Span {
/// Creates a fresh expansion with given properties.
/// Expansions are normally created by macros, but in some cases expansions are created for
/// other compiler-generated code to set per-span properties like allowed unstable features.
/// The returned span belongs to the created expansion and has the new properties,
/// but its location is inherited from the current span.
pub fn fresh_expansion(self, expn_id: LocalExpnId) -> Span {
HygieneData::with(|data| {
self.with_ctxt(data.apply_mark(
self.ctxt(),
expn_id.to_expn_id(),
Transparency::Transparent,
))
})
}

/// Reuses the span but adds information like the kind of the desugaring and features that are
/// allowed inside this span.
pub fn mark_with_reason(
Expand All @@ -879,7 +864,7 @@ impl Span {
..ExpnData::default(ExpnKind::Desugaring(reason), self, edition, None, None)
};
let expn_id = LocalExpnId::fresh(expn_data, ctx);
self.fresh_expansion(expn_id)
self.apply_mark(expn_id.to_expn_id(), Transparency::Transparent)
}
}

Expand Down
96 changes: 47 additions & 49 deletions compiler/rustc_span/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,39 @@ impl Span {
)
}

/// Prepare two spans to a combine operation like `to` or `between`.
/// FIXME: consider using declarative macro metavariable spans for the given spans if they are
/// better suitable for combining (#119412).
fn prepare_to_combine(
a_orig: Span,
b_orig: Span,
) -> Result<(SpanData, SpanData, Option<LocalDefId>), Span> {
let (a, b) = (a_orig.data(), b_orig.data());

if a.ctxt != b.ctxt {
// Context mismatches usually happen when procedural macros combine spans copied from
// the macro input with spans produced by the macro (`Span::*_site`).
// In that case we consider the combined span to be produced by the macro and return
// the original macro-produced span as the result.
// Otherwise we just fall back to returning the first span.
// Combining locations typically doesn't make sense in case of context mismatches.
// `is_root` here is a fast path optimization.
let a_is_callsite = a.ctxt.is_root() || a.ctxt == b.span().source_callsite().ctxt();
return Err(if a_is_callsite { b_orig } else { a_orig });
}

let parent = if a.parent == b.parent { a.parent } else { None };
Ok((a, b, parent))
}

/// This span, but in a larger context, may switch to the metavariable span if suitable.
pub fn with_neighbor(self, neighbor: Span) -> Span {
match Span::prepare_to_combine(self, neighbor) {
Ok((this, ..)) => Span::new(this.lo, this.hi, this.ctxt, this.parent),
Err(_) => self,
}
}

/// Returns a `Span` that would enclose both `self` and `end`.
///
/// Note that this can also be used to extend the span "backwards":
Expand All @@ -837,26 +870,12 @@ impl Span {
/// ^^^^^^^^^^^^^^^^^^^^
/// ```
pub fn to(self, end: Span) -> Span {
let span_data = self.data();
let end_data = end.data();
// FIXME(jseyfried): `self.ctxt` should always equal `end.ctxt` here (cf. issue #23480).
// Return the macro span on its own to avoid weird diagnostic output. It is preferable to
// have an incomplete span than a completely nonsensical one.
if span_data.ctxt != end_data.ctxt {
if span_data.ctxt.is_root() {
return end;
} else if end_data.ctxt.is_root() {
return self;
match Span::prepare_to_combine(self, end) {
Ok((from, to, parent)) => {
Span::new(cmp::min(from.lo, to.lo), cmp::max(from.hi, to.hi), from.ctxt, parent)
}
// Both spans fall within a macro.
// FIXME(estebank): check if it is the *same* macro.
Err(fallback) => fallback,
}
Span::new(
cmp::min(span_data.lo, end_data.lo),
cmp::max(span_data.hi, end_data.hi),
if span_data.ctxt.is_root() { end_data.ctxt } else { span_data.ctxt },
if span_data.parent == end_data.parent { span_data.parent } else { None },
)
}

/// Returns a `Span` between the end of `self` to the beginning of `end`.
Expand All @@ -867,14 +886,12 @@ impl Span {
/// ^^^^^^^^^^^^^
/// ```
pub fn between(self, end: Span) -> Span {
let span = self.data();
let end = end.data();
Span::new(
span.hi,
end.lo,
if end.ctxt.is_root() { end.ctxt } else { span.ctxt },
if span.parent == end.parent { span.parent } else { None },
)
match Span::prepare_to_combine(self, end) {
Ok((from, to, parent)) => {
Span::new(cmp::min(from.hi, to.hi), cmp::max(from.lo, to.lo), from.ctxt, parent)
}
Err(fallback) => fallback,
}
}

/// Returns a `Span` from the beginning of `self` until the beginning of `end`.
Expand All @@ -885,31 +902,12 @@ impl Span {
/// ^^^^^^^^^^^^^^^^^
/// ```
pub fn until(self, end: Span) -> Span {
// Most of this function's body is copied from `to`.
// We can't just do `self.to(end.shrink_to_lo())`,
// because to also does some magic where it uses min/max so
// it can handle overlapping spans. Some advanced mis-use of
// `until` with different ctxts makes this visible.
let span_data = self.data();
let end_data = end.data();
// FIXME(jseyfried): `self.ctxt` should always equal `end.ctxt` here (cf. issue #23480).
// Return the macro span on its own to avoid weird diagnostic output. It is preferable to
// have an incomplete span than a completely nonsensical one.
if span_data.ctxt != end_data.ctxt {
if span_data.ctxt.is_root() {
return end;
} else if end_data.ctxt.is_root() {
return self;
match Span::prepare_to_combine(self, end) {
Ok((from, to, parent)) => {
Span::new(cmp::min(from.lo, to.lo), cmp::max(from.lo, to.lo), from.ctxt, parent)
}
// Both spans fall within a macro.
// FIXME(estebank): check if it is the *same* macro.
Err(fallback) => fallback,
}
Span::new(
span_data.lo,
end_data.lo,
if end_data.ctxt.is_root() { end_data.ctxt } else { span_data.ctxt },
if span_data.parent == end_data.parent { span_data.parent } else { None },
)
}

pub fn from_inner(self, inner: InnerSpan) -> Span {
Expand Down
1 change: 1 addition & 0 deletions library/core/src/intrinsics/mir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
//! ```rust
//! #![feature(core_intrinsics, custom_mir)]
//! #![allow(internal_features)]
//! #![allow(unused_assignments)]
//!
//! use core::intrinsics::mir::*;
//!
Expand Down
4 changes: 2 additions & 2 deletions src/librustdoc/passes/lint/check_code_block_syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use rustc_errors::{
use rustc_parse::parse_stream_from_source_str;
use rustc_resolve::rustdoc::source_span_for_markdown_range;
use rustc_session::parse::ParseSess;
use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId};
use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId, Transparency};
use rustc_span::source_map::{FilePathMapping, SourceMap};
use rustc_span::{FileName, InnerSpan, DUMMY_SP};

Expand Down Expand Up @@ -50,7 +50,7 @@ fn check_rust_syntax(
let expn_data =
ExpnData::default(ExpnKind::AstPass(AstPass::TestHarness), DUMMY_SP, edition, None, None);
let expn_id = cx.tcx.with_stable_hashing_context(|hcx| LocalExpnId::fresh(expn_data, hcx));
let span = DUMMY_SP.fresh_expansion(expn_id);
let span = DUMMY_SP.apply_mark(expn_id.to_expn_id(), Transparency::Transparent);

let is_empty = rustc_driver::catch_fatal_errors(|| {
parse_stream_from_source_str(
Expand Down
5 changes: 3 additions & 2 deletions tests/ui/anon-params/anon-params-edition-hygiene.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// check-pass
// edition:2018
// aux-build:anon-params-edition-hygiene.rs

Expand All @@ -8,6 +7,8 @@
#[macro_use]
extern crate anon_params_edition_hygiene;

generate_trait_2015!(u8);
generate_trait_2015_ident!(u8);
// FIXME: Edition hygiene doesn't work correctly with `tt`s in this case.
generate_trait_2015_tt!(u8); //~ ERROR expected one of `:`, `@`, or `|`, found `)`

fn main() {}
23 changes: 23 additions & 0 deletions tests/ui/anon-params/anon-params-edition-hygiene.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
error: expected one of `:`, `@`, or `|`, found `)`
--> $DIR/anon-params-edition-hygiene.rs:12:1
|
LL | generate_trait_2015_tt!(u8);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected one of `:`, `@`, or `|`
|
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
= note: this error originates in the macro `generate_trait_2015_tt` (in Nightly builds, run with -Z macro-backtrace for more info)
help: if this is a `self` type, give it a parameter name
|
LL | generate_trait_2015_tt!(self: u8);
| +++++
help: if this is a parameter name, give it a type
|
LL | generate_trait_2015_tt!(u8: TypeName);
| ++++++++++
help: if this is a type, explicitly ignore the parameter name
|
LL | generate_trait_2015_tt!(_: u8);
| ++

error: aborting due to 1 previous error

13 changes: 11 additions & 2 deletions tests/ui/anon-params/auxiliary/anon-params-edition-hygiene.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
// edition:2015

#[macro_export]
macro_rules! generate_trait_2015 {
macro_rules! generate_trait_2015_ident {
($Type: ident) => {
trait Trait {
trait Trait1 {
fn method($Type) {}
}
};
}

#[macro_export]
macro_rules! generate_trait_2015_tt {
($Type: tt) => {
trait Trait2 {
fn method($Type) {}
}
};
Expand Down
5 changes: 5 additions & 0 deletions tests/ui/editions/auxiliary/edition-kw-macro-2015.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,8 @@ macro_rules! consumes_async_raw {
macro_rules! passes_ident {
($i: ident) => ($i)
}

#[macro_export]
macro_rules! passes_tt {
($i: tt) => ($i)
}
5 changes: 5 additions & 0 deletions tests/ui/editions/auxiliary/edition-kw-macro-2018.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,8 @@ macro_rules! consumes_async_raw {
macro_rules! passes_ident {
($i: ident) => ($i)
}

#[macro_export]
macro_rules! passes_tt {
($i: tt) => ($i)
}
2 changes: 2 additions & 0 deletions tests/ui/editions/edition-keywords-2015-2015-parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub fn check_async() {

if passes_ident!(async) == 1 {} // OK
if passes_ident!(r#async) == 1 {} // OK
if passes_tt!(async) == 1 {} // OK
if passes_tt!(r#async) == 1 {} // OK
module::async(); // OK
module::r#async(); // OK
}
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/editions/edition-keywords-2015-2015.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub fn check_async() {

if passes_ident!(async) == 1 {} // OK
if passes_ident!(r#async) == 1 {} // OK
if passes_tt!(async) == 1 {} // OK
if passes_tt!(r#async) == 1 {} // OK
one_async::async(); // OK
one_async::r#async(); // OK
two_async::async(); // OK
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/editions/edition-keywords-2015-2018-parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub fn check_async() {

if passes_ident!(async) == 1 {} // OK
if passes_ident!(r#async) == 1 {} // OK
if passes_tt!(async) == 1 {} // OK
if passes_tt!(r#async) == 1 {} // OK
module::async(); // OK
module::r#async(); // OK
}
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/editions/edition-keywords-2015-2018.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub fn check_async() {

if passes_ident!(async) == 1 {} // OK
if passes_ident!(r#async) == 1 {} // OK
if passes_tt!(async) == 1 {} // OK
if passes_tt!(r#async) == 1 {} // OK
// one_async::async(); // ERROR, unresolved name
// one_async::r#async(); // ERROR, unresolved name
two_async::async(); // OK
Expand Down
4 changes: 3 additions & 1 deletion tests/ui/editions/edition-keywords-2018-2015-parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ pub fn check_async() {
r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async`
r#async = consumes_async_raw!(r#async); // OK

if passes_ident!(async) == 1 {}
if passes_ident!(async) == 1 {} // FIXME: Edition hygiene bug, async here is 2018 and reserved
if passes_ident!(r#async) == 1 {} // OK
if passes_tt!(async) == 1 {} //~ ERROR macro expansion ends with an incomplete expression
if passes_tt!(r#async) == 1 {} // OK
module::async(); //~ ERROR expected identifier, found keyword `async`
module::r#async(); // OK

Expand Down
14 changes: 10 additions & 4 deletions tests/ui/editions/edition-keywords-2018-2015-parsing.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ LL | let mut r#async = 1;
| ++

error: expected identifier, found keyword `async`
--> $DIR/edition-keywords-2018-2015-parsing.rs:26:13
--> $DIR/edition-keywords-2018-2015-parsing.rs:28:13
|
LL | module::async();
| ^^^^^ expected identifier, found keyword
Expand Down Expand Up @@ -52,17 +52,23 @@ LL | ($i: ident) => ($i)
|
::: $DIR/edition-keywords-2018-2015-parsing.rs:24:8
|
LL | if passes_ident!(async) == 1 {}
LL | if passes_ident!(async) == 1 {} // FIXME: Edition hygiene bug, async here is 2018 and reserved
| -------------------- in this macro invocation

error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||`
--> $DIR/edition-keywords-2018-2015-parsing.rs:26:24
|
LL | if passes_tt!(async) == 1 {}
| ^ expected one of `move`, `|`, or `||`

error[E0308]: mismatched types
--> $DIR/edition-keywords-2018-2015-parsing.rs:29:33
--> $DIR/edition-keywords-2018-2015-parsing.rs:31:33
|
LL | let _recovery_witness: () = 0;
| -- ^ expected `()`, found integer
| |
| expected due to this

error: aborting due to 6 previous errors
error: aborting due to 7 previous errors

For more information about this error, try `rustc --explain E0308`.
2 changes: 2 additions & 0 deletions tests/ui/editions/edition-keywords-2018-2015.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub fn check_async() {

// if passes_ident!(async) == 1 {} // ERROR, reserved
if passes_ident!(r#async) == 1 {} // OK
// if passes_tt!(async) == 1 {} // ERROR, reserved
if passes_tt!(r#async) == 1 {} // OK
// one_async::async(); // ERROR, reserved
one_async::r#async(); // OK
// two_async::async(); // ERROR, reserved
Expand Down
Loading
Loading