1use std::collections::VecDeque;
4
5use base_db::SourceDatabase;
6use hir::{Crate, ItemInNs, ModuleDef, Name, Semantics};
7use span::{Edition, FileId};
8use syntax::{
9 AstToken, SyntaxKind, SyntaxToken, ToSmolStr, TokenAtOffset,
10 ast::{self, make, syntax_factory::SyntaxFactory},
11};
12
13use crate::{
14 RootDatabase,
15 defs::{Definition, IdentClass},
16 generated,
17};
18
19pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option<Name> {
20 match item {
21 ItemInNs::Types(module_def_id) => module_def_id.name(db),
22 ItemInNs::Values(module_def_id) => module_def_id.name(db),
23 ItemInNs::Macros(macro_def_id) => Some(macro_def_id.name(db)),
24 }
25}
26
27pub fn pick_best_token(
29 tokens: TokenAtOffset<SyntaxToken>,
30 f: impl Fn(SyntaxKind) -> usize,
31) -> Option<SyntaxToken> {
32 tokens.max_by_key(move |t| f(t.kind()))
33}
34pub fn pick_token<T: AstToken>(mut tokens: TokenAtOffset<SyntaxToken>) -> Option<T> {
35 tokens.find_map(T::cast)
36}
37
38pub fn mod_path_to_ast(path: &hir::ModPath, edition: Edition) -> ast::Path {
40 let _p = tracing::info_span!("mod_path_to_ast").entered();
41
42 let mut segments = Vec::new();
43 let mut is_abs = false;
44 match path.kind {
45 hir::PathKind::Plain => {}
46 hir::PathKind::SELF => segments.push(make::path_segment_self()),
47 hir::PathKind::Super(n) => segments.extend((0..n).map(|_| make::path_segment_super())),
48 hir::PathKind::DollarCrate(_) | hir::PathKind::Crate => {
49 segments.push(make::path_segment_crate())
50 }
51 hir::PathKind::Abs => is_abs = true,
52 }
53
54 segments.extend(path.segments().iter().map(|segment| {
55 make::path_segment(make::name_ref(&segment.display_no_db(edition).to_smolstr()))
56 }));
57 make::path_from_segments(segments, is_abs)
58}
59
60pub fn mod_path_to_ast_with_factory(
61 make: &SyntaxFactory,
62 path: &hir::ModPath,
63 edition: Edition,
64) -> ast::Path {
65 let _p = tracing::info_span!("mod_path_to_ast").entered();
66
67 let mut segments = Vec::new();
68 let mut is_abs = false;
69 match path.kind {
70 hir::PathKind::Plain => {}
71 hir::PathKind::SELF => segments.push(make.path_segment_self()),
72 hir::PathKind::Super(n) => segments.extend((0..n).map(|_| make.path_segment_super())),
73 hir::PathKind::DollarCrate(_) | hir::PathKind::Crate => {
74 segments.push(make.path_segment_crate())
75 }
76 hir::PathKind::Abs => is_abs = true,
77 }
78
79 segments.extend(path.segments().iter().map(|segment| {
80 make.path_segment(make.name_ref(&segment.display_no_db(edition).to_smolstr()))
81 }));
82 make.path_from_segments(segments, is_abs)
83}
84
85pub fn visit_file_defs(
87 sema: &Semantics<'_, RootDatabase>,
88 file_id: FileId,
89 cb: &mut dyn FnMut(Definition),
90) {
91 let db = sema.db;
92 let module = match sema.file_to_module_def(file_id) {
93 Some(it) => it,
94 None => return,
95 };
96 let mut defs: VecDeque<_> = module.declarations(db).into();
97 while let Some(def) = defs.pop_front() {
98 if let ModuleDef::Module(submodule) = def
99 && submodule.is_inline(db)
100 {
101 defs.extend(submodule.declarations(db));
102 submodule.impl_defs(db).into_iter().for_each(|impl_| cb(impl_.into()));
103 }
104 cb(def.into());
105 }
106 module.impl_defs(db).into_iter().for_each(|impl_| cb(impl_.into()));
107
108 let is_root = module.is_crate_root(db);
109 module
110 .legacy_macros(db)
111 .into_iter()
112 .filter(|it| !(is_root && it.is_macro_export(db)))
114 .for_each(|mac| cb(mac.into()));
115}
116
117pub fn lint_eq_or_in_group(lint: &str, lint_is: &str) -> bool {
119 if lint == lint_is {
120 return true;
121 }
122
123 if let Some(group) = generated::lints::DEFAULT_LINT_GROUPS
124 .iter()
125 .chain(generated::lints::CLIPPY_LINT_GROUPS.iter())
126 .chain(generated::lints::RUSTDOC_LINT_GROUPS.iter())
127 .find(|&check| check.lint.label == lint_is)
128 {
129 group.children.contains(&lint)
130 } else {
131 false
132 }
133}
134
135pub fn is_editable_crate(krate: Crate, db: &RootDatabase) -> bool {
136 let root_file = krate.root_file(db);
137 let source_root_id = db.file_source_root(root_file).source_root_id(db);
138 !db.source_root(source_root_id).source_root(db).is_library
139}
140
141pub fn get_definition(
143 sema: &Semantics<'_, RootDatabase>,
144 token: SyntaxToken,
145) -> Option<Definition> {
146 for token in sema.descend_into_macros_exact(token) {
147 let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops);
148 if let Some(&[x]) = def.as_deref() {
149 return Some(x);
150 }
151 }
152 None
153}