mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-27 12:29:21 +00:00
Properly account for editions in names
This PR touches a lot of parts. But the main changes are changing `hir_expand::Name` to be raw edition-dependently and only when necessary (unrelated to how the user originally wrote the identifier), and changing `is_keyword()` and `is_raw_identifier()` to be edition-aware (this was done in #17896, but the FIXMEs were fixed here). It is possible that I missed some cases, but most IDE parts should properly escape (or not escape) identifiers now. The rules of thumb are: - If we show the identifier to the user, its rawness should be determined by the edition of the edited crate. This is nice for IDE features, but really important for changes we insert to the source code. - For tests, I chose `Edition::CURRENT` (so we only have to (maybe) update tests when an edition becomes stable, to avoid churn). - For debugging tools (helper methods and logs), I used `Edition::LATEST`.
This commit is contained in:
parent
91aa3f46b3
commit
9d3368f2c2
179 changed files with 2485 additions and 1250 deletions
|
@ -13,6 +13,7 @@ use ide_db::{
|
|||
documentation::{Documentation, HasDocs},
|
||||
FilePosition, FxIndexMap,
|
||||
};
|
||||
use span::Edition;
|
||||
use stdx::format_to;
|
||||
use syntax::{
|
||||
algo,
|
||||
|
@ -82,6 +83,8 @@ pub(crate) fn signature_help(
|
|||
// this prevents us from leaving the CallExpression
|
||||
.and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?;
|
||||
let token = sema.descend_into_macros_single(DescendPreference::None, token);
|
||||
let edition =
|
||||
sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT);
|
||||
|
||||
for node in token.parent_ancestors() {
|
||||
match_ast! {
|
||||
|
@ -91,49 +94,49 @@ pub(crate) fn signature_help(
|
|||
if cursor_outside {
|
||||
continue;
|
||||
}
|
||||
return signature_help_for_call(&sema, arg_list, token);
|
||||
return signature_help_for_call(&sema, arg_list, token, edition);
|
||||
},
|
||||
ast::GenericArgList(garg_list) => {
|
||||
let cursor_outside = garg_list.r_angle_token().as_ref() == Some(&token);
|
||||
if cursor_outside {
|
||||
continue;
|
||||
}
|
||||
return signature_help_for_generics(&sema, garg_list, token);
|
||||
return signature_help_for_generics(&sema, garg_list, token, edition);
|
||||
},
|
||||
ast::RecordExpr(record) => {
|
||||
let cursor_outside = record.record_expr_field_list().and_then(|list| list.r_curly_token()).as_ref() == Some(&token);
|
||||
if cursor_outside {
|
||||
continue;
|
||||
}
|
||||
return signature_help_for_record_lit(&sema, record, token);
|
||||
return signature_help_for_record_lit(&sema, record, token, edition);
|
||||
},
|
||||
ast::RecordPat(record) => {
|
||||
let cursor_outside = record.record_pat_field_list().and_then(|list| list.r_curly_token()).as_ref() == Some(&token);
|
||||
if cursor_outside {
|
||||
continue;
|
||||
}
|
||||
return signature_help_for_record_pat(&sema, record, token);
|
||||
return signature_help_for_record_pat(&sema, record, token, edition);
|
||||
},
|
||||
ast::TupleStructPat(tuple_pat) => {
|
||||
let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token);
|
||||
if cursor_outside {
|
||||
continue;
|
||||
}
|
||||
return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token);
|
||||
return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token, edition);
|
||||
},
|
||||
ast::TuplePat(tuple_pat) => {
|
||||
let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token);
|
||||
if cursor_outside {
|
||||
continue;
|
||||
}
|
||||
return signature_help_for_tuple_pat(&sema, tuple_pat, token);
|
||||
return signature_help_for_tuple_pat(&sema, tuple_pat, token, edition);
|
||||
},
|
||||
ast::TupleExpr(tuple_expr) => {
|
||||
let cursor_outside = tuple_expr.r_paren_token().as_ref() == Some(&token);
|
||||
if cursor_outside {
|
||||
continue;
|
||||
}
|
||||
return signature_help_for_tuple_expr(&sema, tuple_expr, token);
|
||||
return signature_help_for_tuple_expr(&sema, tuple_expr, token, edition);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
@ -157,6 +160,7 @@ fn signature_help_for_call(
|
|||
sema: &Semantics<'_, RootDatabase>,
|
||||
arg_list: ast::ArgList,
|
||||
token: SyntaxToken,
|
||||
edition: Edition,
|
||||
) -> Option<SignatureHelp> {
|
||||
// Find the calling expression and its NameRef
|
||||
let mut nodes = arg_list.syntax().ancestors().skip(1);
|
||||
|
@ -181,7 +185,7 @@ fn signature_help_for_call(
|
|||
match callable.kind() {
|
||||
hir::CallableKind::Function(func) => {
|
||||
res.doc = func.docs(db);
|
||||
format_to!(res.signature, "fn {}", func.name(db).display(db));
|
||||
format_to!(res.signature, "fn {}", func.name(db).display(db, edition));
|
||||
fn_params = Some(match callable.receiver_param(db) {
|
||||
Some(_self) => func.params_without_self(db),
|
||||
None => func.assoc_fn_params(db),
|
||||
|
@ -189,15 +193,15 @@ fn signature_help_for_call(
|
|||
}
|
||||
hir::CallableKind::TupleStruct(strukt) => {
|
||||
res.doc = strukt.docs(db);
|
||||
format_to!(res.signature, "struct {}", strukt.name(db).display(db));
|
||||
format_to!(res.signature, "struct {}", strukt.name(db).display(db, edition));
|
||||
}
|
||||
hir::CallableKind::TupleEnumVariant(variant) => {
|
||||
res.doc = variant.docs(db);
|
||||
format_to!(
|
||||
res.signature,
|
||||
"enum {}::{}",
|
||||
variant.parent_enum(db).name(db).display(db),
|
||||
variant.name(db).display(db)
|
||||
variant.parent_enum(db).name(db).display(db, edition),
|
||||
variant.name(db).display(db, edition)
|
||||
);
|
||||
}
|
||||
hir::CallableKind::Closure(closure) => {
|
||||
|
@ -210,7 +214,7 @@ fn signature_help_for_call(
|
|||
Some(adt) => format_to!(
|
||||
res.signature,
|
||||
"<{} as {fn_trait}>::{}",
|
||||
adt.name(db).display(db),
|
||||
adt.name(db).display(db, edition),
|
||||
fn_trait.function_name()
|
||||
),
|
||||
None => format_to!(res.signature, "impl {fn_trait}"),
|
||||
|
@ -220,7 +224,7 @@ fn signature_help_for_call(
|
|||
res.signature.push('(');
|
||||
{
|
||||
if let Some((self_param, _)) = callable.receiver_param(db) {
|
||||
format_to!(res.signature, "{}", self_param.display(db))
|
||||
format_to!(res.signature, "{}", self_param.display(db, edition))
|
||||
}
|
||||
let mut buf = String::new();
|
||||
for (idx, p) in callable.params().into_iter().enumerate() {
|
||||
|
@ -240,8 +244,10 @@ fn signature_help_for_call(
|
|||
// This is overly conservative: we do not substitute known type vars
|
||||
// (see FIXME in tests::impl_trait) and falling back on any unknowns.
|
||||
match (p.ty().contains_unknown(), fn_params.as_deref()) {
|
||||
(true, Some(fn_params)) => format_to!(buf, "{}", fn_params[idx].ty().display(db)),
|
||||
_ => format_to!(buf, "{}", p.ty().display(db)),
|
||||
(true, Some(fn_params)) => {
|
||||
format_to!(buf, "{}", fn_params[idx].ty().display(db, edition))
|
||||
}
|
||||
_ => format_to!(buf, "{}", p.ty().display(db, edition)),
|
||||
}
|
||||
res.push_call_param(&buf);
|
||||
}
|
||||
|
@ -250,7 +256,7 @@ fn signature_help_for_call(
|
|||
|
||||
let mut render = |ret_type: hir::Type| {
|
||||
if !ret_type.is_unit() {
|
||||
format_to!(res.signature, " -> {}", ret_type.display(db));
|
||||
format_to!(res.signature, " -> {}", ret_type.display(db, edition));
|
||||
}
|
||||
};
|
||||
match callable.kind() {
|
||||
|
@ -270,6 +276,7 @@ fn signature_help_for_generics(
|
|||
sema: &Semantics<'_, RootDatabase>,
|
||||
arg_list: ast::GenericArgList,
|
||||
token: SyntaxToken,
|
||||
edition: Edition,
|
||||
) -> Option<SignatureHelp> {
|
||||
let (generics_def, mut active_parameter, first_arg_is_non_lifetime, variant) =
|
||||
generic_def_for_node(sema, &arg_list, &token)?;
|
||||
|
@ -284,11 +291,11 @@ fn signature_help_for_generics(
|
|||
match generics_def {
|
||||
hir::GenericDef::Function(it) => {
|
||||
res.doc = it.docs(db);
|
||||
format_to!(res.signature, "fn {}", it.name(db).display(db));
|
||||
format_to!(res.signature, "fn {}", it.name(db).display(db, edition));
|
||||
}
|
||||
hir::GenericDef::Adt(hir::Adt::Enum(it)) => {
|
||||
res.doc = it.docs(db);
|
||||
format_to!(res.signature, "enum {}", it.name(db).display(db));
|
||||
format_to!(res.signature, "enum {}", it.name(db).display(db, edition));
|
||||
if let Some(variant) = variant {
|
||||
// In paths, generics of an enum can be specified *after* one of its variants.
|
||||
// eg. `None::<u8>`
|
||||
|
@ -298,23 +305,23 @@ fn signature_help_for_generics(
|
|||
}
|
||||
hir::GenericDef::Adt(hir::Adt::Struct(it)) => {
|
||||
res.doc = it.docs(db);
|
||||
format_to!(res.signature, "struct {}", it.name(db).display(db));
|
||||
format_to!(res.signature, "struct {}", it.name(db).display(db, edition));
|
||||
}
|
||||
hir::GenericDef::Adt(hir::Adt::Union(it)) => {
|
||||
res.doc = it.docs(db);
|
||||
format_to!(res.signature, "union {}", it.name(db).display(db));
|
||||
format_to!(res.signature, "union {}", it.name(db).display(db, edition));
|
||||
}
|
||||
hir::GenericDef::Trait(it) => {
|
||||
res.doc = it.docs(db);
|
||||
format_to!(res.signature, "trait {}", it.name(db).display(db));
|
||||
format_to!(res.signature, "trait {}", it.name(db).display(db, edition));
|
||||
}
|
||||
hir::GenericDef::TraitAlias(it) => {
|
||||
res.doc = it.docs(db);
|
||||
format_to!(res.signature, "trait {}", it.name(db).display(db));
|
||||
format_to!(res.signature, "trait {}", it.name(db).display(db, edition));
|
||||
}
|
||||
hir::GenericDef::TypeAlias(it) => {
|
||||
res.doc = it.docs(db);
|
||||
format_to!(res.signature, "type {}", it.name(db).display(db));
|
||||
format_to!(res.signature, "type {}", it.name(db).display(db, edition));
|
||||
}
|
||||
// These don't have generic args that can be specified
|
||||
hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) => return None,
|
||||
|
@ -339,11 +346,11 @@ fn signature_help_for_generics(
|
|||
}
|
||||
|
||||
buf.clear();
|
||||
format_to!(buf, "{}", param.display(db));
|
||||
format_to!(buf, "{}", param.display(db, edition));
|
||||
res.push_generic_param(&buf);
|
||||
}
|
||||
if let hir::GenericDef::Trait(tr) = generics_def {
|
||||
add_assoc_type_bindings(db, &mut res, tr, arg_list);
|
||||
add_assoc_type_bindings(db, &mut res, tr, arg_list, edition);
|
||||
}
|
||||
res.signature.push('>');
|
||||
|
||||
|
@ -355,6 +362,7 @@ fn add_assoc_type_bindings(
|
|||
res: &mut SignatureHelp,
|
||||
tr: Trait,
|
||||
args: ast::GenericArgList,
|
||||
edition: Edition,
|
||||
) {
|
||||
if args.syntax().ancestors().find_map(ast::TypeBound::cast).is_none() {
|
||||
// Assoc type bindings are only valid in type bound position.
|
||||
|
@ -378,7 +386,7 @@ fn add_assoc_type_bindings(
|
|||
|
||||
for item in tr.items_with_supertraits(db) {
|
||||
if let AssocItem::TypeAlias(ty) = item {
|
||||
let name = ty.name(db).display_no_db().to_smolstr();
|
||||
let name = ty.name(db).display_no_db(edition).to_smolstr();
|
||||
if !present_bindings.contains(&*name) {
|
||||
buf.clear();
|
||||
format_to!(buf, "{} = …", name);
|
||||
|
@ -392,6 +400,7 @@ fn signature_help_for_record_lit(
|
|||
sema: &Semantics<'_, RootDatabase>,
|
||||
record: ast::RecordExpr,
|
||||
token: SyntaxToken,
|
||||
edition: Edition,
|
||||
) -> Option<SignatureHelp> {
|
||||
signature_help_for_record_(
|
||||
sema,
|
||||
|
@ -403,6 +412,7 @@ fn signature_help_for_record_lit(
|
|||
.filter_map(|field| sema.resolve_record_field(&field))
|
||||
.map(|(field, _, ty)| (field, ty)),
|
||||
token,
|
||||
edition,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -410,6 +420,7 @@ fn signature_help_for_record_pat(
|
|||
sema: &Semantics<'_, RootDatabase>,
|
||||
record: ast::RecordPat,
|
||||
token: SyntaxToken,
|
||||
edition: Edition,
|
||||
) -> Option<SignatureHelp> {
|
||||
signature_help_for_record_(
|
||||
sema,
|
||||
|
@ -420,6 +431,7 @@ fn signature_help_for_record_pat(
|
|||
.fields()
|
||||
.filter_map(|field| sema.resolve_record_pat_field(&field)),
|
||||
token,
|
||||
edition,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -427,6 +439,7 @@ fn signature_help_for_tuple_struct_pat(
|
|||
sema: &Semantics<'_, RootDatabase>,
|
||||
pat: ast::TupleStructPat,
|
||||
token: SyntaxToken,
|
||||
edition: Edition,
|
||||
) -> Option<SignatureHelp> {
|
||||
let path = pat.path()?;
|
||||
let path_res = sema.resolve_path(&path)?;
|
||||
|
@ -445,8 +458,8 @@ fn signature_help_for_tuple_struct_pat(
|
|||
format_to!(
|
||||
res.signature,
|
||||
"enum {}::{} (",
|
||||
en.name(db).display(db),
|
||||
variant.name(db).display(db)
|
||||
en.name(db).display(db, edition),
|
||||
variant.name(db).display(db, edition)
|
||||
);
|
||||
variant.fields(db)
|
||||
} else {
|
||||
|
@ -459,7 +472,7 @@ fn signature_help_for_tuple_struct_pat(
|
|||
match adt {
|
||||
hir::Adt::Struct(it) => {
|
||||
res.doc = it.docs(db);
|
||||
format_to!(res.signature, "struct {} (", it.name(db).display(db));
|
||||
format_to!(res.signature, "struct {} (", it.name(db).display(db, edition));
|
||||
it.fields(db)
|
||||
}
|
||||
_ => return None,
|
||||
|
@ -472,6 +485,7 @@ fn signature_help_for_tuple_struct_pat(
|
|||
token,
|
||||
pat.fields(),
|
||||
fields.into_iter().map(|it| it.ty(db)),
|
||||
edition,
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -479,6 +493,7 @@ fn signature_help_for_tuple_pat(
|
|||
sema: &Semantics<'_, RootDatabase>,
|
||||
pat: ast::TuplePat,
|
||||
token: SyntaxToken,
|
||||
edition: Edition,
|
||||
) -> Option<SignatureHelp> {
|
||||
let db = sema.db;
|
||||
let field_pats = pat.fields();
|
||||
|
@ -498,6 +513,7 @@ fn signature_help_for_tuple_pat(
|
|||
token,
|
||||
field_pats,
|
||||
fields.into_iter(),
|
||||
edition,
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -505,6 +521,7 @@ fn signature_help_for_tuple_expr(
|
|||
sema: &Semantics<'_, RootDatabase>,
|
||||
expr: ast::TupleExpr,
|
||||
token: SyntaxToken,
|
||||
edition: Edition,
|
||||
) -> Option<SignatureHelp> {
|
||||
let active_parameter = Some(
|
||||
expr.syntax()
|
||||
|
@ -526,7 +543,7 @@ fn signature_help_for_tuple_expr(
|
|||
let fields = expr.original.tuple_fields(db);
|
||||
let mut buf = String::new();
|
||||
for ty in fields {
|
||||
format_to!(buf, "{}", ty.display_truncated(db, Some(20)));
|
||||
format_to!(buf, "{}", ty.display_truncated(db, Some(20), edition));
|
||||
res.push_call_param(&buf);
|
||||
buf.clear();
|
||||
}
|
||||
|
@ -540,6 +557,7 @@ fn signature_help_for_record_(
|
|||
path: &ast::Path,
|
||||
fields2: impl Iterator<Item = (hir::Field, hir::Type)>,
|
||||
token: SyntaxToken,
|
||||
edition: Edition,
|
||||
) -> Option<SignatureHelp> {
|
||||
let active_parameter = field_list_children
|
||||
.filter_map(NodeOrToken::into_token)
|
||||
|
@ -566,8 +584,8 @@ fn signature_help_for_record_(
|
|||
format_to!(
|
||||
res.signature,
|
||||
"enum {}::{} {{ ",
|
||||
en.name(db).display(db),
|
||||
variant.name(db).display(db)
|
||||
en.name(db).display(db, edition),
|
||||
variant.name(db).display(db, edition)
|
||||
);
|
||||
} else {
|
||||
let adt = match path_res {
|
||||
|
@ -580,12 +598,12 @@ fn signature_help_for_record_(
|
|||
hir::Adt::Struct(it) => {
|
||||
fields = it.fields(db);
|
||||
res.doc = it.docs(db);
|
||||
format_to!(res.signature, "struct {} {{ ", it.name(db).display(db));
|
||||
format_to!(res.signature, "struct {} {{ ", it.name(db).display(db, edition));
|
||||
}
|
||||
hir::Adt::Union(it) => {
|
||||
fields = it.fields(db);
|
||||
res.doc = it.docs(db);
|
||||
format_to!(res.signature, "union {} {{ ", it.name(db).display(db));
|
||||
format_to!(res.signature, "union {} {{ ", it.name(db).display(db, edition));
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
|
@ -596,7 +614,12 @@ fn signature_help_for_record_(
|
|||
let mut buf = String::new();
|
||||
for (field, ty) in fields2 {
|
||||
let name = field.name(db);
|
||||
format_to!(buf, "{}: {}", name.display(db), ty.display_truncated(db, Some(20)));
|
||||
format_to!(
|
||||
buf,
|
||||
"{}: {}",
|
||||
name.display(db, edition),
|
||||
ty.display_truncated(db, Some(20), edition)
|
||||
);
|
||||
res.push_record_field(&buf);
|
||||
buf.clear();
|
||||
|
||||
|
@ -606,7 +629,12 @@ fn signature_help_for_record_(
|
|||
}
|
||||
for (name, field) in fields {
|
||||
let Some(field) = field else { continue };
|
||||
format_to!(buf, "{}: {}", name.display(db), field.ty(db).display_truncated(db, Some(20)));
|
||||
format_to!(
|
||||
buf,
|
||||
"{}: {}",
|
||||
name.display(db, edition),
|
||||
field.ty(db).display_truncated(db, Some(20), edition)
|
||||
);
|
||||
res.push_record_field(&buf);
|
||||
buf.clear();
|
||||
}
|
||||
|
@ -621,6 +649,7 @@ fn signature_help_for_tuple_pat_ish(
|
|||
token: SyntaxToken,
|
||||
mut field_pats: AstChildren<ast::Pat>,
|
||||
fields: impl ExactSizeIterator<Item = hir::Type>,
|
||||
edition: Edition,
|
||||
) -> SignatureHelp {
|
||||
let rest_pat = field_pats.find(|it| matches!(it, ast::Pat::RestPat(_)));
|
||||
let is_left_of_rest_pat =
|
||||
|
@ -647,7 +676,7 @@ fn signature_help_for_tuple_pat_ish(
|
|||
|
||||
let mut buf = String::new();
|
||||
for ty in fields {
|
||||
format_to!(buf, "{}", ty.display_truncated(db, Some(20)));
|
||||
format_to!(buf, "{}", ty.display_truncated(db, Some(20), edition));
|
||||
res.push_call_param(&buf);
|
||||
buf.clear();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue