]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | //! A support library for macro authors when defining new macros. |
2 | //! | |
3 | //! This library, provided by the standard distribution, provides the types | |
4 | //! consumed in the interfaces of procedurally defined macro definitions such as | |
5 | //! function-like macros `#[proc_macro]`, macro attributes `#[proc_macro_attribute]` and | |
6 | //! custom derive attributes`#[proc_macro_derive]`. | |
7 | //! | |
8 | //! See [the book] for more. | |
9 | //! | |
10 | //! [the book]: ../book/ch19-06-macros.html#procedural-macros-for-generating-code-from-attributes | |
11 | ||
12 | #[doc(hidden)] | |
13 | pub mod bridge; | |
14 | ||
15 | mod diagnostic; | |
16 | ||
17 | pub use diagnostic::{Diagnostic, Level, MultiSpan}; | |
18 | ||
19 | use std::cmp::Ordering; | |
20 | use std::ops::RangeBounds; | |
21 | use std::path::PathBuf; | |
22 | use std::str::FromStr; | |
23 | use std::{error, fmt, iter, mem}; | |
24 | ||
25 | /// Determines whether proc_macro has been made accessible to the currently | |
26 | /// running program. | |
27 | /// | |
28 | /// The proc_macro crate is only intended for use inside the implementation of | |
29 | /// procedural macros. All the functions in this crate panic if invoked from | |
30 | /// outside of a procedural macro, such as from a build script or unit test or | |
31 | /// ordinary Rust binary. | |
32 | /// | |
33 | /// With consideration for Rust libraries that are designed to support both | |
34 | /// macro and non-macro use cases, `proc_macro::is_available()` provides a | |
35 | /// non-panicking way to detect whether the infrastructure required to use the | |
36 | /// API of proc_macro is presently available. Returns true if invoked from | |
37 | /// inside of a procedural macro, false if invoked from any other binary. | |
38 | pub fn is_available() -> bool { | |
39 | bridge::Bridge::is_available() | |
40 | } | |
41 | ||
42 | /// The main type provided by this crate, representing an abstract stream of | |
43 | /// tokens, or, more specifically, a sequence of token trees. | |
44 | /// The type provide interfaces for iterating over those token trees and, conversely, | |
45 | /// collecting a number of token trees into one stream. | |
46 | /// | |
47 | /// This is both the input and output of `#[proc_macro]`, `#[proc_macro_attribute]` | |
48 | /// and `#[proc_macro_derive]` definitions. | |
49 | #[derive(Clone)] | |
50 | pub struct TokenStream(Option<bridge::client::TokenStream>); | |
51 | ||
52 | /// Error returned from `TokenStream::from_str`. | |
53 | #[non_exhaustive] | |
54 | #[derive(Debug)] | |
55 | pub struct LexError; | |
56 | ||
57 | impl fmt::Display for LexError { | |
58 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
59 | f.write_str("cannot parse string into token stream") | |
60 | } | |
61 | } | |
62 | ||
63 | impl error::Error for LexError {} | |
64 | ||
65 | /// Error returned from `TokenStream::expand_expr`. | |
66 | #[non_exhaustive] | |
67 | #[derive(Debug)] | |
68 | pub struct ExpandError; | |
69 | ||
70 | impl fmt::Display for ExpandError { | |
71 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
72 | f.write_str("macro expansion failed") | |
73 | } | |
74 | } | |
75 | ||
76 | impl error::Error for ExpandError {} | |
77 | ||
78 | impl TokenStream { | |
79 | /// Returns an empty `TokenStream` containing no token trees. | |
80 | pub fn new() -> TokenStream { | |
81 | TokenStream(None) | |
82 | } | |
83 | ||
84 | /// Checks if this `TokenStream` is empty. | |
85 | pub fn is_empty(&self) -> bool { | |
86 | self.0.as_ref().map(|h| h.is_empty()).unwrap_or(true) | |
87 | } | |
88 | ||
89 | /// Parses this `TokenStream` as an expression and attempts to expand any | |
90 | /// macros within it. Returns the expanded `TokenStream`. | |
91 | /// | |
92 | /// Currently only expressions expanding to literals will succeed, although | |
93 | /// this may be relaxed in the future. | |
94 | /// | |
95 | /// NOTE: In error conditions, `expand_expr` may leave macros unexpanded, | |
96 | /// report an error, failing compilation, and/or return an `Err(..)`. The | |
97 | /// specific behavior for any error condition, and what conditions are | |
98 | /// considered errors, is unspecified and may change in the future. | |
99 | pub fn expand_expr(&self) -> Result<TokenStream, ExpandError> { | |
100 | let stream = self.0.as_ref().ok_or(ExpandError)?; | |
101 | match bridge::client::TokenStream::expand_expr(stream) { | |
102 | Ok(stream) => Ok(TokenStream(Some(stream))), | |
103 | Err(_) => Err(ExpandError), | |
104 | } | |
105 | } | |
106 | } | |
107 | ||
108 | /// Attempts to break the string into tokens and parse those tokens into a token stream. | |
109 | /// May fail for a number of reasons, for example, if the string contains unbalanced delimiters | |
110 | /// or characters not existing in the language. | |
111 | /// All tokens in the parsed stream get `Span::call_site()` spans. | |
112 | /// | |
113 | /// NOTE: some errors may cause panics instead of returning `LexError`. We reserve the right to | |
114 | /// change these errors into `LexError`s later. | |
115 | impl FromStr for TokenStream { | |
116 | type Err = LexError; | |
117 | ||
118 | fn from_str(src: &str) -> Result<TokenStream, LexError> { | |
119 | Ok(TokenStream(Some(bridge::client::TokenStream::from_str(src)))) | |
120 | } | |
121 | } | |
122 | ||
123 | /// Prints the token stream as a string that is supposed to be losslessly convertible back | |
124 | /// into the same token stream (modulo spans), except for possibly `TokenTree::Group`s | |
125 | /// with `Delimiter::None` delimiters and negative numeric literals. | |
126 | impl fmt::Display for TokenStream { | |
127 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
128 | f.write_str(&self.to_string()) | |
129 | } | |
130 | } | |
131 | ||
132 | /// Prints token in a form convenient for debugging. | |
133 | impl fmt::Debug for TokenStream { | |
134 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
135 | f.write_str("TokenStream ")?; | |
136 | f.debug_list().entries(self.clone()).finish() | |
137 | } | |
138 | } | |
139 | ||
140 | impl Default for TokenStream { | |
141 | fn default() -> Self { | |
142 | TokenStream::new() | |
143 | } | |
144 | } | |
145 | ||
146 | pub use quote::{quote, quote_span}; | |
147 | ||
148 | fn tree_to_bridge_tree( | |
149 | tree: TokenTree, | |
150 | ) -> bridge::TokenTree< | |
151 | bridge::client::Group, | |
152 | bridge::client::Punct, | |
153 | bridge::client::Ident, | |
154 | bridge::client::Literal, | |
155 | > { | |
156 | match tree { | |
157 | TokenTree::Group(tt) => bridge::TokenTree::Group(tt.0), | |
158 | TokenTree::Punct(tt) => bridge::TokenTree::Punct(tt.0), | |
159 | TokenTree::Ident(tt) => bridge::TokenTree::Ident(tt.0), | |
160 | TokenTree::Literal(tt) => bridge::TokenTree::Literal(tt.0), | |
161 | } | |
162 | } | |
163 | ||
164 | /// Creates a token stream containing a single token tree. | |
165 | impl From<TokenTree> for TokenStream { | |
166 | fn from(tree: TokenTree) -> TokenStream { | |
167 | TokenStream(Some(bridge::client::TokenStream::from_token_tree(tree_to_bridge_tree(tree)))) | |
168 | } | |
169 | } | |
170 | ||
171 | /// Non-generic helper for implementing `FromIterator<TokenStream>` and | |
172 | /// `Extend<TokenStream>` with less monomorphization in calling crates. | |
173 | struct ConcatStreamsHelper { | |
174 | streams: Vec<bridge::client::TokenStream>, | |
175 | } | |
176 | ||
177 | impl ConcatStreamsHelper { | |
178 | fn new(capacity: usize) -> Self { | |
179 | ConcatStreamsHelper { streams: Vec::with_capacity(capacity) } | |
180 | } | |
181 | ||
182 | fn push(&mut self, stream: TokenStream) { | |
183 | if let Some(stream) = stream.0 { | |
184 | self.streams.push(stream); | |
185 | } | |
186 | } | |
187 | ||
188 | fn build(mut self) -> TokenStream { | |
189 | if self.streams.len() <= 1 { | |
190 | TokenStream(self.streams.pop()) | |
191 | } else { | |
192 | TokenStream(Some(bridge::client::TokenStream::concat_streams(None, self.streams))) | |
193 | } | |
194 | } | |
195 | ||
196 | fn append_to(mut self, stream: &mut TokenStream) { | |
197 | if self.streams.is_empty() { | |
198 | return; | |
199 | } | |
200 | let base = stream.0.take(); | |
201 | if base.is_none() && self.streams.len() == 1 { | |
202 | stream.0 = self.streams.pop(); | |
203 | } else { | |
204 | stream.0 = Some(bridge::client::TokenStream::concat_streams(base, self.streams)); | |
205 | } | |
206 | } | |
207 | } | |
208 | ||
209 | /// Collects a number of token trees into a single stream. | |
f2b60f7d | 210 | impl FromIterator<TokenTree> for TokenStream { |
064997fb FG |
211 | fn from_iter<I: IntoIterator<Item = TokenTree>>(trees: I) -> Self { |
212 | trees.into_iter().map(TokenStream::from).collect() | |
213 | } | |
214 | } | |
215 | ||
216 | /// A "flattening" operation on token streams, collects token trees | |
217 | /// from multiple token streams into a single stream. | |
f2b60f7d | 218 | impl FromIterator<TokenStream> for TokenStream { |
064997fb FG |
219 | fn from_iter<I: IntoIterator<Item = TokenStream>>(streams: I) -> Self { |
220 | let iter = streams.into_iter(); | |
221 | let mut builder = ConcatStreamsHelper::new(iter.size_hint().0); | |
222 | iter.for_each(|stream| builder.push(stream)); | |
223 | builder.build() | |
224 | } | |
225 | } | |
226 | ||
227 | impl Extend<TokenTree> for TokenStream { | |
228 | fn extend<I: IntoIterator<Item = TokenTree>>(&mut self, trees: I) { | |
229 | self.extend(trees.into_iter().map(TokenStream::from)); | |
230 | } | |
231 | } | |
232 | ||
233 | impl Extend<TokenStream> for TokenStream { | |
234 | fn extend<I: IntoIterator<Item = TokenStream>>(&mut self, streams: I) { | |
235 | // FIXME(eddyb) Use an optimized implementation if/when possible. | |
236 | *self = iter::once(mem::replace(self, Self::new())).chain(streams).collect(); | |
237 | } | |
238 | } | |
239 | ||
240 | /// Public implementation details for the `TokenStream` type, such as iterators. | |
241 | pub mod token_stream { | |
242 | use super::{bridge, Group, Ident, Literal, Punct, TokenStream, TokenTree}; | |
243 | ||
244 | /// An iterator over `TokenStream`'s `TokenTree`s. | |
245 | /// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups, | |
246 | /// and returns whole groups as token trees. | |
247 | #[derive(Clone)] | |
248 | pub struct IntoIter( | |
249 | std::vec::IntoIter< | |
250 | bridge::TokenTree< | |
251 | bridge::client::Group, | |
252 | bridge::client::Punct, | |
253 | bridge::client::Ident, | |
254 | bridge::client::Literal, | |
255 | >, | |
256 | >, | |
257 | ); | |
258 | ||
259 | impl Iterator for IntoIter { | |
260 | type Item = TokenTree; | |
261 | ||
262 | fn next(&mut self) -> Option<TokenTree> { | |
263 | self.0.next().map(|tree| match tree { | |
264 | bridge::TokenTree::Group(tt) => TokenTree::Group(Group(tt)), | |
265 | bridge::TokenTree::Punct(tt) => TokenTree::Punct(Punct(tt)), | |
266 | bridge::TokenTree::Ident(tt) => TokenTree::Ident(Ident(tt)), | |
267 | bridge::TokenTree::Literal(tt) => TokenTree::Literal(Literal(tt)), | |
268 | }) | |
269 | } | |
270 | } | |
271 | ||
272 | impl IntoIterator for TokenStream { | |
273 | type Item = TokenTree; | |
274 | type IntoIter = IntoIter; | |
275 | ||
276 | fn into_iter(self) -> IntoIter { | |
277 | IntoIter(self.0.map(|v| v.into_trees()).unwrap_or_default().into_iter()) | |
278 | } | |
279 | } | |
280 | } | |
281 | ||
282 | #[doc(hidden)] | |
283 | mod quote; | |
284 | ||
285 | /// A region of source code, along with macro expansion information. | |
286 | #[derive(Copy, Clone)] | |
287 | pub struct Span(bridge::client::Span); | |
288 | ||
289 | macro_rules! diagnostic_method { | |
290 | ($name:ident, $level:expr) => { | |
291 | /// Creates a new `Diagnostic` with the given `message` at the span | |
292 | /// `self`. | |
293 | pub fn $name<T: Into<String>>(self, message: T) -> Diagnostic { | |
294 | Diagnostic::spanned(self, $level, message) | |
295 | } | |
296 | }; | |
297 | } | |
298 | ||
299 | impl Span { | |
300 | /// A span that resolves at the macro definition site. | |
301 | pub fn def_site() -> Span { | |
302 | Span(bridge::client::Span::def_site()) | |
303 | } | |
304 | ||
305 | /// The span of the invocation of the current procedural macro. | |
306 | /// Identifiers created with this span will be resolved as if they were written | |
307 | /// directly at the macro call location (call-site hygiene) and other code | |
308 | /// at the macro call site will be able to refer to them as well. | |
309 | pub fn call_site() -> Span { | |
310 | Span(bridge::client::Span::call_site()) | |
311 | } | |
312 | ||
313 | /// A span that represents `macro_rules` hygiene, and sometimes resolves at the macro | |
314 | /// definition site (local variables, labels, `$crate`) and sometimes at the macro | |
315 | /// call site (everything else). | |
316 | /// The span location is taken from the call-site. | |
317 | pub fn mixed_site() -> Span { | |
318 | Span(bridge::client::Span::mixed_site()) | |
319 | } | |
320 | ||
321 | /// The original source file into which this span points. | |
322 | pub fn source_file(&self) -> SourceFile { | |
323 | SourceFile(self.0.source_file()) | |
324 | } | |
325 | ||
326 | /// The `Span` for the tokens in the previous macro expansion from which | |
327 | /// `self` was generated from, if any. | |
328 | pub fn parent(&self) -> Option<Span> { | |
329 | self.0.parent().map(Span) | |
330 | } | |
331 | ||
332 | /// The span for the origin source code that `self` was generated from. If | |
333 | /// this `Span` wasn't generated from other macro expansions then the return | |
334 | /// value is the same as `*self`. | |
335 | pub fn source(&self) -> Span { | |
336 | Span(self.0.source()) | |
337 | } | |
338 | ||
339 | /// Gets the starting line/column in the source file for this span. | |
340 | pub fn start(&self) -> LineColumn { | |
341 | self.0.start().add_1_to_column() | |
342 | } | |
343 | ||
344 | /// Gets the ending line/column in the source file for this span. | |
345 | pub fn end(&self) -> LineColumn { | |
346 | self.0.end().add_1_to_column() | |
347 | } | |
348 | ||
349 | /// Creates an empty span pointing to directly before this span. | |
350 | pub fn before(&self) -> Span { | |
351 | Span(self.0.before()) | |
352 | } | |
353 | ||
354 | /// Creates an empty span pointing to directly after this span. | |
355 | pub fn after(&self) -> Span { | |
356 | Span(self.0.after()) | |
357 | } | |
358 | ||
359 | /// Creates a new span encompassing `self` and `other`. | |
360 | /// | |
361 | /// Returns `None` if `self` and `other` are from different files. | |
362 | pub fn join(&self, other: Span) -> Option<Span> { | |
363 | self.0.join(other.0).map(Span) | |
364 | } | |
365 | ||
366 | /// Creates a new span with the same line/column information as `self` but | |
367 | /// that resolves symbols as though it were at `other`. | |
368 | pub fn resolved_at(&self, other: Span) -> Span { | |
369 | Span(self.0.resolved_at(other.0)) | |
370 | } | |
371 | ||
372 | /// Creates a new span with the same name resolution behavior as `self` but | |
373 | /// with the line/column information of `other`. | |
374 | pub fn located_at(&self, other: Span) -> Span { | |
375 | other.resolved_at(*self) | |
376 | } | |
377 | ||
378 | /// Compares to spans to see if they're equal. | |
379 | pub fn eq(&self, other: &Span) -> bool { | |
380 | self.0 == other.0 | |
381 | } | |
382 | ||
383 | /// Returns the source text behind a span. This preserves the original source | |
384 | /// code, including spaces and comments. It only returns a result if the span | |
385 | /// corresponds to real source code. | |
386 | /// | |
387 | /// Note: The observable result of a macro should only rely on the tokens and | |
388 | /// not on this source text. The result of this function is a best effort to | |
389 | /// be used for diagnostics only. | |
390 | pub fn source_text(&self) -> Option<String> { | |
391 | self.0.source_text() | |
392 | } | |
393 | ||
394 | // Used by the implementation of `Span::quote` | |
395 | #[doc(hidden)] | |
396 | pub fn save_span(&self) -> usize { | |
397 | self.0.save_span() | |
398 | } | |
399 | ||
400 | // Used by the implementation of `Span::quote` | |
401 | #[doc(hidden)] | |
402 | pub fn recover_proc_macro_span(id: usize) -> Span { | |
403 | Span(bridge::client::Span::recover_proc_macro_span(id)) | |
404 | } | |
405 | ||
406 | diagnostic_method!(error, Level::Error); | |
407 | diagnostic_method!(warning, Level::Warning); | |
408 | diagnostic_method!(note, Level::Note); | |
409 | diagnostic_method!(help, Level::Help); | |
410 | } | |
411 | ||
412 | /// Prints a span in a form convenient for debugging. | |
413 | impl fmt::Debug for Span { | |
414 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
415 | self.0.fmt(f) | |
416 | } | |
417 | } | |
418 | ||
419 | /// A line-column pair representing the start or end of a `Span`. | |
420 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | |
421 | pub struct LineColumn { | |
422 | /// The 1-indexed line in the source file on which the span starts or ends (inclusive). | |
423 | pub line: usize, | |
424 | /// The 1-indexed column (number of bytes in UTF-8 encoding) in the source | |
425 | /// file on which the span starts or ends (inclusive). | |
426 | pub column: usize, | |
427 | } | |
428 | ||
429 | impl LineColumn { | |
430 | fn add_1_to_column(self) -> Self { | |
431 | LineColumn { line: self.line, column: self.column + 1 } | |
432 | } | |
433 | } | |
434 | ||
435 | impl Ord for LineColumn { | |
436 | fn cmp(&self, other: &Self) -> Ordering { | |
437 | self.line.cmp(&other.line).then(self.column.cmp(&other.column)) | |
438 | } | |
439 | } | |
440 | ||
441 | impl PartialOrd for LineColumn { | |
442 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | |
443 | Some(self.cmp(other)) | |
444 | } | |
445 | } | |
446 | ||
447 | /// The source file of a given `Span`. | |
448 | #[derive(Clone)] | |
449 | pub struct SourceFile(bridge::client::SourceFile); | |
450 | ||
451 | impl SourceFile { | |
452 | /// Gets the path to this source file. | |
453 | /// | |
454 | /// ### Note | |
455 | /// If the code span associated with this `SourceFile` was generated by an external macro, this | |
456 | /// macro, this might not be an actual path on the filesystem. Use [`is_real`] to check. | |
457 | /// | |
458 | /// Also note that even if `is_real` returns `true`, if `--remap-path-prefix` was passed on | |
459 | /// the command line, the path as given might not actually be valid. | |
460 | /// | |
461 | /// [`is_real`]: Self::is_real | |
462 | pub fn path(&self) -> PathBuf { | |
463 | PathBuf::from(self.0.path()) | |
464 | } | |
465 | ||
466 | /// Returns `true` if this source file is a real source file, and not generated by an external | |
467 | /// macro's expansion. | |
468 | pub fn is_real(&self) -> bool { | |
469 | // This is a hack until intercrate spans are implemented and we can have real source files | |
470 | // for spans generated in external macros. | |
471 | // https://github.com/rust-lang/rust/pull/43604#issuecomment-333334368 | |
472 | self.0.is_real() | |
473 | } | |
474 | } | |
475 | ||
476 | impl fmt::Debug for SourceFile { | |
477 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
478 | f.debug_struct("SourceFile") | |
479 | .field("path", &self.path()) | |
480 | .field("is_real", &self.is_real()) | |
481 | .finish() | |
482 | } | |
483 | } | |
484 | ||
485 | impl PartialEq for SourceFile { | |
486 | fn eq(&self, other: &Self) -> bool { | |
487 | self.0.eq(&other.0) | |
488 | } | |
489 | } | |
490 | ||
491 | impl Eq for SourceFile {} | |
492 | ||
493 | /// A single token or a delimited sequence of token trees (e.g., `[1, (), ..]`). | |
494 | #[derive(Clone)] | |
495 | pub enum TokenTree { | |
496 | /// A token stream surrounded by bracket delimiters. | |
497 | Group(Group), | |
498 | /// An identifier. | |
499 | Ident(Ident), | |
500 | /// A single punctuation character (`+`, `,`, `$`, etc.). | |
501 | Punct(Punct), | |
502 | /// A literal character (`'a'`), string (`"hello"`), number (`2.3`), etc. | |
503 | Literal(Literal), | |
504 | } | |
505 | ||
506 | impl TokenTree { | |
507 | /// Returns the span of this tree, delegating to the `span` method of | |
508 | /// the contained token or a delimited stream. | |
509 | pub fn span(&self) -> Span { | |
510 | match *self { | |
511 | TokenTree::Group(ref t) => t.span(), | |
512 | TokenTree::Ident(ref t) => t.span(), | |
513 | TokenTree::Punct(ref t) => t.span(), | |
514 | TokenTree::Literal(ref t) => t.span(), | |
515 | } | |
516 | } | |
517 | ||
518 | /// Configures the span for *only this token*. | |
519 | /// | |
520 | /// Note that if this token is a `Group` then this method will not configure | |
521 | /// the span of each of the internal tokens, this will simply delegate to | |
522 | /// the `set_span` method of each variant. | |
523 | pub fn set_span(&mut self, span: Span) { | |
524 | match *self { | |
525 | TokenTree::Group(ref mut t) => t.set_span(span), | |
526 | TokenTree::Ident(ref mut t) => t.set_span(span), | |
527 | TokenTree::Punct(ref mut t) => t.set_span(span), | |
528 | TokenTree::Literal(ref mut t) => t.set_span(span), | |
529 | } | |
530 | } | |
531 | } | |
532 | ||
533 | /// Prints token tree in a form convenient for debugging. | |
534 | impl fmt::Debug for TokenTree { | |
535 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
536 | // Each of these has the name in the struct type in the derived debug, | |
537 | // so don't bother with an extra layer of indirection | |
538 | match *self { | |
539 | TokenTree::Group(ref tt) => tt.fmt(f), | |
540 | TokenTree::Ident(ref tt) => tt.fmt(f), | |
541 | TokenTree::Punct(ref tt) => tt.fmt(f), | |
542 | TokenTree::Literal(ref tt) => tt.fmt(f), | |
543 | } | |
544 | } | |
545 | } | |
546 | ||
547 | impl From<Group> for TokenTree { | |
548 | fn from(g: Group) -> TokenTree { | |
549 | TokenTree::Group(g) | |
550 | } | |
551 | } | |
552 | ||
553 | impl From<Ident> for TokenTree { | |
554 | fn from(g: Ident) -> TokenTree { | |
555 | TokenTree::Ident(g) | |
556 | } | |
557 | } | |
558 | ||
559 | impl From<Punct> for TokenTree { | |
560 | fn from(g: Punct) -> TokenTree { | |
561 | TokenTree::Punct(g) | |
562 | } | |
563 | } | |
564 | ||
565 | impl From<Literal> for TokenTree { | |
566 | fn from(g: Literal) -> TokenTree { | |
567 | TokenTree::Literal(g) | |
568 | } | |
569 | } | |
570 | ||
571 | /// Prints the token tree as a string that is supposed to be losslessly convertible back | |
572 | /// into the same token tree (modulo spans), except for possibly `TokenTree::Group`s | |
573 | /// with `Delimiter::None` delimiters and negative numeric literals. | |
574 | impl fmt::Display for TokenTree { | |
575 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
576 | f.write_str(&self.to_string()) | |
577 | } | |
578 | } | |
579 | ||
580 | /// A delimited token stream. | |
581 | /// | |
582 | /// A `Group` internally contains a `TokenStream` which is surrounded by `Delimiter`s. | |
583 | #[derive(Clone)] | |
584 | pub struct Group(bridge::client::Group); | |
585 | ||
586 | /// Describes how a sequence of token trees is delimited. | |
587 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | |
588 | pub enum Delimiter { | |
589 | /// `( ... )` | |
590 | Parenthesis, | |
591 | /// `{ ... }` | |
592 | Brace, | |
593 | /// `[ ... ]` | |
594 | Bracket, | |
595 | /// `Ø ... Ø` | |
596 | /// An invisible delimiter, that may, for example, appear around tokens coming from a | |
597 | /// "macro variable" `$var`. It is important to preserve operator priorities in cases like | |
598 | /// `$var * 3` where `$var` is `1 + 2`. | |
599 | /// Invisible delimiters might not survive roundtrip of a token stream through a string. | |
600 | None, | |
601 | } | |
602 | ||
603 | impl Group { | |
604 | /// Creates a new `Group` with the given delimiter and token stream. | |
605 | /// | |
606 | /// This constructor will set the span for this group to | |
607 | /// `Span::call_site()`. To change the span you can use the `set_span` | |
608 | /// method below. | |
609 | pub fn new(delimiter: Delimiter, stream: TokenStream) -> Group { | |
610 | Group(bridge::client::Group::new(delimiter, stream.0)) | |
611 | } | |
612 | ||
613 | /// Returns the delimiter of this `Group` | |
614 | pub fn delimiter(&self) -> Delimiter { | |
615 | self.0.delimiter() | |
616 | } | |
617 | ||
618 | /// Returns the `TokenStream` of tokens that are delimited in this `Group`. | |
619 | /// | |
620 | /// Note that the returned token stream does not include the delimiter | |
621 | /// returned above. | |
622 | pub fn stream(&self) -> TokenStream { | |
623 | TokenStream(Some(self.0.stream())) | |
624 | } | |
625 | ||
626 | /// Returns the span for the delimiters of this token stream, spanning the | |
627 | /// entire `Group`. | |
628 | /// | |
629 | /// ```text | |
630 | /// pub fn span(&self) -> Span { | |
631 | /// ^^^^^^^ | |
632 | /// ``` | |
633 | pub fn span(&self) -> Span { | |
634 | Span(self.0.span()) | |
635 | } | |
636 | ||
637 | /// Returns the span pointing to the opening delimiter of this group. | |
638 | /// | |
639 | /// ```text | |
640 | /// pub fn span_open(&self) -> Span { | |
641 | /// ^ | |
642 | /// ``` | |
643 | pub fn span_open(&self) -> Span { | |
644 | Span(self.0.span_open()) | |
645 | } | |
646 | ||
647 | /// Returns the span pointing to the closing delimiter of this group. | |
648 | /// | |
649 | /// ```text | |
650 | /// pub fn span_close(&self) -> Span { | |
651 | /// ^ | |
652 | /// ``` | |
653 | pub fn span_close(&self) -> Span { | |
654 | Span(self.0.span_close()) | |
655 | } | |
656 | ||
657 | /// Configures the span for this `Group`'s delimiters, but not its internal | |
658 | /// tokens. | |
659 | /// | |
660 | /// This method will **not** set the span of all the internal tokens spanned | |
661 | /// by this group, but rather it will only set the span of the delimiter | |
662 | /// tokens at the level of the `Group`. | |
663 | pub fn set_span(&mut self, span: Span) { | |
664 | self.0.set_span(span.0); | |
665 | } | |
666 | } | |
667 | ||
668 | /// Prints the group as a string that should be losslessly convertible back | |
669 | /// into the same group (modulo spans), except for possibly `TokenTree::Group`s | |
670 | /// with `Delimiter::None` delimiters. | |
671 | impl fmt::Display for Group { | |
672 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
673 | f.write_str(&self.to_string()) | |
674 | } | |
675 | } | |
676 | ||
677 | impl fmt::Debug for Group { | |
678 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
679 | f.debug_struct("Group") | |
680 | .field("delimiter", &self.delimiter()) | |
681 | .field("stream", &self.stream()) | |
682 | .field("span", &self.span()) | |
683 | .finish() | |
684 | } | |
685 | } | |
686 | ||
687 | /// A `Punct` is a single punctuation character such as `+`, `-` or `#`. | |
688 | /// | |
689 | /// Multi-character operators like `+=` are represented as two instances of `Punct` with different | |
690 | /// forms of `Spacing` returned. | |
691 | #[derive(Clone)] | |
692 | pub struct Punct(bridge::client::Punct); | |
693 | ||
694 | /// Describes whether a `Punct` is followed immediately by another `Punct` ([`Spacing::Joint`]) or | |
695 | /// by a different token or whitespace ([`Spacing::Alone`]). | |
696 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | |
697 | pub enum Spacing { | |
698 | /// A `Punct` is not immediately followed by another `Punct`. | |
699 | /// E.g. `+` is `Alone` in `+ =`, `+ident` and `+()`. | |
700 | Alone, | |
701 | /// A `Punct` is immediately followed by another `Punct`. | |
702 | /// E.g. `+` is `Joint` in `+=` and `++`. | |
703 | /// | |
704 | /// Additionally, single quote `'` can join with identifiers to form lifetimes: `'ident`. | |
705 | Joint, | |
706 | } | |
707 | ||
708 | impl Punct { | |
709 | /// Creates a new `Punct` from the given character and spacing. | |
710 | /// The `ch` argument must be a valid punctuation character permitted by the language, | |
711 | /// otherwise the function will panic. | |
712 | /// | |
713 | /// The returned `Punct` will have the default span of `Span::call_site()` | |
714 | /// which can be further configured with the `set_span` method below. | |
715 | pub fn new(ch: char, spacing: Spacing) -> Punct { | |
716 | Punct(bridge::client::Punct::new(ch, spacing)) | |
717 | } | |
718 | ||
719 | /// Returns the value of this punctuation character as `char`. | |
720 | pub fn as_char(&self) -> char { | |
721 | self.0.as_char() | |
722 | } | |
723 | ||
724 | /// Returns the spacing of this punctuation character, indicating whether it's immediately | |
725 | /// followed by another `Punct` in the token stream, so they can potentially be combined into | |
726 | /// a multi-character operator (`Joint`), or it's followed by some other token or whitespace | |
727 | /// (`Alone`) so the operator has certainly ended. | |
728 | pub fn spacing(&self) -> Spacing { | |
729 | self.0.spacing() | |
730 | } | |
731 | ||
732 | /// Returns the span for this punctuation character. | |
733 | pub fn span(&self) -> Span { | |
734 | Span(self.0.span()) | |
735 | } | |
736 | ||
737 | /// Configure the span for this punctuation character. | |
738 | pub fn set_span(&mut self, span: Span) { | |
739 | self.0 = self.0.with_span(span.0); | |
740 | } | |
741 | } | |
742 | ||
743 | /// Prints the punctuation character as a string that should be losslessly convertible | |
744 | /// back into the same character. | |
745 | impl fmt::Display for Punct { | |
746 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
747 | f.write_str(&self.to_string()) | |
748 | } | |
749 | } | |
750 | ||
751 | impl fmt::Debug for Punct { | |
752 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
753 | f.debug_struct("Punct") | |
754 | .field("ch", &self.as_char()) | |
755 | .field("spacing", &self.spacing()) | |
756 | .field("span", &self.span()) | |
757 | .finish() | |
758 | } | |
759 | } | |
760 | ||
761 | impl PartialEq<char> for Punct { | |
762 | fn eq(&self, rhs: &char) -> bool { | |
763 | self.as_char() == *rhs | |
764 | } | |
765 | } | |
766 | ||
767 | impl PartialEq<Punct> for char { | |
768 | fn eq(&self, rhs: &Punct) -> bool { | |
769 | *self == rhs.as_char() | |
770 | } | |
771 | } | |
772 | ||
773 | /// An identifier (`ident`). | |
774 | #[derive(Clone)] | |
775 | pub struct Ident(bridge::client::Ident); | |
776 | ||
777 | impl Ident { | |
778 | /// Creates a new `Ident` with the given `string` as well as the specified | |
779 | /// `span`. | |
780 | /// The `string` argument must be a valid identifier permitted by the | |
781 | /// language (including keywords, e.g. `self` or `fn`). Otherwise, the function will panic. | |
782 | /// | |
783 | /// Note that `span`, currently in rustc, configures the hygiene information | |
784 | /// for this identifier. | |
785 | /// | |
786 | /// As of this time `Span::call_site()` explicitly opts-in to "call-site" hygiene | |
787 | /// meaning that identifiers created with this span will be resolved as if they were written | |
788 | /// directly at the location of the macro call, and other code at the macro call site will be | |
789 | /// able to refer to them as well. | |
790 | /// | |
791 | /// Later spans like `Span::def_site()` will allow to opt-in to "definition-site" hygiene | |
792 | /// meaning that identifiers created with this span will be resolved at the location of the | |
793 | /// macro definition and other code at the macro call site will not be able to refer to them. | |
794 | /// | |
795 | /// Due to the current importance of hygiene this constructor, unlike other | |
796 | /// tokens, requires a `Span` to be specified at construction. | |
797 | pub fn new(string: &str, span: Span) -> Ident { | |
798 | Ident(bridge::client::Ident::new(string, span.0, false)) | |
799 | } | |
800 | ||
801 | /// Same as `Ident::new`, but creates a raw identifier (`r#ident`). | |
802 | /// The `string` argument be a valid identifier permitted by the language | |
803 | /// (including keywords, e.g. `fn`). Keywords which are usable in path segments | |
804 | /// (e.g. `self`, `super`) are not supported, and will cause a panic. | |
805 | pub fn new_raw(string: &str, span: Span) -> Ident { | |
806 | Ident(bridge::client::Ident::new(string, span.0, true)) | |
807 | } | |
808 | ||
809 | /// Returns the span of this `Ident`, encompassing the entire string returned | |
810 | /// by [`to_string`](Self::to_string). | |
811 | pub fn span(&self) -> Span { | |
812 | Span(self.0.span()) | |
813 | } | |
814 | ||
815 | /// Configures the span of this `Ident`, possibly changing its hygiene context. | |
816 | pub fn set_span(&mut self, span: Span) { | |
817 | self.0 = self.0.with_span(span.0); | |
818 | } | |
819 | } | |
820 | ||
821 | /// Prints the identifier as a string that should be losslessly convertible | |
822 | /// back into the same identifier. | |
823 | impl fmt::Display for Ident { | |
824 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
825 | f.write_str(&self.to_string()) | |
826 | } | |
827 | } | |
828 | ||
829 | impl fmt::Debug for Ident { | |
830 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
831 | f.debug_struct("Ident") | |
832 | .field("ident", &self.to_string()) | |
833 | .field("span", &self.span()) | |
834 | .finish() | |
835 | } | |
836 | } | |
837 | ||
838 | /// A literal string (`"hello"`), byte string (`b"hello"`), | |
839 | /// character (`'a'`), byte character (`b'a'`), an integer or floating point number | |
840 | /// with or without a suffix (`1`, `1u8`, `2.3`, `2.3f32`). | |
841 | /// Boolean literals like `true` and `false` do not belong here, they are `Ident`s. | |
842 | #[derive(Clone)] | |
843 | pub struct Literal(bridge::client::Literal); | |
844 | ||
845 | macro_rules! suffixed_int_literals { | |
846 | ($($name:ident => $kind:ident,)*) => ($( | |
847 | /// Creates a new suffixed integer literal with the specified value. | |
848 | /// | |
849 | /// This function will create an integer like `1u32` where the integer | |
850 | /// value specified is the first part of the token and the integral is | |
851 | /// also suffixed at the end. | |
852 | /// Literals created from negative numbers might not survive round-trips through | |
853 | /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). | |
854 | /// | |
855 | /// Literals created through this method have the `Span::call_site()` | |
856 | /// span by default, which can be configured with the `set_span` method | |
857 | /// below. | |
858 | pub fn $name(n: $kind) -> Literal { | |
859 | Literal(bridge::client::Literal::typed_integer(&n.to_string(), stringify!($kind))) | |
860 | } | |
861 | )*) | |
862 | } | |
863 | ||
864 | macro_rules! unsuffixed_int_literals { | |
865 | ($($name:ident => $kind:ident,)*) => ($( | |
866 | /// Creates a new unsuffixed integer literal with the specified value. | |
867 | /// | |
868 | /// This function will create an integer like `1` where the integer | |
869 | /// value specified is the first part of the token. No suffix is | |
870 | /// specified on this token, meaning that invocations like | |
871 | /// `Literal::i8_unsuffixed(1)` are equivalent to | |
872 | /// `Literal::u32_unsuffixed(1)`. | |
873 | /// Literals created from negative numbers might not survive rountrips through | |
874 | /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). | |
875 | /// | |
876 | /// Literals created through this method have the `Span::call_site()` | |
877 | /// span by default, which can be configured with the `set_span` method | |
878 | /// below. | |
879 | pub fn $name(n: $kind) -> Literal { | |
880 | Literal(bridge::client::Literal::integer(&n.to_string())) | |
881 | } | |
882 | )*) | |
883 | } | |
884 | ||
885 | impl Literal { | |
886 | suffixed_int_literals! { | |
887 | u8_suffixed => u8, | |
888 | u16_suffixed => u16, | |
889 | u32_suffixed => u32, | |
890 | u64_suffixed => u64, | |
891 | u128_suffixed => u128, | |
892 | usize_suffixed => usize, | |
893 | i8_suffixed => i8, | |
894 | i16_suffixed => i16, | |
895 | i32_suffixed => i32, | |
896 | i64_suffixed => i64, | |
897 | i128_suffixed => i128, | |
898 | isize_suffixed => isize, | |
899 | } | |
900 | ||
901 | unsuffixed_int_literals! { | |
902 | u8_unsuffixed => u8, | |
903 | u16_unsuffixed => u16, | |
904 | u32_unsuffixed => u32, | |
905 | u64_unsuffixed => u64, | |
906 | u128_unsuffixed => u128, | |
907 | usize_unsuffixed => usize, | |
908 | i8_unsuffixed => i8, | |
909 | i16_unsuffixed => i16, | |
910 | i32_unsuffixed => i32, | |
911 | i64_unsuffixed => i64, | |
912 | i128_unsuffixed => i128, | |
913 | isize_unsuffixed => isize, | |
914 | } | |
915 | ||
916 | /// Creates a new unsuffixed floating-point literal. | |
917 | /// | |
918 | /// This constructor is similar to those like `Literal::i8_unsuffixed` where | |
919 | /// the float's value is emitted directly into the token but no suffix is | |
920 | /// used, so it may be inferred to be a `f64` later in the compiler. | |
921 | /// Literals created from negative numbers might not survive rountrips through | |
922 | /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). | |
923 | /// | |
924 | /// # Panics | |
925 | /// | |
926 | /// This function requires that the specified float is finite, for | |
927 | /// example if it is infinity or NaN this function will panic. | |
928 | pub fn f32_unsuffixed(n: f32) -> Literal { | |
929 | if !n.is_finite() { | |
930 | panic!("Invalid float literal {n}"); | |
931 | } | |
932 | let mut repr = n.to_string(); | |
933 | if !repr.contains('.') { | |
934 | repr.push_str(".0"); | |
935 | } | |
936 | Literal(bridge::client::Literal::float(&repr)) | |
937 | } | |
938 | ||
939 | /// Creates a new suffixed floating-point literal. | |
940 | /// | |
941 | /// This constructor will create a literal like `1.0f32` where the value | |
942 | /// specified is the preceding part of the token and `f32` is the suffix of | |
943 | /// the token. This token will always be inferred to be an `f32` in the | |
944 | /// compiler. | |
945 | /// Literals created from negative numbers might not survive rountrips through | |
946 | /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). | |
947 | /// | |
948 | /// # Panics | |
949 | /// | |
950 | /// This function requires that the specified float is finite, for | |
951 | /// example if it is infinity or NaN this function will panic. | |
952 | pub fn f32_suffixed(n: f32) -> Literal { | |
953 | if !n.is_finite() { | |
954 | panic!("Invalid float literal {n}"); | |
955 | } | |
956 | Literal(bridge::client::Literal::f32(&n.to_string())) | |
957 | } | |
958 | ||
959 | /// Creates a new unsuffixed floating-point literal. | |
960 | /// | |
961 | /// This constructor is similar to those like `Literal::i8_unsuffixed` where | |
962 | /// the float's value is emitted directly into the token but no suffix is | |
963 | /// used, so it may be inferred to be a `f64` later in the compiler. | |
964 | /// Literals created from negative numbers might not survive rountrips through | |
965 | /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). | |
966 | /// | |
967 | /// # Panics | |
968 | /// | |
969 | /// This function requires that the specified float is finite, for | |
970 | /// example if it is infinity or NaN this function will panic. | |
971 | pub fn f64_unsuffixed(n: f64) -> Literal { | |
972 | if !n.is_finite() { | |
973 | panic!("Invalid float literal {n}"); | |
974 | } | |
975 | let mut repr = n.to_string(); | |
976 | if !repr.contains('.') { | |
977 | repr.push_str(".0"); | |
978 | } | |
979 | Literal(bridge::client::Literal::float(&repr)) | |
980 | } | |
981 | ||
982 | /// Creates a new suffixed floating-point literal. | |
983 | /// | |
984 | /// This constructor will create a literal like `1.0f64` where the value | |
985 | /// specified is the preceding part of the token and `f64` is the suffix of | |
986 | /// the token. This token will always be inferred to be an `f64` in the | |
987 | /// compiler. | |
988 | /// Literals created from negative numbers might not survive rountrips through | |
989 | /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). | |
990 | /// | |
991 | /// # Panics | |
992 | /// | |
993 | /// This function requires that the specified float is finite, for | |
994 | /// example if it is infinity or NaN this function will panic. | |
995 | pub fn f64_suffixed(n: f64) -> Literal { | |
996 | if !n.is_finite() { | |
997 | panic!("Invalid float literal {n}"); | |
998 | } | |
999 | Literal(bridge::client::Literal::f64(&n.to_string())) | |
1000 | } | |
1001 | ||
1002 | /// String literal. | |
1003 | pub fn string(string: &str) -> Literal { | |
1004 | Literal(bridge::client::Literal::string(string)) | |
1005 | } | |
1006 | ||
1007 | /// Character literal. | |
1008 | pub fn character(ch: char) -> Literal { | |
1009 | Literal(bridge::client::Literal::character(ch)) | |
1010 | } | |
1011 | ||
1012 | /// Byte string literal. | |
1013 | pub fn byte_string(bytes: &[u8]) -> Literal { | |
1014 | Literal(bridge::client::Literal::byte_string(bytes)) | |
1015 | } | |
1016 | ||
1017 | /// Returns the span encompassing this literal. | |
1018 | pub fn span(&self) -> Span { | |
1019 | Span(self.0.span()) | |
1020 | } | |
1021 | ||
1022 | /// Configures the span associated for this literal. | |
1023 | pub fn set_span(&mut self, span: Span) { | |
1024 | self.0.set_span(span.0); | |
1025 | } | |
1026 | ||
1027 | /// Returns a `Span` that is a subset of `self.span()` containing only the | |
1028 | /// source bytes in range `range`. Returns `None` if the would-be trimmed | |
1029 | /// span is outside the bounds of `self`. | |
1030 | // FIXME(SergioBenitez): check that the byte range starts and ends at a | |
1031 | // UTF-8 boundary of the source. otherwise, it's likely that a panic will | |
1032 | // occur elsewhere when the source text is printed. | |
1033 | // FIXME(SergioBenitez): there is no way for the user to know what | |
1034 | // `self.span()` actually maps to, so this method can currently only be | |
1035 | // called blindly. For example, `to_string()` for the character 'c' returns | |
1036 | // "'\u{63}'"; there is no way for the user to know whether the source text | |
1037 | // was 'c' or whether it was '\u{63}'. | |
1038 | pub fn subspan<R: RangeBounds<usize>>(&self, range: R) -> Option<Span> { | |
1039 | self.0.subspan(range.start_bound().cloned(), range.end_bound().cloned()).map(Span) | |
1040 | } | |
1041 | } | |
1042 | ||
1043 | /// Parse a single literal from its stringified representation. | |
1044 | /// | |
1045 | /// In order to parse successfully, the input string must not contain anything | |
1046 | /// but the literal token. Specifically, it must not contain whitespace or | |
1047 | /// comments in addition to the literal. | |
1048 | /// | |
1049 | /// The resulting literal token will have a `Span::call_site()` span. | |
1050 | /// | |
1051 | /// NOTE: some errors may cause panics instead of returning `LexError`. We | |
1052 | /// reserve the right to change these errors into `LexError`s later. | |
1053 | impl FromStr for Literal { | |
1054 | type Err = LexError; | |
1055 | ||
1056 | fn from_str(src: &str) -> Result<Self, LexError> { | |
1057 | match bridge::client::Literal::from_str(src) { | |
1058 | Ok(literal) => Ok(Literal(literal)), | |
1059 | Err(()) => Err(LexError), | |
1060 | } | |
1061 | } | |
1062 | } | |
1063 | ||
1064 | /// Prints the literal as a string that should be losslessly convertible | |
1065 | /// back into the same literal (except for possible rounding for floating point literals). | |
1066 | impl fmt::Display for Literal { | |
1067 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
1068 | f.write_str(&self.to_string()) | |
1069 | } | |
1070 | } | |
1071 | ||
1072 | impl fmt::Debug for Literal { | |
1073 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
1074 | self.0.fmt(f) | |
1075 | } | |
1076 | } | |
1077 | ||
1078 | /// Tracked access to environment variables. | |
1079 | pub mod tracked_env { | |
1080 | use std::env::{self, VarError}; | |
1081 | use std::ffi::OsStr; | |
1082 | ||
1083 | /// Retrieve an environment variable and add it to build dependency info. | |
1084 | /// Build system executing the compiler will know that the variable was accessed during | |
1085 | /// compilation, and will be able to rerun the build when the value of that variable changes. | |
1086 | /// Besides the dependency tracking this function should be equivalent to `env::var` from the | |
1087 | /// standard library, except that the argument must be UTF-8. | |
1088 | pub fn var<K: AsRef<OsStr> + AsRef<str>>(key: K) -> Result<String, VarError> { | |
1089 | let key: &str = key.as_ref(); | |
1090 | let value = env::var(key); | |
1091 | super::bridge::client::FreeFunctions::track_env_var(key, value.as_deref().ok()); | |
1092 | value | |
1093 | } | |
1094 | } | |
1095 | ||
1096 | /// Tracked access to additional files. | |
1097 | pub mod tracked_path { | |
1098 | ||
1099 | /// Track a file explicitly. | |
1100 | /// | |
1101 | /// Commonly used for tracking asset preprocessing. | |
1102 | pub fn path<P: AsRef<str>>(path: P) { | |
1103 | let path: &str = path.as_ref(); | |
1104 | super::bridge::client::FreeFunctions::track_path(path); | |
1105 | } | |
1106 | } |