Repository: rust-lang/rust-analyzer
Pull request #18907 (2025-01-10 13:25:34)

Implement `#[rust_analyzer::skip]` for bodies
This skips lowering of the annotated body except for its signature. This allows analysis that refers to this to still work fine while making it useful already.
cc https://github.com/rust-lang/rust-analyzer/issues/11556


Patch:
diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs
index 37e2a99e60..710bffcefe 100644
--- a/crates/hir-def/src/attr.rs
+++ b/crates/hir-def/src/attr.rs
@@ -122,6 +122,11 @@ impl Attrs {
         AttrQuery { attrs: self, key }
     }
 
+    pub fn rust_analyzer_tool(&self) -> impl Iterator<Item = &Attr> {
+        self.iter()
+            .filter(|&attr| attr.path.segments().first().is_some_and(|s| *s == sym::rust_analyzer))
+    }
+
     pub fn cfg(&self) -> Option<CfgExpr> {
         let mut cfgs = self.by_key(&sym::cfg).tt_values().map(CfgExpr::parse);
         let first = cfgs.next()?;
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index 49c48a5d59..d7e28da644 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -8,6 +8,7 @@ use std::mem;
 use base_db::CrateId;
 use either::Either;
 use hir_expand::{
+    mod_path::tool_path,
     name::{AsName, Name},
     span_map::{ExpansionSpanMap, SpanMap},
     InFile, MacroDefId,
@@ -27,6 +28,7 @@ use text_size::TextSize;
 use triomphe::Arc;
 
 use crate::{
+    attr::Attrs,
     body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, HygieneId, LabelPtr, PatPtr},
     builtin_type::BuiltinUint,
     data::adt::StructKind,
@@ -212,6 +214,43 @@ impl ExprCollector<'_> {
         body: Option<ast::Expr>,
         is_async_fn: bool,
     ) -> (Body, BodySourceMap) {
+        let skip_body = match self.owner {
+            DefWithBodyId::FunctionId(it) => self.db.attrs(it.into()),
+            DefWithBodyId::StaticId(it) => self.db.attrs(it.into()),
+            DefWithBodyId::ConstId(it) => self.db.attrs(it.into()),
+            DefWithBodyId::InTypeConstId(_) => Attrs::EMPTY,
+            DefWithBodyId::VariantId(it) => self.db.attrs(it.into()),
+        }
+        .rust_analyzer_tool()
+        .any(|attr| *attr.path() == tool_path![skip]);
+        // If #[rust_analyzer::skip] annotated, only construct enough information for the signature
+        // and skip the body.
+        if skip_body {
+            self.body.body_expr = self.missing_expr();
+            if let Some((param_list, mut attr_enabled)) = param_list {
+                if let Some(self_param) =
+                    param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
+                {
+                    let is_mutable =
+                        self_param.mut_token().is_some() && self_param.amp_token().is_none();
+                    let binding_id: la_arena::Idx<Binding> = self.alloc_binding(
+                        Name::new_symbol_root(sym::self_.clone()),
+                        BindingAnnotation::new(is_mutable, false),
+                    );
+                    self.body.self_param = Some(binding_id);
+                    self.source_map.self_param =
+                        Some(self.expander.in_file(AstPtr::new(&self_param)));
+                }
+                self.body.params = param_list
+                    .params()
+                    .zip(attr_enabled)
+                    .filter(|(_, enabled)| *enabled)
+                    .map(|_| self.missing_pat())
+                    .collect();
+            };
+            return (self.body, self.source_map);
+        }
+
         self.awaitable_context.replace(if is_async_fn {
             Awaitable::Yes
         } else {
diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs
index 876919f3ed..4f2f9ec40d 100644
--- a/crates/hir-expand/src/mod_path.rs
+++ b/crates/hir-expand/src/mod_path.rs
@@ -414,3 +414,14 @@ macro_rules! __path {
 }
 
 pub use crate::__path as path;
+
+#[macro_export]
+macro_rules! __tool_path {
+    ($start:ident $(:: $seg:ident)*) => ({
+        $crate::mod_path::ModPath::from_segments($crate::mod_path::PathKind::Plain, vec![
+            $crate::name::Name::new_symbol_root(intern::sym::rust_analyzer.clone()), $crate::name::Name::new_symbol_root(intern::sym::$start.clone()), $($crate::name::Name::new_symbol_root(intern::sym::$seg.clone()),)*
+        ])
+    });
+}
+
+pub use crate::__tool_path as tool_path;
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs
index c7ebd9a353..512b7e62fd 100644
--- a/crates/ide/src/goto_type_definition.rs
+++ b/crates/ide/src/goto_type_definition.rs
@@ -24,9 +24,10 @@ pub(crate) fn goto_type_definition(
     let file: ast::SourceFile = sema.parse_guess_edition(file_id);
     let token: SyntaxToken =
         pick_best_token(file.syntax().token_at_offset(offset), |kind| match kind {
-            IDENT | INT_NUMBER | T![self] => 2,
+            IDENT | INT_NUMBER | T![self] => 3,
             kind if kind.is_trivia() => 0,
-            _ => 1,
+            T![;] => 1,
+            _ => 2,
         })?;
 
     let mut res = Vec::new();
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index 66b8900109..131b21a46b 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -429,6 +429,7 @@ define_symbols! {
     shr,
     simd,
     sized,
+    skip,
     slice_len_fn,
     Some,
     start,


Test patch:
diff --git a/crates/hir-def/src/body/tests.rs b/crates/hir-def/src/body/tests.rs
index 7e15a9f2d6..a9ca1c5069 100644
--- a/crates/hir-def/src/body/tests.rs
+++ b/crates/hir-def/src/body/tests.rs
@@ -444,3 +444,18 @@ fn foo() {
 }"#
     );
 }
+
+#[test]
+fn skip_skips_body() {
+    let (db, body, owner) = lower(
+        r#"
+#[rust_analyzer::skip]
+async fn foo(a: (), b: i32) -> u32 {
+    0 + 1 + b()
+}
+"#,
+    );
+    let printed = body.pretty_print(&db, owner, Edition::CURRENT);
+    expect!["fn foo(�: (), �: i32) -> impl ::core::future::Future::<Output = u32> �"]
+        .assert_eq(&printed);
+}
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index 5f0f341f39..1563660457 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -3802,3 +3802,15 @@ fn foo() {
         "#,
     );
 }
+
+#[test]
+fn tool_attr_skip() {
+    check_no_mismatches(
+        r#"
+#[rust_analyzer::skip]
+async fn foo(a: (), b: i32) -> u32 {
+    0 + 1 + b()
+}
+        "#,
+    );
+}