Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide/src/hover/render.rs')
-rw-r--r--crates/ide/src/hover/render.rs109
1 files changed, 98 insertions, 11 deletions
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index d109c07691..47257f0bfa 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -2,7 +2,9 @@
use std::fmt::Display;
use either::Either;
-use hir::{AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo};
+use hir::{
+ Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo,
+};
use ide_db::{
base_db::SourceDatabase,
defs::Definition,
@@ -14,7 +16,9 @@ use ide_db::{
use itertools::Itertools;
use stdx::format_to;
use syntax::{
- algo, ast, match_ast, AstNode, Direction,
+ algo,
+ ast::{self, RecordPat},
+ match_ast, AstNode, Direction,
SyntaxKind::{LET_EXPR, LET_STMT},
SyntaxToken, T,
};
@@ -250,6 +254,50 @@ pub(super) fn keyword(
Some(HoverResult { markup, actions })
}
+/// Returns missing types in a record pattern.
+/// Only makes sense when there's a rest pattern in the record pattern.
+/// i.e. `let S {a, ..} = S {a: 1, b: 2}`
+pub(super) fn struct_rest_pat(
+ sema: &Semantics<'_, RootDatabase>,
+ config: &HoverConfig,
+ pattern: &RecordPat,
+) -> HoverResult {
+ let missing_fields = sema.record_pattern_missing_fields(pattern);
+
+ // if there are no missing fields, the end result is a hover that shows ".."
+ // should be left in to indicate that there are no more fields in the pattern
+ // example, S {a: 1, b: 2, ..} when struct S {a: u32, b: u32}
+
+ 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);
+ }
+ };
+ for (_, t) in &missing_fields {
+ walk_and_push_ty(sema.db, t, &mut push_new_def);
+ }
+
+ res.markup = {
+ let mut s = String::from(".., ");
+ for (f, _) in &missing_fields {
+ s += f.display(sema.db).to_string().as_ref();
+ s += ", ";
+ }
+ // get rid of trailing comma
+ s.truncate(s.len() - 2);
+
+ if config.markdown() {
+ Markup::fenced_block(&s)
+ } else {
+ s.into()
+ }
+ };
+ res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
+ res
+}
+
pub(super) fn try_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option<HoverResult> {
let (path, tt) = attr.as_simple_call()?;
if !tt.syntax().text_range().contains(token.text_range().start()) {
@@ -342,15 +390,35 @@ pub(super) fn definition(
let mod_path = definition_mod_path(db, &def);
let (label, docs) = match def {
Definition::Macro(it) => label_and_docs(db, it),
- Definition::Field(it) => label_and_docs(db, it),
+ Definition::Field(it) => label_and_layout_info_and_docs(db, it, |&it| {
+ let var_def = it.parent_def(db);
+ let id = it.index();
+ let layout = it.layout(db).ok()?;
+ let offset = match var_def {
+ hir::VariantDef::Struct(s) => Adt::from(s)
+ .layout(db)
+ .ok()
+ .map(|layout| format!(", offset = {}", layout.fields.offset(id).bytes())),
+ _ => None,
+ };
+ Some(format!(
+ "size = {}, align = {}{}",
+ layout.size.bytes(),
+ layout.align.abi.bytes(),
+ offset.as_deref().unwrap_or_default()
+ ))
+ }),
Definition::Module(it) => label_and_docs(db, it),
Definition::Function(it) => label_and_docs(db, it),
- Definition::Adt(it) => label_and_docs(db, it),
+ Definition::Adt(it) => label_and_layout_info_and_docs(db, it, |&it| {
+ let layout = it.layout(db).ok()?;
+ Some(format!("size = {}, align = {}", layout.size.bytes(), layout.align.abi.bytes()))
+ }),
Definition::Variant(it) => label_value_and_docs(db, it, |&it| {
if !it.parent_enum(db).is_data_carrying(db) {
match it.eval(db) {
- Ok(x) => Some(format!("{}", x)),
- Err(_) => it.value(db).map(|x| format!("{:?}", x)),
+ Ok(x) => Some(format!("{x}")),
+ Err(_) => it.value(db).map(|x| format!("{x:?}")),
}
} else {
None
@@ -359,7 +427,7 @@ pub(super) fn definition(
Definition::Const(it) => label_value_and_docs(db, it, |it| {
let body = it.eval(db);
match body {
- Ok(x) => Some(format!("{}", x)),
+ Ok(x) => Some(format!("{x}")),
Err(_) => {
let source = it.source(db)?;
let mut body = source.value.body()?.syntax().clone();
@@ -415,7 +483,7 @@ pub(super) fn definition(
fn render_builtin_attr(db: &RootDatabase, attr: hir::BuiltinAttr) -> Option<Markup> {
let name = attr.name(db);
- let desc = format!("#[{}]", name);
+ let desc = format!("#[{name}]");
let AttributeTemplate { word, list, name_value_str } = match attr.template(db) {
Some(template) => template,
@@ -443,6 +511,25 @@ where
(label, docs)
}
+fn label_and_layout_info_and_docs<D, E, V>(
+ db: &RootDatabase,
+ def: D,
+ value_extractor: E,
+) -> (String, Option<hir::Documentation>)
+where
+ D: HasAttrs + HirDisplay,
+ E: Fn(&D) -> Option<V>,
+ V: Display,
+{
+ let label = if let Some(value) = value_extractor(&def) {
+ format!("{} // {value}", def.display(db))
+ } else {
+ def.display(db).to_string()
+ };
+ let docs = def.attrs(db).docs();
+ (label, docs)
+}
+
fn label_value_and_docs<D, E, V>(
db: &RootDatabase,
def: D,
@@ -454,7 +541,7 @@ where
V: Display,
{
let label = if let Some(value) = value_extractor(&def) {
- format!("{} = {}", def.display(db), value)
+ format!("{} = {value}", def.display(db))
} else {
def.display(db).to_string()
};
@@ -518,9 +605,9 @@ fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
} else {
""
};
- format!("{}{}{}: {}", let_kw, is_mut, name, ty)
+ format!("{let_kw}{is_mut}{name}: {ty}")
}
- Either::Right(_) => format!("{}self: {}", is_mut, ty),
+ Either::Right(_) => format!("{is_mut}self: {ty}"),
};
markup(None, desc, None)
}