rust/src/librustdoc/clean/utils.rs

720 lines
25 KiB
Rust
Raw Normal View History

2019-12-22 17:42:04 -05:00
use crate::clean::auto_trait::AutoTraitFinder;
use crate::clean::blanket_impl::BlanketImplFinder;
2019-12-09 17:53:42 +01:00
use crate::clean::{
inline, Clean, Crate, ExternalCrate, Generic, GenericArg, GenericArgs, ImportSource, Item,
ItemKind, Lifetime, Path, PathSegment, Primitive, PrimitiveType, Type, TypeBinding, Visibility,
2019-12-09 17:53:42 +01:00
};
2019-12-22 17:42:04 -05:00
use crate::core::DocContext;
use crate::formats::item_type::ItemType;
use crate::visit_lib::LibEmbargoVisitor;
2019-12-09 17:46:20 +01:00
2021-07-05 11:00:27 +02:00
use rustc_ast as ast;
use rustc_ast::token::{self, BinOpToken, DelimToken};
use rustc_ast::tokenstream::{TokenStream, TokenTree};
use rustc_ast_pretty::pprust::state::State as Printer;
use rustc_ast_pretty::pprust::PrintState;
use rustc_data_structures::thin_vec::ThinVec;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_middle::mir::interpret::ConstValue;
2020-03-29 17:19:48 +02:00
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
use rustc_middle::ty::{self, DefIdTree, TyCtxt};
use rustc_session::parse::ParseSess;
use rustc_span::source_map::FilePathMapping;
use rustc_span::symbol::{kw, sym, Symbol};
use std::fmt::Write as _;
use std::mem;
2019-12-09 17:53:42 +01:00
#[cfg(test)]
mod tests;
crate fn krate(cx: &mut DocContext<'_>) -> Crate {
let module = crate::visit_ast::RustdocVisitor::new(cx).visit();
2019-12-09 17:46:20 +01:00
for &cnum in cx.tcx.crates(()) {
// Analyze doc-reachability for extern items
LibEmbargoVisitor::new(cx).visit_lib(cnum);
}
2020-02-29 20:16:26 +03:00
// Clean the crate, translating the entire librustc_ast AST to one that is
2019-12-09 17:46:20 +01:00
// understood by rustdoc.
let mut module = module.clean(cx);
match *module.kind {
ItemKind::ModuleItem(ref module) => {
2019-12-09 17:46:20 +01:00
for it in &module.items {
// `compiler_builtins` should be masked too, but we can't apply
// `#[doc(masked)]` to the injected `extern crate` because it's unstable.
if it.is_extern_crate()
&& (it.attrs.has_doc_flag(sym::masked)
|| cx.tcx.is_compiler_builtins(it.def_id.krate()))
2019-12-09 17:46:20 +01:00
{
cx.cache.masked_crates.insert(it.def_id.krate());
2019-12-09 17:46:20 +01:00
}
}
}
_ => unreachable!(),
}
let local_crate = ExternalCrate { crate_num: LOCAL_CRATE };
2021-04-22 19:10:22 -04:00
let primitives = local_crate.primitives(cx.tcx);
2021-04-22 19:16:41 -04:00
let keywords = local_crate.keywords(cx.tcx);
2019-12-09 17:46:20 +01:00
{
let m = match *module.kind {
ItemKind::ModuleItem(ref mut m) => m,
2019-12-09 17:46:20 +01:00
_ => unreachable!(),
};
m.items.extend(primitives.iter().map(|&(def_id, prim)| {
Item::from_def_id_and_parts(
def_id,
Some(prim.as_sym()),
ItemKind::PrimitiveItem(prim),
cx,
)
2019-12-09 17:46:20 +01:00
}));
m.items.extend(keywords.into_iter().map(|(def_id, kw)| {
2020-12-29 19:33:48 +01:00
Item::from_def_id_and_parts(def_id, Some(kw), ItemKind::KeywordItem(kw), cx)
2019-12-09 17:46:20 +01:00
}));
}
Crate { module, primitives, external_traits: cx.external_traits.clone() }
2019-12-09 17:46:20 +01:00
}
2020-12-16 19:14:21 -05:00
fn external_generic_args(
cx: &mut DocContext<'_>,
did: DefId,
2019-12-09 17:46:20 +01:00
has_self: bool,
bindings: Vec<TypeBinding>,
2020-12-16 19:14:21 -05:00
substs: SubstsRef<'_>,
2019-12-09 17:46:20 +01:00
) -> GenericArgs {
let mut skip_self = has_self;
let mut ty_kind = None;
2019-12-22 17:42:04 -05:00
let args: Vec<_> = substs
.iter()
.filter_map(|kind| match kind.unpack() {
GenericArgKind::Lifetime(lt) => match lt {
ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrAnon(_), .. }) => {
Some(GenericArg::Lifetime(Lifetime::elided()))
}
_ => lt.clean(cx).map(GenericArg::Lifetime),
},
2019-12-22 17:42:04 -05:00
GenericArgKind::Type(_) if skip_self => {
skip_self = false;
None
}
GenericArgKind::Type(ty) => {
ty_kind = Some(ty.kind());
2019-12-22 17:42:04 -05:00
Some(GenericArg::Type(ty.clean(cx)))
}
GenericArgKind::Const(ct) => Some(GenericArg::Const(Box::new(ct.clean(cx)))),
2019-12-22 17:42:04 -05:00
})
.collect();
2019-12-09 17:46:20 +01:00
if cx.tcx.fn_trait_kind_from_lang_item(did).is_some() {
let inputs = match ty_kind.unwrap() {
ty::Tuple(tys) => tys.iter().map(|t| t.expect_ty().clean(cx)).collect(),
_ => return GenericArgs::AngleBracketed { args, bindings: bindings.into() },
};
let output = None;
// FIXME(#20299) return type comes from a projection now
// match types[1].kind {
// ty::Tuple(ref v) if v.is_empty() => None, // -> ()
// _ => Some(types[1].clean(cx))
// };
GenericArgs::Parenthesized { inputs, output }
} else {
GenericArgs::AngleBracketed { args, bindings: bindings.into() }
2019-12-09 17:46:20 +01:00
}
}
2020-12-16 19:14:21 -05:00
pub(super) fn external_path(
cx: &mut DocContext<'_>,
did: DefId,
2019-12-22 17:42:04 -05:00
has_self: bool,
bindings: Vec<TypeBinding>,
2020-12-16 19:14:21 -05:00
substs: SubstsRef<'_>,
2019-12-22 17:42:04 -05:00
) -> Path {
let def_kind = cx.tcx.def_kind(did);
let name = cx.tcx.item_name(did);
2019-12-09 17:46:20 +01:00
Path {
res: Res::Def(def_kind, did),
2019-12-09 17:46:20 +01:00
segments: vec![PathSegment {
name,
args: external_generic_args(cx, did, has_self, bindings, substs),
2019-12-09 17:46:20 +01:00
}],
}
}
/// Remove the generic arguments from a path.
2021-12-12 20:16:18 +03:00
crate fn strip_path_generics(mut path: Path) -> Path {
for ps in path.segments.iter_mut() {
ps.args = GenericArgs::AngleBracketed { args: vec![], bindings: ThinVec::new() }
2021-12-12 20:16:18 +03:00
}
2019-12-09 17:46:20 +01:00
2021-12-12 20:16:18 +03:00
path
2019-12-09 17:46:20 +01:00
}
crate fn qpath_to_string(p: &hir::QPath<'_>) -> String {
let segments = match *p {
2021-10-01 17:12:39 +02:00
hir::QPath::Resolved(_, path) => &path.segments,
hir::QPath::TypeRelative(_, segment) => return segment.ident.to_string(),
hir::QPath::LangItem(lang_item, ..) => return lang_item.name().to_string(),
};
let mut s = String::new();
for (i, seg) in segments.iter().enumerate() {
if i > 0 {
s.push_str("::");
}
if seg.ident.name != kw::PathRoot {
s.push_str(seg.ident.as_str());
}
}
s
}
crate fn build_deref_target_impls(cx: &mut DocContext<'_>, items: &[Item], ret: &mut Vec<Item>) {
2019-12-09 17:46:20 +01:00
let tcx = cx.tcx;
for item in items {
let target = match *item.kind {
ItemKind::TypedefItem(ref t, true) => &t.type_,
2019-12-09 17:46:20 +01:00
_ => continue,
};
if let Some(prim) = target.primitive_type() {
let _prof_timer = cx.tcx.sess.prof.generic_activity("build_primitive_inherent_impls");
for &did in prim.impls(tcx).iter().filter(|did| !did.is_local()) {
inline::build_impl(cx, None, did, None, ret);
2019-12-09 17:46:20 +01:00
}
} else if let Type::Path { path } = target {
2021-11-11 15:45:45 -08:00
let did = path.def_id();
2019-12-09 17:46:20 +01:00
if !did.is_local() {
inline::build_impls(cx, None, did, None, ret);
2019-12-09 17:46:20 +01:00
}
}
}
}
crate fn name_from_pat(p: &hir::Pat<'_>) -> Symbol {
use rustc_hir::*;
debug!("trying to get a name from pattern: {:?}", p);
Symbol::intern(&match p.kind {
PatKind::Wild | PatKind::Struct(..) => return kw::Underscore,
PatKind::Binding(_, _, ident, _) => return ident.name,
PatKind::TupleStruct(ref p, ..) | PatKind::Path(ref p) => qpath_to_string(p),
2021-10-01 17:12:39 +02:00
PatKind::Or(pats) => {
2021-07-14 16:17:04 -05:00
pats.iter().map(|p| name_from_pat(p).to_string()).collect::<Vec<String>>().join(" | ")
}
2021-10-01 17:12:39 +02:00
PatKind::Tuple(elts, _) => format!(
"({})",
2021-07-14 16:17:04 -05:00
elts.iter().map(|p| name_from_pat(p).to_string()).collect::<Vec<String>>().join(", ")
),
2021-10-01 17:12:39 +02:00
PatKind::Box(p) => return name_from_pat(&*p),
PatKind::Ref(p, _) => return name_from_pat(&*p),
PatKind::Lit(..) => {
warn!(
"tried to get argument name from PatKind::Lit, which is silly in function arguments"
);
return Symbol::intern("()");
}
PatKind::Range(..) => return kw::Underscore,
2021-10-01 17:12:39 +02:00
PatKind::Slice(begin, ref mid, end) => {
2021-07-14 16:17:04 -05:00
let begin = begin.iter().map(|p| name_from_pat(p).to_string());
let mid = mid.as_ref().map(|p| format!("..{}", name_from_pat(&**p))).into_iter();
2021-07-14 16:17:04 -05:00
let end = end.iter().map(|p| name_from_pat(p).to_string());
format!("[{}]", begin.chain(mid).chain(end).collect::<Vec<_>>().join(", "))
}
})
}
crate fn print_const(cx: &DocContext<'_>, n: &ty::Const<'_>) -> String {
2019-12-09 17:46:20 +01:00
match n.val {
2022-01-12 03:19:52 +00:00
ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs: _, promoted }) => {
2020-07-02 23:13:32 +02:00
let mut s = if let Some(def) = def.as_local() {
let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def.did);
2021-02-11 20:37:36 +01:00
print_const_expr(cx.tcx, cx.tcx.hir().body_owned_by(hir_id))
2019-12-09 17:46:20 +01:00
} else {
2021-03-07 18:09:35 +01:00
inline::print_inlined_const(cx.tcx, def.did)
};
if let Some(promoted) = promoted {
2019-12-13 16:23:23 -03:00
s.push_str(&format!("::{:?}", promoted))
2019-12-09 17:46:20 +01:00
}
s
2019-12-22 17:42:04 -05:00
}
2019-12-09 17:46:20 +01:00
_ => {
let mut s = n.to_string();
// array lengths are obviously usize
if s.ends_with("_usize") {
let n = s.len() - "_usize".len();
2019-12-09 17:46:20 +01:00
s.truncate(n);
if s.ends_with(": ") {
let n = s.len() - ": ".len();
s.truncate(n);
}
}
s
2019-12-22 17:42:04 -05:00
}
2019-12-09 17:46:20 +01:00
}
}
2021-03-07 18:09:35 +01:00
crate fn print_evaluated_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option<String> {
tcx.const_eval_poly(def_id).ok().and_then(|val| {
let ty = tcx.type_of(def_id);
match (val, ty.kind()) {
(_, &ty::Ref(..)) => None,
(ConstValue::Scalar(_), &ty::Adt(_, _)) => None,
(ConstValue::Scalar(_), _) => {
2021-03-07 18:09:35 +01:00
let const_ = ty::Const::from_value(tcx, val, ty);
Some(print_const_with_custom_print_scalar(tcx, const_))
}
_ => None,
}
})
}
fn format_integer_with_underscore_sep(num: &str) -> String {
let num_chars: Vec<_> = num.chars().collect();
let mut num_start_index = if num_chars.get(0) == Some(&'-') { 1 } else { 0 };
let chunk_size = match num[num_start_index..].as_bytes() {
[b'0', b'b' | b'x', ..] => {
num_start_index += 2;
4
}
[b'0', b'o', ..] => {
num_start_index += 2;
let remaining_chars = num_chars.len() - num_start_index;
if remaining_chars <= 6 {
// don't add underscores to Unix permissions like 0755 or 100755
return num.to_string();
}
3
}
_ => 3,
};
num_chars[..num_start_index]
.iter()
.chain(num_chars[num_start_index..].rchunks(chunk_size).rev().intersperse(&['_']).flatten())
.collect()
}
fn print_const_with_custom_print_scalar(tcx: TyCtxt<'_>, ct: &ty::Const<'_>) -> String {
// Use a slightly different format for integer types which always shows the actual value.
// For all other types, fallback to the original `pretty_print_const`.
match (ct.val, ct.ty.kind()) {
(ty::ConstKind::Value(ConstValue::Scalar(int)), ty::Uint(ui)) => {
format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str())
}
(ty::ConstKind::Value(ConstValue::Scalar(int)), ty::Int(i)) => {
2021-03-07 18:09:35 +01:00
let ty = tcx.lift(ct.ty).unwrap();
let size = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size;
let data = int.assert_bits(size);
let sign_extended_data = size.sign_extend(data) as i128;
format!(
"{}{}",
format_integer_with_underscore_sep(&sign_extended_data.to_string()),
i.name_str()
)
}
_ => ct.to_string(),
}
}
2021-03-07 18:09:35 +01:00
crate fn is_literal_expr(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
if let hir::Node::Expr(expr) = tcx.hir().get(hir_id) {
if let hir::ExprKind::Lit(_) = &expr.kind {
return true;
}
if let hir::ExprKind::Unary(hir::UnOp::Neg, expr) = &expr.kind {
if let hir::ExprKind::Lit(_) = &expr.kind {
return true;
}
}
}
false
}
2021-02-11 20:37:36 +01:00
crate fn print_const_expr(tcx: TyCtxt<'_>, body: hir::BodyId) -> String {
let hir = tcx.hir();
let value = &hir.body(body).value;
let snippet = if !value.span.from_expansion() {
2021-02-11 20:37:36 +01:00
tcx.sess.source_map().span_to_snippet(value.span).ok()
} else {
None
};
2021-02-11 20:37:36 +01:00
snippet.unwrap_or_else(|| rustc_hir_pretty::id_to_string(&hir, body.hir_id))
2019-12-09 17:46:20 +01:00
}
/// Given a type Path, resolve it to a Type using the TyCtxt
crate fn resolve_type(cx: &mut DocContext<'_>, path: Path) -> Type {
debug!("resolve_type({:?})", path);
2019-12-09 17:46:20 +01:00
match path.res {
Res::PrimTy(p) => Primitive(PrimitiveType::from(p)),
Res::SelfTy(..) if path.segments.len() == 1 => Generic(kw::SelfUpper),
Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => Generic(path.segments[0].name),
_ => {
2021-11-11 15:45:45 -08:00
let _ = register_res(cx, path.res);
Type::Path { path }
2019-12-09 17:46:20 +01:00
}
}
2019-12-09 17:46:20 +01:00
}
crate fn get_auto_trait_and_blanket_impls(
cx: &mut DocContext<'_>,
item_def_id: DefId,
2019-12-09 17:46:20 +01:00
) -> impl Iterator<Item = Item> {
let auto_impls = cx
.sess()
.prof
.generic_activity("get_auto_trait_impls")
.run(|| AutoTraitFinder::new(cx).get_auto_trait_impls(item_def_id));
let blanket_impls = cx
.sess()
.prof
.generic_activity("get_blanket_impls")
.run(|| BlanketImplFinder { cx }.get_blanket_impls(item_def_id));
2020-12-15 23:51:38 -05:00
auto_impls.into_iter().chain(blanket_impls)
2019-12-09 17:46:20 +01:00
}
/// If `res` has a documentation page associated, store it in the cache.
///
/// This is later used by [`href()`] to determine the HTML link for the item.
///
/// [`href()`]: crate::html::format::href
crate fn register_res(cx: &mut DocContext<'_>, res: Res) -> DefId {
use DefKind::*;
2019-12-09 17:46:20 +01:00
debug!("register_res({:?})", res);
let (did, kind) = match res {
// These should be added to the cache using `record_extern_fqn`.
Res::Def(
2021-11-30 13:08:41 -05:00
kind @ (AssocTy | AssocFn | AssocConst | Variant | Fn | TyAlias | Enum | Trait | Struct
| Union | Mod | ForeignTy | Const | Static | Macro(..) | TraitAlias),
i,
) => (i, kind.into()),
// This is part of a trait definition; document the trait.
Res::SelfTy(Some(trait_def_id), _) => (trait_def_id, ItemType::Trait),
// This is an inherent impl; it doesn't have its own page.
Res::SelfTy(None, Some((impl_def_id, _))) => return impl_def_id,
Res::SelfTy(None, None)
| Res::PrimTy(_)
| Res::ToolMod
| Res::SelfCtor(_)
| Res::Local(_)
| Res::NonMacroAttr(_)
| Res::Err => return res.def_id(),
Res::Def(
2021-10-02 12:59:26 +01:00
TyParam | ConstParam | Ctor(..) | ExternCrate | Use | ForeignMod | AnonConst
| InlineConst | OpaqueTy | Field | LifetimeParam | GlobalAsm | Impl | Closure
| Generator,
id,
) => return id,
2019-12-09 17:46:20 +01:00
};
2019-12-22 17:42:04 -05:00
if did.is_local() {
return did;
}
2019-12-09 17:46:20 +01:00
inline::record_extern_fqn(cx, did, kind);
if let ItemType::Trait = kind {
2019-12-09 17:46:20 +01:00
inline::record_extern_trait(cx, did);
}
did
}
crate fn resolve_use_source(cx: &mut DocContext<'_>, path: Path) -> ImportSource {
2019-12-09 17:46:20 +01:00
ImportSource {
2019-12-22 17:42:04 -05:00
did: if path.res.opt_def_id().is_none() { None } else { Some(register_res(cx, path.res)) },
2019-12-09 17:46:20 +01:00
path,
}
}
crate fn enter_impl_trait<F, R>(cx: &mut DocContext<'_>, f: F) -> R
2019-12-09 17:46:20 +01:00
where
F: FnOnce(&mut DocContext<'_>) -> R,
2019-12-09 17:46:20 +01:00
{
let old_bounds = mem::take(&mut cx.impl_trait_bounds);
let r = f(cx);
assert!(cx.impl_trait_bounds.is_empty());
cx.impl_trait_bounds = old_bounds;
2019-12-09 17:46:20 +01:00
r
}
2020-12-25 15:38:46 -08:00
2020-12-30 16:38:25 -08:00
/// Find the nearest parent module of a [`DefId`].
crate fn find_nearest_parent_module(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
if def_id.is_top_level_module() {
// The crate root has no parent. Use it as the root instead.
Some(def_id)
} else {
let mut current = def_id;
// The immediate parent might not always be a module.
// Find the first parent which is.
while let Some(parent) = tcx.parent(current) {
if tcx.def_kind(parent) == DefKind::Mod {
return Some(parent);
2020-12-25 15:38:46 -08:00
}
current = parent;
2020-12-25 15:38:46 -08:00
}
None
2020-12-25 15:38:46 -08:00
}
}
2021-01-28 17:05:22 +01:00
/// Checks for the existence of `hidden` in the attribute below if `flag` is `sym::hidden`:
2021-01-28 17:05:22 +01:00
///
/// ```
/// #[doc(hidden)]
/// pub fn foo() {}
2021-01-28 17:05:22 +01:00
/// ```
///
/// This function exists because it runs on `hir::Attributes` whereas the other is a
/// `clean::Attributes` method.
2021-02-23 22:13:48 +01:00
crate fn has_doc_flag(attrs: ty::Attributes<'_>, flag: Symbol) -> bool {
2021-01-28 17:05:22 +01:00
attrs.iter().any(|attr| {
attr.has_name(sym::doc)
&& attr.meta_item_list().map_or(false, |l| rustc_attr::list_contains_name(&l, flag))
2021-01-28 17:05:22 +01:00
})
}
/// A link to `doc.rust-lang.org` that includes the channel name. Use this instead of manual links
/// so that the channel is consistent.
///
/// Set by `bootstrap::Builder::doc_rust_lang_org_channel` in order to keep tests passing on beta/stable.
2021-10-01 17:12:39 +02:00
crate const DOC_RUST_LANG_ORG_CHANNEL: &str = env!("DOC_RUST_LANG_ORG_CHANNEL");
/// Render a sequence of macro arms in a format suitable for displaying to the user
/// as part of an item declaration.
pub(super) fn render_macro_arms<'a>(
tcx: TyCtxt<'_>,
matchers: impl Iterator<Item = &'a TokenTree>,
arm_delim: &str,
) -> String {
let mut out = String::new();
for matcher in matchers {
writeln!(out, " {} => {{ ... }}{}", render_macro_matcher(tcx, matcher), arm_delim)
.unwrap();
}
out
}
/// Render a macro matcher in a format suitable for displaying to the user
/// as part of an item declaration.
pub(super) fn render_macro_matcher(tcx: TyCtxt<'_>, matcher: &TokenTree) -> String {
if let Some(snippet) = snippet_equal_to_token(tcx, matcher) {
// If the original source code is known, we display the matcher exactly
// as present in the source code.
return snippet;
}
// If the matcher is macro-generated or some other reason the source code
// snippet is not available, we attempt to nicely render the token tree.
let mut printer = Printer::new();
// If the inner ibox fits on one line, we get:
//
// macro_rules! macroname {
// (the matcher) => {...};
// }
//
// If the inner ibox gets wrapped, the cbox will break and get indented:
//
// macro_rules! macroname {
// (
// the matcher ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~!
// ) => {...};
// }
printer.cbox(8);
printer.word("(");
printer.zerobreak();
printer.ibox(0);
match matcher {
TokenTree::Delimited(_span, _delim, tts) => print_tts(&mut printer, tts),
// Matcher which is not a Delimited is unexpected and should've failed
// to compile, but we render whatever it is wrapped in parens.
TokenTree::Token(_) => print_tt(&mut printer, matcher),
}
printer.end();
printer.break_offset_if_not_bol(0, -4);
printer.word(")");
printer.end();
printer.s.eof()
}
/// Find the source snippet for this token's Span, reparse it, and return the
/// snippet if the reparsed TokenTree matches the argument TokenTree.
fn snippet_equal_to_token(tcx: TyCtxt<'_>, matcher: &TokenTree) -> Option<String> {
// Find what rustc thinks is the source snippet.
// This may not actually be anything meaningful if this matcher was itself
// generated by a macro.
let source_map = tcx.sess.source_map();
let span = matcher.span();
let snippet = source_map.span_to_snippet(span).ok()?;
// Create a Parser.
let sess = ParseSess::new(FilePathMapping::empty());
let file_name = source_map.span_to_filename(span);
let mut parser =
match rustc_parse::maybe_new_parser_from_source_str(&sess, file_name, snippet.clone()) {
Ok(parser) => parser,
Err(diagnostics) => {
for mut diagnostic in diagnostics {
diagnostic.cancel();
}
return None;
}
};
// Reparse a single token tree.
let mut reparsed_trees = match parser.parse_all_token_trees() {
Ok(reparsed_trees) => reparsed_trees,
Err(mut diagnostic) => {
diagnostic.cancel();
return None;
}
};
if reparsed_trees.len() != 1 {
return None;
}
let reparsed_tree = reparsed_trees.pop().unwrap();
// Compare against the original tree.
if reparsed_tree.eq_unspanned(matcher) { Some(snippet) } else { None }
}
2021-07-05 11:00:27 +02:00
fn print_tt(printer: &mut Printer<'_>, tt: &TokenTree) {
match tt {
TokenTree::Token(token) => {
let token_str = printer.token_to_string(token);
printer.word(token_str);
if let token::DocComment(..) = token.kind {
printer.hardbreak()
}
}
TokenTree::Delimited(_span, delim, tts) => {
let open_delim = printer.token_kind_to_string(&token::OpenDelim(*delim));
printer.word(open_delim);
if !tts.is_empty() {
if *delim == DelimToken::Brace {
printer.space();
}
print_tts(printer, tts);
if *delim == DelimToken::Brace {
printer.space();
}
}
let close_delim = printer.token_kind_to_string(&token::CloseDelim(*delim));
printer.word(close_delim);
}
}
}
fn print_tts(printer: &mut Printer<'_>, tts: &TokenStream) {
#[derive(Copy, Clone, PartialEq)]
enum State {
Start,
Dollar,
DollarIdent,
DollarIdentColon,
DollarParen,
DollarParenSep,
Pound,
PoundBang,
Ident,
Other,
}
use State::*;
let mut state = Start;
for tt in tts.trees() {
let (needs_space, next_state) = match &tt {
TokenTree::Token(tt) => match (state, &tt.kind) {
(Dollar, token::Ident(..)) => (false, DollarIdent),
(DollarIdent, token::Colon) => (false, DollarIdentColon),
(DollarIdentColon, token::Ident(..)) => (false, Other),
(
DollarParen,
token::BinOp(BinOpToken::Plus | BinOpToken::Star) | token::Question,
) => (false, Other),
(DollarParen, _) => (false, DollarParenSep),
(DollarParenSep, token::BinOp(BinOpToken::Plus | BinOpToken::Star)) => {
(false, Other)
}
(Pound, token::Not) => (false, PoundBang),
(_, token::Ident(symbol, /* is_raw */ false))
if !usually_needs_space_between_keyword_and_open_delim(*symbol) =>
{
(true, Ident)
}
(_, token::Comma | token::Semi) => (false, Other),
(_, token::Dollar) => (true, Dollar),
(_, token::Pound) => (true, Pound),
(_, _) => (true, Other),
},
TokenTree::Delimited(_, delim, _) => match (state, delim) {
(Dollar, DelimToken::Paren) => (false, DollarParen),
(Pound | PoundBang, DelimToken::Bracket) => (false, Other),
(Ident, DelimToken::Paren | DelimToken::Bracket) => (false, Other),
(_, _) => (true, Other),
},
};
if state != Start && needs_space {
printer.space();
}
print_tt(printer, &tt);
state = next_state;
}
}
// This rough subset of keywords is listed here to distinguish tokens resembling
// `f(0)` (no space between ident and paren) from tokens resembling `if let (0,
// 0) = x` (space between ident and paren).
fn usually_needs_space_between_keyword_and_open_delim(symbol: Symbol) -> bool {
match symbol.as_str() {
"as" | "box" | "break" | "const" | "continue" | "crate" | "else" | "enum" | "extern"
| "for" | "if" | "impl" | "in" | "let" | "loop" | "macro" | "match" | "mod" | "move"
| "mut" | "ref" | "return" | "static" | "struct" | "trait" | "type" | "unsafe" | "use"
| "where" | "while" | "yield" => true,
_ => false,
}
}
2021-07-05 11:00:27 +02:00
pub(super) fn display_macro_source(
cx: &mut DocContext<'_>,
name: Symbol,
def: &ast::MacroDef,
def_id: DefId,
vis: Visibility,
2021-07-05 11:00:27 +02:00
) -> String {
let tts: Vec<_> = def.body.inner_tokens().into_trees().collect();
// Extract the spans of all matchers. They represent the "interface" of the macro.
let matchers = tts.chunks(4).map(|arm| &arm[0]);
if def.macro_rules {
format!("macro_rules! {} {{\n{}}}", name, render_macro_arms(cx.tcx, matchers, ";"))
2021-07-05 11:00:27 +02:00
} else {
if matchers.len() <= 1 {
format!(
"{}macro {}{} {{\n ...\n}}",
vis.to_src_with_space(cx.tcx, def_id),
name,
matchers.map(|matcher| render_macro_matcher(cx.tcx, matcher)).collect::<String>(),
2021-07-05 11:00:27 +02:00
)
} else {
format!(
"{}macro {} {{\n{}}}",
vis.to_src_with_space(cx.tcx, def_id),
name,
render_macro_arms(cx.tcx, matchers, ","),
2021-07-05 11:00:27 +02:00
)
}
}
}