aboutsummaryrefslogtreecommitdiff
path: root/crates/ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide')
-rw-r--r--crates/ide/src/hover.rs145
1 files changed, 133 insertions, 12 deletions
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 455a27e48..ed4f18e1f 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -6,12 +6,18 @@ use hir::{
6use ide_db::{ 6use ide_db::{
7 base_db::SourceDatabase, 7 base_db::SourceDatabase,
8 defs::{Definition, NameClass, NameRefClass}, 8 defs::{Definition, NameClass, NameRefClass},
9 helpers::FamousDefs, 9 helpers::{
10 generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
11 FamousDefs,
12 },
10 RootDatabase, 13 RootDatabase,
11}; 14};
12use itertools::Itertools; 15use itertools::Itertools;
13use stdx::format_to; 16use stdx::format_to;
14use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 17use syntax::{
18 algo, ast, match_ast, AstNode, AstToken, Direction, SyntaxKind::*, SyntaxToken, TokenAtOffset,
19 T,
20};
15 21
16use crate::{ 22use crate::{
17 display::{macro_label, TryToNav}, 23 display::{macro_label, TryToNav},
@@ -118,8 +124,9 @@ pub(crate) fn hover(
118 |d| d.defined(db), 124 |d| d.defined(db),
119 ), 125 ),
120 126
121 _ => ast::Comment::cast(token.clone()) 127 _ => {
122 .and_then(|_| { 128 if ast::Comment::cast(token.clone()).is_some() {
129 cov_mark::hit!(no_highlight_on_comment_hover);
123 let (attributes, def) = doc_attributes(&sema, &node)?; 130 let (attributes, def) = doc_attributes(&sema, &node)?;
124 let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; 131 let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?;
125 let (idl_range, link, ns) = 132 let (idl_range, link, ns) =
@@ -132,9 +139,13 @@ pub(crate) fn hover(
132 } 139 }
133 })?; 140 })?;
134 range = Some(idl_range); 141 range = Some(idl_range);
135 resolve_doc_path_for_def(db, def, &link, ns) 142 resolve_doc_path_for_def(db, def, &link, ns).map(Definition::ModuleDef)
136 }) 143 } else if let res@Some(_) = try_hover_for_attribute(&token) {
137 .map(Definition::ModuleDef), 144 return res;
145 } else {
146 None
147 }
148 },
138 } 149 }
139 }; 150 };
140 151
@@ -168,11 +179,6 @@ pub(crate) fn hover(
168 } 179 }
169 } 180 }
170 181
171 if token.kind() == syntax::SyntaxKind::COMMENT {
172 cov_mark::hit!(no_highlight_on_comment_hover);
173 return None;
174 }
175
176 if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) { 182 if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) {
177 return res; 183 return res;
178 } 184 }
@@ -201,6 +207,51 @@ pub(crate) fn hover(
201 Some(RangeInfo::new(range, res)) 207 Some(RangeInfo::new(range, res))
202} 208}
203 209
210fn 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
204fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { 255fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
205 fn to_action(nav_target: NavigationTarget) -> HoverAction { 256 fn to_action(nav_target: NavigationTarget) -> HoverAction {
206 HoverAction::Implementation(FilePosition { 257 HoverAction::Implementation(FilePosition {
@@ -4004,4 +4055,74 @@ pub fn foo() {}
4004 "#]], 4055 "#]],
4005 ) 4056 )
4006 } 4057 }
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 }
4007} 4128}