@@ -6,12 +6,18 @@ use hir::{
66use 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} ;
1215use itertools:: Itertools ;
1316use 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
1622use 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+
204255fn 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}
0 commit comments