diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8935c5f57b63..c532f4e5a190 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -34,7 +34,6 @@ jobs: - 'editors/code/**' proc-macro-srv: - if: github.repository == 'rust-lang/rust-analyzer' name: proc-macro-srv runs-on: ubuntu-latest env: diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs index 433199746bfa..18f232fc1801 100644 --- a/crates/hir-expand/src/attrs.rs +++ b/crates/hir-expand/src/attrs.rs @@ -51,15 +51,18 @@ impl RawAttrs { ) -> Self { let entries: Vec<_> = collect_attrs(owner) .filter_map(|(id, attr)| match attr { - Either::Left(attr) => { - attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id)) - } + Either::Left(attr) => Attr::from_src(db, attr, span_map, id), Either::Right(comment) => comment.doc_comment().map(|doc| { let span = span_map.span_for_range(comment.syntax().text_range()); let (text, kind) = desugar_doc_comment_text(doc, DocCommentDesugarMode::ProcMacro); Attr { id, + place: if comment.is_inner() { + AttrPlacement::Inner + } else { + AttrPlacement::Outer + }, input: Some(Box::new(AttrInput::Literal(tt::Literal { symbol: text, span, @@ -147,8 +150,9 @@ impl RawAttrs { None => return smallvec![attr.clone()], }; let index = attr.id; + let place = attr.place; let attrs = parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map( - |(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)), + |(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx), place), ); let cfg = TopSubtree::from_token_trees(subtree.top_subtree().delimiter, cfg); @@ -211,6 +215,15 @@ pub struct Attr { pub path: Interned, pub input: Option>, pub ctxt: SyntaxContext, + pub place: AttrPlacement, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum AttrPlacement { + /// `#![attr]` + Inner, + /// `#[attr]` + Outer, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -233,10 +246,16 @@ impl fmt::Display for AttrInput { impl Attr { fn from_src( db: &dyn ExpandDatabase, - ast: ast::Meta, + ast_attr: ast::Attr, span_map: SpanMapRef<'_>, id: AttrId, ) -> Option { + let ast = ast_attr.meta()?; + let place = if ast_attr.excl_token().is_some() { + AttrPlacement::Inner + } else { + AttrPlacement::Outer + }; let path = ast.path()?; let range = path.syntax().text_range(); let path = Interned::new(ModPath::from_src(db, path, &mut |range| { @@ -257,13 +276,14 @@ impl Attr { } else { None }; - Some(Attr { id, path, input, ctxt: span.ctx }) + Some(Attr { id, path, input, ctxt: span.ctx, place }) } fn from_tt( db: &dyn ExpandDatabase, mut tt: tt::TokenTreesView<'_>, id: AttrId, + place: AttrPlacement, ) -> Option { if matches!(tt.flat_tokens(), [tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym, .. })), ..] @@ -314,7 +334,7 @@ impl Attr { } _ => None, }; - Some(Attr { id, path, input, ctxt }) + Some(Attr { id, path, input, ctxt, place }) } pub fn path(&self) -> &ModPath { diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index e71b51bfa432..de052a716b99 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs @@ -10,7 +10,7 @@ use hir_def::{ per_ns::Namespace, resolver::{HasResolver, Resolver, TypeNs}, }; -use hir_expand::{mod_path::PathKind, name::Name}; +use hir_expand::{attrs::AttrPlacement, mod_path::PathKind, name::Name}; use hir_ty::{db::HirDatabase, method_resolution}; use crate::{ @@ -102,11 +102,16 @@ impl HasAttrs for crate::Crate { /// Resolves the item `link` points to in the scope of `def`. pub fn resolve_doc_path_on( db: &dyn HirDatabase, - def: impl HasAttrs, + def: impl HasAttrs + Copy, link: &str, ns: Option, ) -> Option { - resolve_doc_path_on_(db, link, def.attr_id(), ns) + let is_outer = def + .attrs(db) + .by_key(&intern::sym::doc) + .attrs() + .any(|attr| attr.place == AttrPlacement::Outer); + resolve_doc_path_on_(db, link, def.attr_id(), ns, is_outer) } fn resolve_doc_path_on_( @@ -114,9 +119,20 @@ fn resolve_doc_path_on_( link: &str, attr_id: AttrDefId, ns: Option, + is_outer: bool, ) -> Option { let resolver = match attr_id { - AttrDefId::ModuleId(it) => it.resolver(db.upcast()), + AttrDefId::ModuleId(it) => { + if is_outer { + if let Some(parent) = Module::from(it).parent(db) { + parent.id.resolver(db.upcast()) + } else { + it.resolver(db.upcast()) + } + } else { + it.resolver(db.upcast()) + } + } AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()), AttrDefId::AdtId(it) => it.resolver(db.upcast()), AttrDefId::FunctionId(it) => it.resolver(db.upcast()), diff --git a/crates/ide/src/doc_links/tests.rs b/crates/ide/src/doc_links/tests.rs index 91785be8d8ba..6d56b98e57c8 100644 --- a/crates/ide/src/doc_links/tests.rs +++ b/crates/ide/src/doc_links/tests.rs @@ -575,6 +575,40 @@ struct S$0(i32); ); } +#[test] +fn doc_links_module() { + check_doc_links( + r#" +/// [`M`] +/// [`M::f`] +mod M$0 { + //^ M + #![doc = "inner_item[`M::S`]"] + + pub fn f() {} + //^ M::f + pub struct S; + //^ M::S +} +"#, + ); + + check_doc_links( + r#" +mod M$0 { + //^ super::M + //! [`super::M`] + //! [`super::M::f`] + //! [`super::M::S`] + pub fn f() {} + //^ super::M::f + pub struct S; + //^ super::M::S +} +"#, + ); +} + #[test] fn rewrite_html_root_url() { check_rewrite( @@ -690,6 +724,29 @@ fn rewrite_intra_doc_link_with_anchor() { ); } +#[test] +fn rewrite_module() { + check_rewrite( + r#" +//- /main.rs crate:foo +/// [Foo] +pub mod $0Foo{ +}; +"#, + expect![[r#"[Foo](https://docs.rs/foo/*/foo/Foo/index.html)"#]], + ); + + check_rewrite( + r#" +//- /main.rs crate:foo +pub mod $0Foo{ + //! [super::Foo] +}; +"#, + expect![[r#"[super::Foo](https://docs.rs/foo/*/foo/Foo/index.html)"#]], + ); +} + #[test] fn rewrite_intra_doc_link_to_associated_item() { check_rewrite( diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html index 9996a871580f..75d96b422c10 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html @@ -40,9 +40,9 @@ .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
//! [Struct]
+
//! [foo::Struct]
 //! This is an intra doc injection test for modules
-//! [Struct]
+//! [foo::Struct]
 //! This is an intra doc injection test for modules
 
 pub struct Struct;
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 59f2b90333d3..493878bf0f0b 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -1072,9 +1072,9 @@ fn test_mod_hl_injection() {
     check_highlighting(
         r##"
 //- /foo.rs
-//! [Struct]
+//! [foo::Struct]
 //! This is an intra doc injection test for modules
-//! [Struct]
+//! [foo::Struct]
 //! This is an intra doc injection test for modules
 
 pub struct Struct;
@@ -1097,9 +1097,9 @@ mod foo;
 /// This is an intra doc injection test for modules
 mod foo;
 //- /foo.rs
-//! [Struct]
+//! [foo::Struct]
 //! This is an intra doc injection test for modules
-//! [Struct]
+//! [foo::Struct]
 //! This is an intra doc injection test for modules
 
 pub struct Struct;