Auto merge of #63823 - petrochenkov:noapply2, r=matthewjasper

Audit uses of `apply_mark` in built-in macros + Remove default macro transparencies

Every use of `apply_mark` in a built-in or procedural macro is supposed to look like this
```rust
location.with_ctxt(SyntaxContext::root().apply_mark(ecx.current_expansion.id))
```
where `SyntaxContext::root()` means that the built-in/procedural macro is defined directly, rather than expanded from some other macro.

However, few people understood what `apply_mark` does, so we had a lot of copy-pasted uses of it looking e.g. like
```rust
span = span.apply_mark(ecx.current_expansion.id);
```
, which doesn't really make sense for procedural macros, but at the same time is not too harmful, if the macros use the traditional `macro_rules` hygiene.

So, to fight this, we stop using `apply_mark` directly in built-in macro implementations, and follow the example of regular proc macros instead and use analogues of `Span::def_site()` and `Span::call_site()`, which are much more intuitive and less error-prone.
- `ecx.with_def_site_ctxt(span)` takes the `span`'s location and combines it with a def-site context.
- `ecx.with_call_site_ctxt(span)` takes the `span`'s location and combines it with a call-site context.

Even if called multiple times (which sometimes happens due to some historical messiness of the built-in macro code) these functions will produce the same result, unlike `apply_mark` which will grow  the mark chain further in this case.

---

After `apply_mark`s in built-in macros are eliminated, the remaining `apply_mark`s are very few in number, so we can start passing the previously implicit `Transparency` argument to them explicitly, thus eliminating the need in `default_transparency` fields in hygiene structures and `#[rustc_macro_transparency]` annotations on built-in macros.

So, the task of making built-in macros opaque can now be formulated as "eliminate `with_legacy_ctxt` in favor of `with_def_site_ctxt`" rather than "replace `#[rustc_macro_transparency = "semitransparent"]` with `#[rustc_macro_transparency = "opaque"]`".

r? @matthewjasper
This commit is contained in:
bors 2019-08-24 14:07:06 +00:00
commit 5ade61a4f1
28 changed files with 152 additions and 179 deletions

View file

@ -3,7 +3,7 @@ use crate::attr::{HasAttrs, Stability, Deprecation};
use crate::source_map::SourceMap;
use crate::edition::Edition;
use crate::ext::expand::{self, AstFragment, Invocation};
use crate::ext::hygiene::{ExpnId, SyntaxContext, Transparency};
use crate::ext::hygiene::{ExpnId, Transparency};
use crate::mut_visit::{self, MutVisitor};
use crate::parse::{self, parser, DirectoryOwnership};
use crate::parse::token;
@ -549,8 +549,6 @@ pub struct SyntaxExtension {
pub kind: SyntaxExtensionKind,
/// Span of the macro definition.
pub span: Span,
/// Hygienic properties of spans produced by this macro by default.
pub default_transparency: Transparency,
/// Whitelist of unstable features that are treated as stable inside this macro.
pub allow_internal_unstable: Option<Lrc<[Symbol]>>,
/// Suppresses the `unsafe_code` lint for code produced by this macro.
@ -572,22 +570,6 @@ pub struct SyntaxExtension {
pub is_derive_copy: bool,
}
impl SyntaxExtensionKind {
/// When a syntax extension is constructed,
/// its transparency can often be inferred from its kind.
fn default_transparency(&self) -> Transparency {
match self {
SyntaxExtensionKind::Bang(..) |
SyntaxExtensionKind::Attr(..) |
SyntaxExtensionKind::Derive(..) |
SyntaxExtensionKind::NonMacroAttr { .. } => Transparency::Opaque,
SyntaxExtensionKind::LegacyBang(..) |
SyntaxExtensionKind::LegacyAttr(..) |
SyntaxExtensionKind::LegacyDerive(..) => Transparency::SemiTransparent,
}
}
}
impl SyntaxExtension {
/// Returns which kind of macro calls this syntax extension.
pub fn macro_kind(&self) -> MacroKind {
@ -606,7 +588,6 @@ impl SyntaxExtension {
pub fn default(kind: SyntaxExtensionKind, edition: Edition) -> SyntaxExtension {
SyntaxExtension {
span: DUMMY_SP,
default_transparency: kind.default_transparency(),
allow_internal_unstable: None,
allow_internal_unsafe: false,
local_inner_macros: false,
@ -646,7 +627,6 @@ impl SyntaxExtension {
parent,
call_site,
def_site: self.span,
default_transparency: self.default_transparency,
allow_internal_unstable: self.allow_internal_unstable.clone(),
allow_internal_unsafe: self.allow_internal_unsafe,
local_inner_macros: self.local_inner_macros,
@ -760,23 +740,39 @@ impl<'a> ExtCtxt<'a> {
pub fn call_site(&self) -> Span {
self.current_expansion.id.expn_data().call_site
}
pub fn backtrace(&self) -> SyntaxContext {
SyntaxContext::root().apply_mark(self.current_expansion.id)
/// Equivalent of `Span::def_site` from the proc macro API,
/// except that the location is taken from the span passed as an argument.
pub fn with_def_site_ctxt(&self, span: Span) -> Span {
span.with_ctxt_from_mark(self.current_expansion.id, Transparency::Opaque)
}
/// Equivalent of `Span::call_site` from the proc macro API,
/// except that the location is taken from the span passed as an argument.
pub fn with_call_site_ctxt(&self, span: Span) -> Span {
span.with_ctxt_from_mark(self.current_expansion.id, Transparency::Transparent)
}
/// Span with a context reproducing `macro_rules` hygiene (hygienic locals, unhygienic items).
/// FIXME: This should be eventually replaced either with `with_def_site_ctxt` (preferably),
/// or with `with_call_site_ctxt` (where necessary).
pub fn with_legacy_ctxt(&self, span: Span) -> Span {
span.with_ctxt_from_mark(self.current_expansion.id, Transparency::SemiTransparent)
}
/// Returns span for the macro which originally caused the current expansion to happen.
///
/// Stops backtracing at include! boundary.
pub fn expansion_cause(&self) -> Option<Span> {
let mut ctxt = self.backtrace();
let mut expn_id = self.current_expansion.id;
let mut last_macro = None;
loop {
let expn_data = ctxt.outer_expn_data();
let expn_data = expn_id.expn_data();
// Stop going up the backtrace once include! is encountered
if expn_data.is_root() || expn_data.kind.descr() == sym::include {
break;
}
ctxt = expn_data.call_site.ctxt();
expn_id = expn_data.call_site.ctxt().outer_expn();
last_macro = Some(expn_data.call_site);
}
last_macro
@ -865,7 +861,7 @@ impl<'a> ExtCtxt<'a> {
ast::Ident::from_str(st)
}
pub fn std_path(&self, components: &[Symbol]) -> Vec<ast::Ident> {
let def_site = DUMMY_SP.apply_mark(self.current_expansion.id);
let def_site = self.with_def_site_ctxt(DUMMY_SP);
iter::once(Ident::new(kw::DollarCrate, def_site))
.chain(components.iter().map(|&s| Ident::with_dummy_span(s)))
.collect()

View file

@ -565,7 +565,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
return fragment_kind.dummy(span);
}
let meta = ast::MetaItem { node: ast::MetaItemKind::Word, span, path };
let span = span.with_ctxt(self.cx.backtrace());
let items = expander.expand(self.cx, span, &meta, item);
fragment_kind.expect_from_annotatables(items)
}
@ -1389,17 +1388,3 @@ impl<'feat> ExpansionConfig<'feat> {
self.features.map_or(false, |features| features.custom_inner_attributes)
}
}
// A Marker adds the given mark to the syntax context.
#[derive(Debug)]
pub struct Marker(pub ExpnId);
impl MutVisitor for Marker {
fn visit_span(&mut self, span: &mut Span) {
*span = span.apply_mark(self.0)
}
fn visit_mac(&mut self, mac: &mut ast::Mac) {
noop_visit_mac(mac, self)
}
}

View file

@ -7,7 +7,6 @@ use crate::tokenstream::{self, DelimSpan, IsJoint::*, TokenStream, TreeAndJoint}
use errors::{Diagnostic, DiagnosticBuilder};
use rustc_data_structures::sync::Lrc;
use syntax_pos::{BytePos, FileName, MultiSpan, Pos, SourceFile, Span};
use syntax_pos::hygiene::{SyntaxContext, Transparency};
use syntax_pos::symbol::{kw, sym, Symbol};
use proc_macro::{Delimiter, Level, LineColumn, Spacing};
@ -363,16 +362,10 @@ impl<'a> Rustc<'a> {
pub fn new(cx: &'a ExtCtxt<'_>) -> Self {
// No way to determine def location for a proc macro right now, so use call location.
let location = cx.current_expansion.id.expn_data().call_site;
let to_span = |transparency| {
location.with_ctxt(
SyntaxContext::root()
.apply_mark_with_transparency(cx.current_expansion.id, transparency),
)
};
Rustc {
sess: cx.parse_sess,
def_site: to_span(Transparency::Opaque),
call_site: to_span(Transparency::Transparent),
def_site: cx.with_def_site_ctxt(location),
call_site: cx.with_call_site_ctxt(location),
}
}

View file

@ -19,6 +19,7 @@ use crate::{ast, attr, attr::TransparencyError};
use errors::{DiagnosticBuilder, FatalError};
use log::debug;
use syntax_pos::hygiene::Transparency;
use syntax_pos::Span;
use rustc_data_structures::fx::FxHashMap;
@ -128,6 +129,7 @@ impl<'a> ParserAnyMacro<'a> {
struct MacroRulesMacroExpander {
name: ast::Ident,
span: Span,
transparency: Transparency,
lhses: Vec<quoted::TokenTree>,
rhses: Vec<quoted::TokenTree>,
valid: bool,
@ -143,7 +145,9 @@ impl TTMacroExpander for MacroRulesMacroExpander {
if !self.valid {
return DummyResult::any(sp);
}
generic_extension(cx, sp, self.span, self.name, input, &self.lhses, &self.rhses)
generic_extension(
cx, sp, self.span, self.name, self.transparency, input, &self.lhses, &self.rhses
)
}
}
@ -158,6 +162,7 @@ fn generic_extension<'cx>(
sp: Span,
def_span: Span,
name: ast::Ident,
transparency: Transparency,
arg: TokenStream,
lhses: &[quoted::TokenTree],
rhses: &[quoted::TokenTree],
@ -187,7 +192,7 @@ fn generic_extension<'cx>(
let rhs_spans = rhs.iter().map(|t| t.span()).collect::<Vec<_>>();
// rhs has holes ( `$id` and `$(...)` that need filled)
let mut tts = transcribe(cx, &named_matches, rhs);
let mut tts = transcribe(cx, &named_matches, rhs, transparency);
// Replace all the tokens for the corresponding positions in the macro, to maintain
// proper positions in error reporting, while maintaining the macro_backtrace.
@ -415,11 +420,7 @@ pub fn compile(
// that is not lint-checked and trigger the "failed to process buffered lint here" bug.
valid &= macro_check::check_meta_variables(sess, ast::CRATE_NODE_ID, def.span, &lhses, &rhses);
let expander: Box<_> =
Box::new(MacroRulesMacroExpander { name: def.ident, span: def.span, lhses, rhses, valid });
let (default_transparency, transparency_error) =
attr::find_transparency(&def.attrs, body.legacy);
let (transparency, transparency_error) = attr::find_transparency(&def.attrs, body.legacy);
match transparency_error {
Some(TransparencyError::UnknownTransparency(value, span)) =>
sess.span_diagnostic.span_err(
@ -432,6 +433,10 @@ pub fn compile(
None => {}
}
let expander: Box<_> = Box::new(MacroRulesMacroExpander {
name: def.ident, span: def.span, transparency, lhses, rhses, valid
});
let allow_internal_unstable =
attr::find_by_name(&def.attrs, sym::allow_internal_unstable).map(|attr| {
attr.meta_item_list()
@ -473,7 +478,6 @@ pub fn compile(
SyntaxExtension {
kind: SyntaxExtensionKind::LegacyBang(expander),
span: def.span,
default_transparency,
allow_internal_unstable,
allow_internal_unsafe: attr::contains_name(&def.attrs, sym::allow_internal_unsafe),
local_inner_macros,

View file

@ -1,9 +1,8 @@
use crate::ast::Ident;
use crate::ast::{Ident, Mac};
use crate::ext::base::ExtCtxt;
use crate::ext::expand::Marker;
use crate::ext::tt::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch};
use crate::ext::tt::quoted;
use crate::mut_visit::noop_visit_tt;
use crate::mut_visit::{self, MutVisitor};
use crate::parse::token::{self, NtTT, Token};
use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};
@ -11,8 +10,31 @@ use smallvec::{smallvec, SmallVec};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc;
use syntax_pos::hygiene::{ExpnId, Transparency};
use syntax_pos::Span;
use std::mem;
// A Marker adds the given mark to the syntax context.
struct Marker(ExpnId, Transparency);
impl MutVisitor for Marker {
fn visit_span(&mut self, span: &mut Span) {
*span = span.apply_mark(self.0, self.1)
}
fn visit_mac(&mut self, mac: &mut Mac) {
mut_visit::noop_visit_mac(mac, self)
}
}
impl Marker {
fn visit_delim_span(&mut self, dspan: &mut DelimSpan) {
self.visit_span(&mut dspan.open);
self.visit_span(&mut dspan.close);
}
}
/// An iterator over the token trees in a delimited token tree (`{ ... }`) or a sequence (`$(...)`).
enum Frame {
Delimited { forest: Lrc<quoted::Delimited>, idx: usize, span: DelimSpan },
@ -68,6 +90,7 @@ pub(super) fn transcribe(
cx: &ExtCtxt<'_>,
interp: &FxHashMap<Ident, NamedMatch>,
src: Vec<quoted::TokenTree>,
transparency: Transparency,
) -> TokenStream {
// Nothing for us to transcribe...
if src.is_empty() {
@ -96,6 +119,7 @@ pub(super) fn transcribe(
// again, and we are done transcribing.
let mut result: Vec<TreeAndJoint> = Vec::new();
let mut result_stack = Vec::new();
let mut marker = Marker(cx.current_expansion.id, transparency);
loop {
// Look at the last frame on the stack.
@ -207,7 +231,7 @@ pub(super) fn transcribe(
}
// Replace the meta-var with the matched token tree from the invocation.
quoted::TokenTree::MetaVar(mut sp, ident) => {
quoted::TokenTree::MetaVar(mut sp, mut ident) => {
// Find the matched nonterminal from the macro invocation, and use it to replace
// the meta-var.
if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) {
@ -218,7 +242,7 @@ pub(super) fn transcribe(
if let NtTT(ref tt) = **nt {
result.push(tt.clone().into());
} else {
sp = sp.apply_mark(cx.current_expansion.id);
marker.visit_span(&mut sp);
let token = TokenTree::token(token::Interpolated(nt.clone()), sp);
result.push(token.into());
}
@ -232,9 +256,8 @@ pub(super) fn transcribe(
} else {
// If we aren't able to match the meta-var, we push it back into the result but
// with modified syntax context. (I believe this supports nested macros).
let ident =
Ident::new(ident.name, ident.span.apply_mark(cx.current_expansion.id));
sp = sp.apply_mark(cx.current_expansion.id);
marker.visit_span(&mut sp);
marker.visit_ident(&mut ident);
result.push(TokenTree::token(token::Dollar, sp).into());
result.push(TokenTree::Token(Token::from_ast_ident(ident)).into());
}
@ -246,7 +269,7 @@ pub(super) fn transcribe(
// jump back out of the Delimited, pop the result_stack and add the new results back to
// the previous results (from outside the Delimited).
quoted::TokenTree::Delimited(mut span, delimited) => {
span = span.apply_mark(cx.current_expansion.id);
marker.visit_delim_span(&mut span);
stack.push(Frame::Delimited { forest: delimited, idx: 0, span });
result_stack.push(mem::take(&mut result));
}
@ -254,9 +277,8 @@ pub(super) fn transcribe(
// Nothing much to do here. Just push the token to the result, being careful to
// preserve syntax context.
quoted::TokenTree::Token(token) => {
let mut marker = Marker(cx.current_expansion.id);
let mut tt = TokenTree::Token(token);
noop_visit_tt(&mut tt, &mut marker);
marker.visit_tt(&mut tt);
result.push(tt.into());
}

View file

@ -19,7 +19,7 @@ use crate::parse::Directory;
use crate::parse::token::{self, DelimToken, Token, TokenKind};
use crate::print::pprust;
use syntax_pos::{BytePos, ExpnId, Span, DUMMY_SP};
use syntax_pos::{BytePos, Span, DUMMY_SP};
#[cfg(target_arch = "x86_64")]
use rustc_data_structures::static_assert_size;
use rustc_data_structures::sync::Lrc;
@ -547,11 +547,4 @@ impl DelimSpan {
pub fn entire(self) -> Span {
self.open.with_hi(self.close.hi())
}
pub fn apply_mark(self, expn_id: ExpnId) -> Self {
DelimSpan {
open: self.open.apply_mark(expn_id),
close: self.close.apply_mark(expn_id),
}
}
}