Skip to content

Commit b5d0393

Browse files
committed
proc-macro: Use transparent marks for call-site hygiene
1 parent 556bd9a commit b5d0393

File tree

5 files changed

+116
-36
lines changed

5 files changed

+116
-36
lines changed

src/libproc_macro/diagnostic.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ impl Diagnostic {
9797
/// Emit the diagnostic.
9898
#[unstable(feature = "proc_macro", issue = "38356")]
9999
pub fn emit(self) {
100-
::__internal::with_sess(move |(sess, _)| {
100+
::__internal::with_sess(move |sess, _| {
101101
let handler = &sess.span_diagnostic;
102102
let level = __internal::level_to_internal_level(self.level);
103103
let mut diag = rustc::DiagnosticBuilder::new(handler, level, &*self.message);

src/libproc_macro/lib.rs

+54-34
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,7 @@ use syntax::parse::{self, token};
5858
use syntax::symbol::{keywords, Symbol};
5959
use syntax::tokenstream;
6060
use syntax::parse::lexer::{self, comments};
61-
use syntax_pos::{FileMap, Pos, SyntaxContext, FileName};
62-
use syntax_pos::hygiene::Mark;
61+
use syntax_pos::{FileMap, Pos, FileName};
6362

6463
/// The main type provided by this crate, representing an abstract stream of
6564
/// tokens, or, more specifically, a sequence of token trees.
@@ -109,6 +108,7 @@ impl TokenStream {
109108
/// Attempts to break the string into tokens and parse those tokens into a token stream.
110109
/// May fail for a number of reasons, for example, if the string contains unbalanced delimiters
111110
/// or characters not existing in the language.
111+
/// All tokens in the parsed stream get `Span::call_site()` spans.
112112
///
113113
/// NOTE: Some errors may cause panics instead of returning `LexError`. We reserve the right to
114114
/// change these errors into `LexError`s later.
@@ -117,17 +117,10 @@ impl FromStr for TokenStream {
117117
type Err = LexError;
118118

119119
fn from_str(src: &str) -> Result<TokenStream, LexError> {
120-
__internal::with_sess(|(sess, mark)| {
121-
let src = src.to_string();
122-
let name = FileName::ProcMacroSourceCode;
123-
let expn_info = mark.expn_info().unwrap();
124-
let call_site = expn_info.call_site;
125-
// notify the expansion info that it is unhygienic
126-
let mark = Mark::fresh(mark);
127-
mark.set_expn_info(expn_info);
128-
let span = call_site.with_ctxt(SyntaxContext::empty().apply_mark(mark));
129-
let stream = parse::parse_stream_from_source_str(name, src, sess, Some(span));
130-
Ok(__internal::token_stream_wrap(stream))
120+
__internal::with_sess(|sess, data| {
121+
Ok(__internal::token_stream_wrap(parse::parse_stream_from_source_str(
122+
FileName::ProcMacroSourceCode, src.to_string(), sess, Some(data.call_site.0)
123+
)))
131124
})
132125
}
133126
}
@@ -284,10 +277,7 @@ impl Span {
284277
/// A span that resolves at the macro definition site.
285278
#[unstable(feature = "proc_macro", issue = "38356")]
286279
pub fn def_site() -> Span {
287-
::__internal::with_sess(|(_, mark)| {
288-
let call_site = mark.expn_info().unwrap().call_site;
289-
Span(call_site.with_ctxt(SyntaxContext::empty().apply_mark(mark)))
290-
})
280+
::__internal::with_sess(|_, data| data.def_site)
291281
}
292282

293283
/// The span of the invocation of the current procedural macro.
@@ -296,7 +286,7 @@ impl Span {
296286
/// at the macro call site will be able to refer to them as well.
297287
#[unstable(feature = "proc_macro", issue = "38356")]
298288
pub fn call_site() -> Span {
299-
::__internal::with_sess(|(_, mark)| Span(mark.expn_info().unwrap().call_site))
289+
::__internal::with_sess(|_, data| data.call_site)
300290
}
301291

302292
/// The original source file into which this span points.
@@ -1243,7 +1233,7 @@ impl TokenTree {
12431233
}
12441234

12451235
Interpolated(_) => {
1246-
__internal::with_sess(|(sess, _)| {
1236+
__internal::with_sess(|sess, _| {
12471237
let tts = token.interpolated_to_tokenstream(sess, span);
12481238
tt!(Group::new(Delimiter::None, TokenStream(tts)))
12491239
})
@@ -1354,20 +1344,21 @@ pub mod __internal {
13541344
pub use quote::{LiteralKind, SpannedSymbol, Quoter, unquote};
13551345

13561346
use std::cell::Cell;
1347+
use std::ptr;
13571348

13581349
use syntax::ast;
13591350
use syntax::ext::base::ExtCtxt;
1360-
use syntax::ext::hygiene::Mark;
13611351
use syntax::ptr::P;
13621352
use syntax::parse::{self, ParseSess};
13631353
use syntax::parse::token::{self, Token};
13641354
use syntax::tokenstream;
13651355
use syntax_pos::{BytePos, Loc, DUMMY_SP};
1356+
use syntax_pos::hygiene::{Mark, SyntaxContext, Transparency};
13661357

1367-
use super::{TokenStream, LexError};
1358+
use super::{TokenStream, LexError, Span};
13681359

13691360
pub fn lookup_char_pos(pos: BytePos) -> Loc {
1370-
with_sess(|(sess, _)| sess.codemap().lookup_char_pos(pos))
1361+
with_sess(|sess, _| sess.codemap().lookup_char_pos(pos))
13711362
}
13721363

13731364
pub fn new_token_stream(item: P<ast::Item>) -> TokenStream {
@@ -1380,7 +1371,7 @@ pub mod __internal {
13801371
}
13811372

13821373
pub fn token_stream_parse_items(stream: TokenStream) -> Result<Vec<P<ast::Item>>, LexError> {
1383-
with_sess(move |(sess, _)| {
1374+
with_sess(move |sess, _| {
13841375
let mut parser = parse::stream_to_parser(sess, stream.0);
13851376
let mut items = Vec::new();
13861377

@@ -1411,16 +1402,30 @@ pub mod __internal {
14111402
expand: fn(TokenStream) -> TokenStream);
14121403
}
14131404

1405+
#[derive(Clone, Copy)]
1406+
pub struct ProcMacroData {
1407+
pub def_site: Span,
1408+
pub call_site: Span,
1409+
}
1410+
1411+
#[derive(Clone, Copy)]
1412+
struct ProcMacroSess {
1413+
parse_sess: *const ParseSess,
1414+
data: ProcMacroData,
1415+
}
1416+
14141417
// Emulate scoped_thread_local!() here essentially
14151418
thread_local! {
1416-
static CURRENT_SESS: Cell<(*const ParseSess, Mark)> =
1417-
Cell::new((0 as *const _, Mark::root()));
1419+
static CURRENT_SESS: Cell<ProcMacroSess> = Cell::new(ProcMacroSess {
1420+
parse_sess: ptr::null(),
1421+
data: ProcMacroData { def_site: Span(DUMMY_SP), call_site: Span(DUMMY_SP) },
1422+
});
14181423
}
14191424

14201425
pub fn set_sess<F, R>(cx: &ExtCtxt, f: F) -> R
14211426
where F: FnOnce() -> R
14221427
{
1423-
struct Reset { prev: (*const ParseSess, Mark) }
1428+
struct Reset { prev: ProcMacroSess }
14241429

14251430
impl Drop for Reset {
14261431
fn drop(&mut self) {
@@ -1430,24 +1435,39 @@ pub mod __internal {
14301435

14311436
CURRENT_SESS.with(|p| {
14321437
let _reset = Reset { prev: p.get() };
1433-
p.set((cx.parse_sess, cx.current_expansion.mark));
1438+
1439+
// No way to determine def location for a proc macro rigth now, so use call location.
1440+
let location = cx.current_expansion.mark.expn_info().unwrap().call_site;
1441+
// Opaque mark was already created by expansion, now create its transparent twin.
1442+
let opaque_mark = cx.current_expansion.mark;
1443+
let transparent_mark = Mark::fresh_cloned(opaque_mark);
1444+
transparent_mark.set_transparency(Transparency::Transparent);
1445+
1446+
let to_span = |mark| Span(location.with_ctxt(SyntaxContext::empty().apply_mark(mark)));
1447+
p.set(ProcMacroSess {
1448+
parse_sess: cx.parse_sess,
1449+
data: ProcMacroData {
1450+
def_site: to_span(opaque_mark),
1451+
call_site: to_span(transparent_mark),
1452+
},
1453+
});
14341454
f()
14351455
})
14361456
}
14371457

14381458
pub fn in_sess() -> bool
14391459
{
1440-
let p = CURRENT_SESS.with(|p| p.get());
1441-
!p.0.is_null()
1460+
!CURRENT_SESS.with(|sess| sess.get()).parse_sess.is_null()
14421461
}
14431462

14441463
pub fn with_sess<F, R>(f: F) -> R
1445-
where F: FnOnce((&ParseSess, Mark)) -> R
1464+
where F: FnOnce(&ParseSess, &ProcMacroData) -> R
14461465
{
1447-
let p = CURRENT_SESS.with(|p| p.get());
1448-
assert!(!p.0.is_null(), "proc_macro::__internal::with_sess() called \
1449-
before set_parse_sess()!");
1450-
f(unsafe { (&*p.0, p.1) })
1466+
let sess = CURRENT_SESS.with(|sess| sess.get());
1467+
if sess.parse_sess.is_null() {
1468+
panic!("procedural macro API is used outside of a procedural macro");
1469+
}
1470+
f(unsafe { &*sess.parse_sess }, &sess.data)
14511471
}
14521472
}
14531473

src/test/compile-fail-fulldeps/proc-macro/lints_in_proc_macros.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ fn main() {
2323
bang_proc_macro2!();
2424
//~^ ERROR cannot find value `foobar2` in this scope
2525
//~^^ did you mean `foobar`?
26-
println!("{}", x); //~ ERROR cannot find value `x` in this scope
26+
println!("{}", x);
2727
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// no-prefer-dynamic
12+
13+
#![crate_type = "proc-macro"]
14+
#![feature(proc_macro)]
15+
16+
extern crate proc_macro;
17+
use proc_macro::*;
18+
19+
#[proc_macro]
20+
pub fn check(input: TokenStream) -> TokenStream {
21+
// Parsed `x2` can refer to `x2` from `input`
22+
let parsed1: TokenStream = "let x3 = x2;".parse().unwrap();
23+
// `x3` parsed from one string can refer to `x3` parsed from another string.
24+
let parsed2: TokenStream = "let x4 = x3;".parse().unwrap();
25+
// Manually assembled `x4` can refer to parsed `x4`.
26+
let manual: Vec<TokenTree> = vec![
27+
Ident::new("let", Span::call_site()).into(),
28+
Ident::new("x5", Span::call_site()).into(),
29+
Punct::new('=', Spacing::Alone).into(),
30+
Ident::new("x4", Span::call_site()).into(),
31+
Punct::new(';', Spacing::Alone).into(),
32+
];
33+
input.into_iter().chain(parsed1.into_iter())
34+
.chain(parsed2.into_iter())
35+
.chain(manual.into_iter())
36+
.collect()
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// aux-build:call-site.rs
12+
// ignore-stage1
13+
14+
#![feature(proc_macro, proc_macro_non_items)]
15+
16+
extern crate call_site;
17+
use call_site::*;
18+
19+
fn main() {
20+
let x1 = 10;
21+
call_site::check!(let x2 = x1;);
22+
let x6 = x5;
23+
}

0 commit comments

Comments
 (0)