Skip to content

Commit

Permalink
Handle imports & re-exports of all kinds of macro items. (#666) (#667)
Browse files Browse the repository at this point in the history
  • Loading branch information
obi1kenobi authored Dec 14, 2024
1 parent 3038ad7 commit 182d53f
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 11 deletions.
18 changes: 16 additions & 2 deletions src/adapter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,22 @@ impl<'a> Adapter<'a> for &'a RustdocAdapter<'a> {
match type_name.as_ref() {
"CrateDiff" => edges::resolve_crate_diff_edge(contexts, edge_name),
"Crate" => edges::resolve_crate_edge(self, contexts, edge_name, resolve_info),
"Importable" | "ImplOwner" | "Struct" | "Enum" | "Union" | "Trait" | "Function"
| "GlobalValue" | "Constant" | "Static" | "Module"
"Importable"
| "ImplOwner"
| "Struct"
| "Enum"
| "Union"
| "Trait"
| "Function"
| "GlobalValue"
| "Constant"
| "Static"
| "Module"
| "Macro"
| "ProcMacro"
| "FunctionLikeProcMacro"
| "AttributeProcMacro"
| "DeriveProcMacro"
if matches!(edge_name.as_ref(), "importable_path" | "canonical_path") =>
{
edges::resolve_importable_edge(
Expand Down
17 changes: 17 additions & 0 deletions src/adapter/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2337,6 +2337,11 @@ fn proc_macros() {
name @output
public_api_eligible @output
visibility_limit @output
importable_path {
path @output
public_api @output
}
}
}
}
Expand All @@ -2354,6 +2359,8 @@ fn proc_macros() {
name: String,
public_api_eligible: bool,
visibility_limit: String,
path: Vec<String>,
public_api: bool,
}

let mut results: Vec<_> =
Expand All @@ -2373,30 +2380,40 @@ fn proc_macros() {
name: "make_answer".into(),
public_api_eligible: true,
visibility_limit: "public".into(),
path: vec!["proc_macros".into(), "make_answer".into()],
public_api: true,
},
Output {
kind: "AttributeProcMacro".into(),
name: "return_as_is".into(),
public_api_eligible: true,
visibility_limit: "public".into(),
path: vec!["proc_macros".into(), "return_as_is".into()],
public_api: true,
},
Output {
kind: "DeriveProcMacro".into(),
name: "AnswerFn".into(),
public_api_eligible: true,
visibility_limit: "public".into(),
path: vec!["proc_macros".into(), "AnswerFn".into()],
public_api: true,
},
Output {
kind: "DeriveProcMacro".into(),
name: "HelperAttr".into(),
public_api_eligible: true,
visibility_limit: "public".into(),
path: vec!["proc_macros".into(), "HelperAttr".into()],
public_api: true,
},
Output {
kind: "FunctionLikeProcMacro".into(),
name: "hidden".into(),
public_api_eligible: false,
visibility_limit: "public".into(),
path: vec!["proc_macros".into(), "hidden".into()],
public_api: false,
},
];
expected_results.sort_unstable();
Expand Down
29 changes: 29 additions & 0 deletions src/indexed_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2373,5 +2373,34 @@ expected exactly one importable path for `Foo` items in this crate but got: {act
}
}
}

#[test]
fn reexport_declarative_macro() {
let test_crate = "reexport_declarative_macro";
let expected_items = btreemap! {
"top_level_exported" => btreeset![
"reexport_declarative_macro::top_level_exported",
],
"private_mod_exported" => btreeset![
"reexport_declarative_macro::private_mod_exported",
],
"top_level_reexported" => btreeset![
"reexport_declarative_macro::top_level_reexported",
"reexport_declarative_macro::macros::top_level_reexported",
"reexport_declarative_macro::reexports::top_level_reexported",
"reexport_declarative_macro::glob_reexports::top_level_reexported",
],
"private_mod_reexported" => btreeset![
"reexport_declarative_macro::private_mod_reexported",
"reexport_declarative_macro::macros::private_mod_reexported",
"reexport_declarative_macro::reexports::private_mod_reexported",
"reexport_declarative_macro::glob_reexports::private_mod_reexported",
],
"top_level_not_exported" => btreeset![],
"private_mod_not_exported" => btreeset![],
};

assert_exported_items_match(test_crate, &expected_items);
}
}
}
38 changes: 29 additions & 9 deletions src/rustdoc_schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -1983,15 +1983,15 @@ type AssociatedConstant implements Item {
"""
A `macro_rules` declarative macro.
When such macros are exported by a crate, they are always exported at the top level of the crate,
When declarative macros are exported by a crate, they are always exported at the top level of the crate,
even if the macro's definition is located inside a `mod`. In essence, the `#[macro_export]`
attribute behaves as a `pub use` of the macro into the crate's root module.
This is why we don't model macro items as `Importable`:
- They always have only one importable path: `the_crate::macro_name`.
- Their names are in their own namespace, and do not mix with the types or values namespaces.
However, declarative macros *can* be re-exported by other modules as well.
Those other modules just have to re-export the `#[macro_export]`-ed item from the crate root,
instead of the item at its original declaration location.
"""
type Macro implements Item {
type Macro implements Item & Importable {
# properties from Item
id: String!
crate_id: Int!
Expand Down Expand Up @@ -2035,6 +2035,10 @@ type Macro implements Item {
# edges from Item
attribute: [Attribute!]
span: Span

# edges from Importable
importable_path: [ImportablePath!]
canonical_path: Path
}

"""
Expand All @@ -2049,7 +2053,7 @@ This is why we don't model macro items as `Importable`:
- They always have only one importable path: `the_crate::macro_name`.
- Their names are in their own namespace, and do not mix with the types or values namespaces.
"""
interface ProcMacro implements Item {
interface ProcMacro implements Item & Importable {
# properties from Item
id: String!
crate_id: Int!
Expand Down Expand Up @@ -2093,6 +2097,10 @@ interface ProcMacro implements Item {
# edges from Item
attribute: [Attribute!]
span: Span

# edges from Importable
importable_path: [ImportablePath!]
canonical_path: Path
}

"""
Expand All @@ -2107,7 +2115,7 @@ This is why we don't model macro items as `Importable`:
- They always have only one importable path: `the_crate::macro_name`.
- Their names are in their own namespace, and do not mix with the types or values namespaces.
"""
type FunctionLikeProcMacro implements Item & ProcMacro {
type FunctionLikeProcMacro implements Item & Importable & ProcMacro {
# properties from Item
id: String!
crate_id: Int!
Expand Down Expand Up @@ -2151,6 +2159,10 @@ type FunctionLikeProcMacro implements Item & ProcMacro {
# edges from Item
attribute: [Attribute!]
span: Span

# edges from Importable
importable_path: [ImportablePath!]
canonical_path: Path
}

"""
Expand All @@ -2164,7 +2176,7 @@ This is why we don't model macro items as `Importable`:
- They always have only one importable path: `the_crate::macro_name`.
- Their names are in their own namespace, and do not mix with the types or values namespaces.
"""
type AttributeProcMacro implements Item & ProcMacro {
type AttributeProcMacro implements Item & Importable & ProcMacro {
# properties from Item
id: String!
crate_id: Int!
Expand Down Expand Up @@ -2208,6 +2220,10 @@ type AttributeProcMacro implements Item & ProcMacro {
# edges from Item
attribute: [Attribute!]
span: Span

# edges from Importable
importable_path: [ImportablePath!]
canonical_path: Path
}

"""
Expand All @@ -2221,7 +2237,7 @@ This is why we don't model macro items as `Importable`:
- They always have only one importable path: `the_crate::MacroName`.
- Their names are in their own namespace, and do not mix with the types or values namespaces.
"""
type DeriveProcMacro implements Item & ProcMacro {
type DeriveProcMacro implements Item & Importable & ProcMacro {
# properties from Item
id: String!
crate_id: Int!
Expand Down Expand Up @@ -2266,6 +2282,10 @@ type DeriveProcMacro implements Item & ProcMacro {
attribute: [Attribute!]
span: Span

# edges from Importable
importable_path: [ImportablePath!]
canonical_path: Path

# own edges
"""
Any additional helper attributes defined by this macro.
Expand Down
38 changes: 38 additions & 0 deletions src/visibility_tracker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,18 @@ impl<'a> VisibilityTracker<'a> {
enum NamespacedName<'a> {
Values(&'a str),
Types(&'a str),
// https://doc.rust-lang.org/reference/names/namespaces.html#sub-namespaces
BangMacros(&'a str),
AttrOrDeriveMacros(&'a str),
}

impl<'a> NamespacedName<'a> {
fn rename(&self, new_name: &'a str) -> Self {
match self {
NamespacedName::Values(_) => NamespacedName::Values(new_name),
NamespacedName::Types(_) => NamespacedName::Types(new_name),
NamespacedName::BangMacros(_) => NamespacedName::BangMacros(new_name),
NamespacedName::AttrOrDeriveMacros(_) => NamespacedName::AttrOrDeriveMacros(new_name),
}
}
}
Expand Down Expand Up @@ -360,6 +365,21 @@ fn get_names_for_item<'a>(
.into_iter()
.flatten()
}
ItemEnum::Macro(..) => {
let item_name = item.name.as_deref().expect("item did not have a name");
[Some(NamespacedName::BangMacros(item_name)), None]
.into_iter()
.flatten()
}
ItemEnum::ProcMacro(m) => {
let item_name = item.name.as_deref().expect("item did not have a name");
let namespaced_name = if m.kind == rustdoc_types::MacroKind::Bang {
NamespacedName::BangMacros(item_name)
} else {
NamespacedName::AttrOrDeriveMacros(item_name)
};
[Some(namespaced_name), None].into_iter().flatten()
}
_ => [None, None].into_iter().flatten(),
}
}
Expand Down Expand Up @@ -437,6 +457,24 @@ fn resolve_crate_names(crate_: &Crate) -> NameResolution<'_> {
);
}
}
} else if let ItemEnum::Macro(..) = &inner_item.inner {
// `#[macro_export]` moves declarative macros to the crate root,
// regardless of what module they were declared in. Such macros are always public.
// Without `#[macro_export]`, declarative macros have no path-based scope:
// https://doc.rust-lang.org/reference/macros-by-example.html#path-based-scope
if inner_item
.attrs
.iter()
.any(|attr| attr.as_str() == "#[macro_export]")
{
for name in get_names_for_item(crate_, inner_item) {
result
.names_defined_in_module
.entry(crate_.root.0)
.or_default()
.insert(name, (Definition::new_direct(inner_item.id.0), true));
}
}
} else {
for name in get_names_for_item(crate_, inner_item) {
result
Expand Down
6 changes: 6 additions & 0 deletions test_crates/reexport_declarative_macro/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "reexport_declarative_macro"
version = "0.1.0"
edition = "2021"

[dependencies]
53 changes: 53 additions & 0 deletions test_crates/reexport_declarative_macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//! This crate exports the following macros:
//! - `top_level_exported`, at top level only
//! - `private_mod_exported`, at top level only
//! - `top_level_reexported`, at top level, and inside
//! the `macros`, `reexports`, and `glob_reexports` modules
//! - `private_mod_reexported`, at top level, and inside
//! the `macros`, `reexports`, and `glob_reexports` modules
//!
//! The `top_level_not_exported` and `private_mod_not_exported` macros are not exported.
#![allow(unused_macros)]

macro_rules! top_level_not_exported {
() => {}
}

#[macro_export]
macro_rules! top_level_exported {
() => {}
}

#[macro_export]
macro_rules! top_level_reexported {
() => {}
}

mod private {
#[macro_export]
macro_rules! private_mod_exported {
() => {}
}

macro_rules! private_mod_not_exported {
() => {}
}

#[macro_export]
macro_rules! private_mod_reexported {
() => {}
}
}

pub mod macros {
pub use crate::private_mod_reexported;
pub use crate::top_level_reexported;
}

pub mod glob_reexports {
pub use crate::macros::*;
}

pub mod reexports {
pub use crate::macros::{private_mod_reexported, top_level_reexported};
}

0 comments on commit 182d53f

Please sign in to comment.