@@ -6,12 +6,18 @@ use hir::{
6
6
use ide_db:: {
7
7
base_db:: SourceDatabase ,
8
8
defs:: { Definition , NameClass , NameRefClass } ,
9
- helpers:: FamousDefs ,
9
+ helpers:: {
10
+ generated_lints:: { CLIPPY_LINTS , DEFAULT_LINTS , FEATURES } ,
11
+ FamousDefs ,
12
+ } ,
10
13
RootDatabase ,
11
14
} ;
12
15
use itertools:: Itertools ;
13
16
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
+ } ;
15
21
16
22
use crate :: {
17
23
display:: { macro_label, TryToNav } ,
@@ -118,8 +124,9 @@ pub(crate) fn hover(
118
124
|d| d. defined( db) ,
119
125
) ,
120
126
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) ;
123
130
let ( attributes, def) = doc_attributes( & sema, & node) ?;
124
131
let ( docs, doc_mapping) = attributes. docs_with_rangemap( db) ?;
125
132
let ( idl_range, link, ns) =
@@ -132,9 +139,13 @@ pub(crate) fn hover(
132
139
}
133
140
} ) ?;
134
141
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
+ } ,
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
182
if let res @ Some ( _) = hover_for_keyword ( & sema, links_in_hover, markdown, & token) {
177
183
return res;
178
184
}
@@ -201,6 +207,51 @@ pub(crate) fn hover(
201
207
Some ( RangeInfo :: new ( range, res) )
202
208
}
203
209
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
+
204
255
fn show_implementations_action ( db : & RootDatabase , def : Definition ) -> Option < HoverAction > {
205
256
fn to_action ( nav_target : NavigationTarget ) -> HoverAction {
206
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
}
0 commit comments