Merge branch 'master' into sync-from-rust

This commit is contained in:
Laurențiu Nicola 2024-01-04 12:44:34 +02:00
commit a66777cc71
26 changed files with 741 additions and 382 deletions

View File

@ -622,7 +622,8 @@ impl ExprCollector<'_> {
ast::Expr::IndexExpr(e) => {
let base = self.collect_expr_opt(e.base());
let index = self.collect_expr_opt(e.index());
self.alloc_expr(Expr::Index { base, index }, syntax_ptr)
let is_assignee_expr = self.is_lowering_assignee_expr;
self.alloc_expr(Expr::Index { base, index, is_assignee_expr }, syntax_ptr)
}
ast::Expr::RangeExpr(e) => {
let lhs = e.start().map(|lhs| self.collect_expr(lhs));

View File

@ -376,7 +376,7 @@ impl Printer<'_> {
w!(self, ") ");
}
}
Expr::Index { base, index } => {
Expr::Index { base, index, is_assignee_expr: _ } => {
self.print_expr(*base);
w!(self, "[");
self.print_expr(*index);

View File

@ -265,6 +265,7 @@ pub enum Expr {
Index {
base: ExprId,
index: ExprId,
is_assignee_expr: bool,
},
Closure {
args: Box<[PatId]>,
@ -432,7 +433,7 @@ impl Expr {
f(rhs);
}
}
Expr::Index { base, index } => {
Expr::Index { base, index, .. } => {
f(*base);
f(*index);
}

View File

@ -390,7 +390,13 @@ fn parse_macro_expansion(
let expand_to = loc.expand_to();
let mbe::ValueResult { value: tt, err } = macro_expand(db, macro_file.macro_call_id, loc);
let (parse, rev_token_map) = token_tree_to_syntax_node(&tt, expand_to);
let (parse, rev_token_map) = token_tree_to_syntax_node(
match &tt {
CowArc::Arc(it) => it,
CowArc::Owned(it) => it,
},
expand_to,
);
ExpandResult { value: (parse, Arc::new(rev_token_map)), err }
}
@ -669,15 +675,20 @@ fn macro_expander(db: &dyn ExpandDatabase, id: MacroDefId) -> TokenExpander {
}
}
enum CowArc<T> {
Arc(Arc<T>),
Owned(T),
}
fn macro_expand(
db: &dyn ExpandDatabase,
macro_call_id: MacroCallId,
loc: MacroCallLoc,
) -> ExpandResult<Arc<tt::Subtree>> {
) -> ExpandResult<CowArc<tt::Subtree>> {
let _p = profile::span("macro_expand");
let ExpandResult { value: tt, mut err } = match loc.def.kind {
MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(macro_call_id),
MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(macro_call_id).map(CowArc::Arc),
MacroDefKind::BuiltInDerive(expander, ..) => {
let (root, map) = parse_with_map(db, loc.kind.file_id());
let root = root.syntax_node();
@ -692,7 +703,7 @@ fn macro_expand(
let ValueResult { value, err } = db.macro_arg(macro_call_id);
let Some((macro_arg, undo_info)) = value else {
return ExpandResult {
value: Arc::new(tt::Subtree {
value: CowArc::Owned(tt::Subtree {
delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
token_trees: Vec::new(),
}),
@ -718,7 +729,7 @@ fn macro_expand(
// As such we just return the input subtree here.
MacroDefKind::BuiltInEager(..) if loc.eager.is_none() => {
return ExpandResult {
value: macro_arg.clone(),
value: CowArc::Arc(macro_arg.clone()),
err: err.map(|err| {
let mut buf = String::new();
for err in &**err {
@ -752,12 +763,17 @@ fn macro_expand(
// Skip checking token tree limit for include! macro call
if !loc.def.is_include() {
// Set a hard limit for the expanded tt
if let Err(value) = check_tt_count(&tt, loc.call_site) {
return value;
if let Err(value) = check_tt_count(&tt) {
return value.map(|()| {
CowArc::Owned(tt::Subtree {
delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
token_trees: vec![],
})
});
}
}
ExpandResult { value: Arc::new(tt), err }
ExpandResult { value: CowArc::Owned(tt), err }
}
fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt::Subtree>> {
@ -796,8 +812,13 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<A
);
// Set a hard limit for the expanded tt
if let Err(value) = check_tt_count(&tt, loc.call_site) {
return value;
if let Err(value) = check_tt_count(&tt) {
return value.map(|()| {
Arc::new(tt::Subtree {
delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
token_trees: vec![],
})
});
}
fixup::reverse_fixups(&mut tt, &undo_info);
@ -819,14 +840,11 @@ fn token_tree_to_syntax_node(
mbe::token_tree_to_syntax_node(tt, entry_point)
}
fn check_tt_count(tt: &tt::Subtree, call_site: Span) -> Result<(), ExpandResult<Arc<tt::Subtree>>> {
fn check_tt_count(tt: &tt::Subtree) -> Result<(), ExpandResult<()>> {
let count = tt.count();
if TOKEN_LIMIT.check(count).is_err() {
Err(ExpandResult {
value: Arc::new(tt::Subtree {
delimiter: tt::Delimiter::invisible_spanned(call_site),
token_trees: vec![],
}),
value: (),
err: Some(ExpandError::other(format!(
"macro invocation exceeds token limit: produced {} tokens, limit is {}",
count,

View File

@ -220,6 +220,8 @@ pub enum MacroCallKind {
},
Attr {
ast_id: AstId<ast::Item>,
// FIXME: This is being interned, subtrees can very quickly differ just slightly causing
// leakage problems here
attr_args: Option<Arc<tt::Subtree>>,
/// Syntactical index of the invoking `#[attribute]`.
///

View File

@ -598,7 +598,7 @@ impl InferenceContext<'_> {
self.consume_expr(expr);
}
}
Expr::Index { base, index } => {
Expr::Index { base, index, is_assignee_expr: _ } => {
self.select_from_expr(*base);
self.consume_expr(*index);
}

View File

@ -744,7 +744,7 @@ impl InferenceContext<'_> {
(RangeOp::Inclusive, _, None) => self.err_ty(),
}
}
Expr::Index { base, index } => {
Expr::Index { base, index, is_assignee_expr } => {
let base_ty = self.infer_expr_inner(*base, &Expectation::none());
let index_ty = self.infer_expr(*index, &Expectation::none());
@ -772,11 +772,24 @@ impl InferenceContext<'_> {
.build();
self.write_method_resolution(tgt_expr, func, substs);
}
self.resolve_associated_type_with_params(
self_ty,
self.resolve_ops_index_output(),
&[index_ty.cast(Interner)],
)
let assoc = self.resolve_ops_index_output();
let res = self.resolve_associated_type_with_params(
self_ty.clone(),
assoc,
&[index_ty.clone().cast(Interner)],
);
if *is_assignee_expr {
if let Some(index_trait) = self.resolve_lang_trait(LangItem::IndexMut) {
let trait_ref = TyBuilder::trait_ref(self.db, index_trait)
.push(self_ty)
.fill(|_| index_ty.clone().cast(Interner))
.build();
self.push_obligation(trait_ref.cast(Interner));
}
}
res
} else {
self.err_ty()
}

View File

@ -96,7 +96,7 @@ impl InferenceContext<'_> {
Expr::RecordLit { path: _, fields, spread, ellipsis: _, is_assignee_expr: _ } => {
self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread))
}
&Expr::Index { base, index } => {
&Expr::Index { base, index, is_assignee_expr: _ } => {
if mutability == Mutability::Mut {
if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
if let Some(index_trait) = self

View File

@ -218,7 +218,7 @@ impl MirLowerCtx<'_> {
self.push_field_projection(&mut r, expr_id)?;
Ok(Some((r, current)))
}
Expr::Index { base, index } => {
Expr::Index { base, index, is_assignee_expr: _ } => {
let base_ty = self.expr_ty_after_adjustments(*base);
let index_ty = self.expr_ty_after_adjustments(*index);
if index_ty != TyBuilder::usize()

View File

@ -4506,3 +4506,50 @@ fn ttt() {
"#,
);
}
#[test]
fn infer_borrow() {
check_types(
r#"
//- minicore: index
pub struct SomeMap<K>;
pub trait Borrow<Borrowed: ?Sized> {
fn borrow(&self) -> &Borrowed;
}
impl<T: ?Sized> Borrow<T> for T {
fn borrow(&self) -> &T {
self
}
}
impl<T: ?Sized> Borrow<T> for &T {
fn borrow(&self) -> &T {
&**self
}
}
impl<K, KB: Borrow<K>> core::ops::Index<KB> for SomeMap<K> {
type Output = ();
fn index(&self, _: KB) -> &() {
&()
}
}
impl<K> core::ops::IndexMut<K> for SomeMap<K> {
fn index_mut(&mut self, _: K) -> &mut () {
&mut ()
}
}
fn foo() {
let mut map = SomeMap;
map["a"] = ();
map;
//^^^ SomeMap<&str>
}
"#,
);
}

View File

@ -1,5 +1,7 @@
//! Attributes & documentation for hir types.
use std::ops::ControlFlow;
use base_db::FileId;
use hir_def::{
attr::AttrsWithOwner,
@ -13,13 +15,13 @@ use hir_expand::{
name::Name,
span_map::{RealSpanMap, SpanMapRef},
};
use hir_ty::db::HirDatabase;
use hir_ty::{db::HirDatabase, method_resolution};
use syntax::{ast, AstNode};
use crate::{
Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl,
Field, Function, GenericParam, Impl, LifetimeParam, Macro, Module, ModuleDef, Static, Struct,
Trait, TraitAlias, TypeAlias, TypeParam, Union, Variant, VariantDef,
Field, Function, GenericParam, HasCrate, Impl, LifetimeParam, Macro, Module, ModuleDef, Static,
Struct, Trait, TraitAlias, Type, TypeAlias, TypeParam, Union, Variant, VariantDef,
};
pub trait HasAttrs {
@ -205,8 +207,14 @@ fn resolve_assoc_or_field(
}
};
// FIXME: Resolve associated items here, e.g. `Option::map`. Note that associated items take
// precedence over fields.
// Resolve inherent items first, then trait items, then fields.
if let Some(assoc_item_def) = resolve_assoc_item(db, &ty, &name, ns) {
return Some(assoc_item_def);
}
if let Some(impl_trait_item_def) = resolve_impl_trait_item(db, resolver, &ty, &name, ns) {
return Some(impl_trait_item_def);
}
let variant_def = match ty.as_adt()? {
Adt::Struct(it) => it.into(),
@ -216,6 +224,69 @@ fn resolve_assoc_or_field(
resolve_field(db, variant_def, name, ns)
}
fn resolve_assoc_item(
db: &dyn HirDatabase,
ty: &Type,
name: &Name,
ns: Option<Namespace>,
) -> Option<DocLinkDef> {
ty.iterate_assoc_items(db, ty.krate(db), move |assoc_item| {
if assoc_item.name(db)? != *name {
return None;
}
as_module_def_if_namespace_matches(assoc_item, ns)
})
}
fn resolve_impl_trait_item(
db: &dyn HirDatabase,
resolver: Resolver,
ty: &Type,
name: &Name,
ns: Option<Namespace>,
) -> Option<DocLinkDef> {
let canonical = ty.canonical();
let krate = ty.krate(db);
let environment = resolver.generic_def().map_or_else(
|| crate::TraitEnvironment::empty(krate.id).into(),
|d| db.trait_environment(d),
);
let traits_in_scope = resolver.traits_in_scope(db.upcast());
let mut result = None;
// `ty.iterate_path_candidates()` require a scope, which is not available when resolving
// attributes here. Use path resolution directly instead.
//
// FIXME: resolve type aliases (which are not yielded by iterate_path_candidates)
method_resolution::iterate_path_candidates(
&canonical,
db,
environment,
&traits_in_scope,
method_resolution::VisibleFromModule::None,
Some(name),
&mut |assoc_item_id| {
let assoc_item: AssocItem = assoc_item_id.into();
debug_assert_eq!(assoc_item.name(db).as_ref(), Some(name));
// If two traits in scope define the same item, Rustdoc links to no specific trait (for
// instance, given two methods `a`, Rustdoc simply links to `method.a` with no
// disambiguation) so we just pick the first one we find as well.
result = as_module_def_if_namespace_matches(assoc_item, ns);
if result.is_some() {
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
},
);
result
}
fn resolve_field(
db: &dyn HirDatabase,
def: VariantDef,
@ -228,6 +299,19 @@ fn resolve_field(
def.fields(db).into_iter().find(|f| f.name(db) == name).map(DocLinkDef::Field)
}
fn as_module_def_if_namespace_matches(
assoc_item: AssocItem,
ns: Option<Namespace>,
) -> Option<DocLinkDef> {
let (def, expected_ns) = match assoc_item {
AssocItem::Function(it) => (ModuleDef::Function(it), Namespace::Values),
AssocItem::Const(it) => (ModuleDef::Const(it), Namespace::Values),
AssocItem::TypeAlias(it) => (ModuleDef::TypeAlias(it), Namespace::Types),
};
(ns.unwrap_or(expected_ns) == expected_ns).then(|| DocLinkDef::ModuleDef(def))
}
fn modpath_from_str(db: &dyn HirDatabase, link: &str) -> Option<ModPath> {
// FIXME: this is not how we should get a mod path here.
let try_get_modpath = |link: &str| {

View File

@ -4121,6 +4121,10 @@ impl Type {
}
}
pub(crate) fn canonical(&self) -> Canonical<Ty> {
hir_ty::replace_errors_with_variables(&self.ty)
}
/// Returns types that this type dereferences to (including this type itself). The returned
/// iterator won't yield the same type more than once even if the deref chain contains a cycle.
pub fn autoderef(&self, db: &dyn HirDatabase) -> impl Iterator<Item = Type> + '_ {

View File

@ -7,17 +7,19 @@
use arrayvec::ArrayVec;
use hir::{
Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, DocLinkDef,
ExternCrateDecl, Field, Function, GenericParam, HasVisibility, Impl, Label, Local, Macro,
Module, ModuleDef, Name, PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias,
TypeAlias, Variant, Visibility,
Adt, AsAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType, Const, Crate,
DefWithBody, DeriveHelper, DocLinkDef, ExternCrateDecl, Field, Function, GenericParam,
HasVisibility, HirDisplay, Impl, Label, Local, Macro, Module, ModuleDef, Name, PathResolution,
Semantics, Static, ToolModule, Trait, TraitAlias, TypeAlias, Variant, VariantDef, Visibility,
};
use stdx::impl_from;
use stdx::{format_to, impl_from};
use syntax::{
ast::{self, AstNode},
match_ast, SyntaxKind, SyntaxNode, SyntaxToken,
};
use crate::documentation::{Documentation, HasDocs};
use crate::famous_defs::FamousDefs;
use crate::RootDatabase;
// FIXME: a more precise name would probably be `Symbol`?
@ -83,6 +85,13 @@ impl Definition {
Some(module)
}
pub fn enclosing_definition(&self, db: &RootDatabase) -> Option<Definition> {
match self {
Definition::Local(it) => it.parent(db).try_into().ok(),
_ => None,
}
}
pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> {
let vis = match self {
Definition::Field(sf) => sf.visibility(db),
@ -134,6 +143,125 @@ impl Definition {
};
Some(name)
}
pub fn docs(
&self,
db: &RootDatabase,
famous_defs: Option<&FamousDefs<'_, '_>>,
) -> Option<Documentation> {
let docs = match self {
Definition::Macro(it) => it.docs(db),
Definition::Field(it) => it.docs(db),
Definition::Module(it) => it.docs(db),
Definition::Function(it) => it.docs(db),
Definition::Adt(it) => it.docs(db),
Definition::Variant(it) => it.docs(db),
Definition::Const(it) => it.docs(db),
Definition::Static(it) => it.docs(db),
Definition::Trait(it) => it.docs(db),
Definition::TraitAlias(it) => it.docs(db),
Definition::TypeAlias(it) => it.docs(db),
Definition::BuiltinType(it) => {
famous_defs.and_then(|fd| {
// std exposes prim_{} modules with docstrings on the root to document the builtins
let primitive_mod = format!("prim_{}", it.name().display(fd.0.db));
let doc_owner = find_std_module(fd, &primitive_mod)?;
doc_owner.docs(fd.0.db)
})
}
Definition::Local(_) => None,
Definition::SelfType(impl_def) => {
impl_def.self_ty(db).as_adt().map(|adt| adt.docs(db))?
}
Definition::GenericParam(_) => None,
Definition::Label(_) => None,
Definition::ExternCrateDecl(it) => it.docs(db),
Definition::BuiltinAttr(it) => {
let name = it.name(db);
let AttributeTemplate { word, list, name_value_str } = it.template(db)?;
let mut docs = "Valid forms are:".to_owned();
if word {
format_to!(docs, "\n - #\\[{}]", name);
}
if let Some(list) = list {
format_to!(docs, "\n - #\\[{}({})]", name, list);
}
if let Some(name_value_str) = name_value_str {
format_to!(docs, "\n - #\\[{} = {}]", name, name_value_str);
}
Some(Documentation::new(docs.replace('*', "\\*")))
}
Definition::ToolModule(_) => None,
Definition::DeriveHelper(_) => None,
};
docs.or_else(|| {
// docs are missing, for assoc items of trait impls try to fall back to the docs of the
// original item of the trait
let assoc = self.as_assoc_item(db)?;
let trait_ = assoc.containing_trait_impl(db)?;
let name = Some(assoc.name(db)?);
let item = trait_.items(db).into_iter().find(|it| it.name(db) == name)?;
item.docs(db)
})
}
pub fn label(&self, db: &RootDatabase) -> Option<String> {
let label = match *self {
Definition::Macro(it) => it.display(db).to_string(),
Definition::Field(it) => it.display(db).to_string(),
Definition::Module(it) => it.display(db).to_string(),
Definition::Function(it) => it.display(db).to_string(),
Definition::Adt(it) => it.display(db).to_string(),
Definition::Variant(it) => it.display(db).to_string(),
Definition::Const(it) => it.display(db).to_string(),
Definition::Static(it) => it.display(db).to_string(),
Definition::Trait(it) => it.display(db).to_string(),
Definition::TraitAlias(it) => it.display(db).to_string(),
Definition::TypeAlias(it) => it.display(db).to_string(),
Definition::BuiltinType(it) => it.name().display(db).to_string(),
Definition::Local(it) => {
let ty = it.ty(db);
let ty = ty.display_truncated(db, None);
let is_mut = if it.is_mut(db) { "mut " } else { "" };
let desc = match it.primary_source(db).into_ident_pat() {
Some(ident) => {
let name = it.name(db);
let let_kw = if ident.syntax().parent().map_or(false, |p| {
p.kind() == SyntaxKind::LET_STMT || p.kind() == SyntaxKind::LET_EXPR
}) {
"let "
} else {
""
};
format!("{let_kw}{is_mut}{}: {ty}", name.display(db))
}
None => format!("{is_mut}self: {ty}"),
};
desc
}
Definition::SelfType(impl_def) => {
impl_def.self_ty(db).as_adt().and_then(|adt| Definition::Adt(adt).label(db))?
}
Definition::GenericParam(it) => it.display(db).to_string(),
Definition::Label(it) => it.name(db).display(db).to_string(),
Definition::ExternCrateDecl(it) => it.display(db).to_string(),
Definition::BuiltinAttr(it) => format!("#[{}]", it.name(db)),
Definition::ToolModule(it) => it.name(db).to_string(),
Definition::DeriveHelper(it) => format!("derive_helper {}", it.name(db).display(db)),
};
Some(label)
}
}
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();
std_root_module.children(db).find(|module| {
module.name(db).map_or(false, |module| module.display(db).to_string() == name)
})
}
// FIXME: IdentClass as a name no longer fits
@ -662,3 +790,22 @@ impl From<DocLinkDef> for Definition {
}
}
}
impl From<VariantDef> for Definition {
fn from(def: VariantDef) -> Self {
ModuleDef::from(def).into()
}
}
impl TryFrom<DefWithBody> for Definition {
type Error = ();
fn try_from(def: DefWithBody) -> Result<Self, Self::Error> {
match def {
DefWithBody::Function(it) => Ok(it.into()),
DefWithBody::Static(it) => Ok(it.into()),
DefWithBody::Const(it) => Ok(it.into()),
DefWithBody::Variant(it) => Ok(it.into()),
DefWithBody::InTypeConst(_) => Err(()),
}
}
}

View File

@ -462,14 +462,15 @@ mod module {}
fn doc_links_inherent_impl_items() {
check_doc_links(
r#"
// /// [`Struct::CONST`]
// /// [`Struct::function`]
/// FIXME #9694
/// [`Struct::CONST`]
/// [`Struct::function`]
struct Struct$0;
impl Struct {
const CONST: () = ();
// ^^^^^ Struct::CONST
fn function() {}
// ^^^^^^^^ Struct::function
}
"#,
)
@ -482,12 +483,13 @@ fn doc_links_trait_impl_items() {
trait Trait {
type Type;
const CONST: usize;
// ^^^^^ Struct::CONST
fn function();
// ^^^^^^^^ Struct::function
}
// /// [`Struct::Type`]
// /// [`Struct::CONST`]
// /// [`Struct::function`]
/// FIXME #9694
// FIXME #9694: [`Struct::Type`]
/// [`Struct::CONST`]
/// [`Struct::function`]
struct Struct$0;
impl Trait for Struct {

View File

@ -1,15 +1,12 @@
//! Logic for rendering the different hover messages
use std::fmt::Display;
use either::Either;
use hir::{
Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasSource, HirDisplay, Layout, LayoutError,
Semantics, TypeInfo,
Adt, AsAssocItem, CaptureKind, HasSource, HirDisplay, Layout, LayoutError, Semantics, TypeInfo,
};
use ide_db::{
base_db::SourceDatabase,
defs::Definition,
documentation::{Documentation, HasDocs},
documentation::HasDocs,
famous_defs::FamousDefs,
generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
syntax_helpers::insert_whitespace_into_node,
@ -20,9 +17,7 @@ use stdx::format_to;
use syntax::{
algo,
ast::{self, RecordPat},
match_ast, AstNode, Direction,
SyntaxKind::{LET_EXPR, LET_STMT},
SyntaxToken, T,
match_ast, AstNode, Direction, SyntaxToken, T,
};
use crate::{
@ -393,48 +388,23 @@ pub(super) fn definition(
config: &HoverConfig,
) -> Option<Markup> {
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_layout_info_and_docs(
db,
it,
config,
|&it| it.layout(db),
|_| {
let var_def = it.parent_def(db);
match var_def {
hir::VariantDef::Struct(s) => {
Adt::from(s).layout(db).ok().and_then(|layout| layout.field_offset(it))
let label = def.label(db)?;
let docs = def.docs(db, famous_defs);
let value = match def {
Definition::Variant(it) => {
if !it.parent_enum(db).is_data_carrying(db) {
match it.eval(db) {
Ok(it) => {
Some(if it >= 10 { format!("{it} ({it:#X})") } else { format!("{it}") })
}
_ => None,
Err(_) => it.value(db).map(|it| format!("{it:?}")),
}
},
),
Definition::Module(it) => label_and_docs(db, it),
Definition::Function(it) => label_and_docs(db, it),
Definition::Adt(it) => {
label_and_layout_info_and_docs(db, it, config, |&it| it.layout(db), |_| None)
} else {
None
}
}
Definition::Variant(it) => label_value_and_layout_info_and_docs(
db,
it,
config,
|&it| {
if !it.parent_enum(db).is_data_carrying(db) {
match it.eval(db) {
Ok(it) => {
Some(if it >= 10 { format!("{it} ({it:#X})") } else { format!("{it}") })
}
Err(_) => it.value(db).map(|it| format!("{it:?}")),
}
} else {
None
}
},
|it| it.layout(db),
|layout| layout.enum_tag_size(),
),
Definition::Const(it) => label_value_and_docs(db, it, |it| {
Definition::Const(it) => {
let body = it.render_eval(db);
match body {
Ok(it) => Some(it),
@ -447,53 +417,59 @@ pub(super) fn definition(
Some(body.to_string())
}
}
}),
Definition::Static(it) => label_value_and_docs(db, it, |it| {
}
Definition::Static(it) => {
let source = it.source(db)?;
let mut body = source.value.body()?.syntax().clone();
if source.file_id.is_macro() {
body = insert_whitespace_into_node::insert_ws_into(body);
}
Some(body.to_string())
}),
Definition::Trait(it) => label_and_docs(db, it),
Definition::TraitAlias(it) => label_and_docs(db, it),
Definition::TypeAlias(it) => {
label_and_layout_info_and_docs(db, it, config, |&it| it.ty(db).layout(db), |_| None)
}
Definition::BuiltinType(it) => {
return famous_defs
.and_then(|fd| builtin(fd, it))
.or_else(|| Some(Markup::fenced_block(&it.name().display(db))))
}
Definition::Local(it) => return local(db, it, config),
Definition::SelfType(impl_def) => {
impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))?
}
Definition::GenericParam(it) => (it.display(db).to_string(), None),
Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db).display(db))),
Definition::ExternCrateDecl(it) => label_and_docs(db, it),
// FIXME: We should be able to show more info about these
Definition::BuiltinAttr(it) => return render_builtin_attr(db, it),
Definition::ToolModule(it) => return Some(Markup::fenced_block(&it.name(db))),
Definition::DeriveHelper(it) => {
(format!("derive_helper {}", it.name(db).display(db)), None)
}
_ => None,
};
let docs = docs
.filter(|_| config.documentation)
.or_else(|| {
// docs are missing, for assoc items of trait impls try to fall back to the docs of the
// original item of the trait
let assoc = def.as_assoc_item(db)?;
let trait_ = assoc.containing_trait_impl(db)?;
let name = Some(assoc.name(db)?);
let item = trait_.items(db).into_iter().find(|it| it.name(db) == name)?;
item.docs(db)
})
.map(Into::into);
markup(docs, label, mod_path)
let layout_info = match def {
Definition::Field(it) => render_memory_layout(
config.memory_layout,
|| it.layout(db),
|_| {
let var_def = it.parent_def(db);
match var_def {
hir::VariantDef::Struct(s) => {
Adt::from(s).layout(db).ok().and_then(|layout| layout.field_offset(it))
}
_ => None,
}
},
|_| None,
),
Definition::Adt(it) => {
render_memory_layout(config.memory_layout, || it.layout(db), |_| None, |_| None)
}
Definition::Variant(it) => render_memory_layout(
config.memory_layout,
|| it.layout(db),
|_| None,
|layout| layout.enum_tag_size(),
),
Definition::TypeAlias(it) => {
render_memory_layout(config.memory_layout, || it.ty(db).layout(db), |_| None, |_| None)
}
Definition::Local(it) => {
render_memory_layout(config.memory_layout, || it.ty(db).layout(db), |_| None, |_| None)
}
_ => None,
};
let label = match (value, layout_info) {
(Some(value), Some(layout_info)) => format!("{label} = {value}{layout_info}"),
(Some(value), None) => format!("{label} = {value}"),
(None, Some(layout_info)) => format!("{label}{layout_info}"),
(None, None) => label,
};
markup(docs.map(Into::into), label, mod_path)
}
fn type_info(
@ -595,114 +571,16 @@ fn closure_ty(
Some(res)
}
fn render_builtin_attr(db: &RootDatabase, attr: hir::BuiltinAttr) -> Option<Markup> {
let name = attr.name(db);
let desc = format!("#[{name}]");
let AttributeTemplate { word, list, name_value_str } = match attr.template(db) {
Some(template) => template,
None => return Some(Markup::fenced_block(&attr.name(db))),
};
let mut docs = "Valid forms are:".to_owned();
if word {
format_to!(docs, "\n - #\\[{}]", name);
}
if let Some(list) = list {
format_to!(docs, "\n - #\\[{}({})]", name, list);
}
if let Some(name_value_str) = name_value_str {
format_to!(docs, "\n - #\\[{} = {}]", name, name_value_str);
}
markup(Some(docs.replace('*', "\\*")), desc, None)
}
fn label_and_docs<D>(db: &RootDatabase, def: D) -> (String, Option<Documentation>)
where
D: HasDocs + HirDisplay,
{
let label = def.display(db).to_string();
let docs = def.docs(db);
(label, docs)
}
fn label_and_layout_info_and_docs<D, E, E2>(
db: &RootDatabase,
def: D,
config: &HoverConfig,
layout_extractor: E,
layout_offset_extractor: E2,
) -> (String, Option<Documentation>)
where
D: HasDocs + HirDisplay,
E: Fn(&D) -> Result<Layout, LayoutError>,
E2: Fn(&Layout) -> Option<u64>,
{
let mut label = def.display(db).to_string();
if let Some(layout) = render_memory_layout(
config.memory_layout,
|| layout_extractor(&def),
layout_offset_extractor,
|_| None,
) {
format_to!(label, "{layout}");
}
let docs = def.docs(db);
(label, docs)
}
fn label_value_and_layout_info_and_docs<D, E, E2, E3, V>(
db: &RootDatabase,
def: D,
config: &HoverConfig,
value_extractor: E,
layout_extractor: E2,
layout_tag_extractor: E3,
) -> (String, Option<Documentation>)
where
D: HasDocs + HirDisplay,
E: Fn(&D) -> Option<V>,
E2: Fn(&D) -> Result<Layout, LayoutError>,
E3: Fn(&Layout) -> Option<usize>,
V: Display,
{
let value = value_extractor(&def);
let mut label = match value {
Some(value) => format!("{} = {value}", def.display(db)),
None => def.display(db).to_string(),
};
if let Some(layout) = render_memory_layout(
config.memory_layout,
|| layout_extractor(&def),
|_| None,
layout_tag_extractor,
) {
format_to!(label, "{layout}");
}
let docs = def.docs(db);
(label, docs)
}
fn label_value_and_docs<D, E, V>(
db: &RootDatabase,
def: D,
value_extractor: E,
) -> (String, Option<Documentation>)
where
D: HasDocs + 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.docs(db);
(label, docs)
}
fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
if let Definition::GenericParam(_) = def {
if matches!(
def,
Definition::GenericParam(_)
| Definition::BuiltinType(_)
| Definition::Local(_)
| Definition::Label(_)
| Definition::BuiltinAttr(_)
| Definition::ToolModule(_)
) {
return None;
}
def.module(db).map(|module| path(db, module, definition_owner_name(db, def)))
@ -724,14 +602,6 @@ fn markup(docs: Option<String>, desc: String, mod_path: Option<String>) -> Optio
Some(buf.into())
}
fn 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().display(famous_defs.0.db));
let doc_owner = find_std_module(famous_defs, &primitive_mod)?;
let docs = doc_owner.docs(famous_defs.0.db)?;
markup(Some(docs.into()), builtin.name().display(famous_defs.0.db).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()?;
@ -741,34 +611,6 @@ fn find_std_module(famous_defs: &FamousDefs<'_, '_>, name: &str) -> Option<hir::
})
}
fn local(db: &RootDatabase, it: hir::Local, config: &HoverConfig) -> Option<Markup> {
let ty = it.ty(db);
let ty = ty.display_truncated(db, None);
let is_mut = if it.is_mut(db) { "mut " } else { "" };
let mut desc = match it.primary_source(db).into_ident_pat() {
Some(ident) => {
let name = it.name(db);
let let_kw = if ident
.syntax()
.parent()
.map_or(false, |p| p.kind() == LET_STMT || p.kind() == LET_EXPR)
{
"let "
} else {
""
};
format!("{let_kw}{is_mut}{}: {ty}", name.display(db))
}
None => format!("{is_mut}self: {ty}"),
};
if let Some(layout) =
render_memory_layout(config.memory_layout, || it.ty(db).layout(db), |_| None, |_| None)
{
format_to!(desc, "{layout}");
}
markup(None, desc, None)
}
fn render_memory_layout(
config: Option<MemoryLayoutHoverConfig>,
layout: impl FnOnce() -> Result<Layout, LayoutError>,

View File

@ -99,7 +99,10 @@ pub use crate::{
},
join_lines::JoinLinesConfig,
markup::Markup,
moniker::{MonikerDescriptorKind, MonikerKind, MonikerResult, PackageInformation},
moniker::{
MonikerDescriptorKind, MonikerKind, MonikerResult, PackageInformation,
SymbolInformationKind,
},
move_item::Direction,
navigation_target::{NavigationTarget, UpmappingResult},
prime_caches::ParallelPrimeCachesProgress,

View File

@ -1,7 +1,7 @@
//! This module generates [moniker](https://microsoft.github.io/language-server-protocol/specifications/lsif/0.6.0/specification/#exportsImports)
//! for LSIF and LSP.
use hir::{AsAssocItem, AssocItemContainer, Crate, DescendPreference, Semantics};
use hir::{Adt, AsAssocItem, AssocItemContainer, Crate, DescendPreference, MacroKind, Semantics};
use ide_db::{
base_db::{CrateOrigin, FilePosition, LangCrateOrigin},
defs::{Definition, IdentClass},
@ -25,6 +25,62 @@ pub enum MonikerDescriptorKind {
Meta,
}
// Subset of scip_types::SymbolInformation::Kind
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SymbolInformationKind {
AssociatedType,
Attribute,
Constant,
Enum,
EnumMember,
Field,
Function,
Macro,
Method,
Module,
Parameter,
SelfParameter,
StaticMethod,
StaticVariable,
Struct,
Trait,
TraitMethod,
Type,
TypeAlias,
TypeParameter,
Union,
Variable,
}
impl From<SymbolInformationKind> for MonikerDescriptorKind {
fn from(value: SymbolInformationKind) -> Self {
match value {
SymbolInformationKind::AssociatedType => Self::TypeParameter,
SymbolInformationKind::Attribute => Self::Macro,
SymbolInformationKind::Constant => Self::Term,
SymbolInformationKind::Enum => Self::Type,
SymbolInformationKind::EnumMember => Self::Type,
SymbolInformationKind::Field => Self::Term,
SymbolInformationKind::Function => Self::Method,
SymbolInformationKind::Macro => Self::Macro,
SymbolInformationKind::Method => Self::Method,
SymbolInformationKind::Module => Self::Namespace,
SymbolInformationKind::Parameter => Self::Parameter,
SymbolInformationKind::SelfParameter => Self::Parameter,
SymbolInformationKind::StaticMethod => Self::Method,
SymbolInformationKind::StaticVariable => Self::Meta,
SymbolInformationKind::Struct => Self::Type,
SymbolInformationKind::Trait => Self::Type,
SymbolInformationKind::TraitMethod => Self::Method,
SymbolInformationKind::Type => Self::Type,
SymbolInformationKind::TypeAlias => Self::Type,
SymbolInformationKind::TypeParameter => Self::TypeParameter,
SymbolInformationKind::Union => Self::Type,
SymbolInformationKind::Variable => Self::Term,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MonikerDescriptor {
pub name: String,
@ -112,6 +168,69 @@ pub(crate) fn moniker(
Some(RangeInfo::new(original_token.text_range(), navs))
}
pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformationKind {
use SymbolInformationKind::*;
match def {
Definition::Macro(it) => match it.kind(db) {
MacroKind::Declarative => Macro,
MacroKind::Derive => Attribute,
MacroKind::BuiltIn => Macro,
MacroKind::Attr => Attribute,
MacroKind::ProcMacro => Macro,
},
Definition::Field(..) => Field,
Definition::Module(..) => Module,
Definition::Function(it) => {
if it.as_assoc_item(db).is_some() {
if it.has_self_param(db) {
if it.has_body(db) {
Method
} else {
TraitMethod
}
} else {
StaticMethod
}
} else {
Function
}
}
Definition::Adt(Adt::Struct(..)) => Struct,
Definition::Adt(Adt::Union(..)) => Union,
Definition::Adt(Adt::Enum(..)) => Enum,
Definition::Variant(..) => EnumMember,
Definition::Const(..) => Constant,
Definition::Static(..) => StaticVariable,
Definition::Trait(..) => Trait,
Definition::TraitAlias(..) => Trait,
Definition::TypeAlias(it) => {
if it.as_assoc_item(db).is_some() {
AssociatedType
} else {
TypeAlias
}
}
Definition::BuiltinType(..) => Type,
Definition::SelfType(..) => TypeAlias,
Definition::GenericParam(..) => TypeParameter,
Definition::Local(it) => {
if it.is_self(db) {
SelfParameter
} else if it.is_param(db) {
Parameter
} else {
Variable
}
}
Definition::Label(..) => Variable, // For lack of a better variant
Definition::DeriveHelper(..) => Attribute,
Definition::BuiltinAttr(..) => Attribute,
Definition::ToolModule(..) => Module,
Definition::ExternCrateDecl(..) => Module,
}
}
pub(crate) fn def_to_moniker(
db: &RootDatabase,
def: Definition,
@ -134,7 +253,7 @@ pub(crate) fn def_to_moniker(
description.extend(module.path_to_root(db).into_iter().filter_map(|x| {
Some(MonikerDescriptor {
name: x.name(db)?.display(db).to_string(),
desc: MonikerDescriptorKind::Namespace,
desc: def_to_kind(db, x.into()).into(),
})
}));
@ -147,7 +266,7 @@ pub(crate) fn def_to_moniker(
// we have to include the trait name as part of the moniker for uniqueness.
description.push(MonikerDescriptor {
name: trait_.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Type,
desc: def_to_kind(db, trait_.into()).into(),
});
}
AssocItemContainer::Impl(impl_) => {
@ -156,14 +275,14 @@ pub(crate) fn def_to_moniker(
if let Some(adt) = impl_.self_ty(db).as_adt() {
description.push(MonikerDescriptor {
name: adt.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Type,
desc: def_to_kind(db, adt.into()).into(),
});
}
if let Some(trait_) = impl_.trait_(db) {
description.push(MonikerDescriptor {
name: trait_.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Type,
desc: def_to_kind(db, trait_.into()).into(),
});
}
}
@ -173,21 +292,26 @@ pub(crate) fn def_to_moniker(
if let Definition::Field(it) = def {
description.push(MonikerDescriptor {
name: it.parent_def(db).name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Type,
desc: def_to_kind(db, it.parent_def(db).into()).into(),
});
}
// Qualify locals/parameters by their parent definition name.
if let Definition::Local(it) = def {
let parent_name = it.parent(db).name(db);
if let Some(name) = parent_name {
description.push(MonikerDescriptor {
name: name.display(db).to_string(),
desc: MonikerDescriptorKind::Method,
});
let parent = Definition::try_from(it.parent(db)).ok();
if let Some(parent) = parent {
let parent_name = parent.name(db);
if let Some(name) = parent_name {
description.push(MonikerDescriptor {
name: name.display(db).to_string(),
desc: def_to_kind(db, parent).into(),
});
}
}
}
let desc = def_to_kind(db, def).into();
let name_desc = match def {
// These are handled by top-level guard (for performance).
Definition::GenericParam(_)
@ -201,67 +325,51 @@ pub(crate) fn def_to_moniker(
return None;
}
MonikerDescriptor {
name: local.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Parameter,
}
MonikerDescriptor { name: local.name(db).display(db).to_string(), desc }
}
Definition::Macro(m) => {
MonikerDescriptor { name: m.name(db).display(db).to_string(), desc }
}
Definition::Function(f) => {
MonikerDescriptor { name: f.name(db).display(db).to_string(), desc }
}
Definition::Variant(v) => {
MonikerDescriptor { name: v.name(db).display(db).to_string(), desc }
}
Definition::Const(c) => {
MonikerDescriptor { name: c.name(db)?.display(db).to_string(), desc }
}
Definition::Trait(trait_) => {
MonikerDescriptor { name: trait_.name(db).display(db).to_string(), desc }
}
Definition::TraitAlias(ta) => {
MonikerDescriptor { name: ta.name(db).display(db).to_string(), desc }
}
Definition::TypeAlias(ta) => {
MonikerDescriptor { name: ta.name(db).display(db).to_string(), desc }
}
Definition::Module(m) => {
MonikerDescriptor { name: m.name(db)?.display(db).to_string(), desc }
}
Definition::BuiltinType(b) => {
MonikerDescriptor { name: b.name().display(db).to_string(), desc }
}
Definition::Macro(m) => MonikerDescriptor {
name: m.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Macro,
},
Definition::Function(f) => MonikerDescriptor {
name: f.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Method,
},
Definition::Variant(v) => MonikerDescriptor {
name: v.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Type,
},
Definition::Const(c) => MonikerDescriptor {
name: c.name(db)?.display(db).to_string(),
desc: MonikerDescriptorKind::Term,
},
Definition::Trait(trait_) => MonikerDescriptor {
name: trait_.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Type,
},
Definition::TraitAlias(ta) => MonikerDescriptor {
name: ta.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Type,
},
Definition::TypeAlias(ta) => MonikerDescriptor {
name: ta.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::TypeParameter,
},
Definition::Module(m) => MonikerDescriptor {
name: m.name(db)?.display(db).to_string(),
desc: MonikerDescriptorKind::Namespace,
},
Definition::BuiltinType(b) => MonikerDescriptor {
name: b.name().display(db).to_string(),
desc: MonikerDescriptorKind::Type,
},
Definition::SelfType(imp) => MonikerDescriptor {
name: imp.self_ty(db).as_adt()?.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Type,
},
Definition::Field(it) => MonikerDescriptor {
name: it.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Term,
},
Definition::Adt(adt) => MonikerDescriptor {
name: adt.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Type,
},
Definition::Static(s) => MonikerDescriptor {
name: s.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Meta,
},
Definition::ExternCrateDecl(m) => MonikerDescriptor {
name: m.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Namespace,
desc,
},
Definition::Field(it) => {
MonikerDescriptor { name: it.name(db).display(db).to_string(), desc }
}
Definition::Adt(adt) => {
MonikerDescriptor { name: adt.name(db).display(db).to_string(), desc }
}
Definition::Static(s) => {
MonikerDescriptor { name: s.name(db).display(db).to_string(), desc }
}
Definition::ExternCrateDecl(m) => {
MonikerDescriptor { name: m.name(db).display(db).to_string(), desc }
}
};
description.push(name_desc);

View File

@ -724,11 +724,8 @@ fn orig_range_with_focus(
) -> UpmappingResult<(FileRange, Option<TextRange>)> {
let Some(name) = name else { return orig_range(db, hir_file, value) };
let call_range = || {
db.lookup_intern_macro_call(hir_file.macro_file().unwrap().macro_call_id)
.kind
.original_call_range(db)
};
let call_kind =
|| db.lookup_intern_macro_call(hir_file.macro_file().unwrap().macro_call_id).kind;
let def_range = || {
db.lookup_intern_macro_call(hir_file.macro_file().unwrap().macro_call_id)
@ -755,7 +752,22 @@ fn orig_range_with_focus(
}
// name lies outside the node, so instead point to the macro call which
// *should* contain the name
_ => call_range(),
_ => {
let kind = call_kind();
let range = kind.clone().original_call_range_with_body(db);
//If the focus range is in the attribute/derive body, we
// need to point the call site to the entire body, if not, fall back
// to the name range of the attribute/derive call
// FIXME: Do this differently, this is very inflexible the caller
// should choose this behavior
if range.file_id == focus_range.file_id
&& range.range.contains_range(focus_range.range)
{
range
} else {
kind.original_call_range(db)
}
}
},
Some(focus_range),
),
@ -784,7 +796,7 @@ fn orig_range_with_focus(
// node is in macro def, just show the focus
_ => (
// show the macro call
(call_range(), None),
(call_kind().original_call_range(db), None),
Some((focus_range, Some(focus_range))),
),
}

View File

@ -17,7 +17,7 @@ use crate::navigation_target::UpmappingResult;
use crate::{
hover::hover_for_definition,
inlay_hints::AdjustmentHintsMode,
moniker::{def_to_moniker, MonikerResult},
moniker::{def_to_kind, def_to_moniker, MonikerResult, SymbolInformationKind},
parent_module::crates_for,
Analysis, Fold, HoverConfig, HoverResult, InlayHint, InlayHintsConfig, TryToNav,
};
@ -46,6 +46,10 @@ pub struct TokenStaticData {
pub definition: Option<FileRange>,
pub references: Vec<ReferenceData>,
pub moniker: Option<MonikerResult>,
pub display_name: Option<String>,
pub enclosing_moniker: Option<MonikerResult>,
pub signature: Option<String>,
pub kind: SymbolInformationKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -172,6 +176,12 @@ impl StaticIndex<'_> {
}),
references: vec![],
moniker: current_crate.and_then(|cc| def_to_moniker(self.db, def, cc)),
display_name: def.name(self.db).map(|name| name.display(self.db).to_string()),
enclosing_moniker: current_crate
.zip(def.enclosing_definition(self.db))
.and_then(|(cc, enclosing_def)| def_to_moniker(self.db, enclosing_def, cc)),
signature: def.label(self.db),
kind: def_to_kind(self.db, def),
});
self.def_map.insert(def, it);
it

View File

@ -7,8 +7,8 @@ use std::{
};
use ide::{
LineCol, MonikerDescriptorKind, StaticIndex, StaticIndexedFile, TextRange, TokenId,
TokenStaticData,
LineCol, MonikerDescriptorKind, MonikerResult, StaticIndex, StaticIndexedFile,
SymbolInformationKind, TextRange, TokenId,
};
use ide_db::LineIndexDatabase;
use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice};
@ -78,6 +78,7 @@ impl flags::Scip {
let mut symbols_emitted: HashSet<TokenId> = HashSet::default();
let mut tokens_to_symbol: HashMap<TokenId, String> = HashMap::new();
let mut tokens_to_enclosing_symbol: HashMap<TokenId, Option<String>> = HashMap::new();
for StaticIndexedFile { file_id, tokens, .. } in si.files {
let mut local_count = 0;
@ -109,10 +110,24 @@ impl flags::Scip {
let symbol = tokens_to_symbol
.entry(id)
.or_insert_with(|| {
let symbol = token_to_symbol(token).unwrap_or_else(&mut new_local_symbol);
let symbol = token
.moniker
.as_ref()
.map(moniker_to_symbol)
.unwrap_or_else(&mut new_local_symbol);
scip::symbol::format_symbol(symbol)
})
.clone();
let enclosing_symbol = tokens_to_enclosing_symbol
.entry(id)
.or_insert_with(|| {
token
.enclosing_moniker
.as_ref()
.map(moniker_to_symbol)
.map(scip::symbol::format_symbol)
})
.clone();
let mut symbol_roles = Default::default();
@ -128,15 +143,22 @@ impl flags::Scip {
.map(|hover| hover.markup.as_str())
.filter(|it| !it.is_empty())
.map(|it| vec![it.to_owned()]);
let signature_documentation =
token.signature.clone().map(|text| scip_types::Document {
relative_path: relative_path.clone(),
language: "rust".to_string(),
text,
..Default::default()
});
let symbol_info = scip_types::SymbolInformation {
symbol: symbol.clone(),
documentation: documentation.unwrap_or_default(),
relationships: Vec::new(),
special_fields: Default::default(),
kind: Default::default(),
display_name: String::new(),
signature_documentation: Default::default(),
enclosing_symbol: String::new(),
kind: symbol_kind(token.kind).into(),
display_name: token.display_name.clone().unwrap_or_default(),
signature_documentation: signature_documentation.into(),
enclosing_symbol: enclosing_symbol.unwrap_or_default(),
};
symbols.push(symbol_info)
@ -228,14 +250,36 @@ fn new_descriptor(name: &str, suffix: scip_types::descriptor::Suffix) -> scip_ty
}
}
/// Loosely based on `def_to_moniker`
///
/// Only returns a Symbol when it's a non-local symbol.
/// So if the visibility isn't outside of a document, then it will return None
fn token_to_symbol(token: &TokenStaticData) -> Option<scip_types::Symbol> {
use scip_types::descriptor::Suffix::*;
fn symbol_kind(kind: SymbolInformationKind) -> scip_types::symbol_information::Kind {
use scip_types::symbol_information::Kind as ScipKind;
match kind {
SymbolInformationKind::AssociatedType => ScipKind::AssociatedType,
SymbolInformationKind::Attribute => ScipKind::Attribute,
SymbolInformationKind::Constant => ScipKind::Constant,
SymbolInformationKind::Enum => ScipKind::Enum,
SymbolInformationKind::EnumMember => ScipKind::EnumMember,
SymbolInformationKind::Field => ScipKind::Field,
SymbolInformationKind::Function => ScipKind::Function,
SymbolInformationKind::Macro => ScipKind::Macro,
SymbolInformationKind::Method => ScipKind::Method,
SymbolInformationKind::Module => ScipKind::Module,
SymbolInformationKind::Parameter => ScipKind::Parameter,
SymbolInformationKind::SelfParameter => ScipKind::SelfParameter,
SymbolInformationKind::StaticMethod => ScipKind::StaticMethod,
SymbolInformationKind::StaticVariable => ScipKind::StaticVariable,
SymbolInformationKind::Struct => ScipKind::Struct,
SymbolInformationKind::Trait => ScipKind::Trait,
SymbolInformationKind::TraitMethod => ScipKind::TraitMethod,
SymbolInformationKind::Type => ScipKind::Type,
SymbolInformationKind::TypeAlias => ScipKind::TypeAlias,
SymbolInformationKind::TypeParameter => ScipKind::TypeParameter,
SymbolInformationKind::Union => ScipKind::Union,
SymbolInformationKind::Variable => ScipKind::Variable,
}
}
let moniker = token.moniker.as_ref()?;
fn moniker_to_symbol(moniker: &MonikerResult) -> scip_types::Symbol {
use scip_types::descriptor::Suffix::*;
let package_name = moniker.package_information.name.clone();
let version = moniker.package_information.version.clone();
@ -260,7 +304,7 @@ fn token_to_symbol(token: &TokenStaticData) -> Option<scip_types::Symbol> {
})
.collect();
Some(scip_types::Symbol {
scip_types::Symbol {
scheme: "rust-analyzer".into(),
package: Some(scip_types::Package {
manager: "cargo".to_string(),
@ -271,7 +315,7 @@ fn token_to_symbol(token: &TokenStaticData) -> Option<scip_types::Symbol> {
.into(),
descriptors,
special_fields: Default::default(),
})
}
}
#[cfg(test)]
@ -309,7 +353,7 @@ mod test {
for &(range, id) in &file.tokens {
if range.contains(offset - TextSize::from(1)) {
let token = si.tokens.get(id).unwrap();
found_symbol = token_to_symbol(token);
found_symbol = token.moniker.as_ref().map(moniker_to_symbol);
break;
}
}
@ -359,6 +403,21 @@ pub mod module {
);
}
#[test]
fn symbol_for_trait_alias() {
check_symbol(
r#"
//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
#![feature(trait_alias)]
pub mod module {
pub trait MyTrait {}
pub trait MyTraitAlias$0 = MyTrait;
}
"#,
"rust-analyzer cargo foo 0.1.0 module/MyTraitAlias#",
);
}
#[test]
fn symbol_for_trait_constant() {
check_symbol(
@ -525,4 +584,15 @@ pub mod example_mod {
"rust-analyzer cargo main . foo/Bar#",
);
}
#[test]
fn symbol_for_for_type_alias() {
check_symbol(
r#"
//- /lib.rs crate:main
pub type MyTypeAlias$0 = u8;
"#,
"rust-analyzer cargo main . MyTypeAlias#",
);
}
}

View File

@ -186,18 +186,10 @@ $ pacman -S rust-analyzer
==== Gentoo Linux
`rust-analyzer` is available in the GURU repository:
`rust-analyzer` is installed when the `rust-analyzer` USE flag is set for `dev-lang/rust` or `dev-lang/rust-bin`.
You also need to set the `rust-src` USE flag.
- https://gitweb.gentoo.org/repo/proj/guru.git/tree/dev-util/rust-analyzer?id=9895cea62602cfe599bd48e0fb02127411ca6e81[`dev-util/rust-analyzer`] builds from source
- https://gitweb.gentoo.org/repo/proj/guru.git/tree/dev-util/rust-analyzer-bin?id=9895cea62602cfe599bd48e0fb02127411ca6e81[`dev-util/rust-analyzer-bin`] installs an official binary release
If not already, GURU must be enabled (e.g. using `app-eselect/eselect-repository`) and sync'd before running `emerge`:
[source,bash]
----
$ eselect repository enable guru && emaint sync -r guru
$ emerge rust-analyzer-bin
----
This is similar to using the `rustup` component, so it will install a version that lags behind the official releases on GitHub .
==== macOS

View File

@ -5,6 +5,7 @@ import * as vscode from "vscode";
import type { Env } from "./client";
import { log } from "./util";
import { expectNotUndefined, unwrapUndefinable } from "./undefinable";
import type { JsonProject } from "./rust_project";
export type RunnableEnvCfgItem = {
mask?: string;

View File

@ -23,6 +23,7 @@ import { execRevealDependency } from "./commands";
import { PersistentState } from "./persistent_state";
import { bootstrap } from "./bootstrap";
import type { RustAnalyzerExtensionApi } from "./main";
import type { JsonProject } from "./rust_project";
// We only support local folders, not eg. Live Share (`vlsl:` scheme), so don't activate if
// only those are in use. We use "Empty" to represent these scenarios

View File

@ -6,6 +6,7 @@ import { type CommandFactory, Ctx, fetchWorkspace } from "./ctx";
import * as diagnostics from "./diagnostics";
import { activateTaskProvider } from "./tasks";
import { setContextValue } from "./util";
import type { JsonProject } from "./rust_project";
const RUST_PROJECT_CONTEXT_NAME = "inRustProject";

View File

@ -1,4 +1,4 @@
interface JsonProject {
export interface JsonProject {
/// Path to the directory with *source code* of
/// sysroot crates.
///
@ -21,7 +21,7 @@ interface JsonProject {
crates: Crate[];
}
interface Crate {
export interface Crate {
/// Optional crate name used for display purposes,
/// without affecting semantics. See the `deps`
/// key for semantically-significant crate names.
@ -82,7 +82,7 @@ interface Crate {
proc_macro_dylib_path?: string;
}
interface Dep {
export interface Dep {
/// Index of a crate in the `crates` array.
crate: number;
/// Name as should appear in the (implicit)

View File

@ -2,7 +2,7 @@
"extends": "@tsconfig/strictest/tsconfig.json",
"compilerOptions": {
"esModuleInterop": false,
"module": "CommonJS",
"module": "Node16",
"moduleResolution": "Node16",
"target": "ES2021",
"outDir": "out",