diff options
Diffstat (limited to 'src/libsyntax/codemap.rs')
| -rw-r--r-- | src/libsyntax/codemap.rs | 70 |
1 files changed, 38 insertions, 32 deletions
diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index 162da2ac54c..44df8a6d3da 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -243,6 +243,10 @@ pub struct NameAndSpan { pub name: String, /// The format with which the macro was invoked. pub format: MacroFormat, + /// Whether the macro is allowed to use #[unstable]/feature-gated + /// features internally without forcing the whole crate to opt-in + /// to them. + pub allow_internal_unstable: bool, /// The span of the macro definition itself. The macro may not /// have a sensible definition span (e.g. something defined /// completely inside libsyntax) in which case this is None. @@ -830,41 +834,43 @@ impl CodeMap { } } - /// Check if a span is "internal" to a macro. This means that it is entirely generated by a - /// macro expansion and contains no code that was passed in as an argument. - pub fn span_is_internal(&self, span: Span) -> bool { - // first, check if the given expression was generated by a macro or not - // we need to go back the expn_info tree to check only the arguments - // of the initial macro call, not the nested ones. - let mut is_internal = false; - let mut expnid = span.expn_id; - while self.with_expn_info(expnid, |expninfo| { - match expninfo { - Some(ref info) => { - // save the parent expn_id for next loop iteration - expnid = info.call_site.expn_id; - if info.callee.name == "format_args" { - // This is a hack because the format_args builtin calls unstable APIs. - // I spent like 6 hours trying to solve this more generally but am stupid. - is_internal = true; - false - } else if info.callee.span.is_none() { - // it's a compiler built-in, we *really* don't want to mess with it - // so we skip it, unless it was called by a regular macro, in which case - // we will handle the caller macro next turn - is_internal = true; - true // continue looping + /// Check if a span is "internal" to a macro in which #[unstable] + /// items can be used (that is, a macro marked with + /// `#[allow_internal_unstable]`). + pub fn span_allows_unstable(&self, span: Span) -> bool { + debug!("span_allows_unstable(span = {:?})", span); + let mut allows_unstable = false; + let mut expn_id = span.expn_id; + loop { + let quit = self.with_expn_info(expn_id, |expninfo| { + debug!("span_allows_unstable: expninfo = {:?}", expninfo); + expninfo.map_or(/* hit the top level */ true, |info| { + + let span_comes_from_this_expansion = + info.callee.span.map_or(span == info.call_site, |mac_span| { + mac_span.lo <= span.lo && span.hi < mac_span.hi + }); + + debug!("span_allows_unstable: from this expansion? {}, allows unstable? {}", + span_comes_from_this_expansion, + info.callee.allow_internal_unstable); + if span_comes_from_this_expansion { + allows_unstable = info.callee.allow_internal_unstable; + // we've found the right place, stop looking + true } else { - // was this expression from the current macro arguments ? - is_internal = !( span.lo > info.call_site.lo && - span.hi < info.call_site.hi ); - true // continue looping + // not the right place, keep looking + expn_id = info.call_site.expn_id; + false } - }, - _ => false // stop looping + }) + }); + if quit { + break } - }) { /* empty while loop body */ } - return is_internal; + } + debug!("span_allows_unstable? {}", allows_unstable); + allows_unstable } } |
