mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 11:31:15 +00:00
feat: implement completion for diagnostic module
This commit is contained in:
parent
fb921c3039
commit
7e8079fbad
@ -25,6 +25,7 @@ use crate::{
|
|||||||
|
|
||||||
mod cfg;
|
mod cfg;
|
||||||
mod derive;
|
mod derive;
|
||||||
|
mod diagnostic;
|
||||||
mod lint;
|
mod lint;
|
||||||
mod macro_use;
|
mod macro_use;
|
||||||
mod repr;
|
mod repr;
|
||||||
@ -40,14 +41,23 @@ pub(crate) fn complete_known_attribute_input(
|
|||||||
extern_crate: Option<&ast::ExternCrate>,
|
extern_crate: Option<&ast::ExternCrate>,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
let attribute = fake_attribute_under_caret;
|
let attribute = fake_attribute_under_caret;
|
||||||
let name_ref = match attribute.path() {
|
let path = attribute.path()?;
|
||||||
Some(p) => Some(p.as_single_name_ref()?),
|
let name_ref = path.segment()?.name_ref();
|
||||||
None => None,
|
let (name_ref, tt) = name_ref.zip(attribute.token_tree())?;
|
||||||
};
|
|
||||||
let (path, tt) = name_ref.zip(attribute.token_tree())?;
|
|
||||||
tt.l_paren_token()?;
|
tt.l_paren_token()?;
|
||||||
|
|
||||||
match path.text().as_str() {
|
if let Some(qualifier) = path.qualifier() {
|
||||||
|
let qualifier_name_ref = qualifier.as_single_name_ref()?;
|
||||||
|
match (qualifier_name_ref.text().as_str(), name_ref.text().as_str()) {
|
||||||
|
("diagnostic", "on_unimplemented") => {
|
||||||
|
diagnostic::complete_on_unimplemented(acc, ctx, tt)
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
return Some(());
|
||||||
|
}
|
||||||
|
|
||||||
|
match name_ref.text().as_str() {
|
||||||
"repr" => repr::complete_repr(acc, ctx, tt),
|
"repr" => repr::complete_repr(acc, ctx, tt),
|
||||||
"feature" => lint::complete_lint(
|
"feature" => lint::complete_lint(
|
||||||
acc,
|
acc,
|
||||||
@ -139,6 +149,8 @@ pub(crate) fn complete_attribute_path(
|
|||||||
}
|
}
|
||||||
Qualified::TypeAnchor { .. } | Qualified::With { .. } => {}
|
Qualified::TypeAnchor { .. } | Qualified::With { .. } => {}
|
||||||
}
|
}
|
||||||
|
let qualifier_path =
|
||||||
|
if let Qualified::With { path, .. } = qualified { Some(path) } else { None };
|
||||||
|
|
||||||
let attributes = annotated_item_kind.and_then(|kind| {
|
let attributes = annotated_item_kind.and_then(|kind| {
|
||||||
if ast::Expr::can_cast(kind) {
|
if ast::Expr::can_cast(kind) {
|
||||||
@ -149,18 +161,28 @@ pub(crate) fn complete_attribute_path(
|
|||||||
});
|
});
|
||||||
|
|
||||||
let add_completion = |attr_completion: &AttrCompletion| {
|
let add_completion = |attr_completion: &AttrCompletion| {
|
||||||
let mut item = CompletionItem::new(
|
// if we already have the qualifier of the completion, then trim it from the label and the snippet
|
||||||
SymbolKind::Attribute,
|
let mut label = attr_completion.label;
|
||||||
ctx.source_range(),
|
let mut snippet = attr_completion.snippet;
|
||||||
attr_completion.label,
|
if let Some(name_ref) = qualifier_path.and_then(|q| q.as_single_name_ref()) {
|
||||||
ctx.edition,
|
if let Some((label_qual, label_seg)) = attr_completion.label.split_once("::") {
|
||||||
);
|
if name_ref.text() == label_qual {
|
||||||
|
label = label_seg;
|
||||||
|
snippet = snippet.map(|snippet| {
|
||||||
|
snippet.trim_start_matches(label_qual).trim_start_matches("::")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut item =
|
||||||
|
CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label, ctx.edition);
|
||||||
|
|
||||||
if let Some(lookup) = attr_completion.lookup {
|
if let Some(lookup) = attr_completion.lookup {
|
||||||
item.lookup_by(lookup);
|
item.lookup_by(lookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((snippet, cap)) = attr_completion.snippet.zip(ctx.config.snippet_cap) {
|
if let Some((snippet, cap)) = snippet.zip(ctx.config.snippet_cap) {
|
||||||
item.insert_snippet(cap, snippet);
|
item.insert_snippet(cap, snippet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,8 +292,8 @@ static KIND_TO_ATTRIBUTES: LazyLock<FxHashMap<SyntaxKind, &[&str]>> = LazyLock::
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
(STATIC, attrs!(item, linkable, "global_allocator", "used")),
|
(STATIC, attrs!(item, linkable, "global_allocator", "used")),
|
||||||
(TRAIT, attrs!(item)),
|
(TRAIT, attrs!(item, "diagnostic::on_unimplemented")),
|
||||||
(IMPL, attrs!(item, "automatically_derived")),
|
(IMPL, attrs!(item, "automatically_derived", "diagnostic::do_not_recommend")),
|
||||||
(ASSOC_ITEM_LIST, attrs!(item)),
|
(ASSOC_ITEM_LIST, attrs!(item)),
|
||||||
(EXTERN_BLOCK, attrs!(item, "link")),
|
(EXTERN_BLOCK, attrs!(item, "link")),
|
||||||
(EXTERN_ITEM_LIST, attrs!(item, "link")),
|
(EXTERN_ITEM_LIST, attrs!(item, "link")),
|
||||||
@ -311,6 +333,8 @@ const ATTRIBUTES: &[AttrCompletion] = &[
|
|||||||
attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
|
attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
|
||||||
attr(r#"deprecated"#, Some("deprecated"), Some(r#"deprecated"#)),
|
attr(r#"deprecated"#, Some("deprecated"), Some(r#"deprecated"#)),
|
||||||
attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
|
attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
|
||||||
|
attr("diagnostic::do_not_recommend", None, None),
|
||||||
|
attr("diagnostic::on_unimplemented", None, Some(r#"diagnostic::on_unimplemented(${0:keys})"#)),
|
||||||
attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
|
attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
|
||||||
attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)),
|
attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)),
|
||||||
attr(r#"doc(hidden)"#, Some("dochidden"), Some(r#"doc(hidden)"#)),
|
attr(r#"doc(hidden)"#, Some("dochidden"), Some(r#"doc(hidden)"#)),
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
//! Completion for diagnostic attributes.
|
||||||
|
|
||||||
|
use ide_db::SymbolKind;
|
||||||
|
use syntax::ast::{self};
|
||||||
|
|
||||||
|
use crate::{CompletionItem, Completions, context::CompletionContext};
|
||||||
|
|
||||||
|
use super::AttrCompletion;
|
||||||
|
|
||||||
|
pub(super) fn complete_on_unimplemented(
|
||||||
|
acc: &mut Completions,
|
||||||
|
ctx: &CompletionContext<'_>,
|
||||||
|
input: ast::TokenTree,
|
||||||
|
) {
|
||||||
|
if let Some(existing_keys) = super::parse_comma_sep_expr(input) {
|
||||||
|
for attr in ATTRIBUTES {
|
||||||
|
let already_annotated = existing_keys
|
||||||
|
.iter()
|
||||||
|
.filter_map(|expr| match expr {
|
||||||
|
ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(),
|
||||||
|
ast::Expr::BinExpr(bin)
|
||||||
|
if bin.op_kind() == Some(ast::BinaryOp::Assignment { op: None }) =>
|
||||||
|
{
|
||||||
|
match bin.lhs()? {
|
||||||
|
ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.any(|it| {
|
||||||
|
let text = it.text();
|
||||||
|
attr.key() == text && text != "note"
|
||||||
|
});
|
||||||
|
if already_annotated {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut item = CompletionItem::new(
|
||||||
|
SymbolKind::BuiltinAttr,
|
||||||
|
ctx.source_range(),
|
||||||
|
attr.label,
|
||||||
|
ctx.edition,
|
||||||
|
);
|
||||||
|
if let Some(lookup) = attr.lookup {
|
||||||
|
item.lookup_by(lookup);
|
||||||
|
}
|
||||||
|
if let Some((snippet, cap)) = attr.snippet.zip(ctx.config.snippet_cap) {
|
||||||
|
item.insert_snippet(cap, snippet);
|
||||||
|
}
|
||||||
|
item.add_to(acc, ctx.db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ATTRIBUTES: &[AttrCompletion] = &[
|
||||||
|
super::attr(r#"label = "…""#, Some("label"), Some(r#"label = "${0:label}""#)),
|
||||||
|
super::attr(r#"message = "…""#, Some("message"), Some(r#"message = "${0:message}""#)),
|
||||||
|
super::attr(r#"note = "…""#, Some("note"), Some(r#"note = "${0:note}""#)),
|
||||||
|
];
|
@ -30,6 +30,8 @@ pub struct Foo(#[m$0] i32);
|
|||||||
at deprecated
|
at deprecated
|
||||||
at derive macro derive
|
at derive macro derive
|
||||||
at derive(…)
|
at derive(…)
|
||||||
|
at diagnostic::do_not_recommend
|
||||||
|
at diagnostic::on_unimplemented
|
||||||
at doc = "…"
|
at doc = "…"
|
||||||
at doc(alias = "…")
|
at doc(alias = "…")
|
||||||
at doc(hidden)
|
at doc(hidden)
|
||||||
@ -472,13 +474,13 @@ fn attr_on_trait() {
|
|||||||
at cfg_attr(…)
|
at cfg_attr(…)
|
||||||
at deny(…)
|
at deny(…)
|
||||||
at deprecated
|
at deprecated
|
||||||
|
at diagnostic::on_unimplemented
|
||||||
at doc = "…"
|
at doc = "…"
|
||||||
at doc(alias = "…")
|
at doc(alias = "…")
|
||||||
at doc(hidden)
|
at doc(hidden)
|
||||||
at expect(…)
|
at expect(…)
|
||||||
at forbid(…)
|
at forbid(…)
|
||||||
at must_use
|
at must_use
|
||||||
at must_use
|
|
||||||
at no_mangle
|
at no_mangle
|
||||||
at warn(…)
|
at warn(…)
|
||||||
kw crate::
|
kw crate::
|
||||||
@ -498,6 +500,7 @@ fn attr_on_impl() {
|
|||||||
at cfg_attr(…)
|
at cfg_attr(…)
|
||||||
at deny(…)
|
at deny(…)
|
||||||
at deprecated
|
at deprecated
|
||||||
|
at diagnostic::do_not_recommend
|
||||||
at doc = "…"
|
at doc = "…"
|
||||||
at doc(alias = "…")
|
at doc(alias = "…")
|
||||||
at doc(hidden)
|
at doc(hidden)
|
||||||
@ -532,6 +535,76 @@ fn attr_on_impl() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn attr_with_qualifier() {
|
||||||
|
check(
|
||||||
|
r#"#[diagnostic::$0] impl () {}"#,
|
||||||
|
expect![[r#"
|
||||||
|
at allow(…)
|
||||||
|
at automatically_derived
|
||||||
|
at cfg(…)
|
||||||
|
at cfg_attr(…)
|
||||||
|
at deny(…)
|
||||||
|
at deprecated
|
||||||
|
at do_not_recommend
|
||||||
|
at doc = "…"
|
||||||
|
at doc(alias = "…")
|
||||||
|
at doc(hidden)
|
||||||
|
at expect(…)
|
||||||
|
at forbid(…)
|
||||||
|
at must_use
|
||||||
|
at no_mangle
|
||||||
|
at warn(…)
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"#[diagnostic::$0] trait Foo {}"#,
|
||||||
|
expect![[r#"
|
||||||
|
at allow(…)
|
||||||
|
at cfg(…)
|
||||||
|
at cfg_attr(…)
|
||||||
|
at deny(…)
|
||||||
|
at deprecated
|
||||||
|
at doc = "…"
|
||||||
|
at doc(alias = "…")
|
||||||
|
at doc(hidden)
|
||||||
|
at expect(…)
|
||||||
|
at forbid(…)
|
||||||
|
at must_use
|
||||||
|
at no_mangle
|
||||||
|
at on_unimplemented
|
||||||
|
at warn(…)
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn attr_diagnostic_on_unimplemented() {
|
||||||
|
check(
|
||||||
|
r#"#[diagnostic::on_unimplemented($0)] trait Foo {}"#,
|
||||||
|
expect![[r#"
|
||||||
|
ba label = "…"
|
||||||
|
ba message = "…"
|
||||||
|
ba note = "…"
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"#[diagnostic::on_unimplemented(message = "foo", $0)] trait Foo {}"#,
|
||||||
|
expect![[r#"
|
||||||
|
ba label = "…"
|
||||||
|
ba note = "…"
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"#[diagnostic::on_unimplemented(note = "foo", $0)] trait Foo {}"#,
|
||||||
|
expect![[r#"
|
||||||
|
ba label = "…"
|
||||||
|
ba message = "…"
|
||||||
|
ba note = "…"
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn attr_on_extern_block() {
|
fn attr_on_extern_block() {
|
||||||
check(
|
check(
|
||||||
@ -619,7 +692,6 @@ fn attr_on_fn() {
|
|||||||
at link_name = "…"
|
at link_name = "…"
|
||||||
at link_section = "…"
|
at link_section = "…"
|
||||||
at must_use
|
at must_use
|
||||||
at must_use
|
|
||||||
at no_mangle
|
at no_mangle
|
||||||
at panic_handler
|
at panic_handler
|
||||||
at proc_macro
|
at proc_macro
|
||||||
@ -649,6 +721,8 @@ fn attr_in_source_file_end() {
|
|||||||
at deny(…)
|
at deny(…)
|
||||||
at deprecated
|
at deprecated
|
||||||
at derive(…)
|
at derive(…)
|
||||||
|
at diagnostic::do_not_recommend
|
||||||
|
at diagnostic::on_unimplemented
|
||||||
at doc = "…"
|
at doc = "…"
|
||||||
at doc(alias = "…")
|
at doc(alias = "…")
|
||||||
at doc(hidden)
|
at doc(hidden)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user