Skip to content

Commit d9080ad

Browse files
bors[bot]Veykril
andauthored
Merge #10562
10562: fix: Fix clippy attribute completions always prefixing inserting `clippy::` r=Veykril a=Veykril Fixes #7144 Co-authored-by: Lukas Wirth <[email protected]>
2 parents c16d04e + 99906ba commit d9080ad

File tree

7 files changed

+426
-351
lines changed

7 files changed

+426
-351
lines changed

crates/ide_completion/src/completions/attribute.rs

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
66
use hir::HasAttrs;
77
use ide_db::helpers::generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES};
8+
use itertools::Itertools;
89
use once_cell::sync::Lazy;
9-
use rustc_hash::{FxHashMap, FxHashSet};
10-
use syntax::{algo::non_trivia_sibling, ast, AstNode, Direction, NodeOrToken, SyntaxKind, T};
10+
use rustc_hash::FxHashMap;
11+
use syntax::{algo::non_trivia_sibling, ast, AstNode, Direction, SyntaxKind, T};
1112

1213
use crate::{
1314
context::CompletionContext,
@@ -303,31 +304,38 @@ const ATTRIBUTES: &[AttrCompletion] = &[
303304
.prefer_inner(),
304305
];
305306

306-
fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Option<FxHashSet<String>> {
307-
let (l_paren, r_paren) = derive_input.l_paren_token().zip(derive_input.r_paren_token())?;
308-
let mut input_derives = FxHashSet::default();
309-
let mut tokens = derive_input
307+
fn parse_comma_sep_paths(input: ast::TokenTree) -> Option<Vec<ast::Path>> {
308+
let r_paren = input.r_paren_token()?;
309+
let tokens = input
310310
.syntax()
311311
.children_with_tokens()
312-
.filter_map(NodeOrToken::into_token)
313-
.skip_while(|token| token != &l_paren)
314312
.skip(1)
315-
.take_while(|token| token != &r_paren)
316-
.peekable();
317-
let mut input = String::new();
318-
while tokens.peek().is_some() {
319-
for token in tokens.by_ref().take_while(|t| t.kind() != T![,]) {
320-
input.push_str(token.text());
321-
}
322-
323-
if !input.is_empty() {
324-
input_derives.insert(input.trim().to_owned());
325-
}
326-
327-
input.clear();
328-
}
313+
.take_while(|it| it.as_token() != Some(&r_paren));
314+
let input_expressions = tokens.into_iter().group_by(|tok| tok.kind() == T![,]);
315+
Some(
316+
input_expressions
317+
.into_iter()
318+
.filter_map(|(is_sep, group)| (!is_sep).then(|| group))
319+
.filter_map(|mut tokens| ast::Path::parse(&tokens.join("")).ok())
320+
.collect::<Vec<ast::Path>>(),
321+
)
322+
}
329323

330-
Some(input_derives)
324+
fn parse_comma_sep_expr(input: ast::TokenTree) -> Option<Vec<ast::Expr>> {
325+
let r_paren = input.r_paren_token()?;
326+
let tokens = input
327+
.syntax()
328+
.children_with_tokens()
329+
.skip(1)
330+
.take_while(|it| it.as_token() != Some(&r_paren));
331+
let input_expressions = tokens.into_iter().group_by(|tok| tok.kind() == T![,]);
332+
Some(
333+
input_expressions
334+
.into_iter()
335+
.filter_map(|(is_sep, group)| (!is_sep).then(|| group))
336+
.filter_map(|mut tokens| ast::Expr::parse(&tokens.join("")).ok())
337+
.collect::<Vec<ast::Expr>>(),
338+
)
331339
}
332340

333341
#[test]

crates/ide_completion/src/completions/attribute/derive.rs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
use hir::HasAttrs;
33
use itertools::Itertools;
44
use rustc_hash::FxHashMap;
5-
use syntax::ast;
5+
use syntax::{ast, SmolStr};
66

77
use crate::{
88
context::CompletionContext,
@@ -15,26 +15,31 @@ pub(super) fn complete_derive(
1515
ctx: &CompletionContext,
1616
derive_input: ast::TokenTree,
1717
) {
18-
if let Some(existing_derives) = super::parse_comma_sep_input(derive_input) {
18+
if let Some(existing_derives) = super::parse_comma_sep_paths(derive_input) {
1919
for (derive, docs) in get_derive_names_in_scope(ctx) {
20+
let label;
2021
let (label, lookup) = if let Some(derive_completion) = DEFAULT_DERIVE_COMPLETIONS
2122
.iter()
2223
.find(|derive_completion| derive_completion.label == derive)
2324
{
2425
let mut components = vec![derive_completion.label];
25-
components.extend(
26-
derive_completion
27-
.dependencies
26+
components.extend(derive_completion.dependencies.iter().filter(|&&dependency| {
27+
!existing_derives
2828
.iter()
29-
.filter(|&&dependency| !existing_derives.contains(dependency)),
30-
);
29+
.filter_map(|it| it.as_single_name_ref())
30+
.any(|it| it.text() == dependency)
31+
}));
3132
let lookup = components.join(", ");
32-
let label = components.iter().rev().join(", ");
33-
(label, Some(lookup))
34-
} else if existing_derives.contains(&derive) {
33+
label = components.iter().rev().join(", ");
34+
(&*label, Some(lookup))
35+
} else if existing_derives
36+
.iter()
37+
.filter_map(|it| it.as_single_name_ref())
38+
.any(|it| it.text().as_str() == derive)
39+
{
3540
continue;
3641
} else {
37-
(derive, None)
42+
(&*derive, None)
3843
};
3944
let mut item =
4045
CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
@@ -52,12 +57,12 @@ pub(super) fn complete_derive(
5257

5358
fn get_derive_names_in_scope(
5459
ctx: &CompletionContext,
55-
) -> FxHashMap<String, Option<hir::Documentation>> {
60+
) -> FxHashMap<SmolStr, Option<hir::Documentation>> {
5661
let mut result = FxHashMap::default();
5762
ctx.process_all_names(&mut |name, scope_def| {
5863
if let hir::ScopeDef::MacroDef(mac) = scope_def {
5964
if mac.kind() == hir::MacroKind::Derive {
60-
result.insert(name.to_string(), mac.docs(ctx.db));
65+
result.insert(name.to_smol_str(), mac.docs(ctx.db));
6166
}
6267
}
6368
});

crates/ide_completion/src/completions/attribute/lint.rs

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Completion for lints
22
use ide_db::helpers::generated_lints::Lint;
3-
use syntax::ast;
3+
use syntax::{ast, T};
44

55
use crate::{
66
context::CompletionContext,
@@ -14,17 +14,51 @@ pub(super) fn complete_lint(
1414
derive_input: ast::TokenTree,
1515
lints_completions: &[Lint],
1616
) {
17-
if let Some(existing_lints) = super::parse_comma_sep_input(derive_input) {
18-
for lint_completion in
19-
lints_completions.iter().filter(|completion| !existing_lints.contains(completion.label))
20-
{
21-
let mut item = CompletionItem::new(
22-
CompletionKind::Attribute,
23-
ctx.source_range(),
24-
lint_completion.label,
25-
);
17+
if let Some(existing_lints) = super::parse_comma_sep_paths(derive_input) {
18+
for &Lint { label, description } in lints_completions {
19+
let (qual, name) = {
20+
// FIXME: change `Lint`'s label to not store a path in it but split the prefix off instead?
21+
let mut parts = label.split("::");
22+
let ns_or_label = match parts.next() {
23+
Some(it) => it,
24+
None => continue,
25+
};
26+
let label = parts.next();
27+
match label {
28+
Some(label) => (Some(ns_or_label), label),
29+
None => (None, ns_or_label),
30+
}
31+
};
32+
let lint_already_annotated = existing_lints
33+
.iter()
34+
.filter_map(|path| {
35+
let q = path.qualifier();
36+
if q.as_ref().and_then(|it| it.qualifier()).is_some() {
37+
return None;
38+
}
39+
Some((q.and_then(|it| it.as_single_name_ref()), path.segment()?.name_ref()?))
40+
})
41+
.any(|(q, name_ref)| {
42+
let qualifier_matches = match (q, qual) {
43+
(None, None) => true,
44+
(None, Some(_)) => false,
45+
(Some(_), None) => false,
46+
(Some(q), Some(ns)) => q.text() == ns,
47+
};
48+
qualifier_matches && name_ref.text() == name
49+
});
50+
if lint_already_annotated {
51+
continue;
52+
}
53+
let insert = match qual {
54+
Some(qual) if !ctx.previous_token_is(T![:]) => format!("{}::{}", qual, name),
55+
_ => name.to_owned(),
56+
};
57+
let mut item =
58+
CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
2659
item.kind(CompletionItemKind::Attribute)
27-
.documentation(hir::Documentation::new(lint_completion.description.to_owned()));
60+
.insert_text(insert)
61+
.documentation(hir::Documentation::new(description.to_owned()));
2862
item.add_to(acc)
2963
}
3064
}

crates/ide_completion/src/completions/attribute/repr.rs

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,34 @@ use crate::{
88
Completions,
99
};
1010

11-
pub(super) fn complete_repr(
12-
acc: &mut Completions,
13-
ctx: &CompletionContext,
14-
derive_input: ast::TokenTree,
15-
) {
16-
if let Some(existing_reprs) = super::parse_comma_sep_input(derive_input) {
17-
for repr_completion in REPR_COMPLETIONS {
18-
if existing_reprs
11+
pub(super) fn complete_repr(acc: &mut Completions, ctx: &CompletionContext, input: ast::TokenTree) {
12+
if let Some(existing_reprs) = super::parse_comma_sep_expr(input) {
13+
for &ReprCompletion { label, snippet, lookup, collides } in REPR_COMPLETIONS {
14+
let repr_already_annotated = existing_reprs
1915
.iter()
20-
.any(|it| repr_completion.label == it || repr_completion.collides.contains(&&**it))
21-
{
16+
.filter_map(|expr| match expr {
17+
ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(),
18+
ast::Expr::CallExpr(call) => match call.expr()? {
19+
ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(),
20+
_ => return None,
21+
},
22+
_ => None,
23+
})
24+
.any(|it| {
25+
let text = it.text();
26+
lookup.unwrap_or(label) == text || collides.contains(&text.as_str())
27+
});
28+
if repr_already_annotated {
2229
continue;
2330
}
24-
let mut item = CompletionItem::new(
25-
CompletionKind::Attribute,
26-
ctx.source_range(),
27-
repr_completion.label,
28-
);
31+
32+
let mut item =
33+
CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
2934
item.kind(CompletionItemKind::Attribute);
30-
if let Some(lookup) = repr_completion.lookup {
35+
if let Some(lookup) = lookup {
3136
item.lookup_by(lookup);
3237
}
33-
if let Some((snippet, cap)) = repr_completion.snippet.zip(ctx.config.snippet_cap) {
38+
if let Some((snippet, cap)) = snippet.zip(ctx.config.snippet_cap) {
3439
item.insert_snippet(cap, snippet);
3540
}
3641
item.add_to(acc);

0 commit comments

Comments
 (0)