Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22083 from ada4a/22062-deprecatedness
fix: respect `#[deprecated]` attr when deciding if a `ModuleDef` completion is `deprecated`
Chayim Refael Friedman 5 weeks ago
parent 82902ef · parent 400c929 · commit 05808ac
-rw-r--r--crates/hir/src/attrs.rs6
-rw-r--r--crates/hir/src/lib.rs17
-rw-r--r--crates/ide-completion/src/completions.rs112
-rw-r--r--crates/ide-completion/src/render.rs375
-rw-r--r--crates/ide-completion/src/render/const_.rs2
-rw-r--r--crates/ide-completion/src/render/function.rs2
-rw-r--r--crates/ide-completion/src/render/literal.rs8
-rw-r--r--crates/ide-completion/src/render/macro_.rs2
-rw-r--r--crates/ide-completion/src/render/pattern.rs4
-rw-r--r--crates/ide-completion/src/render/type_alias.rs2
-rw-r--r--crates/ide-completion/src/render/union_literal.rs2
11 files changed, 431 insertions, 101 deletions
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index bec91032b9..223103b6e5 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -38,7 +38,11 @@ pub enum AttrsOwner {
Field(FieldId),
LifetimeParam(LifetimeParamId),
TypeOrConstParam(TypeOrConstParamId),
- /// Things that do not have attributes. Used for builtin derives.
+ /// Things that do not have attributes.
+ ///
+ /// Used for:
+ /// - builtin derives
+ /// - builtin types (as those do not have attributes)
Dummy,
}
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 27516ed80b..1ae6643294 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -556,6 +556,23 @@ impl HasCrate for ModuleDef {
}
}
+impl HasAttrs for ModuleDef {
+ fn attr_id(self, db: &dyn HirDatabase) -> attrs::AttrsOwner {
+ match self {
+ ModuleDef::Module(it) => it.attr_id(db),
+ ModuleDef::Function(it) => it.attr_id(db),
+ ModuleDef::Adt(it) => it.attr_id(db),
+ ModuleDef::EnumVariant(it) => it.attr_id(db),
+ ModuleDef::Const(it) => it.attr_id(db),
+ ModuleDef::Static(it) => it.attr_id(db),
+ ModuleDef::Trait(it) => it.attr_id(db),
+ ModuleDef::TypeAlias(it) => it.attr_id(db),
+ ModuleDef::Macro(it) => it.attr_id(db),
+ ModuleDef::BuiltinType(_) => attrs::AttrsOwner::Dummy,
+ }
+ }
+}
+
impl HasVisibility for ModuleDef {
fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
match *self {
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index 460a0b139a..2ed582598b 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -234,17 +234,13 @@ impl Completions {
Visible::Editable => true,
Visible::No => return,
};
- self.add(
- render_path_resolution(
- RenderContext::new(ctx)
- .private_editable(is_private_editable)
- .doc_aliases(doc_aliases),
- path_ctx,
- local_name,
- resolution,
- )
- .build(ctx.db),
- );
+ render_path_resolution(
+ RenderContext::new(ctx).private_editable(is_private_editable).doc_aliases(doc_aliases),
+ path_ctx,
+ local_name,
+ resolution,
+ )
+ .add_to(self, ctx.db);
}
pub(crate) fn add_pattern_resolution(
@@ -259,15 +255,13 @@ impl Completions {
Visible::Editable => true,
Visible::No => return,
};
- self.add(
- render_pattern_resolution(
- RenderContext::new(ctx).private_editable(is_private_editable),
- pattern_ctx,
- local_name,
- resolution,
- )
- .build(ctx.db),
- );
+ render_pattern_resolution(
+ RenderContext::new(ctx).private_editable(is_private_editable),
+ pattern_ctx,
+ local_name,
+ resolution,
+ )
+ .add_to(self, ctx.db);
}
pub(crate) fn add_enum_variants(
@@ -310,15 +304,13 @@ impl Completions {
Visible::Editable => true,
Visible::No => return,
};
- self.add(
- render_macro(
- RenderContext::new(ctx).private_editable(is_private_editable),
- path_ctx,
- local_name,
- mac,
- )
- .build(ctx.db),
- );
+ render_macro(
+ RenderContext::new(ctx).private_editable(is_private_editable),
+ path_ctx,
+ local_name,
+ mac,
+ )
+ .add_to(self, ctx.db);
}
pub(crate) fn add_function(
@@ -334,17 +326,13 @@ impl Completions {
Visible::No => return,
};
let doc_aliases = ctx.doc_aliases(&func);
- self.add(
- render_fn(
- RenderContext::new(ctx)
- .private_editable(is_private_editable)
- .doc_aliases(doc_aliases),
- path_ctx,
- local_name,
- func,
- )
- .build(ctx.db),
- );
+ render_fn(
+ RenderContext::new(ctx).private_editable(is_private_editable).doc_aliases(doc_aliases),
+ path_ctx,
+ local_name,
+ func,
+ )
+ .add_to(self, ctx.db);
}
pub(crate) fn add_method(
@@ -361,18 +349,14 @@ impl Completions {
Visible::No => return,
};
let doc_aliases = ctx.doc_aliases(&func);
- self.add(
- render_method(
- RenderContext::new(ctx)
- .private_editable(is_private_editable)
- .doc_aliases(doc_aliases),
- dot_access,
- receiver,
- local_name,
- func,
- )
- .build(ctx.db),
- );
+ render_method(
+ RenderContext::new(ctx).private_editable(is_private_editable).doc_aliases(doc_aliases),
+ dot_access,
+ receiver,
+ local_name,
+ func,
+ )
+ .add_to(self, ctx.db);
}
pub(crate) fn add_method_with_import(
@@ -388,19 +372,17 @@ impl Completions {
Visible::No => return,
};
let doc_aliases = ctx.doc_aliases(&func);
- self.add(
- render_method(
- RenderContext::new(ctx)
- .private_editable(is_private_editable)
- .doc_aliases(doc_aliases)
- .import_to_add(Some(import)),
- dot_access,
- None,
- None,
- func,
- )
- .build(ctx.db),
- );
+ render_method(
+ RenderContext::new(ctx)
+ .private_editable(is_private_editable)
+ .doc_aliases(doc_aliases)
+ .import_to_add(Some(import)),
+ dot_access,
+ None,
+ None,
+ func,
+ )
+ .add_to(self, ctx.db);
}
pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) {
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index f14fd9221f..a636c0603b 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -92,27 +92,32 @@ impl<'a> RenderContext<'a> {
&& self.completion.token.parent().is_some_and(|it| it.kind() == SyntaxKind::MACRO_CALL)
}
- fn is_deprecated(&self, def: impl HasAttrs) -> bool {
- def.attrs(self.db()).is_deprecated()
- }
-
- fn is_deprecated_assoc_item(&self, as_assoc_item: impl AsAssocItem) -> bool {
+ /// Whether `def` is deprecated.
+ ///
+ /// This can happen for two reasons:
+ /// - the def is marked with `#[deprecated]`
+ /// - the def is an assoc item whose trait is deprecated
+ ///
+ /// In order to be able to check for the latter, we'd ideally want to `try_as_dyn<_, dyn AsAssocItem>(def)`
+ /// (see [`try_as_dyn`][]), but that function is currently unstable. Therefore, we employ a hack instead:
+ /// if `def` can be an assoc item, it should be passed to this method as follows:
+ /// ```ignore
+ /// self.is_deprecated(def, Some(def))
+ /// ```
+ /// otherwise, it should be passed as:
+ /// ```ignore
+ /// self.is_deprecated(def, None)
+ /// ```
+ ///
+ /// [`try_as_dyn`]: https://doc.rust-lang.org/std/any/fn.try_as_dyn.html
+ fn is_deprecated(&self, def: impl HasAttrs, def_as_assoc_item: Option<hir::AssocItem>) -> bool {
let db = self.db();
- let assoc = match as_assoc_item.as_assoc_item(db) {
- Some(assoc) => assoc,
- None => return false,
- };
-
- let is_assoc_deprecated = match assoc {
- hir::AssocItem::Function(it) => self.is_deprecated(it),
- hir::AssocItem::Const(it) => self.is_deprecated(it),
- hir::AssocItem::TypeAlias(it) => self.is_deprecated(it),
- };
- is_assoc_deprecated
- || assoc
- .container_or_implemented_trait(db)
- .map(|trait_| self.is_deprecated(trait_))
- .unwrap_or(false)
+ def.attrs(db).is_deprecated()
+ || def_as_assoc_item
+ .and_then(|assoc| assoc.container_or_implemented_trait(db))
+ .is_some_and(|trait_| {
+ self.is_deprecated(trait_, None /* traits can't be assoc items */)
+ })
}
// FIXME: remove this
@@ -129,7 +134,7 @@ pub(crate) fn render_field(
ty: &hir::Type<'_>,
) -> CompletionItem {
let db = ctx.db();
- let is_deprecated = ctx.is_deprecated(field);
+ let is_deprecated = ctx.is_deprecated(field, None /* fields can't be assoc items */);
let name = field.name(db);
let (name, escaped_name) =
(name.as_str().to_smolstr(), name.display_no_db(ctx.completion.edition).to_smolstr());
@@ -576,10 +581,15 @@ fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option<Documentati
}
fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: ScopeDef) -> bool {
+ let db = ctx.db();
match resolution {
- ScopeDef::ModuleDef(it) => ctx.is_deprecated_assoc_item(it),
- ScopeDef::GenericParam(it) => ctx.is_deprecated(it),
- ScopeDef::AdtSelfType(it) => ctx.is_deprecated(it),
+ ScopeDef::ModuleDef(it) => ctx.is_deprecated(it, it.as_assoc_item(db)),
+ ScopeDef::GenericParam(it) => {
+ ctx.is_deprecated(it, None /* generic params can't be assoc items */)
+ }
+ ScopeDef::AdtSelfType(it) => {
+ ctx.is_deprecated(it, None /* `Self` can't be an assoc item */)
+ }
_ => false,
}
}
@@ -1561,6 +1571,32 @@ fn main() { let _: m::Spam = S$0 }
check(
r#"
#[deprecated]
+mod something_deprecated {}
+
+fn main() { som$0 }
+"#,
+ SymbolKind::Module,
+ expect![[r#"
+ [
+ CompletionItem {
+ label: "something_deprecated",
+ detail_left: None,
+ detail_right: None,
+ source_range: 55..58,
+ delete: 55..58,
+ insert: "something_deprecated",
+ kind: SymbolKind(
+ Module,
+ ),
+ deprecated: true,
+ },
+ ]
+ "#]],
+ );
+
+ check(
+ r#"
+#[deprecated]
fn something_deprecated() {}
fn main() { som$0 }
@@ -1605,8 +1641,293 @@ fn main() { som$0 }
check(
r#"
+#[deprecated]
+struct A;
+
+fn main() { A$0 }
+"#,
+ SymbolKind::Struct,
+ expect![[r#"
+ [
+ CompletionItem {
+ label: "A",
+ detail_left: None,
+ detail_right: Some(
+ "A",
+ ),
+ source_range: 37..38,
+ delete: 37..38,
+ insert: "A",
+ kind: SymbolKind(
+ Struct,
+ ),
+ detail: "A",
+ deprecated: true,
+ },
+ ]
+ "#]],
+ );
+
+ check(
+ r#"
+#[deprecated]
+enum A {}
+
+fn main() { A$0 }
+"#,
+ SymbolKind::Enum,
+ expect![[r#"
+ [
+ CompletionItem {
+ label: "A",
+ detail_left: None,
+ detail_right: Some(
+ "A",
+ ),
+ source_range: 37..38,
+ delete: 37..38,
+ insert: "A",
+ kind: SymbolKind(
+ Enum,
+ ),
+ detail: "A",
+ deprecated: true,
+ },
+ ]
+ "#]],
+ );
+
+ check(
+ r#"
+enum A {
+ Okay,
+ #[deprecated]
+ Old,
+}
+
+fn main() { A::$0 }
+"#,
+ SymbolKind::Variant,
+ expect![[r#"
+ [
+ CompletionItem {
+ label: "Okay",
+ detail_left: None,
+ detail_right: Some(
+ "Okay",
+ ),
+ source_range: 64..64,
+ delete: 64..64,
+ insert: "Okay$0",
+ kind: SymbolKind(
+ Variant,
+ ),
+ detail: "Okay",
+ relevance: CompletionRelevance {
+ exact_name_match: false,
+ type_match: None,
+ is_local: false,
+ trait_: None,
+ is_name_already_imported: false,
+ requires_import: false,
+ is_private_editable: false,
+ postfix_match: None,
+ function: Some(
+ CompletionRelevanceFn {
+ has_params: false,
+ has_self_param: false,
+ return_type: DirectConstructor,
+ },
+ ),
+ is_skipping_completion: false,
+ has_local_inherent_impl: false,
+ },
+ trigger_call_info: true,
+ },
+ CompletionItem {
+ label: "Old",
+ detail_left: None,
+ detail_right: Some(
+ "Old",
+ ),
+ source_range: 64..64,
+ delete: 64..64,
+ insert: "Old$0",
+ kind: SymbolKind(
+ Variant,
+ ),
+ detail: "Old",
+ deprecated: true,
+ relevance: CompletionRelevance {
+ exact_name_match: false,
+ type_match: None,
+ is_local: false,
+ trait_: None,
+ is_name_already_imported: false,
+ requires_import: false,
+ is_private_editable: false,
+ postfix_match: None,
+ function: Some(
+ CompletionRelevanceFn {
+ has_params: false,
+ has_self_param: false,
+ return_type: DirectConstructor,
+ },
+ ),
+ is_skipping_completion: false,
+ has_local_inherent_impl: false,
+ },
+ trigger_call_info: true,
+ },
+ ]
+ "#]],
+ );
+
+ check(
+ r#"
+#[deprecated]
+const A: i32 = 0;
+
+fn main() { A$0 }
+"#,
+ SymbolKind::Const,
+ expect![[r#"
+ [
+ CompletionItem {
+ label: "A",
+ detail_left: None,
+ detail_right: Some(
+ "i32",
+ ),
+ source_range: 45..46,
+ delete: 45..46,
+ insert: "A",
+ kind: SymbolKind(
+ Const,
+ ),
+ detail: "i32",
+ deprecated: true,
+ },
+ ]
+ "#]],
+ );
+
+ check(
+ r#"
+#[deprecated]
+static A: i32 = 0;
+
+fn main() { A$0 }
+"#,
+ SymbolKind::Static,
+ expect![[r#"
+ [
+ CompletionItem {
+ label: "A",
+ detail_left: None,
+ detail_right: Some(
+ "i32",
+ ),
+ source_range: 46..47,
+ delete: 46..47,
+ insert: "A",
+ kind: SymbolKind(
+ Static,
+ ),
+ detail: "i32",
+ deprecated: true,
+ },
+ ]
+ "#]],
+ );
+
+ check(
+ r#"
+#[deprecated]
+trait A {}
+
+impl A$0
+"#,
+ SymbolKind::Trait,
+ expect![[r#"
+ [
+ CompletionItem {
+ label: "A",
+ detail_left: None,
+ detail_right: None,
+ source_range: 31..32,
+ delete: 31..32,
+ insert: "A",
+ kind: SymbolKind(
+ Trait,
+ ),
+ deprecated: true,
+ },
+ ]
+ "#]],
+ );
+
+ check(
+ r#"
+#[deprecated]
+type A = i32;
+
+fn main() { A$0 }
+"#,
+ SymbolKind::TypeAlias,
+ expect![[r#"
+ [
+ CompletionItem {
+ label: "A",
+ detail_left: None,
+ detail_right: None,
+ source_range: 41..42,
+ delete: 41..42,
+ insert: "A",
+ kind: SymbolKind(
+ TypeAlias,
+ ),
+ deprecated: true,
+ },
+ ]
+ "#]],
+ );
+
+ check(
+ r#"
+#[deprecated]
+macro_rules! a { _ => {}}
+
+fn main() { a$0 }
+"#,
+ SymbolKind::Macro,
+ expect![[r#"
+ [
+ CompletionItem {
+ label: "a!(…)",
+ detail_left: None,
+ detail_right: Some(
+ "macro_rules! a",
+ ),
+ source_range: 53..54,
+ delete: 53..54,
+ insert: "a!($0)",
+ kind: SymbolKind(
+ Macro,
+ ),
+ lookup: "a!",
+ detail: "macro_rules! a",
+ deprecated: true,
+ },
+ ]
+ "#]],
+ );
+
+ check(
+ r#"
struct A { #[deprecated] the_field: u32 }
-fn foo() { A { the$0 } }
+
+fn main() { A { the$0 } }
"#,
SymbolKind::Field,
expect![[r#"
@@ -1617,8 +1938,8 @@ fn foo() { A { the$0 } }
detail_right: Some(
"u32",
),
- source_range: 57..60,
- delete: 57..60,
+ source_range: 59..62,
+ delete: 59..62,
insert: "the_field",
kind: SymbolKind(
Field,
diff --git a/crates/ide-completion/src/render/const_.rs b/crates/ide-completion/src/render/const_.rs
index 707a8aed4f..134a77a899 100644
--- a/crates/ide-completion/src/render/const_.rs
+++ b/crates/ide-completion/src/render/const_.rs
@@ -21,7 +21,7 @@ fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option<CompletionItem>
let mut item =
CompletionItem::new(SymbolKind::Const, ctx.source_range(), name, ctx.completion.edition);
item.set_documentation(ctx.docs(const_))
- .set_deprecated(ctx.is_deprecated(const_) || ctx.is_deprecated_assoc_item(const_))
+ .set_deprecated(ctx.is_deprecated(const_, const_.as_assoc_item(db)))
.detail(detail)
.set_relevance(ctx.completion_relevance());
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs
index dfa30841e7..18151cffcd 100644
--- a/crates/ide-completion/src/render/function.rs
+++ b/crates/ide-completion/src/render/function.rs
@@ -147,7 +147,7 @@ fn render(
detail(ctx.completion, func)
};
item.set_documentation(ctx.docs(func))
- .set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func))
+ .set_deprecated(ctx.is_deprecated(func, func.as_assoc_item(db)))
.detail(detail)
.lookup_by(name.as_str().to_smolstr());
diff --git a/crates/ide-completion/src/render/literal.rs b/crates/ide-completion/src/render/literal.rs
index 6e49af980a..b7de3da468 100644
--- a/crates/ide-completion/src/render/literal.rs
+++ b/crates/ide-completion/src/render/literal.rs
@@ -189,8 +189,12 @@ impl Variant {
fn is_deprecated(self, ctx: &RenderContext<'_>) -> bool {
match self {
- Variant::Struct(it) => ctx.is_deprecated(it),
- Variant::EnumVariant(it) => ctx.is_deprecated(it),
+ Variant::Struct(it) => {
+ ctx.is_deprecated(it, None /* structs can't be assoc items */)
+ }
+ Variant::EnumVariant(it) => {
+ ctx.is_deprecated(it, None /* enum variants can't be assoc items */)
+ }
}
}
diff --git a/crates/ide-completion/src/render/macro_.rs b/crates/ide-completion/src/render/macro_.rs
index 8cdeb8abbf..ff4cf9a75b 100644
--- a/crates/ide-completion/src/render/macro_.rs
+++ b/crates/ide-completion/src/render/macro_.rs
@@ -64,7 +64,7 @@ fn render(
label(&ctx, needs_bang, bra, ket, &name.to_smolstr()),
completion.edition,
);
- item.set_deprecated(ctx.is_deprecated(macro_))
+ item.set_deprecated(ctx.is_deprecated(macro_, None /* macros can't be assoc items */))
.detail(macro_.display(completion.db, completion.display_target).to_string())
.set_documentation(docs)
.set_relevance(ctx.completion_relevance());
diff --git a/crates/ide-completion/src/render/pattern.rs b/crates/ide-completion/src/render/pattern.rs
index fb35d7b9b6..022e97e4f7 100644
--- a/crates/ide-completion/src/render/pattern.rs
+++ b/crates/ide-completion/src/render/pattern.rs
@@ -126,7 +126,9 @@ fn build_completion(
ctx.completion.edition,
);
item.set_documentation(ctx.docs(def))
- .set_deprecated(ctx.is_deprecated(def))
+ .set_deprecated(
+ ctx.is_deprecated(def, None /* the two current `def` arguments to this function, `Struct` and `EnumVariant`, both can't be assoc items */),
+ )
.detail(&pat)
.lookup_by(lookup)
.set_relevance(relevance);
diff --git a/crates/ide-completion/src/render/type_alias.rs b/crates/ide-completion/src/render/type_alias.rs
index 3fc0f369e5..2b79ca2deb 100644
--- a/crates/ide-completion/src/render/type_alias.rs
+++ b/crates/ide-completion/src/render/type_alias.rs
@@ -47,7 +47,7 @@ fn render(
ctx.completion.edition,
);
item.set_documentation(ctx.docs(type_alias))
- .set_deprecated(ctx.is_deprecated(type_alias) || ctx.is_deprecated_assoc_item(type_alias))
+ .set_deprecated(ctx.is_deprecated(type_alias, type_alias.as_assoc_item(db)))
.detail(detail)
.set_relevance(ctx.completion_relevance());
diff --git a/crates/ide-completion/src/render/union_literal.rs b/crates/ide-completion/src/render/union_literal.rs
index 23f0d4e06f..7164c94fde 100644
--- a/crates/ide-completion/src/render/union_literal.rs
+++ b/crates/ide-completion/src/render/union_literal.rs
@@ -95,7 +95,7 @@ pub(crate) fn render_union_literal(
);
item.set_documentation(ctx.docs(un))
- .set_deprecated(ctx.is_deprecated(un))
+ .set_deprecated(ctx.is_deprecated(un, None /* unions can't be assoc items */))
.detail(detail)
.set_relevance(ctx.completion_relevance());