Skip to content

Commit 6ca4020

Browse files
bors[bot]Veykril
andauthored
Merge #9138
9138: feat: Implement hover for lints r=Veykril a=Veykril fixes #8857, fixes #3941 ![URXBanNxYe](https://user-images.githubusercontent.com/3757771/120830905-4bd8da80-c55f-11eb-9f55-ff5a321726fa.gif) We also generate the default lints(and lint groups 🎉) instead now by invoking `rustc -W help` and parsing the output from that. Co-authored-by: Lukas Wirth <[email protected]>
2 parents 92d9105 + 52a6f79 commit 6ca4020

File tree

8 files changed

+4619
-4030
lines changed

8 files changed

+4619
-4030
lines changed

crates/ide/src/hover.rs

Lines changed: 133 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,18 @@ use hir::{
66
use ide_db::{
77
base_db::SourceDatabase,
88
defs::{Definition, NameClass, NameRefClass},
9-
helpers::FamousDefs,
9+
helpers::{
10+
generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
11+
FamousDefs,
12+
},
1013
RootDatabase,
1114
};
1215
use itertools::Itertools;
1316
use stdx::format_to;
14-
use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
17+
use syntax::{
18+
algo, ast, match_ast, AstNode, AstToken, Direction, SyntaxKind::*, SyntaxToken, TokenAtOffset,
19+
T,
20+
};
1521

1622
use crate::{
1723
display::{macro_label, TryToNav},
@@ -118,8 +124,9 @@ pub(crate) fn hover(
118124
|d| d.defined(db),
119125
),
120126

121-
_ => ast::Comment::cast(token.clone())
122-
.and_then(|_| {
127+
_ => {
128+
if ast::Comment::cast(token.clone()).is_some() {
129+
cov_mark::hit!(no_highlight_on_comment_hover);
123130
let (attributes, def) = doc_attributes(&sema, &node)?;
124131
let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?;
125132
let (idl_range, link, ns) =
@@ -132,9 +139,13 @@ pub(crate) fn hover(
132139
}
133140
})?;
134141
range = Some(idl_range);
135-
resolve_doc_path_for_def(db, def, &link, ns)
136-
})
137-
.map(Definition::ModuleDef),
142+
resolve_doc_path_for_def(db, def, &link, ns).map(Definition::ModuleDef)
143+
} else if let res@Some(_) = try_hover_for_attribute(&token) {
144+
return res;
145+
} else {
146+
None
147+
}
148+
},
138149
}
139150
};
140151

@@ -168,11 +179,6 @@ pub(crate) fn hover(
168179
}
169180
}
170181

171-
if token.kind() == syntax::SyntaxKind::COMMENT {
172-
cov_mark::hit!(no_highlight_on_comment_hover);
173-
return None;
174-
}
175-
176182
if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) {
177183
return res;
178184
}
@@ -201,6 +207,51 @@ pub(crate) fn hover(
201207
Some(RangeInfo::new(range, res))
202208
}
203209

210+
fn try_hover_for_attribute(token: &SyntaxToken) -> Option<RangeInfo<HoverResult>> {
211+
let attr = token.ancestors().nth(1).and_then(ast::Attr::cast)?;
212+
let (path, tt) = attr.as_simple_call()?;
213+
if !tt.syntax().text_range().contains(token.text_range().start()) {
214+
return None;
215+
}
216+
let (is_clippy, lints) = match &*path {
217+
"feature" => (false, FEATURES),
218+
"allow" | "deny" | "forbid" | "warn" => {
219+
let is_clippy = algo::non_trivia_sibling(token.clone().into(), Direction::Prev)
220+
.filter(|t| t.kind() == T![:])
221+
.and_then(|t| algo::non_trivia_sibling(t, Direction::Prev))
222+
.filter(|t| t.kind() == T![:])
223+
.and_then(|t| algo::non_trivia_sibling(t, Direction::Prev))
224+
.map_or(false, |t| {
225+
t.kind() == T![ident] && t.into_token().map_or(false, |t| t.text() == "clippy")
226+
});
227+
if is_clippy {
228+
(true, CLIPPY_LINTS)
229+
} else {
230+
(false, DEFAULT_LINTS)
231+
}
232+
}
233+
_ => return None,
234+
};
235+
236+
let tmp;
237+
let needle = if is_clippy {
238+
tmp = format!("clippy::{}", token.text());
239+
&tmp
240+
} else {
241+
&*token.text()
242+
};
243+
244+
let lint =
245+
lints.binary_search_by_key(&needle, |lint| lint.label).ok().map(|idx| &lints[idx])?;
246+
Some(RangeInfo::new(
247+
token.text_range(),
248+
HoverResult {
249+
markup: Markup::from(format!("```\n{}\n```\n___\n\n{}", lint.label, lint.description)),
250+
..Default::default()
251+
},
252+
))
253+
}
254+
204255
fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
205256
fn to_action(nav_target: NavigationTarget) -> HoverAction {
206257
HoverAction::Implementation(FilePosition {
@@ -4004,4 +4055,74 @@ pub fn foo() {}
40044055
"#]],
40054056
)
40064057
}
4058+
4059+
#[test]
4060+
fn hover_feature() {
4061+
check(
4062+
r#"#![feature(box_syntax$0)]"#,
4063+
expect![[r##"
4064+
*box_syntax*
4065+
```
4066+
box_syntax
4067+
```
4068+
___
4069+
4070+
# `box_syntax`
4071+
4072+
The tracking issue for this feature is: [#49733]
4073+
4074+
[#49733]: https://github.com/rust-lang/rust/issues/49733
4075+
4076+
See also [`box_patterns`](box-patterns.md)
4077+
4078+
------------------------
4079+
4080+
Currently the only stable way to create a `Box` is via the `Box::new` method.
4081+
Also it is not possible in stable Rust to destructure a `Box` in a match
4082+
pattern. The unstable `box` keyword can be used to create a `Box`. An example
4083+
usage would be:
4084+
4085+
```rust
4086+
#![feature(box_syntax)]
4087+
4088+
fn main() {
4089+
let b = box 5;
4090+
}
4091+
```
4092+
4093+
"##]],
4094+
)
4095+
}
4096+
4097+
#[test]
4098+
fn hover_lint() {
4099+
check(
4100+
r#"#![allow(arithmetic_overflow$0)]"#,
4101+
expect![[r#"
4102+
*arithmetic_overflow*
4103+
```
4104+
arithmetic_overflow
4105+
```
4106+
___
4107+
4108+
arithmetic operation overflows
4109+
"#]],
4110+
)
4111+
}
4112+
4113+
#[test]
4114+
fn hover_clippy_lint() {
4115+
check(
4116+
r#"#![allow(clippy::almost_swapped$0)]"#,
4117+
expect![[r#"
4118+
*almost_swapped*
4119+
```
4120+
clippy::almost_swapped
4121+
```
4122+
___
4123+
4124+
Checks for `foo = bar; bar = foo` sequences.
4125+
"#]],
4126+
)
4127+
}
40074128
}

crates/ide_completion/src/completions/attribute.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,19 @@
33
//! This module uses a bit of static metadata to provide completions
44
//! for built-in attributes.
55
6+
use ide_db::helpers::generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES};
67
use once_cell::sync::Lazy;
78
use rustc_hash::{FxHashMap, FxHashSet};
89
use syntax::{algo::non_trivia_sibling, ast, AstNode, Direction, NodeOrToken, SyntaxKind, T};
910

1011
use crate::{
1112
context::CompletionContext,
12-
generated_lint_completions::{CLIPPY_LINTS, FEATURES},
1313
item::{CompletionItem, CompletionItemKind, CompletionKind},
1414
Completions,
1515
};
1616

1717
mod derive;
1818
mod lint;
19-
pub(crate) use self::lint::LintCompletion;
2019

2120
pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
2221
let attribute = ctx.attribute_under_caret.as_ref()?;
@@ -25,7 +24,7 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
2524
"derive" => derive::complete_derive(acc, ctx, token_tree),
2625
"feature" => lint::complete_lint(acc, ctx, token_tree, FEATURES),
2726
"allow" | "warn" | "deny" | "forbid" => {
28-
lint::complete_lint(acc, ctx, token_tree.clone(), lint::DEFAULT_LINT_COMPLETIONS);
27+
lint::complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINTS);
2928
lint::complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
3029
}
3130
_ => (),

0 commit comments

Comments
 (0)