diff --git a/src/utils.rs b/src/utils.rs index c67ded3d93d9..b99ed45b3c38 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,12 +1,42 @@ use rustc::lint::Context; -use syntax::codemap::ExpnInfo; +use syntax::ast::{DefId, Name, Path}; +use syntax::codemap::{ExpnInfo, Span}; +use rustc::middle::ty; +/// returns true if the macro that expanded the crate was outside of +/// the current crate or was a compiler plugin pub fn in_macro(cx: &Context, opt_info: Option<&ExpnInfo>) -> bool { + // no ExpnInfo = no macro opt_info.map_or(false, |info| { + // no span for the callee = external macro info.callee.span.map_or(true, |span| { + // no snippet = external macro or compiler-builtin expansion cx.sess().codemap().span_to_snippet(span).ok().map_or(true, |code| + // macro doesn't start with "macro_rules" + // = compiler plugin !code.starts_with("macro_rules") ) }) }) } + +/// invokes in_macro with the expansion info of the given span +pub fn in_external_macro(cx: &Context, span: Span) -> bool { + cx.sess().codemap().with_expn_info(span.expn_id, + |info| in_macro(cx, info)) +} + +/// check if a DefId's path matches the given absolute type path +/// usage e.g. with +/// `match_def_path(cx, id, &["core", "option", "Option"])` +pub fn match_def_path(cx: &Context, def_id: DefId, path: &[&str]) -> bool { + ty::with_path(cx.tcx, def_id, |iter| iter.map(|elem| elem.name()) + .zip(path.iter()).all(|(nm, p)| &nm.as_str() == p)) +} + +/// match a Path against a slice of segment string literals, e.g. +/// `match_path(path, &["std", "rt", "begin_unwind"])` +pub fn match_path(path: &Path, segments: &[&str]) -> bool { + path.segments.iter().rev().zip(segments.iter().rev()).all( + |(a,b)| a.identifier.as_str() == *b) +}