use std::collections::VecDeque;
use base_db::SourceRootDatabase;
use hir::{Crate, ItemInNs, ModuleDef, Name, Semantics};
use span::{Edition, FileId};
use syntax::{
ast::{self, make},
AstToken, SyntaxKind, SyntaxToken, ToSmolStr, TokenAtOffset,
};
use crate::{
defs::{Definition, IdentClass},
generated, RootDatabase,
};
pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option<Name> {
match item {
ItemInNs::Types(module_def_id) => module_def_id.name(db),
ItemInNs::Values(module_def_id) => module_def_id.name(db),
ItemInNs::Macros(macro_def_id) => Some(macro_def_id.name(db)),
}
}
pub fn pick_best_token(
tokens: TokenAtOffset<SyntaxToken>,
f: impl Fn(SyntaxKind) -> usize,
) -> Option<SyntaxToken> {
tokens.max_by_key(move |t| f(t.kind()))
}
pub fn pick_token<T: AstToken>(mut tokens: TokenAtOffset<SyntaxToken>) -> Option<T> {
tokens.find_map(T::cast)
}
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();
let mut is_abs = false;
match path.kind {
hir::PathKind::Plain => {}
hir::PathKind::SELF => segments.push(make::path_segment_self()),
hir::PathKind::Super(n) => segments.extend((0..n).map(|_| make::path_segment_super())),
hir::PathKind::DollarCrate(_) | hir::PathKind::Crate => {
segments.push(make::path_segment_crate())
}
hir::PathKind::Abs => is_abs = true,
}
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)
}
pub fn visit_file_defs(
sema: &Semantics<'_, RootDatabase>,
file_id: FileId,
cb: &mut dyn FnMut(Definition),
) {
let db = sema.db;
let module = match sema.file_to_module_def(file_id) {
Some(it) => it,
None => return,
};
let mut defs: VecDeque<_> = module.declarations(db).into();
while let Some(def) = defs.pop_front() {
if let ModuleDef::Module(submodule) = def {
if submodule.is_inline(db) {
defs.extend(submodule.declarations(db));
submodule.impl_defs(db).into_iter().for_each(|impl_| cb(impl_.into()));
}
}
cb(def.into());
}
module.impl_defs(db).into_iter().for_each(|impl_| cb(impl_.into()));
let is_root = module.is_crate_root();
module
.legacy_macros(db)
.into_iter()
.filter(|it| !(is_root && it.is_macro_export(db)))
.for_each(|mac| cb(mac.into()));
}
pub fn lint_eq_or_in_group(lint: &str, lint_is: &str) -> bool {
if lint == lint_is {
return true;
}
if let Some(group) = generated::lints::DEFAULT_LINT_GROUPS
.iter()
.chain(generated::lints::CLIPPY_LINT_GROUPS.iter())
.chain(generated::lints::RUSTDOC_LINT_GROUPS.iter())
.find(|&check| check.lint.label == lint_is)
{
group.children.contains(&lint)
} else {
false
}
}
pub fn is_editable_crate(krate: Crate, db: &RootDatabase) -> bool {
let root_file = krate.root_file(db);
let source_root_id = db.file_source_root(root_file);
!db.source_root(source_root_id).is_library
}
pub fn get_definition(
sema: &Semantics<'_, RootDatabase>,
token: SyntaxToken,
) -> Option<Definition> {
for token in sema.descend_into_macros_exact(token) {
let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops);
if let Some(&[x]) = def.as_deref() {
return Some(x);
}
}
None
}