未验证 提交 97f4d7bd 编写于 作者: Y Yuki Okushi 提交者: GitHub

Rollup merge of #91264 - GuillaumeGomez:macro-jump-to-def, r=jsha

Add macro support in jump to definition feature

Fixes #91174.

To do so, I check if the span comes from an expansion, and if so, I infer the original macro `DefId` or `Span` depending if it's a defined in the current crate or not.

There is one limitation due to macro expansion though:

```rust
macro_rules! yolo { () => {}}

fn foo() {
    yolo!();
}
```

In `foo`, `yolo!` won't be linked because after expansion, it is replaced by nothing (which seems logical). So I can't get an item from the `Visitor` from which I could tell if its `Span` comes from an expansion.

I added a test for this specific limitation alongside others.

Demo: https://rustdoc.crud.net/imperio/macro-jump-to-def/src/foo/check-source-code-urls-to-def-std.rs.html

As for the empty macro issue that cannot create a jump to definition, you can see it [here](https://rustdoc.crud.net/imperio/macro-jump-to-def/src/foo/check-source-code-urls-to-def-std.rs.html#35).

r? ```@jyn514```
...@@ -133,6 +133,10 @@ fn alloc_new_crate_num(&mut self) -> CrateNum { ...@@ -133,6 +133,10 @@ fn alloc_new_crate_num(&mut self) -> CrateNum {
CrateNum::new(self.metas.len() - 1) CrateNum::new(self.metas.len() - 1)
} }
pub fn has_crate_data(&self, cnum: CrateNum) -> bool {
self.metas[cnum].is_some()
}
pub(crate) fn get_crate_data(&self, cnum: CrateNum) -> CrateMetadataRef<'_> { pub(crate) fn get_crate_data(&self, cnum: CrateNum) -> CrateMetadataRef<'_> {
let cdata = self.metas[cnum] let cdata = self.metas[cnum]
.as_ref() .as_ref()
......
...@@ -8,14 +8,16 @@ ...@@ -8,14 +8,16 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::Cell; use std::cell::Cell;
use std::fmt; use std::fmt;
use std::iter; use std::iter::{self, once};
use rustc_ast as ast;
use rustc_attr::{ConstStability, StabilityLevel}; use rustc_attr::{ConstStability, StabilityLevel};
use rustc_data_structures::captures::Captures; use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_metadata::creader::{CStore, LoadedMacro};
use rustc_middle::ty; use rustc_middle::ty;
use rustc_middle::ty::DefIdTree; use rustc_middle::ty::DefIdTree;
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
...@@ -519,6 +521,7 @@ fn print<'a, 'tcx: 'a>( ...@@ -519,6 +521,7 @@ fn print<'a, 'tcx: 'a>(
} }
// Possible errors when computing href link source for a `DefId` // Possible errors when computing href link source for a `DefId`
#[derive(PartialEq, Eq)]
pub(crate) enum HrefError { pub(crate) enum HrefError {
/// This item is known to rustdoc, but from a crate that does not have documentation generated. /// This item is known to rustdoc, but from a crate that does not have documentation generated.
/// ///
...@@ -556,6 +559,79 @@ pub(crate) fn join_with_double_colon(syms: &[Symbol]) -> String { ...@@ -556,6 +559,79 @@ pub(crate) fn join_with_double_colon(syms: &[Symbol]) -> String {
s s
} }
/// This function is to get the external macro path because they are not in the cache used in
/// `href_with_root_path`.
fn generate_macro_def_id_path(
def_id: DefId,
cx: &Context<'_>,
root_path: Option<&str>,
) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
let tcx = cx.shared.tcx;
let crate_name = tcx.crate_name(def_id.krate).to_string();
let cache = cx.cache();
let fqp: Vec<Symbol> = tcx
.def_path(def_id)
.data
.into_iter()
.filter_map(|elem| {
// extern blocks (and a few others things) have an empty name.
match elem.data.get_opt_name() {
Some(s) if !s.is_empty() => Some(s),
_ => None,
}
})
.collect();
let relative = fqp.iter().map(|elem| elem.to_string());
let cstore = CStore::from_tcx(tcx);
// We need this to prevent a `panic` when this function is used from intra doc links...
if !cstore.has_crate_data(def_id.krate) {
debug!("No data for crate {}", crate_name);
return Err(HrefError::NotInExternalCache);
}
// Check to see if it is a macro 2.0 or built-in macro.
// More information in <https://rust-lang.github.io/rfcs/1584-macros.html>.
let is_macro_2 = match cstore.load_macro_untracked(def_id, tcx.sess) {
LoadedMacro::MacroDef(def, _) => {
// If `ast_def.macro_rules` is `true`, then it's not a macro 2.0.
matches!(&def.kind, ast::ItemKind::MacroDef(ast_def) if !ast_def.macro_rules)
}
_ => false,
};
let mut path = if is_macro_2 {
once(crate_name.clone()).chain(relative).collect()
} else {
vec![crate_name.clone(), relative.last().unwrap()]
};
if path.len() < 2 {
// The minimum we can have is the crate name followed by the macro name. If shorter, then
// it means that that `relative` was empty, which is an error.
debug!("macro path cannot be empty!");
return Err(HrefError::NotInExternalCache);
}
if let Some(last) = path.last_mut() {
*last = format!("macro.{}.html", last);
}
let url = match cache.extern_locations[&def_id.krate] {
ExternalLocation::Remote(ref s) => {
// `ExternalLocation::Remote` always end with a `/`.
format!("{}{}", s, path.join("/"))
}
ExternalLocation::Local => {
// `root_path` always end with a `/`.
format!("{}{}/{}", root_path.unwrap_or(""), crate_name, path.join("/"))
}
ExternalLocation::Unknown => {
debug!("crate {} not in cache when linkifying macros", crate_name);
return Err(HrefError::NotInExternalCache);
}
};
Ok((url, ItemType::Macro, fqp))
}
pub(crate) fn href_with_root_path( pub(crate) fn href_with_root_path(
did: DefId, did: DefId,
cx: &Context<'_>, cx: &Context<'_>,
...@@ -611,6 +687,8 @@ fn to_module_fqp(shortty: ItemType, fqp: &[Symbol]) -> &[Symbol] { ...@@ -611,6 +687,8 @@ fn to_module_fqp(shortty: ItemType, fqp: &[Symbol]) -> &[Symbol] {
ExternalLocation::Unknown => return Err(HrefError::DocumentationNotBuilt), ExternalLocation::Unknown => return Err(HrefError::DocumentationNotBuilt),
}, },
) )
} else if matches!(def_kind, DefKind::Macro(_)) {
return generate_macro_def_id_path(did, cx, root_path);
} else { } else {
return Err(HrefError::NotInExternalCache); return Err(HrefError::NotInExternalCache);
} }
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
use super::render::LinkFromSrc; use super::render::LinkFromSrc;
/// This type is needed in case we want to render links on items to allow to go to their definition. /// This type is needed in case we want to render links on items to allow to go to their definition.
pub(crate) struct ContextInfo<'a, 'b, 'c> { pub(crate) struct HrefContext<'a, 'b, 'c> {
pub(crate) context: &'a Context<'b>, pub(crate) context: &'a Context<'b>,
/// This span contains the current file we're going through. /// This span contains the current file we're going through.
pub(crate) file_span: Span, pub(crate) file_span: Span,
...@@ -44,7 +44,7 @@ pub(crate) fn render_with_highlighting( ...@@ -44,7 +44,7 @@ pub(crate) fn render_with_highlighting(
tooltip: Option<(Option<Edition>, &str)>, tooltip: Option<(Option<Edition>, &str)>,
edition: Edition, edition: Edition,
extra_content: Option<Buffer>, extra_content: Option<Buffer>,
context_info: Option<ContextInfo<'_, '_, '_>>, href_context: Option<HrefContext<'_, '_, '_>>,
decoration_info: Option<DecorationInfo>, decoration_info: Option<DecorationInfo>,
) { ) {
debug!("highlighting: ================\n{}\n==============", src); debug!("highlighting: ================\n{}\n==============", src);
...@@ -62,7 +62,7 @@ pub(crate) fn render_with_highlighting( ...@@ -62,7 +62,7 @@ pub(crate) fn render_with_highlighting(
} }
write_header(out, class, extra_content); write_header(out, class, extra_content);
write_code(out, src, edition, context_info, decoration_info); write_code(out, src, edition, href_context, decoration_info);
write_footer(out, playground_button); write_footer(out, playground_button);
} }
...@@ -85,8 +85,8 @@ fn write_header(out: &mut Buffer, class: Option<&str>, extra_content: Option<Buf ...@@ -85,8 +85,8 @@ fn write_header(out: &mut Buffer, class: Option<&str>, extra_content: Option<Buf
/// ///
/// Some explanations on the last arguments: /// Some explanations on the last arguments:
/// ///
/// In case we are rendering a code block and not a source code file, `context_info` will be `None`. /// In case we are rendering a code block and not a source code file, `href_context` will be `None`.
/// To put it more simply: if `context_info` is `None`, the code won't try to generate links to an /// To put it more simply: if `href_context` is `None`, the code won't try to generate links to an
/// item definition. /// item definition.
/// ///
/// More explanations about spans and how we use them here are provided in the /// More explanations about spans and how we use them here are provided in the
...@@ -94,22 +94,27 @@ fn write_code( ...@@ -94,22 +94,27 @@ fn write_code(
out: &mut Buffer, out: &mut Buffer,
src: &str, src: &str,
edition: Edition, edition: Edition,
context_info: Option<ContextInfo<'_, '_, '_>>, href_context: Option<HrefContext<'_, '_, '_>>,
decoration_info: Option<DecorationInfo>, decoration_info: Option<DecorationInfo>,
) { ) {
// This replace allows to fix how the code source with DOS backline characters is displayed. // This replace allows to fix how the code source with DOS backline characters is displayed.
let src = src.replace("\r\n", "\n"); let src = src.replace("\r\n", "\n");
let mut closing_tags: Vec<&'static str> = Vec::new();
Classifier::new( Classifier::new(
&src, &src,
edition, edition,
context_info.as_ref().map(|c| c.file_span).unwrap_or(DUMMY_SP), href_context.as_ref().map(|c| c.file_span).unwrap_or(DUMMY_SP),
decoration_info, decoration_info,
) )
.highlight(&mut |highlight| { .highlight(&mut |highlight| {
match highlight { match highlight {
Highlight::Token { text, class } => string(out, Escape(text), class, &context_info), Highlight::Token { text, class } => string(out, Escape(text), class, &href_context),
Highlight::EnterSpan { class } => enter_span(out, class), Highlight::EnterSpan { class } => {
Highlight::ExitSpan => exit_span(out), closing_tags.push(enter_span(out, class, &href_context))
}
Highlight::ExitSpan => {
exit_span(out, closing_tags.pop().expect("ExitSpan without EnterSpan"))
}
}; };
}); });
} }
...@@ -129,7 +134,7 @@ enum Class { ...@@ -129,7 +134,7 @@ enum Class {
RefKeyWord, RefKeyWord,
Self_(Span), Self_(Span),
Op, Op,
Macro, Macro(Span),
MacroNonTerminal, MacroNonTerminal,
String, String,
Number, Number,
...@@ -153,7 +158,7 @@ fn as_html(self) -> &'static str { ...@@ -153,7 +158,7 @@ fn as_html(self) -> &'static str {
Class::RefKeyWord => "kw-2", Class::RefKeyWord => "kw-2",
Class::Self_(_) => "self", Class::Self_(_) => "self",
Class::Op => "op", Class::Op => "op",
Class::Macro => "macro", Class::Macro(_) => "macro",
Class::MacroNonTerminal => "macro-nonterminal", Class::MacroNonTerminal => "macro-nonterminal",
Class::String => "string", Class::String => "string",
Class::Number => "number", Class::Number => "number",
...@@ -171,8 +176,22 @@ fn as_html(self) -> &'static str { ...@@ -171,8 +176,22 @@ fn as_html(self) -> &'static str {
/// a "span" (a tuple representing `(lo, hi)` equivalent of `Span`). /// a "span" (a tuple representing `(lo, hi)` equivalent of `Span`).
fn get_span(self) -> Option<Span> { fn get_span(self) -> Option<Span> {
match self { match self {
Self::Ident(sp) | Self::Self_(sp) => Some(sp), Self::Ident(sp) | Self::Self_(sp) | Self::Macro(sp) => Some(sp),
_ => None, Self::Comment
| Self::DocComment
| Self::Attribute
| Self::KeyWord
| Self::RefKeyWord
| Self::Op
| Self::MacroNonTerminal
| Self::String
| Self::Number
| Self::Bool
| Self::Lifetime
| Self::PreludeTy
| Self::PreludeVal
| Self::QuestionMark
| Self::Decoration(_) => None,
} }
} }
} }
...@@ -611,7 +630,7 @@ fn advance( ...@@ -611,7 +630,7 @@ fn advance(
}, },
TokenKind::Ident | TokenKind::RawIdent if lookahead == Some(TokenKind::Bang) => { TokenKind::Ident | TokenKind::RawIdent if lookahead == Some(TokenKind::Bang) => {
self.in_macro = true; self.in_macro = true;
sink(Highlight::EnterSpan { class: Class::Macro }); sink(Highlight::EnterSpan { class: Class::Macro(self.new_span(before, text)) });
sink(Highlight::Token { text, class: None }); sink(Highlight::Token { text, class: None });
return; return;
} }
...@@ -658,13 +677,20 @@ fn check_if_is_union_keyword(&mut self) -> bool { ...@@ -658,13 +677,20 @@ fn check_if_is_union_keyword(&mut self) -> bool {
/// Called when we start processing a span of text that should be highlighted. /// Called when we start processing a span of text that should be highlighted.
/// The `Class` argument specifies how it should be highlighted. /// The `Class` argument specifies how it should be highlighted.
fn enter_span(out: &mut Buffer, klass: Class) { fn enter_span(
write!(out, "<span class=\"{}\">", klass.as_html()); out: &mut Buffer,
klass: Class,
href_context: &Option<HrefContext<'_, '_, '_>>,
) -> &'static str {
string_without_closing_tag(out, "", Some(klass), href_context).expect(
"internal error: enter_span was called with Some(klass) but did not return a \
closing HTML tag",
)
} }
/// Called at the end of a span of highlighted text. /// Called at the end of a span of highlighted text.
fn exit_span(out: &mut Buffer) { fn exit_span(out: &mut Buffer, closing_tag: &str) {
out.write_str("</span>"); out.write_str(closing_tag);
} }
/// Called for a span of text. If the text should be highlighted differently /// Called for a span of text. If the text should be highlighted differently
...@@ -687,15 +713,39 @@ fn string<T: Display>( ...@@ -687,15 +713,39 @@ fn string<T: Display>(
out: &mut Buffer, out: &mut Buffer,
text: T, text: T,
klass: Option<Class>, klass: Option<Class>,
context_info: &Option<ContextInfo<'_, '_, '_>>, href_context: &Option<HrefContext<'_, '_, '_>>,
) { ) {
if let Some(closing_tag) = string_without_closing_tag(out, text, klass, href_context) {
out.write_str(closing_tag);
}
}
/// This function writes `text` into `out` with some modifications depending on `klass`:
///
/// * If `klass` is `None`, `text` is written into `out` with no modification.
/// * If `klass` is `Some` but `klass.get_span()` is `None`, it writes the text wrapped in a
/// `<span>` with the provided `klass`.
/// * If `klass` is `Some` and has a [`rustc_span::Span`], it then tries to generate a link (`<a>`
/// element) by retrieving the link information from the `span_correspondance_map` that was filled
/// in `span_map.rs::collect_spans_and_sources`. If it cannot retrieve the information, then it's
/// the same as the second point (`klass` is `Some` but doesn't have a [`rustc_span::Span`]).
fn string_without_closing_tag<T: Display>(
out: &mut Buffer,
text: T,
klass: Option<Class>,
href_context: &Option<HrefContext<'_, '_, '_>>,
) -> Option<&'static str> {
let Some(klass) = klass let Some(klass) = klass
else { return write!(out, "{}", text) }; else {
write!(out, "{}", text);
return None;
};
let Some(def_span) = klass.get_span() let Some(def_span) = klass.get_span()
else { else {
write!(out, "<span class=\"{}\">{}</span>", klass.as_html(), text); write!(out, "<span class=\"{}\">{}", klass.as_html(), text);
return; return Some("</span>");
}; };
let mut text_s = text.to_string(); let mut text_s = text.to_string();
if text_s.contains("::") { if text_s.contains("::") {
text_s = text_s.split("::").intersperse("::").fold(String::new(), |mut path, t| { text_s = text_s.split("::").intersperse("::").fold(String::new(), |mut path, t| {
...@@ -715,10 +765,10 @@ fn string<T: Display>( ...@@ -715,10 +765,10 @@ fn string<T: Display>(
path path
}); });
} }
if let Some(context_info) = context_info { if let Some(href_context) = href_context {
if let Some(href) = if let Some(href) =
context_info.context.shared.span_correspondance_map.get(&def_span).and_then(|href| { href_context.context.shared.span_correspondance_map.get(&def_span).and_then(|href| {
let context = context_info.context; let context = href_context.context;
// FIXME: later on, it'd be nice to provide two links (if possible) for all items: // FIXME: later on, it'd be nice to provide two links (if possible) for all items:
// one to the documentation page and one to the source definition. // one to the documentation page and one to the source definition.
// FIXME: currently, external items only generate a link to their documentation, // FIXME: currently, external items only generate a link to their documentation,
...@@ -727,27 +777,28 @@ fn string<T: Display>( ...@@ -727,27 +777,28 @@ fn string<T: Display>(
match href { match href {
LinkFromSrc::Local(span) => context LinkFromSrc::Local(span) => context
.href_from_span(*span, true) .href_from_span(*span, true)
.map(|s| format!("{}{}", context_info.root_path, s)), .map(|s| format!("{}{}", href_context.root_path, s)),
LinkFromSrc::External(def_id) => { LinkFromSrc::External(def_id) => {
format::href_with_root_path(*def_id, context, Some(context_info.root_path)) format::href_with_root_path(*def_id, context, Some(href_context.root_path))
.ok() .ok()
.map(|(url, _, _)| url) .map(|(url, _, _)| url)
} }
LinkFromSrc::Primitive(prim) => format::href_with_root_path( LinkFromSrc::Primitive(prim) => format::href_with_root_path(
PrimitiveType::primitive_locations(context.tcx())[prim], PrimitiveType::primitive_locations(context.tcx())[prim],
context, context,
Some(context_info.root_path), Some(href_context.root_path),
) )
.ok() .ok()
.map(|(url, _, _)| url), .map(|(url, _, _)| url),
} }
}) })
{ {
write!(out, "<a class=\"{}\" href=\"{}\">{}</a>", klass.as_html(), href, text_s); write!(out, "<a class=\"{}\" href=\"{}\">{}", klass.as_html(), href, text_s);
return; return Some("</a>");
} }
} }
write!(out, "<span class=\"{}\">{}</span>", klass.as_html(), text_s); write!(out, "<span class=\"{}\">{}", klass.as_html(), text_s);
Some("</span>")
} }
#[cfg(test)] #[cfg(test)]
......
...@@ -8,7 +8,8 @@ ...@@ -8,7 +8,8 @@
use rustc_hir::{ExprKind, HirId, Mod, Node}; use rustc_hir::{ExprKind, HirId, Mod, Node};
use rustc_middle::hir::nested_filter; use rustc_middle::hir::nested_filter;
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_span::Span; use rustc_span::hygiene::MacroKind;
use rustc_span::{BytePos, ExpnKind, Span};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
...@@ -63,33 +64,72 @@ struct SpanMapVisitor<'tcx> { ...@@ -63,33 +64,72 @@ struct SpanMapVisitor<'tcx> {
impl<'tcx> SpanMapVisitor<'tcx> { impl<'tcx> SpanMapVisitor<'tcx> {
/// This function is where we handle `hir::Path` elements and add them into the "span map". /// This function is where we handle `hir::Path` elements and add them into the "span map".
fn handle_path(&mut self, path: &rustc_hir::Path<'_>, path_span: Option<Span>) { fn handle_path(&mut self, path: &rustc_hir::Path<'_>) {
let info = match path.res { let info = match path.res {
// FIXME: For now, we only handle `DefKind` if it's not `DefKind::TyParam` or // FIXME: For now, we handle `DefKind` if it's not a `DefKind::TyParam`.
// `DefKind::Macro`. Would be nice to support them too alongside the other `DefKind` // Would be nice to support them too alongside the other `DefKind`
// (such as primitive types!). // (such as primitive types!).
Res::Def(kind, def_id) if kind != DefKind::TyParam => { Res::Def(kind, def_id) if kind != DefKind::TyParam => Some(def_id),
if matches!(kind, DefKind::Macro(_)) {
return;
}
Some(def_id)
}
Res::Local(_) => None, Res::Local(_) => None,
Res::PrimTy(p) => { Res::PrimTy(p) => {
// FIXME: Doesn't handle "path-like" primitives like arrays or tuples. // FIXME: Doesn't handle "path-like" primitives like arrays or tuples.
let span = path_span.unwrap_or(path.span); self.matches.insert(path.span, LinkFromSrc::Primitive(PrimitiveType::from(p)));
self.matches.insert(span, LinkFromSrc::Primitive(PrimitiveType::from(p)));
return; return;
} }
Res::Err => return, Res::Err => return,
_ => return, _ => return,
}; };
if let Some(span) = self.tcx.hir().res_span(path.res) { if let Some(span) = self.tcx.hir().res_span(path.res) {
self.matches self.matches.insert(path.span, LinkFromSrc::Local(clean::Span::new(span)));
.insert(path_span.unwrap_or(path.span), LinkFromSrc::Local(clean::Span::new(span)));
} else if let Some(def_id) = info { } else if let Some(def_id) = info {
self.matches.insert(path_span.unwrap_or(path.span), LinkFromSrc::External(def_id)); self.matches.insert(path.span, LinkFromSrc::External(def_id));
}
}
/// Adds the macro call into the span map. Returns `true` if the `span` was inside a macro
/// expansion, whether or not it was added to the span map.
///
/// The idea for the macro support is to check if the current `Span` comes from expansion. If
/// so, we loop until we find the macro definition by using `outer_expn_data` in a loop.
/// Finally, we get the information about the macro itself (`span` if "local", `DefId`
/// otherwise) and store it inside the span map.
fn handle_macro(&mut self, span: Span) -> bool {
if !span.from_expansion() {
return false;
}
// So if the `span` comes from a macro expansion, we need to get the original
// macro's `DefId`.
let mut data = span.ctxt().outer_expn_data();
let mut call_site = data.call_site;
// Macros can expand to code containing macros, which will in turn be expanded, etc.
// So the idea here is to "go up" until we're back to code that was generated from
// macro expansion so that we can get the `DefId` of the original macro that was at the
// origin of this expansion.
while call_site.from_expansion() {
data = call_site.ctxt().outer_expn_data();
call_site = data.call_site;
} }
let macro_name = match data.kind {
ExpnKind::Macro(MacroKind::Bang, macro_name) => macro_name,
// Even though we don't handle this kind of macro, this `data` still comes from
// expansion so we return `true` so we don't go any deeper in this code.
_ => return true,
};
let link_from_src = match data.macro_def_id {
Some(macro_def_id) if macro_def_id.is_local() => {
LinkFromSrc::Local(clean::Span::new(data.def_site))
}
Some(macro_def_id) => LinkFromSrc::External(macro_def_id),
None => return true,
};
let new_span = data.call_site;
let macro_name = macro_name.as_str();
// The "call_site" includes the whole macro with its "arguments". We only want
// the macro name.
let new_span = new_span.with_hi(new_span.lo() + BytePos(macro_name.len() as u32));
self.matches.insert(new_span, link_from_src);
true
} }
} }
...@@ -101,7 +141,10 @@ fn nested_visit_map(&mut self) -> Self::Map { ...@@ -101,7 +141,10 @@ fn nested_visit_map(&mut self) -> Self::Map {
} }
fn visit_path(&mut self, path: &'tcx rustc_hir::Path<'tcx>, _id: HirId) { fn visit_path(&mut self, path: &'tcx rustc_hir::Path<'tcx>, _id: HirId) {
self.handle_path(path, None); if self.handle_macro(path.span) {
return;
}
self.handle_path(path);
intravisit::walk_path(self, path); intravisit::walk_path(self, path);
} }
...@@ -143,12 +186,18 @@ fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) { ...@@ -143,12 +186,18 @@ fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) {
); );
} }
} }
} else if self.handle_macro(expr.span) {
// We don't want to go deeper into the macro.
return;
} }
intravisit::walk_expr(self, expr); intravisit::walk_expr(self, expr);
} }
fn visit_use(&mut self, path: &'tcx rustc_hir::Path<'tcx>, id: HirId) { fn visit_use(&mut self, path: &'tcx rustc_hir::Path<'tcx>, id: HirId) {
self.handle_path(path, None); if self.handle_macro(path.span) {
return;
}
self.handle_path(path);
intravisit::walk_use(self, path, id); intravisit::walk_use(self, path, id);
} }
} }
...@@ -297,7 +297,7 @@ pub(crate) fn print_src( ...@@ -297,7 +297,7 @@ pub(crate) fn print_src(
None, None,
edition, edition,
Some(line_numbers), Some(line_numbers),
Some(highlight::ContextInfo { context, file_span, root_path }), Some(highlight::HrefContext { context, file_span, root_path }),
decoration_info, decoration_info,
); );
} }
...@@ -15,3 +15,28 @@ pub fn foo(a: u32, b: &str, c: String) { ...@@ -15,3 +15,28 @@ pub fn foo(a: u32, b: &str, c: String) {
let y: bool = true; let y: bool = true;
babar(); babar();
} }
macro_rules! yolo { () => {}}
fn bar(a: i32) {}
macro_rules! bar {
($a:ident) => { bar($a) }
}
macro_rules! data {
($x:expr) => { $x * 2 }
}
pub fn another_foo() {
// This is known limitation: if the macro doesn't generate anything, the visitor
// can't find any item or anything that could tell us that it comes from expansion.
// @!has - '//a[@href="../../src/foo/check-source-code-urls-to-def-std.rs.html#19"]' 'yolo!'
yolo!();
// @has - '//a[@href="{{channel}}/std/macro.eprintln.html"]' 'eprintln!'
eprintln!();
// @has - '//a[@href="../../src/foo/check-source-code-urls-to-def-std.rs.html#27-29"]' 'data!'
let x = data!(4);
// @has - '//a[@href="../../src/foo/check-source-code-urls-to-def-std.rs.html#23-25"]' 'bar!'
bar!(x);
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册