diff options
author | Lukas Wirth <[email protected]> | 2021-06-04 16:03:18 +0100 |
---|---|---|
committer | Lukas Wirth <[email protected]> | 2021-06-04 16:03:18 +0100 |
commit | 5d17b6a6873d530eda89d271807dcb70a811a200 (patch) | |
tree | 88b06d926b9311a214d937641990395c5af896de /crates/ide | |
parent | cd46255d7e8bb59b93a32d5cb50581f418ca5f3b (diff) |
Implement hover for lints
Diffstat (limited to 'crates/ide')
-rw-r--r-- | crates/ide/src/hover.rs | 90 |
1 files changed, 84 insertions, 6 deletions
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 04598cd06..4d91c5c4f 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -3,6 +3,7 @@ use hir::{ | |||
3 | AsAssocItem, AssocItemContainer, GenericParam, HasAttrs, HasSource, HirDisplay, InFile, Module, | 3 | AsAssocItem, AssocItemContainer, GenericParam, HasAttrs, HasSource, HirDisplay, InFile, Module, |
4 | ModuleDef, Semantics, | 4 | ModuleDef, Semantics, |
5 | }; | 5 | }; |
6 | use ide_completion::generated_lint_completions::{CLIPPY_LINTS, FEATURES}; | ||
6 | use ide_db::{ | 7 | use ide_db::{ |
7 | base_db::SourceDatabase, | 8 | base_db::SourceDatabase, |
8 | defs::{Definition, NameClass, NameRefClass}, | 9 | defs::{Definition, NameClass, NameRefClass}, |
@@ -11,7 +12,10 @@ use ide_db::{ | |||
11 | }; | 12 | }; |
12 | use itertools::Itertools; | 13 | use itertools::Itertools; |
13 | use stdx::format_to; | 14 | use stdx::format_to; |
14 | use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; | 15 | use syntax::{ |
16 | algo, ast, match_ast, AstNode, AstToken, Direction, SyntaxKind::*, SyntaxToken, TokenAtOffset, | ||
17 | T, | ||
18 | }; | ||
15 | 19 | ||
16 | use crate::{ | 20 | use crate::{ |
17 | display::{macro_label, TryToNav}, | 21 | display::{macro_label, TryToNav}, |
@@ -115,8 +119,8 @@ pub(crate) fn hover( | |||
115 | |d| d.defined(db), | 119 | |d| d.defined(db), |
116 | ), | 120 | ), |
117 | 121 | ||
118 | _ => ast::Comment::cast(token.clone()) | 122 | _ => { |
119 | .and_then(|_| { | 123 | if ast::Comment::cast(token.clone()).is_some() { |
120 | let (attributes, def) = doc_attributes(&sema, &node)?; | 124 | let (attributes, def) = doc_attributes(&sema, &node)?; |
121 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; | 125 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; |
122 | let (idl_range, link, ns) = | 126 | let (idl_range, link, ns) = |
@@ -129,9 +133,11 @@ pub(crate) fn hover( | |||
129 | } | 133 | } |
130 | })?; | 134 | })?; |
131 | range = Some(idl_range); | 135 | range = Some(idl_range); |
132 | resolve_doc_path_for_def(db, def, &link, ns) | 136 | resolve_doc_path_for_def(db, def, &link, ns).map(Definition::ModuleDef) |
133 | }) | 137 | } else { |
134 | .map(Definition::ModuleDef), | 138 | return try_hover_for_attribute(&token); |
139 | } | ||
140 | }, | ||
135 | } | 141 | } |
136 | }; | 142 | }; |
137 | 143 | ||
@@ -194,6 +200,40 @@ pub(crate) fn hover( | |||
194 | Some(RangeInfo::new(range, res)) | 200 | Some(RangeInfo::new(range, res)) |
195 | } | 201 | } |
196 | 202 | ||
203 | fn try_hover_for_attribute(token: &SyntaxToken) -> Option<RangeInfo<HoverResult>> { | ||
204 | let attr = token.ancestors().nth(1).and_then(ast::Attr::cast)?; | ||
205 | let (path, tt) = attr.as_simple_call()?; | ||
206 | if !tt.syntax().text_range().contains(token.text_range().start()) { | ||
207 | return None; | ||
208 | } | ||
209 | let lints = match &*path { | ||
210 | "feature" => FEATURES, | ||
211 | "allow" | "warn" | "forbid" | "error" => { | ||
212 | let is_clippy = algo::skip_trivia_token(token.clone(), Direction::Prev) | ||
213 | .filter(|t| t.kind() == T![::]) | ||
214 | .and_then(|t| algo::skip_trivia_token(t, Direction::Prev)) | ||
215 | .map_or(false, |t| t.kind() == T![ident] && t.text() == "clippy"); | ||
216 | if is_clippy { | ||
217 | CLIPPY_LINTS | ||
218 | } else { | ||
219 | &[] | ||
220 | } | ||
221 | } | ||
222 | _ => return None, | ||
223 | }; | ||
224 | let lint = lints | ||
225 | .binary_search_by_key(&token.text(), |lint| lint.label) | ||
226 | .ok() | ||
227 | .map(|idx| &FEATURES[idx])?; | ||
228 | Some(RangeInfo::new( | ||
229 | token.text_range(), | ||
230 | HoverResult { | ||
231 | markup: Markup::from(format!("```\n{}\n```\n___\n\n{}", lint.label, lint.description)), | ||
232 | ..Default::default() | ||
233 | }, | ||
234 | )) | ||
235 | } | ||
236 | |||
197 | fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { | 237 | fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { |
198 | fn to_action(nav_target: NavigationTarget) -> HoverAction { | 238 | fn to_action(nav_target: NavigationTarget) -> HoverAction { |
199 | HoverAction::Implementation(FilePosition { | 239 | HoverAction::Implementation(FilePosition { |
@@ -3977,4 +4017,42 @@ pub fn foo() {} | |||
3977 | "#]], | 4017 | "#]], |
3978 | ) | 4018 | ) |
3979 | } | 4019 | } |
4020 | |||
4021 | #[test] | ||
4022 | fn hover_feature() { | ||
4023 | check( | ||
4024 | r#"#![feature(box_syntax$0)]"#, | ||
4025 | expect![[r##" | ||
4026 | *box_syntax* | ||
4027 | ``` | ||
4028 | box_syntax | ||
4029 | ``` | ||
4030 | ___ | ||
4031 | |||
4032 | # `box_syntax` | ||
4033 | |||
4034 | The tracking issue for this feature is: [#49733] | ||
4035 | |||
4036 | [#49733]: https://github.com/rust-lang/rust/issues/49733 | ||
4037 | |||
4038 | See also [`box_patterns`](box-patterns.md) | ||
4039 | |||
4040 | ------------------------ | ||
4041 | |||
4042 | Currently the only stable way to create a `Box` is via the `Box::new` method. | ||
4043 | Also it is not possible in stable Rust to destructure a `Box` in a match | ||
4044 | pattern. The unstable `box` keyword can be used to create a `Box`. An example | ||
4045 | usage would be: | ||
4046 | |||
4047 | ```rust | ||
4048 | #![feature(box_syntax)] | ||
4049 | |||
4050 | fn main() { | ||
4051 | let b = box 5; | ||
4052 | } | ||
4053 | ``` | ||
4054 | |||
4055 | "##]], | ||
4056 | ) | ||
4057 | } | ||
3980 | } | 4058 | } |