Rollup merge of #101759 - lnicola:rust-analyzer-2022-09-13, r=lnicola

⬆️ rust-analyzer

r? `@ghost`
This commit is contained in:
Matthias Krüger 2022-09-13 22:25:37 +02:00 committed by GitHub
commit 6ac8dd7d91
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 528 additions and 142 deletions

View file

@ -34,6 +34,7 @@ jobs:
- os: ubuntu-20.04 - os: ubuntu-20.04
target: x86_64-unknown-linux-gnu target: x86_64-unknown-linux-gnu
code-target: linux-x64 code-target: linux-x64
container: ubuntu:18.04
- os: ubuntu-20.04 - os: ubuntu-20.04
target: aarch64-unknown-linux-gnu target: aarch64-unknown-linux-gnu
code-target: linux-arm64 code-target: linux-arm64
@ -49,6 +50,7 @@ jobs:
name: dist (${{ matrix.target }}) name: dist (${{ matrix.target }})
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
container: ${{ matrix.container }}
env: env:
RA_TARGET: ${{ matrix.target }} RA_TARGET: ${{ matrix.target }}
@ -59,6 +61,14 @@ jobs:
with: with:
fetch-depth: ${{ env.FETCH_DEPTH }} fetch-depth: ${{ env.FETCH_DEPTH }}
- name: Install toolchain dependencies
if: matrix.container == 'ubuntu:18.04'
shell: bash
run: |
apt-get update && apt-get install -y build-essential curl
curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused -fsSL "https://sh.rustup.rs" | sh -s -- --profile minimal --default-toolchain none -y
echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH
- name: Install Rust toolchain - name: Install Rust toolchain
run: | run: |
rustup update --no-self-update stable rustup update --no-self-update stable

View file

@ -164,6 +164,8 @@ impl TyExt for Ty {
fn dyn_trait(&self) -> Option<TraitId> { fn dyn_trait(&self) -> Option<TraitId> {
let trait_ref = match self.kind(Interner) { let trait_ref = match self.kind(Interner) {
// The principal trait bound should be the first element of the bounds. This is an
// invariant ensured by `TyLoweringContext::lower_dyn_trait()`.
TyKind::Dyn(dyn_ty) => dyn_ty.bounds.skip_binders().interned().get(0).and_then(|b| { TyKind::Dyn(dyn_ty) => dyn_ty.bounds.skip_binders().interned().get(0).and_then(|b| {
match b.skip_binders() { match b.skip_binders() {
WhereClause::Implemented(trait_ref) => Some(trait_ref), WhereClause::Implemented(trait_ref) => Some(trait_ref),

View file

@ -981,43 +981,72 @@ impl<'a> TyLoweringContext<'a> {
fn lower_dyn_trait(&self, bounds: &[Interned<TypeBound>]) -> Ty { fn lower_dyn_trait(&self, bounds: &[Interned<TypeBound>]) -> Ty {
let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner); let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner);
// INVARIANT: The principal trait bound must come first. Others may be in any order but
// should be in the same order for the same set but possibly different order of bounds in
// the input.
// This invariant is used by `TyExt::dyn_trait()` and chalk.
let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
let bounds = let mut bounds: Vec<_> = bounds
bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)); .iter()
.flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false))
.collect();
let mut auto_traits = SmallVec::<[_; 8]>::new(); let mut multiple_regular_traits = false;
let mut regular_traits = SmallVec::<[_; 2]>::new(); let mut multiple_same_projection = false;
let mut other_bounds = SmallVec::<[_; 8]>::new(); bounds.sort_unstable_by(|lhs, rhs| {
for bound in bounds { use std::cmp::Ordering;
if let Some(id) = bound.trait_id() { match (lhs.skip_binders(), rhs.skip_binders()) {
if ctx.db.trait_data(from_chalk_trait_id(id)).is_auto { (WhereClause::Implemented(lhs), WhereClause::Implemented(rhs)) => {
auto_traits.push(bound); let lhs_id = lhs.trait_id;
} else { let lhs_is_auto = ctx.db.trait_data(from_chalk_trait_id(lhs_id)).is_auto;
regular_traits.push(bound); let rhs_id = rhs.trait_id;
} let rhs_is_auto = ctx.db.trait_data(from_chalk_trait_id(rhs_id)).is_auto;
} else {
other_bounds.push(bound);
}
}
if regular_traits.len() > 1 { if !lhs_is_auto && !rhs_is_auto {
multiple_regular_traits = true;
}
// Note that the ordering here is important; this ensures the invariant
// mentioned above.
(lhs_is_auto, lhs_id).cmp(&(rhs_is_auto, rhs_id))
}
(WhereClause::Implemented(_), _) => Ordering::Less,
(_, WhereClause::Implemented(_)) => Ordering::Greater,
(WhereClause::AliasEq(lhs), WhereClause::AliasEq(rhs)) => {
match (&lhs.alias, &rhs.alias) {
(AliasTy::Projection(lhs_proj), AliasTy::Projection(rhs_proj)) => {
// We only compare the `associated_ty_id`s. We shouldn't have
// multiple bounds for an associated type in the correct Rust code,
// and if we do, we error out.
if lhs_proj.associated_ty_id == rhs_proj.associated_ty_id {
multiple_same_projection = true;
}
lhs_proj.associated_ty_id.cmp(&rhs_proj.associated_ty_id)
}
// We don't produce `AliasTy::Opaque`s yet.
_ => unreachable!(),
}
}
// We don't produce `WhereClause::{TypeOutlives, LifetimeOutlives}` yet.
_ => unreachable!(),
}
});
if multiple_regular_traits || multiple_same_projection {
return None; return None;
} }
auto_traits.sort_unstable_by_key(|b| b.trait_id().unwrap()); // As multiple occurrences of the same auto traits *are* permitted, we dedulicate the
auto_traits.dedup(); // bounds. We shouldn't have repeated elements besides auto traits at this point.
bounds.dedup();
Some(QuantifiedWhereClauses::from_iter( Some(QuantifiedWhereClauses::from_iter(Interner, bounds))
Interner,
regular_traits.into_iter().chain(other_bounds).chain(auto_traits),
))
}); });
if let Some(bounds) = bounds { if let Some(bounds) = bounds {
let bounds = crate::make_single_type_binders(bounds); let bounds = crate::make_single_type_binders(bounds);
TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner) TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner)
} else { } else {
// FIXME: report error (additional non-auto traits) // FIXME: report error (additional non-auto traits or associated type rebound)
TyKind::Error.intern(Interner) TyKind::Error.intern(Interner)
} }
} }

View file

@ -3900,6 +3900,34 @@ fn g(t: &(dyn Sync + T<Proj = ()> + Send)) {
); );
} }
#[test]
fn dyn_multiple_projection_bounds() {
check_no_mismatches(
r#"
trait Trait {
type T;
type U;
}
fn f(t: &dyn Trait<T = (), U = ()>) {}
fn g(t: &dyn Trait<U = (), T = ()>) {
f(t);
}
"#,
);
check_types(
r#"
trait Trait {
type T;
}
fn f(t: &dyn Trait<T = (), T = ()>) {}
//^&{unknown}
"#,
);
}
#[test] #[test]
fn dyn_duplicate_auto_trait() { fn dyn_duplicate_auto_trait() {
check_no_mismatches( check_no_mismatches(

View file

@ -5,6 +5,7 @@ use hir::{Adt, Crate, HasAttrs, HasSource, ModuleDef, Semantics};
use ide_db::RootDatabase; use ide_db::RootDatabase;
use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast}; use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast};
use itertools::Itertools; use itertools::Itertools;
use syntax::ast::edit_in_place::Removable;
use syntax::ast::{self, make, AstNode, HasName, MatchArmList, MatchExpr, Pat}; use syntax::ast::{self, make, AstNode, HasName, MatchArmList, MatchExpr, Pat};
use crate::{ use crate::{

View file

@ -7,6 +7,7 @@ use ide_db::{
imports::insert_use::remove_path_if_in_use_stmt, imports::insert_use::remove_path_if_in_use_stmt,
path_transform::PathTransform, path_transform::PathTransform,
search::{FileReference, SearchScope}, search::{FileReference, SearchScope},
source_change::SourceChangeBuilder,
syntax_helpers::{insert_whitespace_into_node::insert_ws_into, node_ext::expr_as_name_ref}, syntax_helpers::{insert_whitespace_into_node::insert_ws_into, node_ext::expr_as_name_ref},
RootDatabase, RootDatabase,
}; };
@ -100,18 +101,7 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) ->
builder.edit_file(file_id); builder.edit_file(file_id);
let count = refs.len(); let count = refs.len();
// The collects are required as we are otherwise iterating while mutating 🙅‍♀️🙅‍♂️ // The collects are required as we are otherwise iterating while mutating 🙅‍♀️🙅‍♂️
let (name_refs, name_refs_use): (Vec<_>, Vec<_>) = refs let (name_refs, name_refs_use) = split_refs_and_uses(builder, refs, Some);
.into_iter()
.filter_map(|file_ref| match file_ref.name {
ast::NameLike::NameRef(name_ref) => Some(name_ref),
_ => None,
})
.partition_map(|name_ref| {
match name_ref.syntax().ancestors().find_map(ast::UseTree::cast) {
Some(use_tree) => Either::Right(builder.make_mut(use_tree)),
None => Either::Left(name_ref),
}
});
let call_infos: Vec<_> = name_refs let call_infos: Vec<_> = name_refs
.into_iter() .into_iter()
.filter_map(CallInfo::from_name_ref) .filter_map(CallInfo::from_name_ref)
@ -130,11 +120,7 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) ->
.count(); .count();
if replaced + name_refs_use.len() == count { if replaced + name_refs_use.len() == count {
// we replaced all usages in this file, so we can remove the imports // we replaced all usages in this file, so we can remove the imports
name_refs_use.into_iter().for_each(|use_tree| { name_refs_use.iter().for_each(remove_path_if_in_use_stmt);
if let Some(path) = use_tree.path() {
remove_path_if_in_use_stmt(&path);
}
})
} else { } else {
remove_def = false; remove_def = false;
} }
@ -153,6 +139,23 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) ->
) )
} }
pub(super) fn split_refs_and_uses<T: ast::AstNode>(
builder: &mut SourceChangeBuilder,
iter: impl IntoIterator<Item = FileReference>,
mut map_ref: impl FnMut(ast::NameRef) -> Option<T>,
) -> (Vec<T>, Vec<ast::Path>) {
iter.into_iter()
.filter_map(|file_ref| match file_ref.name {
ast::NameLike::NameRef(name_ref) => Some(name_ref),
_ => None,
})
.filter_map(|name_ref| match name_ref.syntax().ancestors().find_map(ast::UseTree::cast) {
Some(use_tree) => builder.make_mut(use_tree).path().map(Either::Right),
None => map_ref(name_ref).map(Either::Left),
})
.partition_map(|either| either)
}
// Assist: inline_call // Assist: inline_call
// //
// Inlines a function or method body creating a `let` statement per parameter unless the parameter // Inlines a function or method body creating a `let` statement per parameter unless the parameter

View file

@ -3,7 +3,10 @@
// - Remove unused aliases if there are no longer any users, see inline_call.rs. // - Remove unused aliases if there are no longer any users, see inline_call.rs.
use hir::{HasSource, PathResolution}; use hir::{HasSource, PathResolution};
use ide_db::{defs::Definition, search::FileReference}; use ide_db::{
defs::Definition, imports::insert_use::ast_to_remove_for_path_in_use_stmt,
search::FileReference,
};
use itertools::Itertools; use itertools::Itertools;
use std::collections::HashMap; use std::collections::HashMap;
use syntax::{ use syntax::{
@ -16,6 +19,8 @@ use crate::{
AssistId, AssistKind, AssistId, AssistKind,
}; };
use super::inline_call::split_refs_and_uses;
// Assist: inline_type_alias_uses // Assist: inline_type_alias_uses
// //
// Inline a type alias into all of its uses where possible. // Inline a type alias into all of its uses where possible.
@ -31,7 +36,7 @@ use crate::{
// ``` // ```
// -> // ->
// ``` // ```
// type A = i32; //
// fn id(x: i32) -> i32 { // fn id(x: i32) -> i32 {
// x // x
// }; // };
@ -58,20 +63,20 @@ pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>)
name.syntax().text_range(), name.syntax().text_range(),
|builder| { |builder| {
let usages = usages.all(); let usages = usages.all();
let mut definition_deleted = false;
let mut inline_refs_for_file = |file_id, refs: Vec<FileReference>| { let mut inline_refs_for_file = |file_id, refs: Vec<FileReference>| {
builder.edit_file(file_id); builder.edit_file(file_id);
let path_types: Vec<ast::PathType> = refs let (path_types, path_type_uses) =
.into_iter() split_refs_and_uses(builder, refs, |path_type| {
.filter_map(|file_ref| match file_ref.name {
ast::NameLike::NameRef(path_type) => {
path_type.syntax().ancestors().nth(3).and_then(ast::PathType::cast) path_type.syntax().ancestors().nth(3).and_then(ast::PathType::cast)
} });
_ => None,
})
.collect();
path_type_uses
.iter()
.flat_map(ast_to_remove_for_path_in_use_stmt)
.for_each(|x| builder.delete(x.syntax().text_range()));
for (target, replacement) in path_types.into_iter().filter_map(|path_type| { for (target, replacement) in path_types.into_iter().filter_map(|path_type| {
let replacement = inline(&ast_alias, &path_type)?.to_text(&concrete_type); let replacement = inline(&ast_alias, &path_type)?.to_text(&concrete_type);
let target = path_type.syntax().text_range(); let target = path_type.syntax().text_range();
@ -79,11 +84,20 @@ pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>)
}) { }) {
builder.replace(target, replacement); builder.replace(target, replacement);
} }
if file_id == ctx.file_id() {
builder.delete(ast_alias.syntax().text_range());
definition_deleted = true;
}
}; };
for (file_id, refs) in usages.into_iter() { for (file_id, refs) in usages.into_iter() {
inline_refs_for_file(file_id, refs); inline_refs_for_file(file_id, refs);
} }
if !definition_deleted {
builder.edit_file(ctx.file_id());
builder.delete(ast_alias.syntax().text_range());
}
}, },
) )
} }
@ -929,7 +943,7 @@ fn foo() {
} }
"#, "#,
r#" r#"
type A = u32;
fn foo() { fn foo() {
let _: u32 = 3; let _: u32 = 3;
@ -960,13 +974,13 @@ fn foo() {
r#" r#"
//- /lib.rs //- /lib.rs
mod foo; mod foo;
type T<E> = Vec<E>;
fn f() -> Vec<&str> { fn f() -> Vec<&str> {
vec!["hello"] vec!["hello"]
} }
//- /foo.rs //- /foo.rs
use super::T;
fn foo() { fn foo() {
let _: Vec<i8> = Vec::new(); let _: Vec<i8> = Vec::new();
} }
@ -990,7 +1004,12 @@ fn foo() {
} }
"#, "#,
r#" r#"
use super::I; //- /lib.rs
mod foo;
//- /foo.rs
fn foo() { fn foo() {
let _: i32 = 0; let _: i32 = 0;
} }

View file

@ -1,6 +1,10 @@
use either::Either; use either::Either;
use ide_db::imports::merge_imports::{try_merge_imports, try_merge_trees, MergeBehavior}; use ide_db::imports::merge_imports::{try_merge_imports, try_merge_trees, MergeBehavior};
use syntax::{algo::neighbor, ast, match_ast, ted, AstNode, SyntaxElement, SyntaxNode}; use syntax::{
algo::neighbor,
ast::{self, edit_in_place::Removable},
match_ast, ted, AstNode, SyntaxElement, SyntaxNode,
};
use crate::{ use crate::{
assist_context::{AssistContext, Assists}, assist_context::{AssistContext, Assists},
@ -76,7 +80,7 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
.collect(); .collect();
for edit in edits_mut { for edit in edits_mut {
match edit { match edit {
Remove(it) => it.as_ref().either(ast::Use::remove, ast::UseTree::remove), Remove(it) => it.as_ref().either(Removable::remove, Removable::remove),
Replace(old, new) => ted::replace(old, new), Replace(old, new) => ted::replace(old, new),
} }
} }

View file

@ -1,5 +1,9 @@
use syntax::{ use syntax::{
ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasName, HasTypeBounds}, ast::{
self,
edit_in_place::{GenericParamsOwnerEdit, Removable},
make, AstNode, HasName, HasTypeBounds,
},
match_ast, match_ast,
}; };

View file

@ -1,5 +1,5 @@
use syntax::{ use syntax::{
ast::{self, make, HasVisibility}, ast::{self, edit_in_place::Removable, make, HasVisibility},
ted::{self, Position}, ted::{self, Position},
AstNode, SyntaxKind, AstNode, SyntaxKind,
}; };

View file

@ -1390,7 +1390,7 @@ fn foo() {
} }
"#####, "#####,
r#####" r#####"
type A = i32;
fn id(x: i32) -> i32 { fn id(x: i32) -> i32 {
x x
}; };

View file

@ -12,7 +12,7 @@ use syntax::{
ast::{ ast::{
self, self,
edit::{self, AstNodeEdit}, edit::{self, AstNodeEdit},
edit_in_place::AttrsOwnerEdit, edit_in_place::{AttrsOwnerEdit, Removable},
make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace, make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
}, },
ted, AstNode, AstToken, Direction, SmolStr, SourceFile, ted, AstNode, AstToken, Direction, SmolStr, SourceFile,

View file

@ -53,6 +53,7 @@ pub(crate) fn complete_mod(
let existing_mod_declarations = current_module let existing_mod_declarations = current_module
.children(ctx.db) .children(ctx.db)
.filter_map(|module| Some(module.name(ctx.db)?.to_string())) .filter_map(|module| Some(module.name(ctx.db)?.to_string()))
.filter(|module| module != ctx.original_token.text())
.collect::<FxHashSet<_>>(); .collect::<FxHashSet<_>>();
let module_declaration_file = let module_declaration_file =
@ -351,4 +352,23 @@ fn ignored_bar() {}
"#]], "#]],
); );
} }
#[test]
fn semi_colon_completion() {
check(
r#"
//- /lib.rs
mod foo;
//- /foo.rs
mod bar {
mod baz$0
}
//- /foo/bar/baz.rs
fn baz() {}
"#,
expect![[r#"
md baz;
"#]],
);
}
} }

View file

@ -671,6 +671,45 @@ fn main() {
); );
} }
#[test]
fn varaiant_with_struct() {
check_empty(
r#"
pub struct YoloVariant {
pub f: usize
}
pub enum HH {
Yolo(YoloVariant),
}
fn brr() {
let t = HH::Yolo(Y$0);
}
"#,
expect![[r#"
en HH
fn brr() fn()
st YoloVariant
st YoloVariant {} YoloVariant { f: usize }
bt u32
kw crate::
kw false
kw for
kw if
kw if let
kw loop
kw match
kw return
kw self::
kw true
kw unsafe
kw while
kw while let
"#]],
);
}
#[test] #[test]
fn return_unit_block() { fn return_unit_block() {
cov_mark::check!(return_unit_block); cov_mark::check!(return_unit_block);

View file

@ -12,7 +12,7 @@ use crate::RootDatabase;
#[derive(Debug)] #[derive(Debug)]
pub struct ActiveParameter { pub struct ActiveParameter {
pub ty: Type, pub ty: Type,
pub pat: Either<ast::SelfParam, ast::Pat>, pub pat: Option<Either<ast::SelfParam, ast::Pat>>,
} }
impl ActiveParameter { impl ActiveParameter {
@ -27,12 +27,12 @@ impl ActiveParameter {
return None; return None;
} }
let (pat, ty) = params.swap_remove(idx); let (pat, ty) = params.swap_remove(idx);
pat.map(|pat| ActiveParameter { ty, pat }) Some(ActiveParameter { ty, pat })
} }
pub fn ident(&self) -> Option<ast::Name> { pub fn ident(&self) -> Option<ast::Name> {
self.pat.as_ref().right().and_then(|param| match param { self.pat.as_ref().and_then(|param| match param {
ast::Pat::IdentPat(ident) => ident.name(), Either::Right(ast::Pat::IdentPat(ident)) => ident.name(),
_ => None, _ => None,
}) })
} }

View file

@ -7,7 +7,10 @@ use std::cmp::Ordering;
use hir::Semantics; use hir::Semantics;
use syntax::{ use syntax::{
algo, algo,
ast::{self, make, AstNode, HasAttrs, HasModuleItem, HasVisibility, PathSegmentKind}, ast::{
self, edit_in_place::Removable, make, AstNode, HasAttrs, HasModuleItem, HasVisibility,
PathSegmentKind,
},
ted, Direction, NodeOrToken, SyntaxKind, SyntaxNode, ted, Direction, NodeOrToken, SyntaxKind, SyntaxNode,
}; };
@ -192,20 +195,24 @@ pub fn insert_use(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) {
insert_use_(scope, &path, cfg.group, use_item); insert_use_(scope, &path, cfg.group, use_item);
} }
pub fn remove_path_if_in_use_stmt(path: &ast::Path) { pub fn ast_to_remove_for_path_in_use_stmt(path: &ast::Path) -> Option<Box<dyn Removable>> {
// FIXME: improve this // FIXME: improve this
if path.parent_path().is_some() { if path.parent_path().is_some() {
return; return None;
} }
if let Some(use_tree) = path.syntax().parent().and_then(ast::UseTree::cast) { let use_tree = path.syntax().parent().and_then(ast::UseTree::cast)?;
if use_tree.use_tree_list().is_some() || use_tree.star_token().is_some() { if use_tree.use_tree_list().is_some() || use_tree.star_token().is_some() {
return; return None;
} }
if let Some(use_) = use_tree.syntax().parent().and_then(ast::Use::cast) { if let Some(use_) = use_tree.syntax().parent().and_then(ast::Use::cast) {
use_.remove(); return Some(Box::new(use_));
return;
} }
use_tree.remove(); Some(Box::new(use_tree))
}
pub fn remove_path_if_in_use_stmt(path: &ast::Path) {
if let Some(node) = ast_to_remove_for_path_in_use_stmt(path) {
node.remove();
} }
} }

View file

@ -95,7 +95,7 @@ pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode {
AS_KW | DYN_KW | IMPL_KW | CONST_KW => { AS_KW | DYN_KW | IMPL_KW | CONST_KW => {
mods.push(do_ws(after, tok)); mods.push(do_ws(after, tok));
} }
T![;] => { T![;] if is_next(|it| it != R_CURLY, true) => {
if indent > 0 { if indent > 0 {
mods.push(do_indent(after, tok, indent)); mods.push(do_indent(after, tok, indent));
} }

View file

@ -2,12 +2,13 @@
use std::fmt::Display; use std::fmt::Display;
use either::Either; use either::Either;
use hir::{AsAssocItem, AttributeTemplate, HasAttrs, HirDisplay, Semantics, TypeInfo}; use hir::{AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo};
use ide_db::{ use ide_db::{
base_db::SourceDatabase, base_db::SourceDatabase,
defs::Definition, defs::Definition,
famous_defs::FamousDefs, famous_defs::FamousDefs,
generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES}, generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
syntax_helpers::insert_whitespace_into_node,
RootDatabase, RootDatabase,
}; };
use itertools::Itertools; use itertools::Itertools;
@ -350,10 +351,24 @@ pub(super) fn definition(
let body = it.eval(db); let body = it.eval(db);
match body { match body {
Ok(x) => Some(format!("{}", x)), Ok(x) => Some(format!("{}", x)),
Err(_) => it.value(db).map(|x| format!("{}", x)), Err(_) => {
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::Static(it) => label_value_and_docs(db, it, |it| it.value(db)), Definition::Static(it) => label_value_and_docs(db, it, |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::Trait(it) => label_and_docs(db, it),
Definition::TypeAlias(it) => label_and_docs(db, it), Definition::TypeAlias(it) => label_and_docs(db, it),
Definition::BuiltinType(it) => { Definition::BuiltinType(it) => {

View file

@ -5113,3 +5113,61 @@ fn f() {
"#]], "#]],
); );
} }
#[test]
fn static_const_macro_expanded_body() {
check(
r#"
macro_rules! m {
() => {
pub const V: i8 = {
let e = 123;
f(e) // Prevent const eval from evaluating this constant, we want to print the body's code.
};
};
}
m!();
fn main() { $0V; }
"#,
expect![[r#"
*V*
```rust
test
```
```rust
pub const V: i8 = {
let e = 123;
f(e)
}
```
"#]],
);
check(
r#"
macro_rules! m {
() => {
pub static V: i8 = {
let e = 123;
};
};
}
m!();
fn main() { $0V; }
"#,
expect![[r#"
*V*
```rust
test
```
```rust
pub static V: i8 = {
let e = 123;
}
```
"#]],
);
}

View file

@ -1,3 +1,5 @@
use std::fmt;
use either::Either; use either::Either;
use hir::{known, Callable, HasVisibility, HirDisplay, Mutability, Semantics, TypeInfo}; use hir::{known, Callable, HasVisibility, HirDisplay, Mutability, Semantics, TypeInfo};
use ide_db::{ use ide_db::{
@ -69,7 +71,7 @@ pub enum InlayKind {
pub struct InlayHint { pub struct InlayHint {
pub range: TextRange, pub range: TextRange,
pub kind: InlayKind, pub kind: InlayKind,
pub label: String, pub label: InlayHintLabel,
pub tooltip: Option<InlayTooltip>, pub tooltip: Option<InlayTooltip>,
} }
@ -80,6 +82,83 @@ pub enum InlayTooltip {
HoverOffset(FileId, TextSize), HoverOffset(FileId, TextSize),
} }
pub struct InlayHintLabel {
pub parts: Vec<InlayHintLabelPart>,
}
impl InlayHintLabel {
pub fn as_simple_str(&self) -> Option<&str> {
match &*self.parts {
[part] => part.as_simple_str(),
_ => None,
}
}
pub fn prepend_str(&mut self, s: &str) {
match &mut *self.parts {
[part, ..] if part.as_simple_str().is_some() => part.text = format!("{s}{}", part.text),
_ => self.parts.insert(0, InlayHintLabelPart { text: s.into(), linked_location: None }),
}
}
pub fn append_str(&mut self, s: &str) {
match &mut *self.parts {
[.., part] if part.as_simple_str().is_some() => part.text.push_str(s),
_ => self.parts.push(InlayHintLabelPart { text: s.into(), linked_location: None }),
}
}
}
impl From<String> for InlayHintLabel {
fn from(s: String) -> Self {
Self { parts: vec![InlayHintLabelPart { text: s, linked_location: None }] }
}
}
impl fmt::Display for InlayHintLabel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.parts.iter().map(|part| &part.text).format(""))
}
}
impl fmt::Debug for InlayHintLabel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(&self.parts).finish()
}
}
pub struct InlayHintLabelPart {
pub text: String,
/// Source location represented by this label part. The client will use this to fetch the part's
/// hover tooltip, and Ctrl+Clicking the label part will navigate to the definition the location
/// refers to (not necessarily the location itself).
/// When setting this, no tooltip must be set on the containing hint, or VS Code will display
/// them both.
pub linked_location: Option<FileRange>,
}
impl InlayHintLabelPart {
pub fn as_simple_str(&self) -> Option<&str> {
match self {
Self { text, linked_location: None } => Some(text),
_ => None,
}
}
}
impl fmt::Debug for InlayHintLabelPart {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.as_simple_str() {
Some(string) => string.fmt(f),
None => f
.debug_struct("InlayHintLabelPart")
.field("text", &self.text)
.field("linked_location", &self.linked_location)
.finish(),
}
}
}
// Feature: Inlay Hints // Feature: Inlay Hints
// //
// rust-analyzer shows additional information inline with the source code. // rust-analyzer shows additional information inline with the source code.
@ -192,10 +271,10 @@ fn closing_brace_hints(
) -> Option<()> { ) -> Option<()> {
let min_lines = config.closing_brace_hints_min_lines?; let min_lines = config.closing_brace_hints_min_lines?;
let name = |it: ast::Name| it.syntax().text_range().start(); let name = |it: ast::Name| it.syntax().text_range();
let mut closing_token; let mut closing_token;
let (label, name_offset) = if let Some(item_list) = ast::AssocItemList::cast(node.clone()) { let (label, name_range) = if let Some(item_list) = ast::AssocItemList::cast(node.clone()) {
closing_token = item_list.r_curly_token()?; closing_token = item_list.r_curly_token()?;
let parent = item_list.syntax().parent()?; let parent = item_list.syntax().parent()?;
@ -205,11 +284,11 @@ fn closing_brace_hints(
let imp = sema.to_def(&imp)?; let imp = sema.to_def(&imp)?;
let ty = imp.self_ty(sema.db); let ty = imp.self_ty(sema.db);
let trait_ = imp.trait_(sema.db); let trait_ = imp.trait_(sema.db);
let hint_text = match trait_ {
(match trait_ {
Some(tr) => format!("impl {} for {}", tr.name(sema.db), ty.display_truncated(sema.db, config.max_length)), Some(tr) => format!("impl {} for {}", tr.name(sema.db), ty.display_truncated(sema.db, config.max_length)),
None => format!("impl {}", ty.display_truncated(sema.db, config.max_length)), None => format!("impl {}", ty.display_truncated(sema.db, config.max_length)),
}, None) };
(hint_text, None)
}, },
ast::Trait(tr) => { ast::Trait(tr) => {
(format!("trait {}", tr.name()?), tr.name().map(name)) (format!("trait {}", tr.name()?), tr.name().map(name))
@ -253,7 +332,7 @@ fn closing_brace_hints(
( (
format!("{}!", mac.path()?), format!("{}!", mac.path()?),
mac.path().and_then(|it| it.segment()).map(|it| it.syntax().text_range().start()), mac.path().and_then(|it| it.segment()).map(|it| it.syntax().text_range()),
) )
} else { } else {
return None; return None;
@ -278,11 +357,12 @@ fn closing_brace_hints(
return None; return None;
} }
let linked_location = name_range.map(|range| FileRange { file_id, range });
acc.push(InlayHint { acc.push(InlayHint {
range: closing_token.text_range(), range: closing_token.text_range(),
kind: InlayKind::ClosingBraceHint, kind: InlayKind::ClosingBraceHint,
label, label: InlayHintLabel { parts: vec![InlayHintLabelPart { text: label, linked_location }] },
tooltip: name_offset.map(|it| InlayTooltip::HoverOffset(file_id, it)), tooltip: None, // provided by label part location
}); });
None None
@ -311,7 +391,7 @@ fn implicit_static_hints(
acc.push(InlayHint { acc.push(InlayHint {
range: t.text_range(), range: t.text_range(),
kind: InlayKind::LifetimeHint, kind: InlayKind::LifetimeHint,
label: "'static".to_owned(), label: "'static".to_owned().into(),
tooltip: Some(InlayTooltip::String("Elided static lifetime".into())), tooltip: Some(InlayTooltip::String("Elided static lifetime".into())),
}); });
} }
@ -329,10 +409,10 @@ fn fn_lifetime_fn_hints(
return None; return None;
} }
let mk_lt_hint = |t: SyntaxToken, label| InlayHint { let mk_lt_hint = |t: SyntaxToken, label: String| InlayHint {
range: t.text_range(), range: t.text_range(),
kind: InlayKind::LifetimeHint, kind: InlayKind::LifetimeHint,
label, label: label.into(),
tooltip: Some(InlayTooltip::String("Elided lifetime".into())), tooltip: Some(InlayTooltip::String("Elided lifetime".into())),
}; };
@ -486,7 +566,8 @@ fn fn_lifetime_fn_hints(
"{}{}", "{}{}",
allocated_lifetimes.iter().format(", "), allocated_lifetimes.iter().format(", "),
if is_empty { "" } else { ", " } if is_empty { "" } else { ", " }
), )
.into(),
tooltip: Some(InlayTooltip::String("Elided lifetimes".into())), tooltip: Some(InlayTooltip::String("Elided lifetimes".into())),
}); });
} }
@ -535,7 +616,8 @@ fn closure_ret_hints(
range: param_list.syntax().text_range(), range: param_list.syntax().text_range(),
kind: InlayKind::ClosureReturnTypeHint, kind: InlayKind::ClosureReturnTypeHint,
label: hint_iterator(sema, &famous_defs, config, &ty) label: hint_iterator(sema, &famous_defs, config, &ty)
.unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string()), .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string())
.into(),
tooltip: Some(InlayTooltip::HoverRanged(file_id, param_list.syntax().text_range())), tooltip: Some(InlayTooltip::HoverRanged(file_id, param_list.syntax().text_range())),
}); });
Some(()) Some(())
@ -562,7 +644,7 @@ fn reborrow_hints(
acc.push(InlayHint { acc.push(InlayHint {
range: expr.syntax().text_range(), range: expr.syntax().text_range(),
kind: InlayKind::ImplicitReborrowHint, kind: InlayKind::ImplicitReborrowHint,
label: label.to_string(), label: label.to_string().into(),
tooltip: Some(InlayTooltip::String("Compiler inserted reborrow".into())), tooltip: Some(InlayTooltip::String("Compiler inserted reborrow".into())),
}); });
Some(()) Some(())
@ -620,9 +702,9 @@ fn chaining_hints(
acc.push(InlayHint { acc.push(InlayHint {
range: expr.syntax().text_range(), range: expr.syntax().text_range(),
kind: InlayKind::ChainingHint, kind: InlayKind::ChainingHint,
label: hint_iterator(sema, &famous_defs, config, &ty).unwrap_or_else(|| { label: hint_iterator(sema, &famous_defs, config, &ty)
ty.display_truncated(sema.db, config.max_length).to_string() .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string())
}), .into(),
tooltip: Some(InlayTooltip::HoverRanged(file_id, expr.syntax().text_range())), tooltip: Some(InlayTooltip::HoverRanged(file_id, expr.syntax().text_range())),
}); });
} }
@ -674,7 +756,7 @@ fn param_name_hints(
InlayHint { InlayHint {
range, range,
kind: InlayKind::ParameterHint, kind: InlayKind::ParameterHint,
label: param_name, label: param_name.into(),
tooltip: tooltip.map(|it| InlayTooltip::HoverOffset(it.file_id, it.range.start())), tooltip: tooltip.map(|it| InlayTooltip::HoverOffset(it.file_id, it.range.start())),
} }
}); });
@ -705,7 +787,7 @@ fn binding_mode_hints(
acc.push(InlayHint { acc.push(InlayHint {
range, range,
kind: InlayKind::BindingModeHint, kind: InlayKind::BindingModeHint,
label: r.to_string(), label: r.to_string().into(),
tooltip: Some(InlayTooltip::String("Inferred binding mode".into())), tooltip: Some(InlayTooltip::String("Inferred binding mode".into())),
}); });
}); });
@ -720,7 +802,7 @@ fn binding_mode_hints(
acc.push(InlayHint { acc.push(InlayHint {
range, range,
kind: InlayKind::BindingModeHint, kind: InlayKind::BindingModeHint,
label: bm.to_string(), label: bm.to_string().into(),
tooltip: Some(InlayTooltip::String("Inferred binding mode".into())), tooltip: Some(InlayTooltip::String("Inferred binding mode".into())),
}); });
} }
@ -772,7 +854,7 @@ fn bind_pat_hints(
None => pat.syntax().text_range(), None => pat.syntax().text_range(),
}, },
kind: InlayKind::TypeHint, kind: InlayKind::TypeHint,
label, label: label.into(),
tooltip: pat tooltip: pat
.name() .name()
.map(|it| it.syntax().text_range()) .map(|it| it.syntax().text_range())
@ -2223,7 +2305,9 @@ fn main() {
InlayHint { InlayHint {
range: 147..172, range: 147..172,
kind: ChainingHint, kind: ChainingHint,
label: "B", label: [
"B",
],
tooltip: Some( tooltip: Some(
HoverRanged( HoverRanged(
FileId( FileId(
@ -2236,7 +2320,9 @@ fn main() {
InlayHint { InlayHint {
range: 147..154, range: 147..154,
kind: ChainingHint, kind: ChainingHint,
label: "A", label: [
"A",
],
tooltip: Some( tooltip: Some(
HoverRanged( HoverRanged(
FileId( FileId(
@ -2294,7 +2380,9 @@ fn main() {
InlayHint { InlayHint {
range: 143..190, range: 143..190,
kind: ChainingHint, kind: ChainingHint,
label: "C", label: [
"C",
],
tooltip: Some( tooltip: Some(
HoverRanged( HoverRanged(
FileId( FileId(
@ -2307,7 +2395,9 @@ fn main() {
InlayHint { InlayHint {
range: 143..179, range: 143..179,
kind: ChainingHint, kind: ChainingHint,
label: "B", label: [
"B",
],
tooltip: Some( tooltip: Some(
HoverRanged( HoverRanged(
FileId( FileId(
@ -2350,7 +2440,9 @@ fn main() {
InlayHint { InlayHint {
range: 246..283, range: 246..283,
kind: ChainingHint, kind: ChainingHint,
label: "B<X<i32, bool>>", label: [
"B<X<i32, bool>>",
],
tooltip: Some( tooltip: Some(
HoverRanged( HoverRanged(
FileId( FileId(
@ -2363,7 +2455,9 @@ fn main() {
InlayHint { InlayHint {
range: 246..265, range: 246..265,
kind: ChainingHint, kind: ChainingHint,
label: "A<X<i32, bool>>", label: [
"A<X<i32, bool>>",
],
tooltip: Some( tooltip: Some(
HoverRanged( HoverRanged(
FileId( FileId(
@ -2408,7 +2502,9 @@ fn main() {
InlayHint { InlayHint {
range: 174..241, range: 174..241,
kind: ChainingHint, kind: ChainingHint,
label: "impl Iterator<Item = ()>", label: [
"impl Iterator<Item = ()>",
],
tooltip: Some( tooltip: Some(
HoverRanged( HoverRanged(
FileId( FileId(
@ -2421,7 +2517,9 @@ fn main() {
InlayHint { InlayHint {
range: 174..224, range: 174..224,
kind: ChainingHint, kind: ChainingHint,
label: "impl Iterator<Item = ()>", label: [
"impl Iterator<Item = ()>",
],
tooltip: Some( tooltip: Some(
HoverRanged( HoverRanged(
FileId( FileId(
@ -2434,7 +2532,9 @@ fn main() {
InlayHint { InlayHint {
range: 174..206, range: 174..206,
kind: ChainingHint, kind: ChainingHint,
label: "impl Iterator<Item = ()>", label: [
"impl Iterator<Item = ()>",
],
tooltip: Some( tooltip: Some(
HoverRanged( HoverRanged(
FileId( FileId(
@ -2447,7 +2547,9 @@ fn main() {
InlayHint { InlayHint {
range: 174..189, range: 174..189,
kind: ChainingHint, kind: ChainingHint,
label: "&mut MyIter", label: [
"&mut MyIter",
],
tooltip: Some( tooltip: Some(
HoverRanged( HoverRanged(
FileId( FileId(
@ -2489,7 +2591,9 @@ fn main() {
InlayHint { InlayHint {
range: 124..130, range: 124..130,
kind: TypeHint, kind: TypeHint,
label: "Struct", label: [
"Struct",
],
tooltip: Some( tooltip: Some(
HoverRanged( HoverRanged(
FileId( FileId(
@ -2502,7 +2606,9 @@ fn main() {
InlayHint { InlayHint {
range: 145..185, range: 145..185,
kind: ChainingHint, kind: ChainingHint,
label: "Struct", label: [
"Struct",
],
tooltip: Some( tooltip: Some(
HoverRanged( HoverRanged(
FileId( FileId(
@ -2515,7 +2621,9 @@ fn main() {
InlayHint { InlayHint {
range: 145..168, range: 145..168,
kind: ChainingHint, kind: ChainingHint,
label: "Struct", label: [
"Struct",
],
tooltip: Some( tooltip: Some(
HoverRanged( HoverRanged(
FileId( FileId(
@ -2528,7 +2636,9 @@ fn main() {
InlayHint { InlayHint {
range: 222..228, range: 222..228,
kind: ParameterHint, kind: ParameterHint,
label: "self", label: [
"self",
],
tooltip: Some( tooltip: Some(
HoverOffset( HoverOffset(
FileId( FileId(

View file

@ -82,8 +82,8 @@ pub use crate::{
highlight_related::{HighlightRelatedConfig, HighlightedRange}, highlight_related::{HighlightRelatedConfig, HighlightedRange},
hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult}, hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult},
inlay_hints::{ inlay_hints::{
ClosureReturnTypeHints, InlayHint, InlayHintsConfig, InlayKind, InlayTooltip, ClosureReturnTypeHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind,
LifetimeElisionHints, ReborrowHints, InlayTooltip, LifetimeElisionHints, ReborrowHints,
}, },
join_lines::JoinLinesConfig, join_lines::JoinLinesConfig,
markup::Markup, markup::Markup,

View file

@ -1362,7 +1362,7 @@ pub(crate) fn handle_inlay_hints(
.map(|it| { .map(|it| {
to_proto::inlay_hint(&snap, &line_index, inlay_hints_config.render_colons, it) to_proto::inlay_hint(&snap, &line_index, inlay_hints_config.render_colons, it)
}) })
.collect(), .collect::<Result<Vec<_>>>()?,
)) ))
} }

View file

@ -314,7 +314,9 @@ impl GlobalState {
let mut args = args.clone(); let mut args = args.clone();
let mut path = path.clone(); let mut path = path.clone();
if let ProjectWorkspace::Cargo { sysroot, .. } = ws { if let ProjectWorkspace::Cargo { sysroot, .. }
| ProjectWorkspace::Json { sysroot, .. } = ws
{
tracing::debug!("Found a cargo workspace..."); tracing::debug!("Found a cargo workspace...");
if let Some(sysroot) = sysroot.as_ref() { if let Some(sysroot) = sysroot.as_ref() {
tracing::debug!("Found a cargo workspace with a sysroot..."); tracing::debug!("Found a cargo workspace with a sysroot...");

View file

@ -9,8 +9,9 @@ use ide::{
Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionItem, Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionItem,
CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit,
Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint, Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint,
InlayKind, Markup, NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, InlayHintLabel, InlayKind, Markup, NavigationTarget, ReferenceCategory, RenameError, Runnable,
SignatureHelp, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, Severity, SignatureHelp, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange,
TextSize,
}; };
use itertools::Itertools; use itertools::Itertools;
use serde_json::to_value; use serde_json::to_value;
@ -426,9 +427,16 @@ pub(crate) fn inlay_hint(
snap: &GlobalStateSnapshot, snap: &GlobalStateSnapshot,
line_index: &LineIndex, line_index: &LineIndex,
render_colons: bool, render_colons: bool,
inlay_hint: InlayHint, mut inlay_hint: InlayHint,
) -> lsp_types::InlayHint { ) -> Result<lsp_types::InlayHint> {
lsp_types::InlayHint { match inlay_hint.kind {
InlayKind::ParameterHint if render_colons => inlay_hint.label.append_str(":"),
InlayKind::TypeHint if render_colons => inlay_hint.label.prepend_str(": "),
InlayKind::ClosureReturnTypeHint => inlay_hint.label.prepend_str(" -> "),
_ => {}
}
Ok(lsp_types::InlayHint {
position: match inlay_hint.kind { position: match inlay_hint.kind {
// before annotated thing // before annotated thing
InlayKind::ParameterHint InlayKind::ParameterHint
@ -459,15 +467,9 @@ pub(crate) fn inlay_hint(
| InlayKind::ImplicitReborrowHint | InlayKind::ImplicitReborrowHint
| InlayKind::TypeHint | InlayKind::TypeHint
| InlayKind::ClosingBraceHint => false, | InlayKind::ClosingBraceHint => false,
InlayKind::BindingModeHint => inlay_hint.label != "&", InlayKind::BindingModeHint => inlay_hint.label.as_simple_str() != Some("&"),
InlayKind::ParameterHint | InlayKind::LifetimeHint => true, InlayKind::ParameterHint | InlayKind::LifetimeHint => true,
}), }),
label: lsp_types::InlayHintLabel::String(match inlay_hint.kind {
InlayKind::ParameterHint if render_colons => format!("{}:", inlay_hint.label),
InlayKind::TypeHint if render_colons => format!(": {}", inlay_hint.label),
InlayKind::ClosureReturnTypeHint => format!(" -> {}", inlay_hint.label),
_ => inlay_hint.label.clone(),
}),
kind: match inlay_hint.kind { kind: match inlay_hint.kind {
InlayKind::ParameterHint => Some(lsp_types::InlayHintKind::PARAMETER), InlayKind::ParameterHint => Some(lsp_types::InlayHintKind::PARAMETER),
InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint => { InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint => {
@ -506,9 +508,36 @@ pub(crate) fn inlay_hint(
})(), })(),
tooltip: Some(match inlay_hint.tooltip { tooltip: Some(match inlay_hint.tooltip {
Some(ide::InlayTooltip::String(s)) => lsp_types::InlayHintTooltip::String(s), Some(ide::InlayTooltip::String(s)) => lsp_types::InlayHintTooltip::String(s),
_ => lsp_types::InlayHintTooltip::String(inlay_hint.label), _ => lsp_types::InlayHintTooltip::String(inlay_hint.label.to_string()),
}), }),
} label: inlay_hint_label(snap, inlay_hint.label)?,
})
}
fn inlay_hint_label(
snap: &GlobalStateSnapshot,
label: InlayHintLabel,
) -> Result<lsp_types::InlayHintLabel> {
Ok(match label.as_simple_str() {
Some(s) => lsp_types::InlayHintLabel::String(s.into()),
None => lsp_types::InlayHintLabel::LabelParts(
label
.parts
.into_iter()
.map(|part| {
Ok(lsp_types::InlayHintLabelPart {
value: part.text,
tooltip: None,
location: part
.linked_location
.map(|range| location(snap, range))
.transpose()?,
command: None,
})
})
.collect::<Result<Vec<_>>>()?,
),
})
} }
static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1); static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1);

View file

@ -248,8 +248,12 @@ impl ast::WhereClause {
} }
} }
impl ast::TypeBoundList { pub trait Removable: AstNode {
pub fn remove(&self) { fn remove(&self);
}
impl Removable for ast::TypeBoundList {
fn remove(&self) {
match self.syntax().siblings_with_tokens(Direction::Prev).find(|it| it.kind() == T![:]) { match self.syntax().siblings_with_tokens(Direction::Prev).find(|it| it.kind() == T![:]) {
Some(colon) => ted::remove_all(colon..=self.syntax().clone().into()), Some(colon) => ted::remove_all(colon..=self.syntax().clone().into()),
None => ted::remove(self.syntax()), None => ted::remove(self.syntax()),
@ -267,8 +271,8 @@ impl ast::PathSegment {
} }
} }
impl ast::UseTree { impl Removable for ast::UseTree {
pub fn remove(&self) { fn remove(&self) {
for dir in [Direction::Next, Direction::Prev] { for dir in [Direction::Next, Direction::Prev] {
if let Some(next_use_tree) = neighbor(self, dir) { if let Some(next_use_tree) = neighbor(self, dir) {
let separators = self let separators = self
@ -282,7 +286,9 @@ impl ast::UseTree {
} }
ted::remove(self.syntax()); ted::remove(self.syntax());
} }
}
impl ast::UseTree {
pub fn get_or_create_use_tree_list(&self) -> ast::UseTreeList { pub fn get_or_create_use_tree_list(&self) -> ast::UseTreeList {
match self.use_tree_list() { match self.use_tree_list() {
Some(it) => it, Some(it) => it,
@ -373,8 +379,8 @@ impl ast::UseTreeList {
} }
} }
impl ast::Use { impl Removable for ast::Use {
pub fn remove(&self) { fn remove(&self) {
let next_ws = self let next_ws = self
.syntax() .syntax()
.next_sibling_or_token() .next_sibling_or_token()
@ -444,8 +450,8 @@ impl ast::Fn {
} }
} }
impl ast::MatchArm { impl Removable for ast::MatchArm {
pub fn remove(&self) { fn remove(&self) {
if let Some(sibling) = self.syntax().prev_sibling_or_token() { if let Some(sibling) = self.syntax().prev_sibling_or_token() {
if sibling.kind() == SyntaxKind::WHITESPACE { if sibling.kind() == SyntaxKind::WHITESPACE {
ted::remove(sibling); ted::remove(sibling);