+mod render;
+
+#[cfg(test)]
+mod tests;
+
+use std::iter;
+
use either::Either;
-use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo};
+use hir::{HasSource, Semantics};
use ide_db::{
- base_db::{FileRange, SourceDatabase},
- defs::{Definition, NameClass, NameRefClass},
- helpers::{
- generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
- pick_best_token, try_resolve_derive_input_at, FamousDefs,
- },
- RootDatabase,
+ base_db::FileRange,
+ defs::Definition,
+ helpers::{pick_best_token, FamousDefs},
+ FxIndexSet, RootDatabase,
};
use itertools::Itertools;
-use stdx::format_to;
-use syntax::{
- algo, ast, display::fn_as_proc_macro_label, match_ast, AstNode, AstToken, Direction,
- SyntaxKind::*, SyntaxNode, SyntaxToken, T,
-};
+use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode, SyntaxToken, T};
use crate::{
- display::{macro_label, TryToNav},
- doc_links::{
- doc_attributes, extract_definitions_from_docs, remove_links, resolve_doc_path_for_def,
- rewrite_links,
- },
- markdown_remove::remove_markdown,
+ doc_links::token_as_doc_comment,
markup::Markup,
runnables::{runnable_fn, runnable_mod},
- FileId, FilePosition, NavigationTarget, RangeInfo, Runnable,
+ FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, TryToNav,
};
#[derive(Clone, Debug, PartialEq, Eq)]
.into_iter()
.filter_map(|it| {
Some(HoverGotoTypeData {
- mod_path: render_path(
+ mod_path: render::path(
db,
it.module(db)?,
it.name(db).map(|name| name.to_string()),
}
}
-#[derive(Debug, Clone, Eq, PartialEq)]
+#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct HoverGotoTypeData {
pub mod_path: String,
pub nav: NavigationTarget,
FileRange { file_id, range }: FileRange,
config: &HoverConfig,
) -> Option<RangeInfo<HoverResult>> {
- let sema = hir::Semantics::new(db);
+ let sema = &hir::Semantics::new(db);
let file = sema.parse(file_id).syntax().clone();
if !range.is_empty() {
- return hover_ranged(&file, range, &sema, config);
+ return hover_ranged(&file, range, sema, config);
}
let offset = range.start();
- let token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
+ let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3,
T!['('] | T![')'] => 2,
kind if kind.is_trivia() => 0,
_ => 1,
})?;
- let token = sema.descend_into_macros(token);
-
- let mut range_override = None;
- let node = token.parent()?;
- let definition = match_ast! {
- match node {
- // We don't use NameClass::referenced_or_defined here as we do not want to resolve
- // field pattern shorthands to their definition.
- ast::Name(name) => NameClass::classify(&sema, &name).map(|class| match class {
- NameClass::Definition(it) | NameClass::ConstReference(it) => it,
- NameClass::PatFieldShorthand { local_def, field_ref: _ } => Definition::Local(local_def),
- }),
- ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|class| match class {
- NameRefClass::Definition(def) => def,
- NameRefClass::FieldShorthand { local_ref: _, field_ref } => {
- Definition::Field(field_ref)
- }
- }),
- ast::Lifetime(lifetime) => NameClass::classify_lifetime(&sema, &lifetime).map_or_else(
- || {
- NameRefClass::classify_lifetime(&sema, &lifetime).and_then(|class| match class {
- NameRefClass::Definition(it) => Some(it),
- _ => None,
- })
- },
- NameClass::defined,
- ),
- _ => {
- 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_docs(&docs).into_iter().find_map(|(range, link, ns)| {
- let mapped = doc_mapping.map(range)?;
- (mapped.file_id == file_id.into() && mapped.value.contains(offset)).then(||(mapped.value, link, ns))
- })?;
- range_override = Some(idl_range);
- Some(match resolve_doc_path_for_def(db,def, &link,ns)? {
- Either::Left(it) => Definition::ModuleDef(it),
- Either::Right(it) => Definition::Macro(it),
- })
- } else if let Some(attr) = token.ancestors().find_map(ast::Attr::cast) {
- if let res@Some(_) = try_hover_for_lint(&attr, &token) {
- return res;
- } else {
- range_override = Some(token.text_range());
- try_resolve_derive_input_at(&sema, &attr, &token).map(Definition::Macro)
- }
- } else {
- None
- }
- },
- }
- };
-
- if let Some(definition) = definition {
- let famous_defs = match &definition {
- Definition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => {
- Some(FamousDefs(&sema, sema.scope(&node).krate()))
- }
- _ => None,
- };
- if let Some(markup) = hover_for_definition(db, definition, famous_defs.as_ref(), config) {
- let mut res = HoverResult::default();
- res.markup = process_markup(sema.db, definition, &markup, config);
- if let Some(action) = show_implementations_action(db, definition) {
- 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, file_id) {
- res.actions.push(action);
- }
+ if let Some(doc_comment) = token_as_doc_comment(&original_token) {
+ cov_mark::hit!(no_highlight_on_comment_hover);
+ return doc_comment.get_definition_with_descend_at(sema, offset, |def, node, range| {
+ let res = hover_for_definition(sema, file_id, def, &node, config)?;
+ Some(RangeInfo::new(range, res))
+ });
+ }
- if let Some(action) = goto_type_action_for_def(db, definition) {
- res.actions.push(action);
- }
+ let descended = sema.descend_into_macros(original_token.clone());
- let range = range_override.unwrap_or_else(|| sema.original_range(&node).range);
- return Some(RangeInfo::new(range, res));
- }
+ // FIXME: Definition should include known lints and the like instead of having this special case here
+ if let Some(res) = descended.iter().find_map(|token| {
+ let attr = token.ancestors().find_map(ast::Attr::cast)?;
+ render::try_for_lint(&attr, token)
+ }) {
+ return Some(RangeInfo::new(original_token.text_range(), res));
}
- if let res @ Some(_) = hover_for_keyword(&sema, config, &token) {
- return res;
+ let result = descended
+ .iter()
+ .filter_map(|token| {
+ let node = token.parent()?;
+ let defs = Definition::from_token(sema, token);
+ Some(defs.into_iter().zip(iter::once(node).cycle()))
+ })
+ .flatten()
+ .unique_by(|&(def, _)| def)
+ .filter_map(|(def, node)| hover_for_definition(sema, file_id, def, &node, config))
+ .reduce(|mut acc: HoverResult, HoverResult { markup, actions }| {
+ acc.actions.extend(actions);
+ acc.markup = Markup::from(format!("{}\n---\n{}", acc.markup, markup));
+ acc
+ });
+
+ if result.is_none() {
+ // fallbacks, show keywords or types
+ if let Some(res) = render::keyword(sema, config, &original_token) {
+ return Some(RangeInfo::new(original_token.text_range(), res));
+ }
+ if let res @ Some(_) =
+ descended.iter().find_map(|token| hover_type_fallback(sema, config, token))
+ {
+ return res;
+ }
}
+ result.map(|mut res: HoverResult| {
+ res.actions = dedupe_or_merge_hover_actions(res.actions);
+ RangeInfo::new(original_token.text_range(), res)
+ })
+}
- // No definition below cursor, fall back to showing type hovers.
+pub(crate) fn hover_for_definition(
+ sema: &Semantics<RootDatabase>,
+ file_id: FileId,
+ definition: Definition,
+ node: &SyntaxNode,
+ config: &HoverConfig,
+) -> Option<HoverResult> {
+ let famous_defs = match &definition {
+ Definition::BuiltinType(_) => Some(FamousDefs(sema, sema.scope(node).krate())),
+ _ => None,
+ };
+ if let Some(markup) = render::definition(sema.db, definition, famous_defs.as_ref(), config) {
+ let mut res = HoverResult::default();
+ res.markup = render::process_markup(sema.db, definition, &markup, config);
+ if let Some(action) = show_implementations_action(sema.db, definition) {
+ res.actions.push(action);
+ }
- let node = token
- .ancestors()
- .take_while(|it| !ast::Item::can_cast(it.kind()))
- .find(|n| ast::Expr::can_cast(n.kind()) || ast::Pat::can_cast(n.kind()))?;
+ if let Some(action) = show_fn_references_action(sema.db, definition) {
+ res.actions.push(action);
+ }
- let expr_or_pat = match_ast! {
- match node {
- ast::Expr(it) => Either::Left(it),
- ast::Pat(it) => Either::Right(it),
- // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
- // (e.g expanding a builtin macro). So we give up here.
- ast::MacroCall(_it) => return None,
- _ => return None,
+ if let Some(action) = runnable_action(sema, definition, file_id) {
+ res.actions.push(action);
}
- };
- let res = hover_type_info(&sema, config, &expr_or_pat)?;
- let range = sema.original_range(&node).range;
- Some(RangeInfo::new(range, res))
+ if let Some(action) = goto_type_action_for_def(sema.db, definition) {
+ res.actions.push(action);
+ }
+ return Some(res);
+ }
+ None
}
fn hover_ranged(
sema: &Semantics<RootDatabase>,
config: &HoverConfig,
) -> Option<RangeInfo<HoverResult>> {
- let expr = file.covering_element(range).ancestors().find_map(|it| {
+ let expr_or_pat = file.covering_element(range).ancestors().find_map(|it| {
match_ast! {
match it {
ast::Expr(expr) => Some(Either::Left(expr)),
}
}
})?;
- hover_type_info(sema, config, &expr).map(|it| {
- let range = match expr {
+ let res = match &expr_or_pat {
+ Either::Left(ast::Expr::TryExpr(try_expr)) => render::try_expr(sema, config, try_expr),
+ Either::Left(ast::Expr::PrefixExpr(prefix_expr))
+ if prefix_expr.op_kind() == Some(ast::UnaryOp::Deref) =>
+ {
+ render::deref_expr(sema, config, prefix_expr)
+ }
+ _ => None,
+ };
+ let res = res.or_else(|| render::type_info(sema, config, &expr_or_pat));
+ res.map(|it| {
+ let range = match expr_or_pat {
Either::Left(it) => it.syntax().text_range(),
Either::Right(it) => it.syntax().text_range(),
};
})
}
-fn hover_type_info(
+fn hover_type_fallback(
sema: &Semantics<RootDatabase>,
config: &HoverConfig,
- expr_or_pat: &Either<ast::Expr, ast::Pat>,
-) -> Option<HoverResult> {
- let TypeInfo { original, adjusted } = match expr_or_pat {
- Either::Left(expr) => sema.type_of_expr(expr)?,
- Either::Right(pat) => sema.type_of_pat(pat)?,
- };
-
- let mut res = HoverResult::default();
- let mut targets: Vec<hir::ModuleDef> = Vec::new();
- let mut push_new_def = |item: hir::ModuleDef| {
- if !targets.contains(&item) {
- targets.push(item);
- }
- };
- walk_and_push_ty(sema.db, &original, &mut push_new_def);
-
- res.markup = if let Some(adjusted_ty) = adjusted {
- walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def);
- let original = original.display(sema.db).to_string();
- let adjusted = adjusted_ty.display(sema.db).to_string();
- format!(
- "```text\nType: {:>apad$}\nCoerced to: {:>opad$}\n```\n",
- uncoerced = original,
- coerced = adjusted,
- // 6 base padding for static text prefix of each line
- apad = 6 + adjusted.len().max(original.len()),
- opad = original.len(),
- )
- .into()
- } else {
- if config.markdown() {
- Markup::fenced_block(&original.display(sema.db))
- } else {
- original.display(sema.db).to_string().into()
- }
- };
- res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
- Some(res)
-}
+ token: &SyntaxToken,
+) -> Option<RangeInfo<HoverResult>> {
+ let node = token
+ .ancestors()
+ .take_while(|it| !ast::Item::can_cast(it.kind()))
+ .find(|n| ast::Expr::can_cast(n.kind()) || ast::Pat::can_cast(n.kind()))?;
-fn try_hover_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option<RangeInfo<HoverResult>> {
- let (path, tt) = attr.as_simple_call()?;
- if !tt.syntax().text_range().contains(token.text_range().start()) {
- return None;
- }
- 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 {
- (true, CLIPPY_LINTS)
- } else {
- (false, DEFAULT_LINTS)
- }
+ let expr_or_pat = match_ast! {
+ match node {
+ ast::Expr(it) => Either::Left(it),
+ ast::Pat(it) => Either::Right(it),
+ // If this node is a MACRO_CALL, it means that `descend_into_macros_many` failed to resolve.
+ // (e.g expanding a builtin macro). So we give up here.
+ ast::MacroCall(_it) => return None,
+ _ => return None,
}
- _ => return None,
- };
-
- 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 {
- markup: Markup::from(format!("```\n{}\n```\n___\n\n{}", lint.label, lint.description)),
- ..Default::default()
- },
- ))
+ let res = render::type_info(sema, config, &expr_or_pat)?;
+ let range = sema.original_range(&node).range;
+ Some(RangeInfo::new(range, res))
}
fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
}
let adt = match def {
- Definition::ModuleDef(hir::ModuleDef::Trait(it)) => {
- return it.try_to_nav(db).map(to_action)
- }
- Definition::ModuleDef(hir::ModuleDef::Adt(it)) => Some(it),
+ Definition::Trait(it) => return it.try_to_nav(db).map(to_action),
+ Definition::Adt(it) => Some(it),
Definition::SelfType(it) => it.self_ty(db).as_adt(),
_ => None,
}?;
fn show_fn_references_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
match def {
- Definition::ModuleDef(hir::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(),
- })
+ Definition::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,
}
}
file_id: FileId,
) -> Option<HoverAction> {
match def {
- Definition::ModuleDef(it) => match it {
- hir::ModuleDef::Module(it) => runnable_mod(sema, it).map(HoverAction::Runnable),
- hir::ModuleDef::Function(func) => {
- let src = func.source(sema.db)?;
- if src.file_id != file_id.into() {
- cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment);
- cov_mark::hit!(hover_macro_generated_struct_fn_doc_attr);
- return None;
- }
-
- runnable_fn(sema, func).map(HoverAction::Runnable)
+ Definition::Module(it) => runnable_mod(sema, it).map(HoverAction::Runnable),
+ Definition::Function(func) => {
+ let src = func.source(sema.db)?;
+ if src.file_id != file_id.into() {
+ cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment);
+ cov_mark::hit!(hover_macro_generated_struct_fn_doc_attr);
+ return None;
}
- _ => None,
- },
+
+ runnable_fn(sema, func).map(HoverAction::Runnable)
+ }
_ => None,
}
}
Definition::Local(it) => it.ty(db),
Definition::GenericParam(hir::GenericParam::ConstParam(it)) => it.ty(db),
Definition::Field(field) => field.ty(db),
+ Definition::Function(function) => function.ret_type(db),
_ => return None,
};
});
}
-fn hover_markup(docs: Option<String>, desc: String, mod_path: Option<String>) -> Option<Markup> {
- let mut buf = String::new();
-
- if let Some(mod_path) = mod_path {
- if !mod_path.is_empty() {
- format_to!(buf, "```rust\n{}\n```\n\n", mod_path);
- }
- }
- format_to!(buf, "```rust\n{}\n```", desc);
-
- if let Some(doc) = docs {
- format_to!(buf, "\n___\n\n{}", doc);
- }
- Some(buf.into())
-}
-
-fn process_markup(
- db: &RootDatabase,
- def: Definition,
- markup: &Markup,
- config: &HoverConfig,
-) -> Markup {
- let markup = markup.as_str();
- let markup = if !config.markdown() {
- remove_markdown(markup)
- } else if config.links_in_hover {
- rewrite_links(db, markup, def)
- } else {
- remove_links(markup)
- };
- Markup::from(markup)
-}
-
-fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> {
- match def {
- Definition::Field(f) => Some(f.parent_def(db).name(db)),
- Definition::Local(l) => l.parent(db).name(db),
- Definition::ModuleDef(md) => match md {
- hir::ModuleDef::Function(f) => match f.as_assoc_item(db)?.container(db) {
- hir::AssocItemContainer::Trait(t) => Some(t.name(db)),
- hir::AssocItemContainer::Impl(i) => i.self_ty(db).as_adt().map(|adt| adt.name(db)),
- },
- hir::ModuleDef::Variant(e) => Some(e.parent_enum(db).name(db)),
- _ => None,
- },
- _ => None,
- }
- .map(|name| name.to_string())
-}
-
-fn render_path(db: &RootDatabase, module: hir::Module, item_name: Option<String>) -> String {
- let crate_name =
- db.crate_graph()[module.krate().into()].display_name.as_ref().map(|it| it.to_string());
- let module_path = module
- .path_to_root(db)
- .into_iter()
- .rev()
- .flat_map(|it| it.name(db).map(|name| name.to_string()));
- crate_name.into_iter().chain(module_path).chain(item_name).join("::")
-}
-
-fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
- if let Definition::GenericParam(_) = def {
- return None;
- }
- def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def)))
-}
+fn dedupe_or_merge_hover_actions(actions: Vec<HoverAction>) -> Vec<HoverAction> {
+ let mut deduped_actions = Vec::with_capacity(actions.len());
+ let mut go_to_type_targets = FxIndexSet::default();
-fn hover_for_definition(
- db: &RootDatabase,
- def: Definition,
- famous_defs: Option<&FamousDefs>,
- config: &HoverConfig,
-) -> Option<Markup> {
- let mod_path = definition_mod_path(db, &def);
- let (label, docs) = match def {
- Definition::Macro(it) => (
- match &it.source(db)?.value {
- Either::Left(mac) => macro_label(mac),
- Either::Right(mac_fn) => fn_as_proc_macro_label(mac_fn),
- },
- it.attrs(db).docs(),
- ),
- Definition::Field(def) => label_and_docs(db, def),
- Definition::ModuleDef(it) => match it {
- hir::ModuleDef::Module(it) => label_and_docs(db, it),
- hir::ModuleDef::Function(it) => label_and_docs(db, it),
- hir::ModuleDef::Adt(it) => label_and_docs(db, it),
- hir::ModuleDef::Variant(it) => label_and_docs(db, it),
- hir::ModuleDef::Const(it) => label_and_docs(db, it),
- hir::ModuleDef::Static(it) => label_and_docs(db, it),
- hir::ModuleDef::Trait(it) => label_and_docs(db, it),
- hir::ModuleDef::TypeAlias(it) => label_and_docs(db, it),
- hir::ModuleDef::BuiltinType(it) => {
- return famous_defs
- .and_then(|fd| hover_for_builtin(fd, it))
- .or_else(|| Some(Markup::fenced_block(&it.name())))
+ let mut seen_implementation = false;
+ let mut seen_reference = false;
+ let mut seen_runnable = false;
+ for action in actions {
+ match action {
+ HoverAction::GoToType(targets) => {
+ go_to_type_targets.extend(targets);
}
- },
- Definition::Local(it) => return hover_for_local(it, db),
- Definition::SelfType(impl_def) => {
- impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))?
- }
- Definition::GenericParam(it) => label_and_docs(db, it),
- Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db))),
- };
-
- return hover_markup(
- docs.filter(|_| config.documentation.is_some()).map(Into::into),
- label,
- mod_path,
- );
-
- fn label_and_docs<D>(db: &RootDatabase, def: D) -> (String, Option<hir::Documentation>)
- where
- D: HasAttrs + HirDisplay,
- {
- let label = def.display(db).to_string();
- let docs = def.attrs(db).docs();
- (label, docs)
- }
-}
-
-fn hover_for_local(it: hir::Local, db: &RootDatabase) -> Option<Markup> {
- let ty = it.ty(db);
- let ty = ty.display(db);
- let is_mut = if it.is_mut(db) { "mut " } else { "" };
- let desc = match it.source(db).value {
- Either::Left(ident) => {
- let name = it.name(db).unwrap();
- let let_kw = if ident
- .syntax()
- .parent()
- .map_or(false, |p| p.kind() == LET_STMT || p.kind() == CONDITION)
- {
- "let "
- } else {
- ""
- };
- format!("{}{}{}: {}", let_kw, is_mut, name, ty)
- }
- Either::Right(_) => format!("{}self: {}", is_mut, ty),
- };
- hover_markup(None, desc, None)
-}
-
-fn hover_for_keyword(
- sema: &Semantics<RootDatabase>,
- config: &HoverConfig,
- token: &SyntaxToken,
-) -> Option<RangeInfo<HoverResult>> {
- if !token.kind().is_keyword() || !config.documentation.is_some() {
- return None;
- }
- 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)?;
- let docs = doc_owner.attrs(sema.db).docs()?;
- let markup = process_markup(
- sema.db,
- Definition::ModuleDef(doc_owner.into()),
- &hover_markup(Some(docs.into()), token.text().into(), None)?,
- config,
- );
- Some(RangeInfo::new(token.text_range(), HoverResult { markup, actions: Default::default() }))
-}
-
-fn hover_for_builtin(famous_defs: &FamousDefs, builtin: hir::BuiltinType) -> Option<Markup> {
- // std exposes prim_{} modules with docstrings on the root to document the builtins
- let primitive_mod = format!("prim_{}", builtin.name());
- let doc_owner = find_std_module(famous_defs, &primitive_mod)?;
- let docs = doc_owner.attrs(famous_defs.0.db).docs()?;
- hover_markup(Some(docs.into()), builtin.name().to_string(), None)
-}
-
-fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module> {
- let db = famous_defs.0.db;
- let std_crate = famous_defs.std()?;
- let std_root_module = std_crate.root_module(db);
- std_root_module
- .children(db)
- .find(|module| module.name(db).map_or(false, |module| module.to_string() == name))
-}
-
-#[cfg(test)]
-mod tests {
- use expect_test::{expect, Expect};
- use ide_db::base_db::{FileLoader, FileRange};
- use syntax::TextRange;
-
- use crate::{fixture, hover::HoverDocFormat, HoverConfig};
-
- fn check_hover_no_result(ra_fixture: &str) {
- let (analysis, position) = fixture::position(ra_fixture);
- let hover = analysis
- .hover(
- &HoverConfig {
- links_in_hover: true,
- documentation: Some(HoverDocFormat::Markdown),
- },
- FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
- )
- .unwrap();
- assert!(hover.is_none());
- }
-
- fn check(ra_fixture: &str, expect: Expect) {
- let (analysis, position) = fixture::position(ra_fixture);
- let hover = analysis
- .hover(
- &HoverConfig {
- links_in_hover: true,
- documentation: Some(HoverDocFormat::Markdown),
- },
- FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
- )
- .unwrap()
- .unwrap();
-
- let content = analysis.db.file_text(position.file_id);
- let hovered_element = &content[hover.range];
-
- let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup);
- expect.assert_eq(&actual)
- }
-
- fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
- let (analysis, position) = fixture::position(ra_fixture);
- let hover = analysis
- .hover(
- &HoverConfig {
- links_in_hover: false,
- documentation: Some(HoverDocFormat::Markdown),
- },
- FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
- )
- .unwrap()
- .unwrap();
-
- let content = analysis.db.file_text(position.file_id);
- let hovered_element = &content[hover.range];
-
- let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup);
- expect.assert_eq(&actual)
- }
-
- fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) {
- let (analysis, position) = fixture::position(ra_fixture);
- let hover = analysis
- .hover(
- &HoverConfig {
- links_in_hover: true,
- documentation: Some(HoverDocFormat::PlainText),
- },
- FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
- )
- .unwrap()
- .unwrap();
-
- let content = analysis.db.file_text(position.file_id);
- let hovered_element = &content[hover.range];
-
- let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup);
- expect.assert_eq(&actual)
- }
-
- fn check_actions(ra_fixture: &str, expect: Expect) {
- let (analysis, file_id, position) = fixture::range_or_position(ra_fixture);
- let hover = analysis
- .hover(
- &HoverConfig {
- links_in_hover: true,
- documentation: Some(HoverDocFormat::Markdown),
- },
- FileRange { file_id, range: position.range_or_empty() },
- )
- .unwrap()
- .unwrap();
- expect.assert_debug_eq(&hover.info.actions)
- }
-
- fn check_hover_range(ra_fixture: &str, expect: Expect) {
- let (analysis, range) = fixture::range(ra_fixture);
- let hover = analysis
- .hover(
- &HoverConfig {
- links_in_hover: false,
- documentation: Some(HoverDocFormat::Markdown),
- },
- range,
- )
- .unwrap()
- .unwrap();
- expect.assert_eq(hover.info.markup.as_str())
- }
-
- fn check_hover_range_no_results(ra_fixture: &str) {
- let (analysis, range) = fixture::range(ra_fixture);
- let hover = analysis
- .hover(
- &HoverConfig {
- links_in_hover: false,
- documentation: Some(HoverDocFormat::Markdown),
- },
- range,
- )
- .unwrap();
- assert!(hover.is_none());
- }
-
- #[test]
- fn hover_shows_type_of_an_expression() {
- check(
- r#"
-pub fn foo() -> u32 { 1 }
-
-fn main() {
- let foo_test = foo()$0;
-}
-"#,
- expect![[r#"
- *foo()*
- ```rust
- u32
- ```
- "#]],
- );
- }
-
- #[test]
- fn hover_remove_markdown_if_configured() {
- check_hover_no_markdown(
- r#"
-pub fn foo() -> u32 { 1 }
-
-fn main() {
- let foo_test = foo()$0;
-}
-"#,
- expect![[r#"
- *foo()*
- u32
- "#]],
- );
- }
-
- #[test]
- fn hover_shows_long_type_of_an_expression() {
- check(
- r#"
-struct Scan<A, B, C> { a: A, b: B, c: C }
-struct Iter<I> { inner: I }
-enum Option<T> { Some(T), None }
-
-struct OtherStruct<T> { i: T }
-
-fn scan<A, B, C>(a: A, b: B, c: C) -> Iter<Scan<OtherStruct<A>, B, C>> {
- Iter { inner: Scan { a, b, c } }
-}
-
-fn main() {
- let num: i32 = 55;
- let closure = |memo: &mut u32, value: &u32, _another: &mut u32| -> Option<u32> {
- Option::Some(*memo + value)
- };
- let number = 5u32;
- let mut iter$0 = scan(OtherStruct { i: num }, closure, number);
-}
-"#,
- expect![[r#"
- *iter*
-
- ```rust
- let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>>
- ```
- "#]],
- );
- }
-
- #[test]
- fn hover_shows_fn_signature() {
- // Single file with result
- check(
- r#"
-pub fn foo() -> u32 { 1 }
-
-fn main() { let foo_test = fo$0o(); }
-"#,
- expect![[r#"
- *foo*
-
- ```rust
- test
- ```
-
- ```rust
- pub fn foo() -> u32
- ```
- "#]],
- );
-
- // Multiple candidates but results are ambiguous.
- check(
- r#"
-//- /a.rs
-pub fn foo() -> u32 { 1 }
-
-//- /b.rs
-pub fn foo() -> &str { "" }
-
-//- /c.rs
-pub fn foo(a: u32, b: u32) {}
-
-//- /main.rs
-mod a;
-mod b;
-mod c;
-
-fn main() { let foo_test = fo$0o(); }
- "#,
- expect![[r#"
- *foo*
- ```rust
- {unknown}
- ```
- "#]],
- );
- }
-
- #[test]
- fn hover_shows_fn_signature_with_type_params() {
- check(
- r#"
-pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str { }
-
-fn main() { let foo_test = fo$0o(); }
- "#,
- expect![[r#"
- *foo*
-
- ```rust
- test
- ```
-
- ```rust
- pub fn foo<'a, T>(b: &'a T) -> &'a str
- where
- T: AsRef<str>,
- ```
- "#]],
- );
- }
-
- #[test]
- fn hover_shows_fn_signature_on_fn_name() {
- check(
- r#"
-pub fn foo$0(a: u32, b: u32) -> u32 {}
-
-fn main() { }
-"#,
- expect![[r#"
- *foo*
-
- ```rust
- test
- ```
-
- ```rust
- pub fn foo(a: u32, b: u32) -> u32
- ```
- "#]],
- );
- }
-
- #[test]
- fn hover_shows_fn_doc() {
- check(
- r#"
-/// # Example
-/// ```
-/// # use std::path::Path;
-/// #
-/// foo(Path::new("hello, world!"))
-/// ```
-pub fn foo$0(_: &Path) {}
-
-fn main() { }
-"#,
- expect![[r##"
- *foo*
-
- ```rust
- test
- ```
-
- ```rust
- pub fn foo(_: &Path)
- ```
-
- ---
-
- # Example
-
- ```
- # use std::path::Path;
- #
- foo(Path::new("hello, world!"))
- ```
- "##]],
- );
- }
-
- #[test]
- fn hover_shows_fn_doc_attr_raw_string() {
- check(
- r##"
-#[doc = r#"Raw string doc attr"#]
-pub fn foo$0(_: &Path) {}
-
-fn main() { }
-"##,
- expect![[r##"
- *foo*
-
- ```rust
- test
- ```
-
- ```rust
- pub fn foo(_: &Path)
- ```
-
- ---
-
- Raw string doc attr
- "##]],
- );
- }
-
- #[test]
- fn hover_shows_struct_field_info() {
- // Hovering over the field when instantiating
- check(
- r#"
-struct Foo { field_a: u32 }
-
-fn main() {
- let foo = Foo { field_a$0: 0, };
-}
-"#,
- expect![[r#"
- *field_a*
-
- ```rust
- test::Foo
- ```
-
- ```rust
- field_a: u32
- ```
- "#]],
- );
-
- // Hovering over the field in the definition
- check(
- r#"
-struct Foo { field_a$0: u32 }
-
-fn main() {
- let foo = Foo { field_a: 0 };
-}
-"#,
- expect![[r#"
- *field_a*
-
- ```rust
- test::Foo
- ```
-
- ```rust
- field_a: u32
- ```
- "#]],
- );
- }
-
- #[test]
- fn hover_const_static() {
- check(
- r#"const foo$0: u32 = 123;"#,
- expect![[r#"
- *foo*
-
- ```rust
- test
- ```
-
- ```rust
- const foo: u32
- ```
- "#]],
- );
- check(
- r#"static foo$0: u32 = 456;"#,
- expect![[r#"
- *foo*
-
- ```rust
- test
- ```
-
- ```rust
- static foo: u32
- ```
- "#]],
- );
- }
-
- #[test]
- fn hover_default_generic_types() {
- check(
- r#"
-struct Test<K, T = u8> { k: K, t: T }
-
-fn main() {
- let zz$0 = Test { t: 23u8, k: 33 };
-}"#,
- expect![[r#"
- *zz*
-
- ```rust
- let zz: Test<i32, u8>
- ```
- "#]],
- );
+ HoverAction::Implementation(..) => {
+ if !seen_implementation {
+ seen_implementation = true;
+ deduped_actions.push(action);
+ }
+ }
+ HoverAction::Reference(..) => {
+ if !seen_reference {
+ seen_reference = true;
+ deduped_actions.push(action);
+ }
+ }
+ HoverAction::Runnable(..) => {
+ if !seen_runnable {
+ seen_runnable = true;
+ deduped_actions.push(action);
+ }
+ }
+ };
}
- #[test]
- fn hover_some() {
- check(
- r#"
-enum Option<T> { Some(T) }
-use Option::Some;
-
-fn main() { So$0me(12); }
-"#,
- expect![[r#"
- *Some*
-
- ```rust
- test::Option
- ```
-
- ```rust
- Some(T)
- ```
- "#]],
- );
-
- check(
- r#"
-enum Option<T> { Some(T) }
-use Option::Some;
-
-fn main() { let b$0ar = Some(12); }
-"#,
- expect![[r#"
- *bar*
-
- ```rust
- let bar: Option<i32>
- ```
- "#]],
- );
+ if !go_to_type_targets.is_empty() {
+ deduped_actions.push(HoverAction::GoToType(go_to_type_targets.into_iter().collect()));
}
- #[test]
- fn hover_enum_variant() {
- check(
- r#"
-enum Option<T> {
- /// The None variant
- Non$0e
-}
-"#,
- expect![[r#"
- *None*
-
- ```rust
- test::Option
- ```
-
- ```rust
- None
- ```
-
- ---
-
- The None variant
- "#]],
- );
-
- check(
- r#"
-enum Option<T> {
- /// The Some variant
- Some(T)
-}
-fn main() {
- let s = Option::Som$0e(12);
-}
-"#,
- expect![[r#"
- *Some*
-
- ```rust
- test::Option
- ```
-
- ```rust
- Some(T)
- ```
-
- ---
-
- The Some variant
- "#]],
- );
- }
-
- #[test]
- fn hover_for_local_variable() {
- check(
- r#"fn func(foo: i32) { fo$0o; }"#,
- expect![[r#"
- *foo*
-
- ```rust
- foo: i32
- ```
- "#]],
- )
- }
-
- #[test]
- fn hover_for_local_variable_pat() {
- check(
- r#"fn func(fo$0o: i32) {}"#,
- expect![[r#"
- *foo*
-
- ```rust
- foo: i32
- ```
- "#]],
- )
- }
-
- #[test]
- fn hover_local_var_edge() {
- check(
- r#"fn func(foo: i32) { if true { $0foo; }; }"#,
- expect![[r#"
- *foo*
-
- ```rust
- foo: i32
- ```
- "#]],
- )
- }
-
- #[test]
- fn hover_for_param_edge() {
- check(
- r#"fn func($0foo: i32) {}"#,
- expect![[r#"
- *foo*
-
- ```rust
- foo: i32
- ```
- "#]],
- )
- }
-
- #[test]
- fn hover_for_param_with_multiple_traits() {
- check(
- r#"
- //- minicore: sized
- trait Deref {
- type Target: ?Sized;
- }
- trait DerefMut {
- type Target: ?Sized;
- }
- fn f(_x$0: impl Deref<Target=u8> + DerefMut<Target=u8>) {}"#,
- expect![[r#"
- *_x*
-
- ```rust
- _x: impl Deref<Target = u8> + DerefMut<Target = u8>
- ```
- "#]],
- )
- }
-
- #[test]
- fn test_hover_infer_associated_method_result() {
- check(
- r#"
-struct Thing { x: u32 }
-
-impl Thing {
- fn new() -> Thing { Thing { x: 0 } }
-}
-
-fn main() { let foo_$0test = Thing::new(); }
-"#,
- expect![[r#"
- *foo_test*
-
- ```rust
- let foo_test: Thing
- ```
- "#]],
- )
- }
-
- #[test]
- fn test_hover_infer_associated_method_exact() {
- check(
- r#"
-mod wrapper {
- struct Thing { x: u32 }
-
- impl Thing {
- fn new() -> Thing { Thing { x: 0 } }
- }
-}
-
-fn main() { let foo_test = wrapper::Thing::new$0(); }
-"#,
- expect![[r#"
- *new*
-
- ```rust
- test::wrapper::Thing
- ```
-
- ```rust
- fn new() -> Thing
- ```
- "#]],
- )
- }
-
- #[test]
- fn test_hover_infer_associated_const_in_pattern() {
- check(
- r#"
-struct X;
-impl X {
- const C: u32 = 1;
-}
-
-fn main() {
- match 1 {
- X::C$0 => {},
- 2 => {},
- _ => {}
- };
-}
-"#,
- expect![[r#"
- *C*
-
- ```rust
- test
- ```
-
- ```rust
- const C: u32
- ```
- "#]],
- )
- }
-
- #[test]
- fn test_hover_self() {
- check(
- r#"
-struct Thing { x: u32 }
-impl Thing {
- fn new() -> Self { Self$0 { x: 0 } }
-}
-"#,
- expect![[r#"
- *Self*
-
- ```rust
- test
- ```
-
- ```rust
- struct Thing
- ```
- "#]],
- );
- check(
- r#"
-struct Thing { x: u32 }
-impl Thing {
- fn new() -> Self$0 { Self { x: 0 } }
-}
-"#,
- expect![[r#"
- *Self*
-
- ```rust
- test
- ```
-
- ```rust
- struct Thing
- ```
- "#]],
- );
- check(
- r#"
-enum Thing { A }
-impl Thing {
- pub fn new() -> Self$0 { Thing::A }
-}
-"#,
- expect![[r#"
- *Self*
-
- ```rust
- test
- ```
-
- ```rust
- enum Thing
- ```
- "#]],
- );
- check(
- r#"
- enum Thing { A }
- impl Thing {
- pub fn thing(a: Self$0) {}
- }
- "#,
- expect![[r#"
- *Self*
-
- ```rust
- test
- ```
-
- ```rust
- enum Thing
- ```
- "#]],
- );
- }
-
- #[test]
- fn test_hover_shadowing_pat() {
- check(
- r#"
-fn x() {}
-
-fn y() {
- let x = 0i32;
- x$0;
-}
-"#,
- expect![[r#"
- *x*
-
- ```rust
- let x: i32
- ```
- "#]],
- )
- }
-
- #[test]
- fn test_hover_macro_invocation() {
- check(
- r#"
-macro_rules! foo { () => {} }
-
-fn f() { fo$0o!(); }
-"#,
- expect![[r#"
- *foo*
-
- ```rust
- test
- ```
-
- ```rust
- macro_rules! foo
- ```
- "#]],
- )
- }
-
- #[test]
- fn test_hover_macro2_invocation() {
- check(
- r#"
-/// foo bar
-///
-/// foo bar baz
-macro foo() {}
-
-fn f() { fo$0o!(); }
-"#,
- expect![[r#"
- *foo*
-
- ```rust
- test
- ```
-
- ```rust
- macro foo
- ```
-
- ---
-
- foo bar
-
- foo bar baz
- "#]],
- )
- }
-
- #[test]
- fn test_hover_tuple_field() {
- check(
- r#"struct TS(String, i32$0);"#,
- expect![[r#"
- *i32*
-
- ```rust
- i32
- ```
- "#]],
- )
- }
-
- #[test]
- fn test_hover_through_macro() {
- check(
- r#"
-macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
-fn foo() {}
-id! {
- fn bar() { fo$0o(); }
-}
-"#,
- expect![[r#"
- *foo*
-
- ```rust
- test
- ```
-
- ```rust
- fn foo()
- ```
- "#]],
- );
- }
-
- #[test]
- fn test_hover_through_expr_in_macro() {
- check(
- r#"
-macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
-fn foo(bar:u32) { let a = id!(ba$0r); }
-"#,
- expect![[r#"
- *bar*
-
- ```rust
- bar: u32
- ```
- "#]],
- );
- }
-
- #[test]
- fn test_hover_through_expr_in_macro_recursive() {
- check(
- r#"
-macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } }
-macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
-fn foo(bar:u32) { let a = id!(ba$0r); }
-"#,
- expect![[r#"
- *bar*
-
- ```rust
- bar: u32
- ```
- "#]],
- );
- }
-
- #[test]
- fn test_hover_through_func_in_macro_recursive() {
- check(
- r#"
-macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } }
-macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
-fn bar() -> u32 { 0 }
-fn foo() { let a = id!([0u32, bar($0)] ); }
-"#,
- expect![[r#"
- *bar()*
- ```rust
- u32
- ```
- "#]],
- );
- }
-
- #[test]
- fn test_hover_through_literal_string_in_macro() {
- check(
- r#"
-macro_rules! arr { ($($tt:tt)*) => { [$($tt)*)] } }
-fn foo() {
- let mastered_for_itunes = "";
- let _ = arr!("Tr$0acks", &mastered_for_itunes);
-}
-"#,
- expect![[r#"
- *"Tracks"*
- ```rust
- &str
- ```
- "#]],
- );
- }
-
- #[test]
- fn test_hover_through_assert_macro() {
- check(
- r#"
-#[rustc_builtin_macro]
-macro_rules! assert {}
-
-fn bar() -> bool { true }
-fn foo() {
- assert!(ba$0r());
-}
-"#,
- expect![[r#"
- *bar*
-
- ```rust
- test
- ```
-
- ```rust
- fn bar() -> bool
- ```
- "#]],
- );
- }
-
- #[test]
- fn test_hover_through_literal_string_in_builtin_macro() {
- check_hover_no_result(
- r#"
- #[rustc_builtin_macro]
- macro_rules! format {}
-
- fn foo() {
- format!("hel$0lo {}", 0);
- }
-"#,
- );
- }
-
- #[test]
- fn test_hover_non_ascii_space_doc() {
- check(
- "
-/// <- `\u{3000}` here
-fn foo() { }
-
-fn bar() { fo$0o(); }
-",
- expect![[r#"
- *foo*
-
- ```rust
- test
- ```
-
- ```rust
- fn foo()
- ```
-
- ---
-
- \<- ` ` here
- "#]],
- );
- }
-
- #[test]
- fn test_hover_function_show_qualifiers() {
- check(
- r#"async fn foo$0() {}"#,
- expect![[r#"
- *foo*
-
- ```rust
- test
- ```
-
- ```rust
- async fn foo()
- ```
- "#]],
- );
- check(
- r#"pub const unsafe fn foo$0() {}"#,
- expect![[r#"
- *foo*
-
- ```rust
- test
- ```
-
- ```rust
- pub const unsafe fn foo()
- ```
- "#]],
- );
- // Top level `pub(crate)` will be displayed as no visibility.
- check(
- r#"mod m { pub(crate) async unsafe extern "C" fn foo$0() {} }"#,
- expect![[r#"
- *foo*
-
- ```rust
- test::m
- ```
-
- ```rust
- pub(crate) async unsafe extern "C" fn foo()
- ```
- "#]],
- );
- }
-
- #[test]
- fn test_hover_trait_show_qualifiers() {
- check_actions(
- r"unsafe trait foo$0() {}",
- expect![[r#"
- [
- Implementation(
- FilePosition {
- file_id: FileId(
- 0,
- ),
- offset: 13,
- },
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_extern_crate() {
- check(
- r#"
-//- /main.rs crate:main deps:std
-extern crate st$0d;
-//- /std/lib.rs crate:std
-//! Standard library for this test
-//!
-//! Printed?
-//! abc123
-"#,
- expect![[r#"
- *std*
-
- ```rust
- extern crate std
- ```
-
- ---
-
- Standard library for this test
-
- Printed?
- abc123
- "#]],
- );
- check(
- r#"
-//- /main.rs crate:main deps:std
-extern crate std as ab$0c;
-//- /std/lib.rs crate:std
-//! Standard library for this test
-//!
-//! Printed?
-//! abc123
-"#,
- expect![[r#"
- *abc*
-
- ```rust
- extern crate std
- ```
-
- ---
-
- Standard library for this test
-
- Printed?
- abc123
- "#]],
- );
- }
-
- #[test]
- fn test_hover_mod_with_same_name_as_function() {
- check(
- r#"
-use self::m$0y::Bar;
-mod my { pub struct Bar; }
-
-fn my() {}
-"#,
- expect![[r#"
- *my*
-
- ```rust
- test
- ```
-
- ```rust
- mod my
- ```
- "#]],
- );
- }
-
- #[test]
- fn test_hover_struct_doc_comment() {
- check(
- r#"
-/// This is an example
-/// multiline doc
-///
-/// # Example
-///
-/// ```
-/// let five = 5;
-///
-/// assert_eq!(6, my_crate::add_one(5));
-/// ```
-struct Bar;
-
-fn foo() { let bar = Ba$0r; }
-"#,
- expect![[r##"
- *Bar*
-
- ```rust
- test
- ```
-
- ```rust
- struct Bar
- ```
-
- ---
-
- This is an example
- multiline doc
-
- # Example
-
- ```
- let five = 5;
-
- assert_eq!(6, my_crate::add_one(5));
- ```
- "##]],
- );
- }
-
- #[test]
- fn test_hover_struct_doc_attr() {
- check(
- r#"
-#[doc = "bar docs"]
-struct Bar;
-
-fn foo() { let bar = Ba$0r; }
-"#,
- expect![[r#"
- *Bar*
-
- ```rust
- test
- ```
-
- ```rust
- struct Bar
- ```
-
- ---
-
- bar docs
- "#]],
- );
- }
-
- #[test]
- fn test_hover_struct_doc_attr_multiple_and_mixed() {
- check(
- r#"
-/// bar docs 0
-#[doc = "bar docs 1"]
-#[doc = "bar docs 2"]
-struct Bar;
-
-fn foo() { let bar = Ba$0r; }
-"#,
- expect![[r#"
- *Bar*
-
- ```rust
- test
- ```
-
- ```rust
- struct Bar
- ```
-
- ---
-
- bar docs 0
- bar docs 1
- bar docs 2
- "#]],
- );
- }
-
- #[test]
- fn test_hover_external_url() {
- check(
- r#"
-pub struct Foo;
-/// [external](https://www.google.com)
-pub struct B$0ar
-"#,
- expect![[r#"
- *Bar*
-
- ```rust
- test
- ```
-
- ```rust
- pub struct Bar
- ```
-
- ---
-
- [external](https://www.google.com)
- "#]],
- );
- }
-
- // Check that we don't rewrite links which we can't identify
- #[test]
- fn test_hover_unknown_target() {
- check(
- r#"
-pub struct Foo;
-/// [baz](Baz)
-pub struct B$0ar
-"#,
- expect![[r#"
- *Bar*
-
- ```rust
- test
- ```
-
- ```rust
- pub struct Bar
- ```
-
- ---
-
- [baz](Baz)
- "#]],
- );
- }
-
- #[test]
- fn test_hover_no_links() {
- check_hover_no_links(
- r#"
-/// Test cases:
-/// case 1. bare URL: https://www.example.com/
-/// case 2. inline URL with title: [example](https://www.example.com/)
-/// case 3. code reference: [`Result`]
-/// case 4. code reference but miss footnote: [`String`]
-/// case 5. autolink: <http://www.example.com/>
-/// case 6. email address: <test@example.com>
-/// case 7. reference: [example][example]
-/// case 8. collapsed link: [example][]
-/// case 9. shortcut link: [example]
-/// case 10. inline without URL: [example]()
-/// case 11. reference: [foo][foo]
-/// case 12. reference: [foo][bar]
-/// case 13. collapsed link: [foo][]
-/// case 14. shortcut link: [foo]
-/// case 15. inline without URL: [foo]()
-/// case 16. just escaped text: \[foo]
-/// case 17. inline link: [Foo](foo::Foo)
-///
-/// [`Result`]: ../../std/result/enum.Result.html
-/// [^example]: https://www.example.com/
-pub fn fo$0o() {}
-"#,
- expect![[r#"
- *foo*
-
- ```rust
- test
- ```
-
- ```rust
- pub fn foo()
- ```
-
- ---
-
- Test cases:
- case 1. bare URL: https://www.example.com/
- case 2. inline URL with title: [example](https://www.example.com/)
- case 3. code reference: `Result`
- case 4. code reference but miss footnote: `String`
- case 5. autolink: http://www.example.com/
- case 6. email address: test@example.com
- case 7. reference: example
- case 8. collapsed link: example
- case 9. shortcut link: example
- case 10. inline without URL: example
- case 11. reference: foo
- case 12. reference: foo
- case 13. collapsed link: foo
- case 14. shortcut link: foo
- case 15. inline without URL: foo
- case 16. just escaped text: \[foo\]
- case 17. inline link: Foo
-
- [^example]: https://www.example.com/
- "#]],
- );
- }
-
- #[test]
- fn test_hover_macro_generated_struct_fn_doc_comment() {
- cov_mark::check!(hover_macro_generated_struct_fn_doc_comment);
-
- check(
- r#"
-macro_rules! bar {
- () => {
- struct Bar;
- impl Bar {
- /// Do the foo
- fn foo(&self) {}
- }
- }
-}
-
-bar!();
-
-fn foo() { let bar = Bar; bar.fo$0o(); }
-"#,
- expect![[r#"
- *foo*
-
- ```rust
- test::Bar
- ```
-
- ```rust
- fn foo(&self)
- ```
-
- ---
-
- Do the foo
- "#]],
- );
- }
-
- #[test]
- fn test_hover_macro_generated_struct_fn_doc_attr() {
- cov_mark::check!(hover_macro_generated_struct_fn_doc_attr);
-
- check(
- r#"
-macro_rules! bar {
- () => {
- struct Bar;
- impl Bar {
- #[doc = "Do the foo"]
- fn foo(&self) {}
- }
- }
-}
-
-bar!();
-
-fn foo() { let bar = Bar; bar.fo$0o(); }
-"#,
- expect![[r#"
- *foo*
-
- ```rust
- test::Bar
- ```
-
- ```rust
- fn foo(&self)
- ```
-
- ---
-
- Do the foo
- "#]],
- );
- }
-
- #[test]
- fn test_hover_trait_has_impl_action() {
- check_actions(
- r#"trait foo$0() {}"#,
- expect![[r#"
- [
- Implementation(
- FilePosition {
- file_id: FileId(
- 0,
- ),
- offset: 6,
- },
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_struct_has_impl_action() {
- check_actions(
- r"struct foo$0() {}",
- expect![[r#"
- [
- Implementation(
- FilePosition {
- file_id: FileId(
- 0,
- ),
- offset: 7,
- },
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_union_has_impl_action() {
- check_actions(
- r#"union foo$0() {}"#,
- expect![[r#"
- [
- Implementation(
- FilePosition {
- file_id: FileId(
- 0,
- ),
- offset: 6,
- },
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_enum_has_impl_action() {
- check_actions(
- r"enum foo$0() { A, B }",
- expect![[r#"
- [
- Implementation(
- FilePosition {
- file_id: FileId(
- 0,
- ),
- offset: 5,
- },
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_self_has_impl_action() {
- check_actions(
- r#"struct foo where Self$0:;"#,
- expect![[r#"
- [
- Implementation(
- FilePosition {
- file_id: FileId(
- 0,
- ),
- offset: 7,
- },
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_test_has_action() {
- check_actions(
- r#"
-#[test]
-fn foo_$0test() {}
-"#,
- expect![[r#"
- [
- Reference(
- FilePosition {
- file_id: FileId(
- 0,
- ),
- offset: 11,
- },
- ),
- Runnable(
- Runnable {
- use_name_in_title: false,
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..24,
- focus_range: 11..19,
- name: "foo_test",
- kind: Function,
- },
- kind: Test {
- test_id: Path(
- "foo_test",
- ),
- attr: TestAttr {
- ignore: false,
- },
- },
- cfg: None,
- },
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_test_mod_has_action() {
- check_actions(
- r#"
-mod tests$0 {
- #[test]
- fn foo_test() {}
-}
-"#,
- expect![[r#"
- [
- Runnable(
- Runnable {
- use_name_in_title: false,
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..46,
- focus_range: 4..9,
- name: "tests",
- kind: Module,
- description: "mod tests",
- },
- kind: TestMod {
- path: "tests",
- },
- cfg: None,
- },
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_struct_has_goto_type_action() {
- check_actions(
- r#"
-struct S{ f1: u32 }
-
-fn main() { let s$0t = S{ f1:0 }; }
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::S",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..19,
- focus_range: 7..8,
- name: "S",
- kind: Struct,
- description: "struct S",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_generic_struct_has_goto_type_actions() {
- check_actions(
- r#"
-struct Arg(u32);
-struct S<T>{ f1: T }
-
-fn main() { let s$0t = S{ f1:Arg(0) }; }
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::S",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 17..37,
- focus_range: 24..25,
- name: "S",
- kind: Struct,
- description: "struct S<T>",
- },
- },
- HoverGotoTypeData {
- mod_path: "test::Arg",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..16,
- focus_range: 7..10,
- name: "Arg",
- kind: Struct,
- description: "struct Arg",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_generic_struct_has_flattened_goto_type_actions() {
- check_actions(
- r#"
-struct Arg(u32);
-struct S<T>{ f1: T }
-
-fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; }
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::S",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 17..37,
- focus_range: 24..25,
- name: "S",
- kind: Struct,
- description: "struct S<T>",
- },
- },
- HoverGotoTypeData {
- mod_path: "test::Arg",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..16,
- focus_range: 7..10,
- name: "Arg",
- kind: Struct,
- description: "struct Arg",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_tuple_has_goto_type_actions() {
- check_actions(
- r#"
-struct A(u32);
-struct B(u32);
-mod M {
- pub struct C(u32);
-}
-
-fn main() { let s$0t = (A(1), B(2), M::C(3) ); }
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::A",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..14,
- focus_range: 7..8,
- name: "A",
- kind: Struct,
- description: "struct A",
- },
- },
- HoverGotoTypeData {
- mod_path: "test::B",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 15..29,
- focus_range: 22..23,
- name: "B",
- kind: Struct,
- description: "struct B",
- },
- },
- HoverGotoTypeData {
- mod_path: "test::M::C",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 42..60,
- focus_range: 53..54,
- name: "C",
- kind: Struct,
- description: "pub struct C",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_return_impl_trait_has_goto_type_action() {
- check_actions(
- r#"
-trait Foo {}
-fn foo() -> impl Foo {}
-
-fn main() { let s$0t = foo(); }
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::Foo",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..12,
- focus_range: 6..9,
- name: "Foo",
- kind: Trait,
- description: "trait Foo",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_generic_return_impl_trait_has_goto_type_action() {
- check_actions(
- r#"
-trait Foo<T> {}
-struct S;
-fn foo() -> impl Foo<S> {}
-
-fn main() { let s$0t = foo(); }
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::Foo",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..15,
- focus_range: 6..9,
- name: "Foo",
- kind: Trait,
- description: "trait Foo<T>",
- },
- },
- HoverGotoTypeData {
- mod_path: "test::S",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 16..25,
- focus_range: 23..24,
- name: "S",
- kind: Struct,
- description: "struct S",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_return_impl_traits_has_goto_type_action() {
- check_actions(
- r#"
-trait Foo {}
-trait Bar {}
-fn foo() -> impl Foo + Bar {}
-
-fn main() { let s$0t = foo(); }
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::Foo",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..12,
- focus_range: 6..9,
- name: "Foo",
- kind: Trait,
- description: "trait Foo",
- },
- },
- HoverGotoTypeData {
- mod_path: "test::Bar",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 13..25,
- focus_range: 19..22,
- name: "Bar",
- kind: Trait,
- description: "trait Bar",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_generic_return_impl_traits_has_goto_type_action() {
- check_actions(
- r#"
-trait Foo<T> {}
-trait Bar<T> {}
-struct S1 {}
-struct S2 {}
-
-fn foo() -> impl Foo<S1> + Bar<S2> {}
-
-fn main() { let s$0t = foo(); }
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::Foo",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..15,
- focus_range: 6..9,
- name: "Foo",
- kind: Trait,
- description: "trait Foo<T>",
- },
- },
- HoverGotoTypeData {
- mod_path: "test::Bar",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 16..31,
- focus_range: 22..25,
- name: "Bar",
- kind: Trait,
- description: "trait Bar<T>",
- },
- },
- HoverGotoTypeData {
- mod_path: "test::S1",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 32..44,
- focus_range: 39..41,
- name: "S1",
- kind: Struct,
- description: "struct S1",
- },
- },
- HoverGotoTypeData {
- mod_path: "test::S2",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 45..57,
- focus_range: 52..54,
- name: "S2",
- kind: Struct,
- description: "struct S2",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_arg_impl_trait_has_goto_type_action() {
- check_actions(
- r#"
-trait Foo {}
-fn foo(ar$0g: &impl Foo) {}
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::Foo",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..12,
- focus_range: 6..9,
- name: "Foo",
- kind: Trait,
- description: "trait Foo",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_arg_impl_traits_has_goto_type_action() {
- check_actions(
- r#"
-trait Foo {}
-trait Bar<T> {}
-struct S{}
-
-fn foo(ar$0g: &impl Foo + Bar<S>) {}
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::Foo",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..12,
- focus_range: 6..9,
- name: "Foo",
- kind: Trait,
- description: "trait Foo",
- },
- },
- HoverGotoTypeData {
- mod_path: "test::Bar",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 13..28,
- focus_range: 19..22,
- name: "Bar",
- kind: Trait,
- description: "trait Bar<T>",
- },
- },
- HoverGotoTypeData {
- mod_path: "test::S",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 29..39,
- focus_range: 36..37,
- name: "S",
- kind: Struct,
- description: "struct S",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- 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 };
-}
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "core::future::Future",
- nav: NavigationTarget {
- file_id: FileId(
- 1,
- ),
- full_range: 253..435,
- focus_range: 292..298,
- name: "Future",
- kind: Trait,
- description: "pub trait Future",
- },
- },
- HoverGotoTypeData {
- mod_path: "test::S",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..9,
- focus_range: 7..8,
- name: "S",
- kind: Struct,
- description: "struct S",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_arg_generic_impl_trait_has_goto_type_action() {
- check_actions(
- r#"
-trait Foo<T> {}
-struct S {}
-fn foo(ar$0g: &impl Foo<S>) {}
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::Foo",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..15,
- focus_range: 6..9,
- name: "Foo",
- kind: Trait,
- description: "trait Foo<T>",
- },
- },
- HoverGotoTypeData {
- mod_path: "test::S",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 16..27,
- focus_range: 23..24,
- name: "S",
- kind: Struct,
- description: "struct S",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_dyn_return_has_goto_type_action() {
- check_actions(
- r#"
-trait Foo {}
-struct S;
-impl Foo for S {}
-
-struct B<T>{}
-fn foo() -> B<dyn Foo> {}
-
-fn main() { let s$0t = foo(); }
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::B",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 42..55,
- focus_range: 49..50,
- name: "B",
- kind: Struct,
- description: "struct B<T>",
- },
- },
- HoverGotoTypeData {
- mod_path: "test::Foo",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..12,
- focus_range: 6..9,
- name: "Foo",
- kind: Trait,
- description: "trait Foo",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_dyn_arg_has_goto_type_action() {
- check_actions(
- r#"
-trait Foo {}
-fn foo(ar$0g: &dyn Foo) {}
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::Foo",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..12,
- focus_range: 6..9,
- name: "Foo",
- kind: Trait,
- description: "trait Foo",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_generic_dyn_arg_has_goto_type_action() {
- check_actions(
- r#"
-trait Foo<T> {}
-struct S {}
-fn foo(ar$0g: &dyn Foo<S>) {}
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::Foo",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..15,
- focus_range: 6..9,
- name: "Foo",
- kind: Trait,
- description: "trait Foo<T>",
- },
- },
- HoverGotoTypeData {
- mod_path: "test::S",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 16..27,
- focus_range: 23..24,
- name: "S",
- kind: Struct,
- description: "struct S",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_goto_type_action_links_order() {
- check_actions(
- r#"
-trait ImplTrait<T> {}
-trait DynTrait<T> {}
-struct B<T> {}
-struct S {}
-
-fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::ImplTrait",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..21,
- focus_range: 6..15,
- name: "ImplTrait",
- kind: Trait,
- description: "trait ImplTrait<T>",
- },
- },
- HoverGotoTypeData {
- mod_path: "test::B",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 43..57,
- focus_range: 50..51,
- name: "B",
- kind: Struct,
- description: "struct B<T>",
- },
- },
- HoverGotoTypeData {
- mod_path: "test::DynTrait",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 22..42,
- focus_range: 28..36,
- name: "DynTrait",
- kind: Trait,
- description: "trait DynTrait<T>",
- },
- },
- HoverGotoTypeData {
- mod_path: "test::S",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 58..69,
- focus_range: 65..66,
- name: "S",
- kind: Struct,
- description: "struct S",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_associated_type_has_goto_type_action() {
- check_actions(
- r#"
-trait Foo {
- type Item;
- fn get(self) -> Self::Item {}
-}
-
-struct Bar{}
-struct S{}
-
-impl Foo for S { type Item = Bar; }
-
-fn test() -> impl Foo { S {} }
-
-fn main() { let s$0t = test().get(); }
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::Foo",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..62,
- focus_range: 6..9,
- name: "Foo",
- kind: Trait,
- description: "trait Foo",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_const_param_has_goto_type_action() {
- check_actions(
- r#"
-struct Bar;
-struct Foo<const BAR: Bar>;
-
-impl<const BAR: Bar> Foo<BAR$0> {}
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::Bar",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..11,
- focus_range: 7..10,
- name: "Bar",
- kind: Struct,
- description: "struct Bar",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_type_param_has_goto_type_action() {
- check_actions(
- r#"
-trait Foo {}
-
-fn foo<T: Foo>(t: T$0){}
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::Foo",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..12,
- focus_range: 6..9,
- name: "Foo",
- kind: Trait,
- description: "trait Foo",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn test_hover_self_has_go_to_type() {
- check_actions(
- r#"
-struct Foo;
-impl Foo {
- fn foo(&self$0) {}
-}
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::Foo",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..11,
- focus_range: 7..10,
- name: "Foo",
- kind: Struct,
- description: "struct Foo",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
-
- #[test]
- fn hover_displays_normalized_crate_names() {
- check(
- r#"
-//- /lib.rs crate:name-with-dashes
-pub mod wrapper {
- pub struct Thing { x: u32 }
-
- impl Thing {
- pub fn new() -> Thing { Thing { x: 0 } }
- }
-}
-
-//- /main.rs crate:main deps:name-with-dashes
-fn main() { let foo_test = name_with_dashes::wrapper::Thing::new$0(); }
-"#,
- expect![[r#"
- *new*
-
- ```rust
- name_with_dashes::wrapper::Thing
- ```
-
- ```rust
- pub fn new() -> Thing
- ```
- "#]],
- )
- }
-
- #[test]
- fn hover_field_pat_shorthand_ref_match_ergonomics() {
- check(
- r#"
-struct S {
- f: i32,
-}
-
-fn main() {
- let s = S { f: 0 };
- let S { f$0 } = &s;
-}
-"#,
- expect![[r#"
- *f*
-
- ```rust
- f: &i32
- ```
- "#]],
- );
- }
-
- #[test]
- fn hover_self_param_shows_type() {
- check(
- r#"
-struct Foo {}
-impl Foo {
- fn bar(&sel$0f) {}
-}
-"#,
- expect![[r#"
- *self*
-
- ```rust
- self: &Foo
- ```
- "#]],
- );
- }
-
- #[test]
- fn hover_self_param_shows_type_for_arbitrary_self_type() {
- check(
- r#"
-struct Arc<T>(T);
-struct Foo {}
-impl Foo {
- fn bar(sel$0f: Arc<Foo>) {}
-}
-"#,
- expect![[r#"
- *self*
-
- ```rust
- self: Arc<Foo>
- ```
- "#]],
- );
- }
-
- #[test]
- fn hover_doc_outer_inner() {
- check(
- r#"
-/// Be quick;
-mod Foo$0 {
- //! time is mana
-
- /// This comment belongs to the function
- fn foo() {}
-}
-"#,
- expect![[r#"
- *Foo*
-
- ```rust
- test
- ```
-
- ```rust
- mod Foo
- ```
-
- ---
-
- Be quick;
- time is mana
- "#]],
- );
- }
-
- #[test]
- fn hover_doc_outer_inner_attribue() {
- check(
- r#"
-#[doc = "Be quick;"]
-mod Foo$0 {
- #![doc = "time is mana"]
-
- #[doc = "This comment belongs to the function"]
- fn foo() {}
-}
-"#,
- expect![[r#"
- *Foo*
-
- ```rust
- test
- ```
-
- ```rust
- mod Foo
- ```
-
- ---
-
- Be quick;
- time is mana
- "#]],
- );
- }
-
- #[test]
- fn hover_doc_block_style_indentend() {
- check(
- r#"
-/**
- foo
- ```rust
- let x = 3;
- ```
-*/
-fn foo$0() {}
-"#,
- expect![[r#"
- *foo*
-
- ```rust
- test
- ```
-
- ```rust
- fn foo()
- ```
-
- ---
-
- foo
-
- ```rust
- let x = 3;
- ```
- "#]],
- );
- }
-
- #[test]
- fn hover_comments_dont_highlight_parent() {
- cov_mark::check!(no_highlight_on_comment_hover);
- check_hover_no_result(
- r#"
-fn no_hover() {
- // no$0hover
-}
-"#,
- );
- }
-
- #[test]
- fn hover_label() {
- check(
- r#"
-fn foo() {
- 'label$0: loop {}
-}
-"#,
- expect![[r#"
- *'label*
-
- ```rust
- 'label
- ```
- "#]],
- );
- }
-
- #[test]
- fn hover_lifetime() {
- check(
- r#"fn foo<'lifetime>(_: &'lifetime$0 ()) {}"#,
- expect![[r#"
- *'lifetime*
-
- ```rust
- 'lifetime
- ```
- "#]],
- );
- }
-
- #[test]
- fn hover_type_param() {
- check(
- r#"
-//- minicore: sized
-struct Foo<T>(T);
-trait Copy {}
-trait Clone {}
-impl<T: Copy + Clone> Foo<T$0> where T: Sized {}
-"#,
- expect![[r#"
- *T*
-
- ```rust
- T: Copy + Clone
- ```
- "#]],
- );
- check(
- r#"
-struct Foo<T>(T);
-impl<T> Foo<T$0> {}
-"#,
- expect![[r#"
- *T*
-
- ```rust
- T
- ```
- "#]],
- );
- // lifetimes bounds arent being tracked yet
- check(
- r#"
-struct Foo<T>(T);
-impl<T: 'static> Foo<T$0> {}
-"#,
- expect![[r#"
- *T*
-
- ```rust
- T
- ```
- "#]],
- );
- }
-
- #[test]
- fn hover_type_param_not_sized() {
- check(
- r#"
-//- minicore: sized
-struct Foo<T>(T);
-trait Copy {}
-trait Clone {}
-impl<T: Copy + Clone> Foo<T$0> where T: ?Sized {}
-"#,
- expect![[r#"
- *T*
-
- ```rust
- T: Copy + Clone + ?Sized
- ```
- "#]],
- );
- }
-
- #[test]
- fn hover_const_param() {
- check(
- r#"
-struct Foo<const LEN: usize>;
-impl<const LEN: usize> Foo<LEN$0> {}
-"#,
- expect![[r#"
- *LEN*
-
- ```rust
- const LEN: usize
- ```
- "#]],
- );
- }
-
- #[test]
- fn hover_const_pat() {
- check(
- r#"
-/// This is a doc
-const FOO: usize = 3;
-fn foo() {
- match 5 {
- FOO$0 => (),
- _ => ()
- }
-}
-"#,
- expect![[r#"
- *FOO*
-
- ```rust
- test
- ```
-
- ```rust
- const FOO: usize
- ```
-
- ---
-
- This is a doc
- "#]],
- );
- }
-
- #[test]
- fn hover_mod_def() {
- check(
- r#"
-//- /main.rs
-mod foo$0;
-//- /foo.rs
-//! For the horde!
-"#,
- expect![[r#"
- *foo*
-
- ```rust
- test
- ```
-
- ```rust
- mod foo
- ```
-
- ---
-
- For the horde!
- "#]],
- );
- }
-
- #[test]
- fn hover_self_in_use() {
- check(
- r#"
-//! This should not appear
-mod foo {
- /// But this should appear
- pub mod bar {}
-}
-use foo::bar::{self$0};
-"#,
- expect![[r#"
- *self*
-
- ```rust
- test::foo
- ```
-
- ```rust
- mod bar
- ```
-
- ---
-
- But this should appear
- "#]],
- )
- }
-
- #[test]
- fn hover_keyword() {
- check(
- 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*
-
- ```rust
- return
- ```
-
- ---
-
- Docs for return_keyword
- "#]],
- );
- }
-
- #[test]
- fn hover_builtin() {
- check(
- r#"
-//- /main.rs crate:main deps:std
-cosnt _: &str$0 = ""; }
-
-//- /libstd.rs crate:std
-/// Docs for prim_str
-mod prim_str {}
-"#,
- expect![[r#"
- *str*
-
- ```rust
- str
- ```
-
- ---
-
- Docs for prim_str
- "#]],
- );
- }
-
- #[test]
- fn hover_macro_expanded_function() {
- check(
- r#"
-struct S<'a, T>(&'a T);
-trait Clone {}
-macro_rules! foo {
- () => {
- fn bar<'t, T: Clone + 't>(s: &mut S<'t, T>, t: u32) -> *mut u32 where
- 't: 't + 't,
- for<'a> T: Clone + 'a
- { 0 as _ }
- };
-}
-
-foo!();
-
-fn main() {
- bar$0;
-}
-"#,
- expect![[r#"
- *bar*
-
- ```rust
- test
- ```
-
- ```rust
- fn bar<'t, T>(s: &mut S<'t, T>, t: u32) -> *mut u32
- where
- T: Clone + 't,
- 't: 't + 't,
- for<'a> T: Clone + 'a,
- ```
- "#]],
- )
- }
-
- #[test]
- fn hover_intra_doc_links() {
- check(
- r#"
-
-pub mod theitem {
- /// This is the item. Cool!
- pub struct TheItem;
-}
-
-/// Gives you a [`TheItem$0`].
-///
-/// [`TheItem`]: theitem::TheItem
-pub fn gimme() -> theitem::TheItem {
- theitem::TheItem
-}
-"#,
- expect![[r#"
- *[`TheItem`]*
-
- ```rust
- test::theitem
- ```
-
- ```rust
- pub struct TheItem
- ```
-
- ---
-
- This is the item. Cool!
- "#]],
- );
- }
-
- #[test]
- fn hover_generic_assoc() {
- check(
- r#"
-fn foo<T: A>() where T::Assoc$0: {}
-
-trait A {
- type Assoc;
-}"#,
- expect![[r#"
- *Assoc*
-
- ```rust
- test
- ```
-
- ```rust
- type Assoc
- ```
- "#]],
- );
- check(
- r#"
-fn foo<T: A>() {
- let _: <T>::Assoc$0;
-}
-
-trait A {
- type Assoc;
-}"#,
- expect![[r#"
- *Assoc*
-
- ```rust
- test
- ```
-
- ```rust
- type Assoc
- ```
- "#]],
- );
- check(
- r#"
-trait A where
- Self::Assoc$0: ,
-{
- type Assoc;
-}"#,
- expect![[r#"
- *Assoc*
-
- ```rust
- test
- ```
-
- ```rust
- type Assoc
- ```
- "#]],
- );
- }
-
- #[test]
- fn string_shadowed_with_inner_items() {
- check(
- r#"
-//- /main.rs crate:main deps:alloc
-
-/// Custom `String` type.
-struct String;
-
-fn f() {
- let _: String$0;
-
- fn inner() {}
-}
-
-//- /alloc.rs crate:alloc
-#[prelude_import]
-pub use string::*;
-
-mod string {
- /// This is `alloc::String`.
- pub struct String;
-}
-"#,
- expect![[r#"
- *String*
-
- ```rust
- main
- ```
-
- ```rust
- struct String
- ```
-
- ---
-
- Custom `String` type.
- "#]],
- )
- }
-
- #[test]
- fn function_doesnt_shadow_crate_in_use_tree() {
- check(
- r#"
-//- /main.rs crate:main deps:foo
-use foo$0::{foo};
-
-//- /foo.rs crate:foo
-pub fn foo() {}
-"#,
- expect![[r#"
- *foo*
-
- ```rust
- extern crate foo
- ```
- "#]],
- )
- }
-
- #[test]
- fn hover_feature() {
- check(
- r#"#![feature(box_syntax$0)]"#,
- expect![[r##"
- *box_syntax*
- ```
- box_syntax
- ```
- ___
-
- # `box_syntax`
-
- The tracking issue for this feature is: [#49733]
-
- [#49733]: https://github.com/rust-lang/rust/issues/49733
-
- See also [`box_patterns`](box-patterns.md)
-
- ------------------------
-
- Currently the only stable way to create a `Box` is via the `Box::new` method.
- Also it is not possible in stable Rust to destructure a `Box` in a match
- pattern. The unstable `box` keyword can be used to create a `Box`. An example
- usage would be:
-
- ```rust
- #![feature(box_syntax)]
-
- fn main() {
- let b = box 5;
- }
- ```
-
- "##]],
- )
- }
-
- #[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.
- "#]],
- )
- }
-
- #[test]
- fn hover_attr_path_qualifier() {
- cov_mark::check!(name_ref_classify_attr_path_qualifier);
- check(
- r#"
-//- /foo.rs crate:foo
-
-//- /lib.rs crate:main.rs deps:foo
-#[fo$0o::bar()]
-struct Foo;
-"#,
- expect![[r#"
- *foo*
-
- ```rust
- extern crate foo
- ```
- "#]],
- )
- }
-
- #[test]
- fn hover_rename() {
- check(
- r#"
-use self as foo$0;
-"#,
- expect![[r#"
- *foo*
-
- ```rust
- extern crate test
- ```
- "#]],
- );
- check(
- r#"
-mod bar {}
-use bar::{self as foo$0};
-"#,
- expect![[r#"
- *foo*
-
- ```rust
- test
- ```
-
- ```rust
- mod bar
- ```
- "#]],
- );
- check(
- r#"
-mod bar {
- use super as foo$0;
-}
-"#,
- expect![[r#"
- *foo*
-
- ```rust
- extern crate test
- ```
- "#]],
- );
- check(
- r#"
-use crate as foo$0;
-"#,
- expect![[r#"
- *foo*
-
- ```rust
- extern crate test
- ```
- "#]],
- );
- }
-
- #[test]
- fn hover_derive_input() {
- check(
- r#"
-#[rustc_builtin_macro]
-pub macro Copy {}
-#[derive(Copy$0)]
-struct Foo;
-"#,
- expect![[r#"
- *Copy*
-
- ```rust
- test
- ```
-
- ```rust
- pub macro Copy
- ```
- "#]],
- );
- check(
- r#"
-mod foo {
- #[rustc_builtin_macro]
- pub macro Copy {}
-}
-#[derive(foo::Copy$0)]
-struct Foo;
-"#,
- expect![[r#"
- *Copy*
-
- ```rust
- test
- ```
-
- ```rust
- pub macro Copy
- ```
- "#]],
- );
- }
-
- #[test]
- fn hover_range_math() {
- check_hover_range(
- r#"
-fn f() { let expr = $01 + 2 * 3$0 }
-"#,
- expect![[r#"
- ```rust
- i32
- ```"#]],
- );
-
- check_hover_range(
- r#"
-fn f() { let expr = 1 $0+ 2 * $03 }
-"#,
- expect![[r#"
- ```rust
- i32
- ```"#]],
- );
-
- check_hover_range(
- r#"
-fn f() { let expr = 1 + $02 * 3$0 }
-"#,
- expect![[r#"
- ```rust
- i32
- ```"#]],
- );
- }
-
- #[test]
- fn hover_range_arrays() {
- check_hover_range(
- r#"
-fn f() { let expr = $0[1, 2, 3, 4]$0 }
-"#,
- expect![[r#"
- ```rust
- [i32; 4]
- ```"#]],
- );
-
- check_hover_range(
- r#"
-fn f() { let expr = [1, 2, $03, 4]$0 }
-"#,
- expect![[r#"
- ```rust
- [i32; 4]
- ```"#]],
- );
-
- check_hover_range(
- r#"
-fn f() { let expr = [1, 2, $03$0, 4] }
-"#,
- expect![[r#"
- ```rust
- i32
- ```"#]],
- );
- }
-
- #[test]
- fn hover_range_functions() {
- check_hover_range(
- r#"
-fn f<T>(a: &[T]) { }
-fn b() { $0f$0(&[1, 2, 3, 4, 5]); }
-"#,
- expect![[r#"
- ```rust
- fn f<i32>(&[i32])
- ```"#]],
- );
-
- check_hover_range(
- r#"
-fn f<T>(a: &[T]) { }
-fn b() { f($0&[1, 2, 3, 4, 5]$0); }
-"#,
- expect![[r#"
- ```rust
- &[i32; 5]
- ```"#]],
- );
- }
-
- #[test]
- fn hover_range_shows_nothing_when_invalid() {
- check_hover_range_no_results(
- r#"
-fn f<T>(a: &[T]) { }
-fn b()$0 { f(&[1, 2, 3, 4, 5]); }$0
-"#,
- );
-
- check_hover_range_no_results(
- r#"
-fn f<T>$0(a: &[T]) { }
-fn b() { f(&[1, 2, 3,$0 4, 5]); }
-"#,
- );
-
- check_hover_range_no_results(
- r#"
-fn $0f() { let expr = [1, 2, 3, 4]$0 }
-"#,
- );
- }
-
- #[test]
- fn hover_range_shows_unit_for_statements() {
- check_hover_range(
- r#"
-fn f<T>(a: &[T]) { }
-fn b() { $0f(&[1, 2, 3, 4, 5]); }$0
-"#,
- expect![[r#"
- ```rust
- ()
- ```"#]],
- );
-
- check_hover_range(
- r#"
-fn f() { let expr$0 = $0[1, 2, 3, 4] }
-"#,
- expect![[r#"
- ```rust
- ()
- ```"#]],
- );
- }
-
- #[test]
- fn hover_range_for_pat() {
- check_hover_range(
- r#"
-fn foo() {
- let $0x$0 = 0;
-}
-"#,
- expect![[r#"
- ```rust
- i32
- ```"#]],
- );
-
- check_hover_range(
- r#"
-fn foo() {
- let $0x$0 = "";
-}
-"#,
- expect![[r#"
- ```rust
- &str
- ```"#]],
- );
- }
-
- #[test]
- fn hover_range_shows_coercions_if_applicable_expr() {
- check_hover_range(
- r#"
-fn foo() {
- let x: &u32 = $0&&&&&0$0;
-}
-"#,
- expect![[r#"
- ```text
- Type: &&&&&u32
- Coerced to: &u32
- ```
- "#]],
- );
- check_hover_range(
- r#"
-fn foo() {
- let x: *const u32 = $0&0$0;
-}
-"#,
- expect![[r#"
- ```text
- Type: &u32
- Coerced to: *const u32
- ```
- "#]],
- );
- }
-
- #[test]
- fn hover_range_shows_type_actions() {
- check_actions(
- r#"
-struct Foo;
-fn foo() {
- let x: &Foo = $0&&&&&Foo$0;
-}
-"#,
- expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::Foo",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..11,
- focus_range: 7..10,
- name: "Foo",
- kind: Struct,
- description: "struct Foo",
- },
- },
- ],
- ),
- ]
- "#]],
- );
- }
+ deduped_actions
}