AsAssocItem, AssocItemContainer, GenericParam, HasAttrs, HasSource, HirDisplay, InFile, Module,
ModuleDef, Semantics,
};
-use ide_completion::generated_lint_completions::{CLIPPY_LINTS, FEATURES};
use ide_db::{
base_db::SourceDatabase,
defs::{Definition, NameClass, NameRefClass},
- helpers::FamousDefs,
+ helpers::{
+ generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
+ FamousDefs,
+ },
RootDatabase,
};
use itertools::Itertools;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HoverConfig {
pub implementations: bool,
+ pub references: bool,
pub run: bool,
pub debug: bool,
pub goto_type_def: bool,
impl HoverConfig {
pub const NO_ACTIONS: Self = Self {
implementations: false,
+ references: false,
run: false,
debug: false,
goto_type_def: false,
};
pub fn any(&self) -> bool {
- self.implementations || self.runnable() || self.goto_type_def
+ self.implementations || self.references || self.runnable() || self.goto_type_def
}
pub fn none(&self) -> bool {
pub enum HoverAction {
Runnable(Runnable),
Implementation(FilePosition),
+ Reference(FilePosition),
GoToType(Vec<HoverGotoTypeData>),
}
_ => {
if ast::Comment::cast(token.clone()).is_some() {
+ cov_mark::hit!(no_highlight_on_comment_hover);
let (attributes, def) = doc_attributes(&sema, &node)?;
let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?;
let (idl_range, link, ns) =
extract_definitions_from_markdown(docs.as_str()).into_iter().find_map(|(range, link, ns)| {
- let InFile { file_id, value: range } = doc_mapping.map(range.clone())?;
+ let InFile { file_id, value: range } = doc_mapping.map(range)?;
if file_id == position.file_id.into() && range.contains(position.offset) {
Some((range, link, ns))
} else {
})?;
range = Some(idl_range);
resolve_doc_path_for_def(db, def, &link, ns).map(Definition::ModuleDef)
+ } else if let res@Some(_) = try_hover_for_attribute(&token) {
+ return res;
} else {
- return try_hover_for_attribute(&token);
+ None
}
},
}
res.actions.push(action);
}
+ if let Some(action) = show_fn_references_action(db, definition) {
+ res.actions.push(action);
+ }
+
if let Some(action) = runnable_action(&sema, definition, position.file_id) {
res.actions.push(action);
}
}
}
- if token.kind() == syntax::SyntaxKind::COMMENT {
- cov_mark::hit!(no_highlight_on_comment_hover);
- return None;
- }
-
if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) {
return res;
}
}
fn try_hover_for_attribute(token: &SyntaxToken) -> Option<RangeInfo<HoverResult>> {
- let attr = token.ancestors().nth(1).and_then(ast::Attr::cast)?;
+ let attr = token.ancestors().find_map(ast::Attr::cast)?;
let (path, tt) = attr.as_simple_call()?;
if !tt.syntax().text_range().contains(token.text_range().start()) {
return None;
}
- let lints = match &*path {
- "feature" => FEATURES,
- "allow" | "warn" | "forbid" | "error" => {
- let is_clippy = algo::skip_trivia_token(token.clone(), Direction::Prev)
- .filter(|t| t.kind() == T![::])
- .and_then(|t| algo::skip_trivia_token(t, Direction::Prev))
- .map_or(false, |t| t.kind() == T![ident] && t.text() == "clippy");
+ let (is_clippy, lints) = match &*path {
+ "feature" => (false, FEATURES),
+ "allow" | "deny" | "forbid" | "warn" => {
+ let is_clippy = algo::non_trivia_sibling(token.clone().into(), Direction::Prev)
+ .filter(|t| t.kind() == T![:])
+ .and_then(|t| algo::non_trivia_sibling(t, Direction::Prev))
+ .filter(|t| t.kind() == T![:])
+ .and_then(|t| algo::non_trivia_sibling(t, Direction::Prev))
+ .map_or(false, |t| {
+ t.kind() == T![ident] && t.into_token().map_or(false, |t| t.text() == "clippy")
+ });
if is_clippy {
- CLIPPY_LINTS
+ (true, CLIPPY_LINTS)
} else {
- &[]
+ (false, DEFAULT_LINTS)
}
}
_ => return None,
};
- let lint = lints
- .binary_search_by_key(&token.text(), |lint| lint.label)
- .ok()
- .map(|idx| &FEATURES[idx])?;
+
+ let tmp;
+ let needle = if is_clippy {
+ tmp = format!("clippy::{}", token.text());
+ &tmp
+ } else {
+ &*token.text()
+ };
+
+ let lint =
+ lints.binary_search_by_key(&needle, |lint| lint.label).ok().map(|idx| &lints[idx])?;
Some(RangeInfo::new(
token.text_range(),
HoverResult {
adt.try_to_nav(db).map(to_action)
}
+fn show_fn_references_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
+ match def {
+ Definition::ModuleDef(ModuleDef::Function(it)) => it.try_to_nav(db).map(|nav_target| {
+ HoverAction::Reference(FilePosition {
+ file_id: nav_target.file_id,
+ offset: nav_target.focus_or_full_range().start(),
+ })
+ }),
+ _ => None,
+ }
+}
+
fn runnable_action(
sema: &Semantics<RootDatabase>,
def: Definition,
) -> Option<HoverAction> {
match def {
Definition::ModuleDef(it) => match it {
- ModuleDef::Module(it) => runnable_mod(&sema, it).map(|it| HoverAction::Runnable(it)),
+ ModuleDef::Module(it) => runnable_mod(sema, it).map(HoverAction::Runnable),
ModuleDef::Function(func) => {
let src = func.source(sema.db)?;
if src.file_id != file_id.into() {
return None;
}
- runnable_fn(&sema, func).map(HoverAction::Runnable)
+ runnable_fn(sema, func).map(HoverAction::Runnable)
}
_ => None,
},
return match def {
Definition::Macro(it) => match &it.source(db)?.value {
Either::Left(mac) => {
- let label = macro_label(&mac);
+ let label = macro_label(mac);
from_def_source_labeled(db, it, Some(label), mod_path)
}
Either::Right(_) => {
if !token.kind().is_keyword() {
return None;
}
- let famous_defs = FamousDefs(&sema, sema.scope(&token.parent()?).krate());
+ let famous_defs = FamousDefs(sema, sema.scope(&token.parent()?).krate());
// std exposes {}_keyword modules with docstrings on the root to document keywords
let keyword_mod = format!("{}_keyword", token.text());
let doc_owner = find_std_module(&famous_defs, &keyword_mod)?;
use crate::fixture;
- use super::*;
-
fn check_hover_no_result(ra_fixture: &str) {
let (analysis, position) = fixture::position(ra_fixture);
assert!(analysis.hover(position, true, true).unwrap().is_none());
);
}
- #[ignore = "path based links currently only support documentation on ModuleDef items"]
#[test]
fn test_hover_path_link_field() {
+ // FIXME: Should be
+ // [Foo](https://docs.rs/test/*/test/struct.Foo.html)
check(
r#"
pub struct Foo;
---
- [Foo](https://docs.rs/test/*/test/struct.Foo.html)
+ [Foo](struct.Foo.html)
"#]],
);
}
"#,
expect![[r#"
[
+ Reference(
+ FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 11,
+ },
+ ),
Runnable(
Runnable {
nav: NavigationTarget {
fn test_hover_async_block_impl_trait_has_goto_type_action() {
check_actions(
r#"
+//- minicore: future
struct S;
fn foo() {
let fo$0o = async { S };
}
-
-#[prelude_import] use future::*;
-mod future {
- #[lang = "future_trait"]
- pub trait Future { type Output; }
-}
"#,
expect![[r#"
[
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::future::Future",
+ mod_path: "core::future::Future",
nav: NavigationTarget {
file_id: FileId(
- 0,
+ 1,
),
- full_range: 101..163,
- focus_range: 140..146,
+ full_range: 248..430,
+ focus_range: 287..293,
name: "Future",
kind: Trait,
description: "pub trait Future",
#[test]
fn hover_keyword() {
- let ra_fixture = r#"//- /main.rs crate:main deps:std
-fn f() { retur$0n; }"#;
- let fixture = format!("{}\n{}", ra_fixture, FamousDefs::FIXTURE);
check(
- &fixture,
+ r#"
+//- /main.rs crate:main deps:std
+fn f() { retur$0n; }
+//- /libstd.rs crate:std
+/// Docs for return_keyword
+mod return_keyword {}
+"#,
expect![[r#"
*return*
#[test]
fn hover_builtin() {
- let ra_fixture = r#"//- /main.rs crate:main deps:std
-cosnt _: &str$0 = ""; }"#;
- let fixture = format!("{}\n{}", ra_fixture, FamousDefs::FIXTURE);
check(
- &fixture,
+ r#"
+//- /main.rs crate:main deps:std
+cosnt _: &str$0 = ""; }
+
+//- /libstd.rs crate:std
+/// Docs for prim_str
+mod prim_str {}
+"#,
expect![[r#"
*str*
"##]],
)
}
+
+ #[test]
+ fn hover_lint() {
+ check(
+ r#"#![allow(arithmetic_overflow$0)]"#,
+ expect![[r#"
+ *arithmetic_overflow*
+ ```
+ arithmetic_overflow
+ ```
+ ___
+
+ arithmetic operation overflows
+ "#]],
+ )
+ }
+
+ #[test]
+ fn hover_clippy_lint() {
+ check(
+ r#"#![allow(clippy::almost_swapped$0)]"#,
+ expect![[r#"
+ *almost_swapped*
+ ```
+ clippy::almost_swapped
+ ```
+ ___
+
+ Checks for `foo = bar; bar = foo` sequences.
+ "#]],
+ )
+ }
}