Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir/src/semantics.rs')
| -rw-r--r-- | crates/hir/src/semantics.rs | 78 |
1 files changed, 70 insertions, 8 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 82e60bff5e..b15e642daa 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -39,8 +39,8 @@ use smallvec::{SmallVec, smallvec}; use span::{FileId, SyntaxContext}; use stdx::{TupleExt, always}; use syntax::{ - AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, - TextSize, + AstNode, AstToken, Direction, SmolStr, SmolStrBuilder, SyntaxElement, SyntaxKind, SyntaxNode, + SyntaxNodePtr, SyntaxToken, T, TextRange, TextSize, algo::skip_trivia_token, ast::{self, HasAttrs as _, HasGenericParams}, }; @@ -174,6 +174,15 @@ impl<'db, DB: ?Sized> ops::Deref for Semantics<'db, DB> { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum LintAttr { + Allow, + Expect, + Warn, + Deny, + Forbid, +} + // Note: while this variant of `Semantics<'_, _>` might seem unused, as it does not // find actual use within the rust-analyzer project itself, it exists to enable the use // within e.g. tracked salsa functions in third-party crates that build upon `ra_ap_hir`. @@ -254,6 +263,59 @@ impl<DB: HirDatabase + ?Sized> Semantics<'_, DB> { .filter_map(ast::NameLike::cast) } + pub fn lint_attrs( + &self, + krate: Crate, + item: ast::AnyHasAttrs, + ) -> impl DoubleEndedIterator<Item = (LintAttr, SmolStr)> { + let mut cfg_options = None; + let cfg_options = || *cfg_options.get_or_insert_with(|| krate.id.cfg_options(self.db)); + let mut result = Vec::new(); + hir_expand::attrs::expand_cfg_attr::<Infallible>( + ast::attrs_including_inner(&item), + cfg_options, + |attr, _, _, _| { + let hir_expand::attrs::Meta::TokenTree { path, tt } = attr else { + return ControlFlow::Continue(()); + }; + if path.segments.len() != 1 { + return ControlFlow::Continue(()); + } + let lint_attr = match path.segments[0].text() { + "allow" => LintAttr::Allow, + "expect" => LintAttr::Expect, + "warn" => LintAttr::Warn, + "deny" => LintAttr::Deny, + "forbid" => LintAttr::Forbid, + _ => return ControlFlow::Continue(()), + }; + let mut lint = SmolStrBuilder::new(); + for token in + tt.syntax().children_with_tokens().filter_map(SyntaxElement::into_token) + { + match token.kind() { + T![:] | T![::] => lint.push_str(token.text()), + kind if kind.is_any_identifier() => lint.push_str(token.text()), + T![,] => { + let lint = mem::replace(&mut lint, SmolStrBuilder::new()).finish(); + if !lint.is_empty() { + result.push((lint_attr, lint)); + } + } + _ => {} + } + } + let lint = lint.finish(); + if !lint.is_empty() { + result.push((lint_attr, lint)); + } + + ControlFlow::Continue(()) + }, + ); + result.into_iter() + } + pub fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option<Struct> { self.imp.resolve_range_pat(range_pat).map(Struct::from) } @@ -380,13 +442,13 @@ impl<'db> SemanticsImpl<'db> { /// If not crate is found for the file, try to return the last crate in topological order. pub fn first_crate(&self, file: FileId) -> Option<Crate> { match self.file_to_module_defs(file).next() { - Some(module) => Some(module.krate()), + Some(module) => Some(module.krate(self.db)), None => self.db.all_crates().last().copied().map(Into::into), } } pub fn attach_first_edition_opt(&self, file: FileId) -> Option<EditionedFileId> { - let krate = self.file_to_module_defs(file).next()?.krate(); + let krate = self.file_to_module_defs(file).next()?.krate(self.db); Some(EditionedFileId::new(self.db, file, krate.edition(self.db), krate.id)) } @@ -416,8 +478,8 @@ impl<'db> SemanticsImpl<'db> { match file_id { HirFileId::FileId(file_id) => { let module = self.file_to_module_defs(file_id.file_id(self.db)).next()?; - let def_map = crate_def_map(self.db, module.krate().id); - match def_map[module.id.local_id].origin { + let def_map = crate_def_map(self.db, module.krate(self.db).id); + match def_map[module.id].origin { ModuleOrigin::CrateRoot { .. } => None, ModuleOrigin::File { declaration, declaration_tree_id, .. } => { let file_id = declaration_tree_id.file_id(); @@ -443,7 +505,7 @@ impl<'db> SemanticsImpl<'db> { /// the `SyntaxNode` of the *definition* file, not of the *declaration*. pub fn module_definition_node(&self, module: Module) -> InFile<SyntaxNode> { let def_map = module.id.def_map(self.db); - let definition = def_map[module.id.local_id].origin.definition_source(self.db); + let definition = def_map[module.id].origin.definition_source(self.db); let definition = definition.map(|it| it.node()); let root_node = find_root(&definition.value); self.cache(root_node, definition.file_id); @@ -472,7 +534,7 @@ impl<'db> SemanticsImpl<'db> { let file_id = self.find_file(attr.syntax()).file_id; let krate = match file_id { HirFileId::FileId(file_id) => { - self.file_to_module_defs(file_id.file_id(self.db)).next()?.krate().id + self.file_to_module_defs(file_id.file_id(self.db)).next()?.krate(self.db).id } HirFileId::MacroFile(macro_file) => self.db.lookup_intern_macro_call(macro_file).krate, }; |