Skip to content

Commit 4a0535c

Browse files
committed
Perform own detection for proc_macro_span (fixes #39)
1 parent f9b4f73 commit 4a0535c

File tree

6 files changed

+140
-67
lines changed

6 files changed

+140
-67
lines changed

examples/java_string.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use genco::prelude::*;
2+
3+
fn main() -> Result<(), genco::fmt::Error> {
4+
let tokens: python::Tokens = quote!($[str](Hello World));
5+
assert_eq!("\"Hello World\"", tokens.to_string()?);
6+
Ok::<_, genco::fmt::Error>(())
7+
}

genco-macros/build.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use std::env;
2+
use std::process::Command;
3+
use std::str;
4+
5+
fn main() {
6+
println!("cargo:rerun-if-changed=build.rs");
7+
8+
let version = rustc_version().unwrap_or(RustcVersion {
9+
minor: u32::MAX,
10+
nightly: false,
11+
});
12+
13+
if version.nightly {
14+
println!("cargo:rustc-cfg=proc_macro_span");
15+
}
16+
}
17+
18+
struct RustcVersion {
19+
#[allow(unused)]
20+
minor: u32,
21+
nightly: bool,
22+
}
23+
24+
fn rustc_version() -> Option<RustcVersion> {
25+
let rustc = env::var_os("RUSTC")?;
26+
let output = Command::new(rustc).arg("--version").output().ok()?;
27+
let version = str::from_utf8(&output.stdout).ok()?;
28+
let nightly = version.contains("nightly") || version.contains("dev");
29+
let mut pieces = version.split('.');
30+
if pieces.next() != Some("rustc 1") {
31+
return None;
32+
}
33+
let minor = pieces.next()?.parse().ok()?;
34+
Some(RustcVersion { minor, nightly })
35+
}

genco-macros/src/fake.rs

Lines changed: 68 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::cell::{RefCell, RefMut};
12
use std::fmt::Arguments;
23

34
use proc_macro2::Span;
@@ -17,84 +18,97 @@ pub(crate) struct LineColumn {
1718
}
1819

1920
impl LineColumn {
20-
fn new(line_column: proc_macro2::LineColumn) -> Self {
21-
Self {
22-
line: line_column.line,
23-
column: line_column.column,
24-
}
21+
#[cfg(proc_macro_span)]
22+
pub(crate) fn start(span: Span) -> Option<Self> {
23+
let span = span.unwrap().start();
24+
25+
Some(Self {
26+
line: span.line(),
27+
column: span.column(),
28+
})
29+
}
30+
31+
#[cfg(proc_macro_span)]
32+
pub(crate) fn end(span: Span) -> Option<Self> {
33+
let span = span.unwrap().end();
34+
35+
Some(Self {
36+
line: span.line(),
37+
column: span.column(),
38+
})
39+
}
40+
41+
#[cfg(not(proc_macro_span))]
42+
pub(crate) fn start(_: Span) -> Option<Self> {
43+
None
44+
}
45+
46+
#[cfg(not(proc_macro_span))]
47+
pub(crate) fn end(_: Span) -> Option<Self> {
48+
None
2549
}
2650
}
2751

2852
#[derive(Default)]
2953
pub(crate) struct Buf {
30-
buf: Option<String>,
54+
buf: RefCell<String>,
3155
}
3256

3357
impl Buf {
3458
/// Format the given arguments and return the associated string.
35-
fn format(&mut self, args: Arguments<'_>) -> &str {
59+
fn format(&self, args: Arguments<'_>) -> RefMut<'_, str> {
3660
use std::fmt::Write;
37-
let buf = self.buf.get_or_insert_with(String::default);
61+
let mut buf = self.buf.borrow_mut();
3862
buf.clear();
3963
buf.write_fmt(args).unwrap();
40-
buf.as_str()
64+
RefMut::map(buf, |buf| buf.as_mut_str())
4165
}
4266

4367
/// Construct a cursor from a span.
44-
pub(crate) fn cursor(&mut self, span: Span) -> syn::Result<Cursor> {
45-
let start = span.start();
46-
let end = span.end();
47-
48-
if (start.line == 0 && start.column == 0) || (end.line == 0 && end.column == 0) {
49-
// Try compat.
50-
let (start, end) = self.find_line_column(span)?;
51-
52-
Ok(Cursor::new(
53-
span,
54-
LineColumn {
55-
line: 1,
56-
column: start,
57-
},
58-
LineColumn {
59-
line: 1,
60-
column: end,
61-
},
62-
))
63-
} else {
64-
Ok(Cursor::new(
65-
span,
66-
LineColumn::new(start),
67-
LineColumn::new(end),
68-
))
68+
pub(crate) fn cursor(&self, span: Span) -> syn::Result<Cursor> {
69+
let start = LineColumn::start(span);
70+
let end = LineColumn::end(span);
71+
72+
if let (Some(start), Some(end)) = (start, end) {
73+
return Ok(Cursor::new(span, start, end));
6974
}
75+
76+
// Try compat.
77+
let (start, end) = self.find_line_column(span)?;
78+
79+
Ok(Cursor::new(
80+
span,
81+
LineColumn {
82+
line: 1,
83+
column: start,
84+
},
85+
LineColumn {
86+
line: 1,
87+
column: end,
88+
},
89+
))
7090
}
7191

7292
/// The start of the given span.
7393
pub(crate) fn start(&mut self, span: Span) -> syn::Result<LineColumn> {
74-
let start = span.start();
75-
76-
// Try to use compat layer.
77-
if start.line == 0 && start.column == 0 {
78-
// Try compat.
79-
let (column, _) = self.find_line_column(span)?;
80-
Ok(LineColumn { line: 1, column })
81-
} else {
82-
Ok(LineColumn::new(start))
94+
if let Some(start) = LineColumn::start(span) {
95+
return Ok(start);
8396
}
97+
98+
// Try compat.
99+
let (column, _) = self.find_line_column(span)?;
100+
Ok(LineColumn { line: 1, column })
84101
}
85102

86103
/// The start of the given span.
87104
pub(crate) fn end(&mut self, span: Span) -> syn::Result<LineColumn> {
88-
let end = span.end();
89-
90-
// Try to use compat layer.
91-
if end.line == 0 && end.column == 0 {
92-
// Try compat.
93-
let (_, column) = self.find_line_column(span)?;
94-
Ok(LineColumn { line: 1, column })
95-
} else {
96-
Ok(LineColumn::new(end))
105+
if let Some(end) = LineColumn::end(span) {
106+
return Ok(end);
97107
}
108+
109+
// Try compat.
110+
let (_, column) = self.find_line_column(span)?;
111+
Ok(LineColumn { line: 1, column })
98112
}
99113

100114
/// Join two spans.
@@ -108,14 +122,14 @@ impl Buf {
108122

109123
/// Try to decode line and column information using the debug implementation of
110124
/// a `span` which leaks the byte offset of a thing.
111-
fn find_line_column(&mut self, span: Span) -> syn::Result<(usize, usize)> {
125+
fn find_line_column(&self, span: Span) -> syn::Result<(usize, usize)> {
112126
match self.find_line_column_inner(span) {
113127
Some((start, end)) => Ok((start, end)),
114128
None => Err(syn::Error::new(span, ERROR)),
115129
}
116130
}
117131

118-
fn find_line_column_inner(&mut self, span: Span) -> Option<(usize, usize)> {
132+
fn find_line_column_inner(&self, span: Span) -> Option<(usize, usize)> {
119133
let text = self.format(format_args!("{:?}", span));
120134
let start = text.find('(')?;
121135
let (start, end) = text

genco-macros/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
55
#![recursion_limit = "256"]
66
#![allow(clippy::type_complexity)]
7+
#![cfg_attr(proc_macro_span, feature(proc_macro_span))]
78

89
extern crate proc_macro;
910

genco-macros/src/quote.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ impl<'a> Quote<'a> {
348348
));
349349
}
350350
(LiteralName::Ident("str"), Some(content)) => {
351-
let parser = StringParser::new(self.receiver, end);
351+
let parser = StringParser::new(self.receiver, &self.buf, end)?;
352352

353353
let (options, r, stream) = parser.parse(&content)?;
354354
encoder.requirements.merge_with(r);

genco-macros/src/string_parser.rs

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
//! Helper to parse quoted strings.
22
33
use crate::ast::LiteralName;
4+
use crate::fake::{Buf, LineColumn};
45
use crate::quote::parse_internal_function;
56
use crate::requirements::Requirements;
6-
use proc_macro2::{LineColumn, Span, TokenStream, TokenTree};
7+
8+
use proc_macro2::{Span, TokenStream, TokenTree};
79
use syn::parse::ParseStream;
810
use syn::spanned::Spanned;
911
use syn::token;
@@ -194,22 +196,26 @@ impl<'a> Encoder<'a> {
194196

195197
pub struct StringParser<'a> {
196198
receiver: &'a syn::Ident,
199+
buf: &'a Buf,
197200
start: LineColumn,
198201
end: LineColumn,
199202
span: Span,
200203
}
201204

202205
impl<'a> StringParser<'a> {
203-
pub(crate) fn new(receiver: &'a syn::Ident, span: Span) -> Self {
204-
Self {
206+
pub(crate) fn new(receiver: &'a syn::Ident, buf: &'a Buf, span: Span) -> syn::Result<Self> {
207+
let cursor = buf.cursor(span)?;
208+
209+
Ok(Self {
205210
receiver,
211+
buf,
206212
// Note: adjusting span since we expect the quoted string to be
207213
// withing a block, where the interior span is one character pulled
208214
// in in each direction.
209-
start: adjust_start(span.start()),
210-
end: adjust_end(span.end()),
215+
start: adjust_start(cursor.start),
216+
end: adjust_end(cursor.end),
211217
span,
212-
}
218+
})
213219
}
214220

215221
pub(crate) fn parse(self, input: ParseStream) -> Result<(Options, Requirements, TokenStream)> {
@@ -220,23 +226,28 @@ impl<'a> StringParser<'a> {
220226
if input.peek(syn::Token![$]) && input.peek2(syn::Token![$]) {
221227
let start = input.parse::<syn::Token![$]>()?;
222228
let escape = input.parse::<syn::Token![$]>()?;
223-
encoder.encode_char('$', start.span().start(), escape.span().end())?;
229+
let start = self.buf.cursor(start.span())?;
230+
let escape = self.buf.cursor(escape.span())?;
231+
encoder.encode_char('$', start.start, escape.end)?;
224232
continue;
225233
}
226234

227235
if input.peek(syn::Token![$]) {
228236
if let Some((name, content, [start, end])) = parse_internal_function(input)? {
229237
match (name.as_literal_name(), content) {
230238
(LiteralName::Ident("const"), Some(content)) => {
239+
let start = self.buf.cursor(start)?;
240+
let end = self.buf.cursor(end)?;
241+
231242
// Compile-time string optimization. A single,
232243
// enclosed literal string can be added to the
233244
// existing static buffer.
234245
if content.peek(syn::LitStr) && content.peek2(crate::token::Eof) {
235246
let s = content.parse::<syn::LitStr>()?;
236-
encoder.encode_str(&s.value(), start.start(), Some(end.end()))?;
247+
encoder.encode_str(&s.value(), start.start, Some(end.end))?;
237248
} else {
238249
let expr = content.parse::<syn::Expr>()?;
239-
encoder.raw_expr(&expr, start.start(), Some(end.end()))?;
250+
encoder.raw_expr(&expr, start.start, Some(end.end))?;
240251
}
241252
}
242253
(literal_name, _) => {
@@ -254,7 +265,9 @@ impl<'a> StringParser<'a> {
254265

255266
if !input.peek(token::Paren) {
256267
let ident = input.parse::<syn::Ident>()?;
257-
encoder.eval_ident(&ident, start.start(), Some(ident.span().end()))?;
268+
let start = self.buf.cursor(start.span())?;
269+
let end = self.buf.cursor(ident.span())?.end;
270+
encoder.eval_ident(&ident, start.start, Some(end))?;
258271
continue;
259272
}
260273

@@ -265,14 +278,17 @@ impl<'a> StringParser<'a> {
265278
.with_span(content.span())?
266279
.parse(&content)?;
267280
requirements.merge_with(req);
268-
encoder.eval_stream(stream, start.start(), Some(end.end()))?;
281+
let start = self.buf.cursor(start.span())?;
282+
let end = self.buf.cursor(end.span())?;
283+
encoder.eval_stream(stream, start.start, Some(end.end))?;
269284
}
270285

271286
continue;
272287
}
273288

274289
let tt = input.parse::<TokenTree>()?;
275-
encoder.extend_tt(&tt, tt.span().start(), Some(tt.span().end()))?;
290+
let cursor = self.buf.cursor(tt.span())?;
291+
encoder.extend_tt(&tt, cursor.start, Some(cursor.end))?;
276292
}
277293

278294
let (options, stream) = encoder.finalize(self.end)?;

0 commit comments

Comments
 (0)