Auto merge of #17905 - ChayimFriedman2:edition-dependent-raw-keyword, r=Veykril

fix: 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`.

Reviewing notes:

This is a really big PR but most of it is mechanical translation. I changed `Name` displayers to require an edition, and followed the compiler errors. Most methods just propagate the edition requirement. The interesting cases are mostly in `ide-assists`, as sometimes the correct crate to fetch the edition from requires awareness (there may be two). `ide-completions` and `ide-diagnostics` were solved pretty easily by introducing an edition field to their context. `ide` contains many features, for most of them it was propagated to the top level function and there the edition was fetched based on the file.

I also fixed all FIXMEs from #17896. Some required introducing an edition parameter (usually not for many methods after the changes to `Name`), some were changed to a new method `is_any_identifier()` because they really want any possible keyword.

Fixes #17895.
Fixes #17774.
This commit is contained in:
bors 2024-08-16 13:49:32 +00:00
commit c9ee892263
179 changed files with 2485 additions and 1250 deletions

View file

@ -14,10 +14,11 @@ use hir::{
ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, ToolModule, Trait,
TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility,
};
use span::Edition;
use stdx::{format_to, impl_from};
use syntax::{
ast::{self, AstNode},
match_ast, SyntaxKind, SyntaxNode, SyntaxToken, ToSmolStr,
match_ast, SyntaxKind, SyntaxNode, SyntaxToken,
};
use crate::documentation::{Documentation, HasDocs};
@ -157,6 +158,7 @@ impl Definition {
&self,
db: &RootDatabase,
famous_defs: Option<&FamousDefs<'_, '_>>,
edition: Edition,
) -> Option<Documentation> {
let docs = match self {
Definition::Macro(it) => it.docs(db),
@ -173,8 +175,8 @@ impl Definition {
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)?;
let primitive_mod = format!("prim_{}", it.name().display(fd.0.db, edition));
let doc_owner = find_std_module(fd, &primitive_mod, edition)?;
doc_owner.docs(fd.0.db)
})
}
@ -192,13 +194,18 @@ impl Definition {
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.display(db));
format_to!(docs, "\n - #\\[{}]", name.display(db, edition));
}
if let Some(list) = list {
format_to!(docs, "\n - #\\[{}({})]", name.display(db), list);
format_to!(docs, "\n - #\\[{}({})]", name.display(db, edition), list);
}
if let Some(name_value_str) = name_value_str {
format_to!(docs, "\n - #\\[{} = {}]", name.display(db), name_value_str);
format_to!(
docs,
"\n - #\\[{} = {}]",
name.display(db, edition),
name_value_str
);
}
Some(Documentation::new(docs.replace('*', "\\*")))
}
@ -218,57 +225,63 @@ impl Definition {
})
}
pub fn label(&self, db: &RootDatabase) -> String {
pub fn label(&self, db: &RootDatabase, edition: Edition) -> String {
match *self {
Definition::Macro(it) => it.display(db).to_string(),
Definition::Field(it) => it.display(db).to_string(),
Definition::TupleField(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::BuiltinLifetime(it) => it.name().display(db).to_string(),
Definition::Macro(it) => it.display(db, edition).to_string(),
Definition::Field(it) => it.display(db, edition).to_string(),
Definition::TupleField(it) => it.display(db, edition).to_string(),
Definition::Module(it) => it.display(db, edition).to_string(),
Definition::Function(it) => it.display(db, edition).to_string(),
Definition::Adt(it) => it.display(db, edition).to_string(),
Definition::Variant(it) => it.display(db, edition).to_string(),
Definition::Const(it) => it.display(db, edition).to_string(),
Definition::Static(it) => it.display(db, edition).to_string(),
Definition::Trait(it) => it.display(db, edition).to_string(),
Definition::TraitAlias(it) => it.display(db, edition).to_string(),
Definition::TypeAlias(it) => it.display(db, edition).to_string(),
Definition::BuiltinType(it) => it.name().display(db, edition).to_string(),
Definition::BuiltinLifetime(it) => it.name().display(db, edition).to_string(),
Definition::Local(it) => {
let ty = it.ty(db);
let ty_display = ty.display_truncated(db, None);
let ty_display = ty.display_truncated(db, None, edition);
let is_mut = if it.is_mut(db) { "mut " } else { "" };
if it.is_self(db) {
format!("{is_mut}self: {ty_display}")
} else {
let name = it.name(db);
let let_kw = if it.is_param(db) { "" } else { "let " };
format!("{let_kw}{is_mut}{}: {ty_display}", name.display(db))
format!("{let_kw}{is_mut}{}: {ty_display}", name.display(db, edition))
}
}
Definition::SelfType(impl_def) => {
let self_ty = &impl_def.self_ty(db);
match self_ty.as_adt() {
Some(it) => it.display(db).to_string(),
None => self_ty.display(db).to_string(),
Some(it) => it.display(db, edition).to_string(),
None => self_ty.display(db, edition).to_string(),
}
}
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).display(db)),
Definition::ToolModule(it) => it.name(db).display(db).to_string(),
Definition::DeriveHelper(it) => format!("derive_helper {}", it.name(db).display(db)),
Definition::GenericParam(it) => it.display(db, edition).to_string(),
Definition::Label(it) => it.name(db).display(db, edition).to_string(),
Definition::ExternCrateDecl(it) => it.display(db, edition).to_string(),
Definition::BuiltinAttr(it) => format!("#[{}]", it.name(db).display(db, edition)),
Definition::ToolModule(it) => it.name(db).display(db, edition).to_string(),
Definition::DeriveHelper(it) => {
format!("derive_helper {}", it.name(db).display(db, edition))
}
}
}
}
fn find_std_module(famous_defs: &FamousDefs<'_, '_>, name: &str) -> Option<hir::Module> {
fn find_std_module(
famous_defs: &FamousDefs<'_, '_>,
name: &str,
edition: Edition,
) -> 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)
module.name(db).map_or(false, |module| module.display(db, edition).to_string() == name)
})
}
@ -670,7 +683,7 @@ impl NameRefClass {
hir::AssocItem::TypeAlias(it) => Some(it),
_ => None,
})
.find(|alias| alias.name(sema.db).display_no_db().to_smolstr() == name_ref.text().as_str())
.find(|alias| alias.name(sema.db).eq_ident(name_ref.text().as_str()))
{
return Some(NameRefClass::Definition(Definition::TypeAlias(ty)));
}

View file

@ -2,7 +2,6 @@
use base_db::{CrateOrigin, LangCrateOrigin, SourceDatabase};
use hir::{Crate, Enum, Function, Macro, Module, ScopeDef, Semantics, Trait};
use syntax::ToSmolStr;
use crate::RootDatabase;
@ -199,18 +198,14 @@ impl FamousDefs<'_, '_> {
for segment in path {
module = module.children(db).find_map(|child| {
let name = child.name(db)?;
if name.display_no_db().to_smolstr() == segment {
if name.eq_ident(segment) {
Some(child)
} else {
None
}
})?;
}
let def = module
.scope(db, None)
.into_iter()
.find(|(name, _def)| name.display_no_db().to_smolstr() == trait_)?
.1;
let def = module.scope(db, None).into_iter().find(|(name, _def)| name.eq_ident(trait_))?.1;
Some(def)
}
}

View file

@ -4,7 +4,7 @@ use std::collections::VecDeque;
use base_db::SourceRootDatabase;
use hir::{Crate, DescendPreference, ItemInNs, ModuleDef, Name, Semantics};
use span::FileId;
use span::{Edition, FileId};
use syntax::{
ast::{self, make},
AstToken, SyntaxKind, SyntaxToken, ToSmolStr, TokenAtOffset,
@ -35,7 +35,7 @@ pub fn pick_token<T: AstToken>(mut tokens: TokenAtOffset<SyntaxToken>) -> Option
}
/// Converts the mod path struct into its ast representation.
pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
pub fn mod_path_to_ast(path: &hir::ModPath, edition: Edition) -> ast::Path {
let _p = tracing::info_span!("mod_path_to_ast").entered();
let mut segments = Vec::new();
@ -50,11 +50,9 @@ pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
hir::PathKind::Abs => is_abs = true,
}
segments.extend(
path.segments().iter().map(|segment| {
make::path_segment(make::name_ref(&segment.display_no_db().to_smolstr()))
}),
);
segments.extend(path.segments().iter().map(|segment| {
make::path_segment(make::name_ref(&segment.display_no_db(edition).to_smolstr()))
}));
make::path_from_segments(segments, is_abs)
}

View file

@ -9,7 +9,7 @@ use itertools::{EitherOrBoth, Itertools};
use rustc_hash::{FxHashMap, FxHashSet};
use syntax::{
ast::{self, make, HasName},
AstNode, SmolStr, SyntaxNode, ToSmolStr,
AstNode, SmolStr, SyntaxNode,
};
use crate::{
@ -459,7 +459,7 @@ fn find_import_for_segment(
unresolved_first_segment: &str,
) -> Option<ItemInNs> {
let segment_is_name = item_name(db, original_item)
.map(|name| name.display_no_db().to_smolstr() == unresolved_first_segment)
.map(|name| name.eq_ident(unresolved_first_segment))
.unwrap_or(false);
Some(if segment_is_name {
@ -483,7 +483,7 @@ fn module_with_segment_name(
};
while let Some(module) = current_module {
if let Some(module_name) = module.name(db) {
if module_name.display_no_db().to_smolstr() == segment_name {
if module_name.eq_ident(segment_name) {
return Some(module);
}
}

View file

@ -5,6 +5,7 @@ use either::Either;
use hir::{AsAssocItem, HirDisplay, ImportPathConfig, ModuleDef, SemanticsScope};
use itertools::Itertools;
use rustc_hash::FxHashMap;
use span::Edition;
use syntax::{
ast::{self, make, AstNode, HasGenericArgs},
ted, NodeOrToken, SyntaxNode,
@ -146,6 +147,7 @@ impl<'a> PathTransform<'a> {
let mut type_substs: FxHashMap<hir::TypeParam, ast::Type> = Default::default();
let mut const_substs: FxHashMap<hir::ConstParam, SyntaxNode> = Default::default();
let mut defaulted_params: Vec<DefaultedParam> = Default::default();
let target_edition = target_module.krate().edition(self.source_scope.db);
self.generic_def
.into_iter()
.flat_map(|it| it.type_or_const_params(db))
@ -190,7 +192,7 @@ impl<'a> PathTransform<'a> {
}
}
(Either::Left(k), None) => {
if let Some(default) = k.default(db) {
if let Some(default) = k.default(db, target_edition) {
if let Some(default) = default.expr() {
const_substs.insert(k, default.syntax().clone_for_update());
defaulted_params.push(Either::Right(k));
@ -204,7 +206,9 @@ impl<'a> PathTransform<'a> {
.into_iter()
.flat_map(|it| it.lifetime_params(db))
.zip(self.substs.lifetimes.clone())
.filter_map(|(k, v)| Some((k.name(db).display(db.upcast()).to_string(), v.lifetime()?)))
.filter_map(|(k, v)| {
Some((k.name(db).display(db.upcast(), target_edition).to_string(), v.lifetime()?))
})
.collect();
let ctx = Ctx {
type_substs,
@ -213,6 +217,7 @@ impl<'a> PathTransform<'a> {
target_module,
source_scope: self.source_scope,
same_self_type: self.target_scope.has_same_self_type(self.source_scope),
target_edition,
};
ctx.transform_default_values(defaulted_params);
ctx
@ -226,6 +231,7 @@ struct Ctx<'a> {
target_module: hir::Module,
source_scope: &'a SemanticsScope<'a>,
same_self_type: bool,
target_edition: Edition,
}
fn preorder_rev(item: &SyntaxNode) -> impl Iterator<Item = SyntaxNode> {
@ -318,7 +324,7 @@ impl Ctx<'_> {
hir::ModuleDef::Trait(trait_ref),
cfg,
)?;
match make::ty_path(mod_path_to_ast(&found_path)) {
match make::ty_path(mod_path_to_ast(&found_path, self.target_edition)) {
ast::Type::PathType(path_ty) => Some(path_ty),
_ => None,
}
@ -374,7 +380,7 @@ impl Ctx<'_> {
};
let found_path =
self.target_module.find_path(self.source_scope.db.upcast(), def, cfg)?;
let res = mod_path_to_ast(&found_path).clone_for_update();
let res = mod_path_to_ast(&found_path, self.target_edition).clone_for_update();
if let Some(args) = path.segment().and_then(|it| it.generic_arg_list()) {
if let Some(segment) = res.segment() {
let old = segment.get_or_create_generic_arg_list();
@ -417,7 +423,9 @@ impl Ctx<'_> {
cfg,
)?;
if let Some(qual) = mod_path_to_ast(&found_path).qualifier() {
if let Some(qual) =
mod_path_to_ast(&found_path, self.target_edition).qualifier()
{
let res = make::path_concat(qual, path_ty.path()?).clone_for_update();
ted::replace(path.syntax(), res.syntax());
return Some(());

View file

@ -29,6 +29,7 @@ use span::{Edition, EditionedFileId, FileId, SyntaxContextId};
use stdx::{never, TupleExt};
use syntax::{
ast::{self, HasName},
utils::is_raw_identifier,
AstNode, SyntaxKind, TextRange, T,
};
use text_edit::{TextEdit, TextEditBuilder};
@ -72,6 +73,9 @@ impl Definition {
sema: &Semantics<'_, RootDatabase>,
new_name: &str,
) -> Result<SourceChange> {
// We append `r#` if needed.
let new_name = new_name.trim_start_matches("r#");
// self.krate() returns None if
// self is a built-in attr, built-in type or tool module.
// it is not allowed for these defs to be renamed.
@ -227,8 +231,7 @@ fn rename_mod(
module: hir::Module,
new_name: &str,
) -> Result<SourceChange> {
if IdentifierKind::classify(module.krate().edition(sema.db), new_name)? != IdentifierKind::Ident
{
if IdentifierKind::classify(new_name)? != IdentifierKind::Ident {
bail!("Invalid name `{0}`: cannot rename module to {0}", new_name);
}
@ -240,7 +243,6 @@ fn rename_mod(
let InFile { file_id, value: def_source } = module.definition_source(sema.db);
if let ModuleSource::SourceFile(..) = def_source {
let new_name = new_name.trim_start_matches("r#");
let anchor = file_id.original_file(sema.db).file_id();
let is_mod_rs = module.is_mod_rs(sema.db);
@ -289,9 +291,14 @@ fn rename_mod(
.original_file_range_opt(sema.db)
.map(TupleExt::head)
{
let new_name = if is_raw_identifier(new_name, file_id.edition()) {
format!("r#{new_name}")
} else {
new_name.to_owned()
};
source_change.insert_source_edit(
file_id.file_id(),
TextEdit::replace(file_range.range, new_name.to_owned()),
TextEdit::replace(file_range.range, new_name),
)
};
}
@ -302,7 +309,10 @@ fn rename_mod(
let def = Definition::Module(module);
let usages = def.usages(sema).all();
let ref_edits = usages.iter().map(|(file_id, references)| {
(EditionedFileId::file_id(file_id), source_edit_from_references(references, def, new_name))
(
EditionedFileId::file_id(file_id),
source_edit_from_references(references, def, new_name, file_id.edition()),
)
});
source_change.extend(ref_edits);
@ -314,12 +324,7 @@ fn rename_reference(
def: Definition,
new_name: &str,
) -> Result<SourceChange> {
let ident_kind = IdentifierKind::classify(
def.krate(sema.db)
.ok_or_else(|| RenameError("definition has no krate?".into()))?
.edition(sema.db),
new_name,
)?;
let ident_kind = IdentifierKind::classify(new_name)?;
if matches!(
def,
@ -351,7 +356,10 @@ fn rename_reference(
}
let mut source_change = SourceChange::default();
source_change.extend(usages.iter().map(|(file_id, references)| {
(EditionedFileId::file_id(file_id), source_edit_from_references(references, def, new_name))
(
EditionedFileId::file_id(file_id),
source_edit_from_references(references, def, new_name, file_id.edition()),
)
}));
let mut insert_def_edit = |def| {
@ -367,7 +375,13 @@ pub fn source_edit_from_references(
references: &[FileReference],
def: Definition,
new_name: &str,
edition: Edition,
) -> TextEdit {
let new_name = if is_raw_identifier(new_name, edition) {
format!("r#{new_name}")
} else {
new_name.to_owned()
};
let mut edit = TextEdit::builder();
// macros can cause multiple refs to occur for the same text range, so keep track of what we have edited so far
let mut edited_ranges = Vec::new();
@ -383,10 +397,10 @@ pub fn source_edit_from_references(
// to make special rewrites like shorthand syntax and such, so just rename the node in
// the macro input
FileReferenceNode::NameRef(name_ref) if name_range == range => {
source_edit_from_name_ref(&mut edit, name_ref, new_name, def)
source_edit_from_name_ref(&mut edit, name_ref, &new_name, def)
}
FileReferenceNode::Name(name) if name_range == range => {
source_edit_from_name(&mut edit, name, new_name)
source_edit_from_name(&mut edit, name, &new_name)
}
_ => false,
};
@ -394,7 +408,7 @@ pub fn source_edit_from_references(
let (range, new_name) = match name {
FileReferenceNode::Lifetime(_) => (
TextRange::new(range.start() + syntax::TextSize::from(1), range.end()),
new_name.strip_prefix('\'').unwrap_or(new_name).to_owned(),
new_name.strip_prefix('\'').unwrap_or(&new_name).to_owned(),
),
_ => (range, new_name.to_owned()),
};
@ -522,6 +536,13 @@ fn source_edit_from_def(
def: Definition,
new_name: &str,
) -> Result<(FileId, TextEdit)> {
let new_name_edition_aware = |new_name: &str, file_id: EditionedFileId| {
if is_raw_identifier(new_name, file_id.edition()) {
format!("r#{new_name}")
} else {
new_name.to_owned()
}
};
let mut edit = TextEdit::builder();
if let Definition::Local(local) = def {
let mut file_id = None;
@ -536,7 +557,7 @@ fn source_edit_from_def(
{
Some(FileRange { file_id: file_id2, range }) => {
file_id = Some(file_id2);
edit.replace(range, new_name.to_owned());
edit.replace(range, new_name_edition_aware(new_name, file_id2));
continue;
}
None => {
@ -550,7 +571,9 @@ fn source_edit_from_def(
// special cases required for renaming fields/locals in Record patterns
if let Some(pat_field) = pat.syntax().parent().and_then(ast::RecordPatField::cast) {
if let Some(name_ref) = pat_field.name_ref() {
if new_name == name_ref.text() && pat.at_token().is_none() {
if new_name == name_ref.text().as_str().trim_start_matches("r#")
&& pat.at_token().is_none()
{
// Foo { field: ref mut local } -> Foo { ref mut field }
// ^^^^^^ delete this
// ^^^^^ replace this with `field`
@ -566,7 +589,10 @@ fn source_edit_from_def(
// Foo { field: ref mut local @ local 2} -> Foo { field: ref mut new_name @ local2 }
// Foo { field: ref mut local } -> Foo { field: ref mut new_name }
// ^^^^^ replace this with `new_name`
edit.replace(name_range, new_name.to_owned());
edit.replace(
name_range,
new_name_edition_aware(new_name, source.file_id),
);
}
} else {
// Foo { ref mut field } -> Foo { field: ref mut new_name }
@ -576,10 +602,10 @@ fn source_edit_from_def(
pat.syntax().text_range().start(),
format!("{}: ", pat_field.field_name().unwrap()),
);
edit.replace(name_range, new_name.to_owned());
edit.replace(name_range, new_name_edition_aware(new_name, source.file_id));
}
} else {
edit.replace(name_range, new_name.to_owned());
edit.replace(name_range, new_name_edition_aware(new_name, source.file_id));
}
}
}
@ -599,7 +625,7 @@ fn source_edit_from_def(
}
_ => (range, new_name.to_owned()),
};
edit.replace(range, new_name);
edit.replace(range, new_name_edition_aware(&new_name, file_id));
Ok((file_id.file_id(), edit.finish()))
}
@ -611,8 +637,9 @@ pub enum IdentifierKind {
}
impl IdentifierKind {
pub fn classify(edition: Edition, new_name: &str) -> Result<IdentifierKind> {
match parser::LexedStr::single_token(edition, new_name) {
pub fn classify(new_name: &str) -> Result<IdentifierKind> {
let new_name = new_name.trim_start_matches("r#");
match parser::LexedStr::single_token(Edition::LATEST, new_name) {
Some(res) => match res {
(SyntaxKind::IDENT, _) => {
if let Some(inner) = new_name.strip_prefix("r#") {
@ -626,6 +653,7 @@ impl IdentifierKind {
(SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => {
Ok(IdentifierKind::Lifetime)
}
_ if is_raw_identifier(new_name, Edition::LATEST) => Ok(IdentifierKind::Ident),
(_, Some(syntax_error)) => bail!("Invalid name `{}`: {}", new_name, syntax_error),
(_, None) => bail!("Invalid name `{}`: not an identifier", new_name),
},

View file

@ -1,5 +1,4 @@
//! Utilities for formatting macro expanded nodes until we get a proper formatter.
use span::Edition;
use syntax::{
ast::make,
ted::{self, Position},
@ -132,6 +131,6 @@ pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode {
}
fn is_text(k: SyntaxKind) -> bool {
// FIXME: Edition
k.is_keyword(Edition::CURRENT) || k.is_literal() || k == IDENT || k == UNDERSCORE
// Consider all keywords in all editions.
k.is_any_identifier() || k.is_literal() || k == UNDERSCORE
}

View file

@ -457,13 +457,15 @@ impl Iterator for TreeWithDepthIterator {
}
/// Parses the input token tree as comma separated plain paths.
pub fn parse_tt_as_comma_sep_paths(input: ast::TokenTree) -> Option<Vec<ast::Path>> {
pub fn parse_tt_as_comma_sep_paths(
input: ast::TokenTree,
edition: Edition,
) -> Option<Vec<ast::Path>> {
let r_paren = input.r_paren_token();
let tokens =
input.syntax().children_with_tokens().skip(1).map_while(|it| match it.into_token() {
// seeing a keyword means the attribute is unclosed so stop parsing here
// FIXME: Edition
Some(tok) if tok.kind().is_keyword(Edition::CURRENT) => None,
Some(tok) if tok.kind().is_keyword(edition) => None,
// don't include the right token tree parenthesis if it exists
tok @ Some(_) if tok == r_paren => None,
// only nodes that we can find are other TokenTrees, those are unexpected in this parse though

View file

@ -34,19 +34,20 @@ pub fn get_missing_assoc_items(
// may share the same name as a function or constant.
let mut impl_fns_consts = FxHashSet::default();
let mut impl_type = FxHashSet::default();
let edition = imp.module(sema.db).krate().edition(sema.db);
for item in imp.items(sema.db) {
match item {
hir::AssocItem::Function(it) => {
impl_fns_consts.insert(it.name(sema.db).display(sema.db).to_string());
impl_fns_consts.insert(it.name(sema.db).display(sema.db, edition).to_string());
}
hir::AssocItem::Const(it) => {
if let Some(name) = it.name(sema.db) {
impl_fns_consts.insert(name.display(sema.db).to_string());
impl_fns_consts.insert(name.display(sema.db, edition).to_string());
}
}
hir::AssocItem::TypeAlias(it) => {
impl_type.insert(it.name(sema.db).display(sema.db).to_string());
impl_type.insert(it.name(sema.db).display(sema.db, edition).to_string());
}
}
}
@ -56,15 +57,14 @@ pub fn get_missing_assoc_items(
.items(sema.db)
.into_iter()
.filter(|i| match i {
hir::AssocItem::Function(f) => {
!impl_fns_consts.contains(&f.name(sema.db).display(sema.db).to_string())
}
hir::AssocItem::Function(f) => !impl_fns_consts
.contains(&f.name(sema.db).display(sema.db, edition).to_string()),
hir::AssocItem::TypeAlias(t) => {
!impl_type.contains(&t.name(sema.db).display(sema.db).to_string())
!impl_type.contains(&t.name(sema.db).display(sema.db, edition).to_string())
}
hir::AssocItem::Const(c) => c
.name(sema.db)
.map(|n| !impl_fns_consts.contains(&n.display(sema.db).to_string()))
.map(|n| !impl_fns_consts.contains(&n.display(sema.db, edition).to_string()))
.unwrap_or_default(),
})
.collect()
@ -116,6 +116,7 @@ mod tests {
use expect_test::{expect, Expect};
use hir::FilePosition;
use hir::Semantics;
use span::Edition;
use syntax::ast::{self, AstNode};
use test_fixture::ChangeFixture;
@ -140,7 +141,7 @@ mod tests {
sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
let trait_ = crate::traits::resolve_target_trait(&sema, &impl_block);
let actual = match trait_ {
Some(trait_) => trait_.name(&db).display(&db).to_string(),
Some(trait_) => trait_.name(&db).display(&db, Edition::CURRENT).to_string(),
None => String::new(),
};
expect.assert_eq(&actual);
@ -155,7 +156,7 @@ mod tests {
let items = crate::traits::get_missing_assoc_items(&sema, &impl_block);
let actual = items
.into_iter()
.map(|item| item.name(&db).unwrap().display(&db).to_string())
.map(|item| item.name(&db).unwrap().display(&db, Edition::CURRENT).to_string())
.collect::<Vec<_>>()
.join("\n");
expect.assert_eq(&actual);

View file

@ -5,10 +5,7 @@
use std::iter;
use hir::Semantics;
use syntax::{
ast::{self, make, Pat},
ToSmolStr,
};
use syntax::ast::{self, make, Pat};
use crate::RootDatabase;
@ -29,7 +26,7 @@ impl TryEnum {
_ => return None,
};
TryEnum::ALL.iter().find_map(|&var| {
if enum_.name(sema.db).display_no_db().to_smolstr() == var.type_name() {
if enum_.name(sema.db).eq_ident(var.type_name()) {
return Some(var);
}
None

View file

@ -1,6 +1,7 @@
//! Functionality for generating trivial constructors
use hir::StructKind;
use span::Edition;
use syntax::{
ast::{make, Expr, Path},
ToSmolStr,
@ -11,6 +12,7 @@ pub fn use_trivial_constructor(
db: &crate::RootDatabase,
path: Path,
ty: &hir::Type,
edition: Edition,
) -> Option<Expr> {
match ty.as_adt() {
Some(hir::Adt::Enum(x)) => {
@ -19,7 +21,7 @@ pub fn use_trivial_constructor(
let path = make::path_qualified(
path,
make::path_segment(make::name_ref(
&variant.name(db).display_no_db().to_smolstr(),
&variant.name(db).display_no_db(edition).to_smolstr(),
)),
);