aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/hover.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/hover.rs')
-rw-r--r--crates/ra_ide/src/hover.rs98
1 files changed, 24 insertions, 74 deletions
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index dfa0f1d97..7ba4bfcac 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -6,7 +6,6 @@ use ra_ide_db::{
6 RootDatabase, 6 RootDatabase,
7}; 7};
8use ra_syntax::{ 8use ra_syntax::{
9 algo::find_covering_element,
10 ast::{self, DocCommentsOwner}, 9 ast::{self, DocCommentsOwner},
11 match_ast, AstNode, 10 match_ast, AstNode,
12 SyntaxKind::*, 11 SyntaxKind::*,
@@ -16,7 +15,7 @@ use ra_syntax::{
16use crate::{ 15use crate::{
17 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, 16 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel},
18 references::classify_name_ref, 17 references::classify_name_ref,
19 FilePosition, FileRange, RangeInfo, 18 FilePosition, RangeInfo,
20}; 19};
21 20
22/// Contains the results when hovering over an item 21/// Contains the results when hovering over an item
@@ -174,23 +173,25 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
174 .ancestors() 173 .ancestors()
175 .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; 174 .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
176 175
177 // if this node is a MACRO_CALL, it means that `descend_into_macros` is failed to resolve. 176 let ty = match_ast! {
178 // (e.g expanding a builtin macro). So we give up here. 177 match node {
179 if node.kind() == MACRO_CALL { 178 ast::MacroCall(_it) => {
180 return None; 179 // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
181 } 180 // (e.g expanding a builtin macro). So we give up here.
182 181 return None;
183 // FIXME: Currently `hover::typeof` do not work inside 182 },
184 // macro expansion such that if the hover range is pointing to 183 ast::Expr(it) => {
185 // a string literal, the following type_of will return None. 184 sema.type_of_expr(&it)
186 // See also `test_hover_through_literal_string_in_macro` 185 },
187 let frange = sema.original_range(&node); 186 ast::Pat(it) => {
188 res.extend(type_of(db, frange).map(rust_code_markup)); 187 sema.type_of_pat(&it)
189 if res.is_empty() { 188 },
190 return None; 189 _ => None,
191 } 190 }
192 let range = node.text_range(); 191 }?;
193 192
193 res.extend(Some(rust_code_markup(ty.display_truncated(db, None).to_string())));
194 let range = sema.original_range(&node).range;
194 Some(RangeInfo::new(range, res)) 195 Some(RangeInfo::new(range, res))
195} 196}
196 197
@@ -206,33 +207,12 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
206 } 207 }
207} 208}
208 209
209pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> {
210 let sema = Semantics::new(db);
211 let source_file = sema.parse(frange.file_id);
212 let leaf_node = find_covering_element(source_file.syntax(), frange.range);
213 // if we picked identifier, expand to pattern/expression
214 let node = leaf_node
215 .ancestors()
216 .take_while(|it| it.text_range() == leaf_node.text_range())
217 .find(|it| ast::Expr::cast(it.clone()).is_some() || ast::Pat::cast(it.clone()).is_some())?;
218 let ty = if let Some(ty) = ast::Expr::cast(node.clone()).and_then(|e| sema.type_of_expr(&e)) {
219 ty
220 } else if let Some(ty) = ast::Pat::cast(node).and_then(|p| sema.type_of_pat(&p)) {
221 ty
222 } else {
223 return None;
224 };
225 Some(ty.display_truncated(db, None).to_string())
226}
227
228#[cfg(test)] 210#[cfg(test)]
229mod tests { 211mod tests {
230 use ra_db::FileLoader; 212 use ra_db::FileLoader;
231 use ra_syntax::TextRange; 213 use ra_syntax::TextRange;
232 214
233 use crate::mock_analysis::{ 215 use crate::mock_analysis::{analysis_and_position, single_file_with_position};
234 analysis_and_position, single_file_with_position, single_file_with_range,
235 };
236 216
237 fn trim_markup(s: &str) -> &str { 217 fn trim_markup(s: &str) -> &str {
238 s.trim_start_matches("```rust\n").trim_end_matches("\n```") 218 s.trim_start_matches("```rust\n").trim_end_matches("\n```")
@@ -525,37 +505,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
525 } 505 }
526 506
527 #[test] 507 #[test]
528 fn test_type_of_for_function() {
529 let (analysis, range) = single_file_with_range(
530 "
531 pub fn foo() -> u32 { 1 };
532
533 fn main() {
534 let foo_test = <|>foo()<|>;
535 }
536 ",
537 );
538
539 let type_name = analysis.type_of(range).unwrap().unwrap();
540 assert_eq!("u32", &type_name);
541 }
542
543 #[test]
544 fn test_type_of_for_expr() {
545 let (analysis, range) = single_file_with_range(
546 "
547 fn main() {
548 let foo: usize = 1;
549 let bar = <|>1 + foo<|>;
550 }
551 ",
552 );
553
554 let type_name = analysis.type_of(range).unwrap().unwrap();
555 assert_eq!("usize", &type_name);
556 }
557
558 #[test]
559 fn test_hover_infer_associated_method_result() { 508 fn test_hover_infer_associated_method_result() {
560 let (analysis, position) = single_file_with_position( 509 let (analysis, position) = single_file_with_position(
561 " 510 "
@@ -791,9 +740,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
791 740
792 #[test] 741 #[test]
793 fn test_hover_through_literal_string_in_macro() { 742 fn test_hover_through_literal_string_in_macro() {
794 // FIXME: Currently `hover::type_of` do not work inside 743 let hover_on = check_hover_result(
795 // macro expansion
796 check_hover_no_result(
797 r#" 744 r#"
798 //- /lib.rs 745 //- /lib.rs
799 macro_rules! arr { 746 macro_rules! arr {
@@ -804,7 +751,10 @@ fn func(foo: i32) { if true { <|>foo; }; }
804 let _ = arr!("Tr<|>acks", &mastered_for_itunes); 751 let _ = arr!("Tr<|>acks", &mastered_for_itunes);
805 } 752 }
806 "#, 753 "#,
754 &["&str"],
807 ); 755 );
756
757 assert_eq!(hover_on, "\"Tracks\"");
808 } 758 }
809 759
810 #[test] 760 #[test]