10358: internal: Remove inherent methods from ast nodes that do non-syntactic complex tasks  r=Veykril a=Veykril



Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
bors[bot] 2021-09-26 14:49:25 +00:00 committed by GitHub
commit c51a3c78cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 392 additions and 365 deletions

View file

@ -1,15 +1,17 @@
//! A module with ide helpers for high-level ide features.
pub mod famous_defs;
pub mod generated_lints;
pub mod import_assets;
pub mod insert_use;
pub mod merge_imports;
pub mod node_ext;
pub mod rust_doc;
pub mod generated_lints;
use std::collections::VecDeque;
use base_db::FileId;
use either::Either;
use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait};
use hir::{ItemInNs, MacroDef, ModuleDef, Name, Semantics};
use syntax::{
ast::{self, make, LoopBodyOwner},
AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxToken, TokenAtOffset, WalkEvent, T,
@ -17,6 +19,8 @@ use syntax::{
use crate::RootDatabase;
pub use self::famous_defs::FamousDefs;
pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option<Name> {
match item {
ItemInNs::Types(module_def_id) => ModuleDef::from(module_def_id).name(db),
@ -27,7 +31,7 @@ pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option<Name> {
/// Resolves the path at the cursor token as a derive macro if it inside a token tree of a derive attribute.
pub fn try_resolve_derive_input_at(
sema: &Semantics<RootDatabase>,
sema: &hir::Semantics<RootDatabase>,
derive_attr: &ast::Attr,
cursor: &SyntaxToken,
) -> Option<MacroDef> {
@ -113,123 +117,6 @@ pub fn visit_file_defs(
module.impl_defs(db).into_iter().for_each(|impl_| cb(Either::Right(impl_)));
}
/// Helps with finding well-know things inside the standard library. This is
/// somewhat similar to the known paths infra inside hir, but it different; We
/// want to make sure that IDE specific paths don't become interesting inside
/// the compiler itself as well.
///
/// Note that, by default, rust-analyzer tests **do not** include core or std
/// libraries. If you are writing tests for functionality using [`FamousDefs`],
/// you'd want to include minicore (see `test_utils::MiniCore`) declaration at
/// the start of your tests:
///
/// ```
/// //- minicore: iterator, ord, derive
/// ```
pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Option<Crate>);
#[allow(non_snake_case)]
impl FamousDefs<'_, '_> {
pub fn std(&self) -> Option<Crate> {
self.find_crate("std")
}
pub fn core(&self) -> Option<Crate> {
self.find_crate("core")
}
pub fn core_cmp_Ord(&self) -> Option<Trait> {
self.find_trait("core:cmp:Ord")
}
pub fn core_convert_From(&self) -> Option<Trait> {
self.find_trait("core:convert:From")
}
pub fn core_convert_Into(&self) -> Option<Trait> {
self.find_trait("core:convert:Into")
}
pub fn core_option_Option(&self) -> Option<Enum> {
self.find_enum("core:option:Option")
}
pub fn core_result_Result(&self) -> Option<Enum> {
self.find_enum("core:result:Result")
}
pub fn core_default_Default(&self) -> Option<Trait> {
self.find_trait("core:default:Default")
}
pub fn core_iter_Iterator(&self) -> Option<Trait> {
self.find_trait("core:iter:traits:iterator:Iterator")
}
pub fn core_iter_IntoIterator(&self) -> Option<Trait> {
self.find_trait("core:iter:traits:collect:IntoIterator")
}
pub fn core_iter(&self) -> Option<Module> {
self.find_module("core:iter")
}
pub fn core_ops_Deref(&self) -> Option<Trait> {
self.find_trait("core:ops:Deref")
}
fn find_trait(&self, path: &str) -> Option<Trait> {
match self.find_def(path)? {
hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it),
_ => None,
}
}
fn find_enum(&self, path: &str) -> Option<Enum> {
match self.find_def(path)? {
hir::ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(it))) => Some(it),
_ => None,
}
}
fn find_module(&self, path: &str) -> Option<Module> {
match self.find_def(path)? {
hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(it)) => Some(it),
_ => None,
}
}
fn find_crate(&self, name: &str) -> Option<Crate> {
let krate = self.1?;
let db = self.0.db;
let res =
krate.dependencies(db).into_iter().find(|dep| dep.name.to_string() == name)?.krate;
Some(res)
}
fn find_def(&self, path: &str) -> Option<ScopeDef> {
let db = self.0.db;
let mut path = path.split(':');
let trait_ = path.next_back()?;
let std_crate = path.next()?;
let std_crate = self.find_crate(std_crate)?;
let mut module = std_crate.root_module(db);
for segment in path {
module = module.children(db).find_map(|child| {
let name = child.name(db)?;
if name.to_string() == segment {
Some(child)
} else {
None
}
})?;
}
let def =
module.scope(db, None).into_iter().find(|(name, _def)| name.to_string() == trait_)?.1;
Some(def)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SnippetCap {
_private: (),

View file

@ -0,0 +1,121 @@
//! See [`FamousDefs`].
use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait};
use crate::RootDatabase;
/// Helps with finding well-know things inside the standard library. This is
/// somewhat similar to the known paths infra inside hir, but it different; We
/// want to make sure that IDE specific paths don't become interesting inside
/// the compiler itself as well.
///
/// Note that, by default, rust-analyzer tests **do not** include core or std
/// libraries. If you are writing tests for functionality using [`FamousDefs`],
/// you'd want to include minicore (see `test_utils::MiniCore`) declaration at
/// the start of your tests:
///
/// ```
/// //- minicore: iterator, ord, derive
/// ```
pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Option<Crate>);
#[allow(non_snake_case)]
impl FamousDefs<'_, '_> {
pub fn std(&self) -> Option<Crate> {
self.find_crate("std")
}
pub fn core(&self) -> Option<Crate> {
self.find_crate("core")
}
pub fn core_cmp_Ord(&self) -> Option<Trait> {
self.find_trait("core:cmp:Ord")
}
pub fn core_convert_From(&self) -> Option<Trait> {
self.find_trait("core:convert:From")
}
pub fn core_convert_Into(&self) -> Option<Trait> {
self.find_trait("core:convert:Into")
}
pub fn core_option_Option(&self) -> Option<Enum> {
self.find_enum("core:option:Option")
}
pub fn core_result_Result(&self) -> Option<Enum> {
self.find_enum("core:result:Result")
}
pub fn core_default_Default(&self) -> Option<Trait> {
self.find_trait("core:default:Default")
}
pub fn core_iter_Iterator(&self) -> Option<Trait> {
self.find_trait("core:iter:traits:iterator:Iterator")
}
pub fn core_iter_IntoIterator(&self) -> Option<Trait> {
self.find_trait("core:iter:traits:collect:IntoIterator")
}
pub fn core_iter(&self) -> Option<Module> {
self.find_module("core:iter")
}
pub fn core_ops_Deref(&self) -> Option<Trait> {
self.find_trait("core:ops:Deref")
}
fn find_trait(&self, path: &str) -> Option<Trait> {
match self.find_def(path)? {
hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it),
_ => None,
}
}
fn find_enum(&self, path: &str) -> Option<Enum> {
match self.find_def(path)? {
hir::ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(it))) => Some(it),
_ => None,
}
}
fn find_module(&self, path: &str) -> Option<Module> {
match self.find_def(path)? {
hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(it)) => Some(it),
_ => None,
}
}
fn find_crate(&self, name: &str) -> Option<Crate> {
let krate = self.1?;
let db = self.0.db;
let res =
krate.dependencies(db).into_iter().find(|dep| dep.name.to_string() == name)?.krate;
Some(res)
}
fn find_def(&self, path: &str) -> Option<ScopeDef> {
let db = self.0.db;
let mut path = path.split(':');
let trait_ = path.next_back()?;
let std_crate = path.next()?;
let std_crate = self.find_crate(std_crate)?;
let mut module = std_crate.root_module(db);
for segment in path {
module = module.children(db).find_map(|child| {
let name = child.name(db)?;
if name.to_string() == segment {
Some(child)
} else {
None
}
})?;
}
let def =
module.scope(db, None).into_iter().find(|(name, _def)| name.to_string() == trait_)?.1;
Some(def)
}
}

View file

@ -7,6 +7,8 @@ use syntax::{
ted,
};
use crate::helpers::node_ext::vis_eq;
/// What type of merges are allowed.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum MergeBehavior {
@ -292,7 +294,7 @@ fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering {
pub fn eq_visibility(vis0: Option<ast::Visibility>, vis1: Option<ast::Visibility>) -> bool {
match (vis0, vis1) {
(None, None) => true,
(Some(vis0), Some(vis1)) => vis0.is_eq_to(&vis1),
(Some(vis0), Some(vis1)) => vis_eq(&vis0, &vis1),
_ => false,
}
}

View file

@ -0,0 +1,212 @@
//! Various helper functions to work with SyntaxNodes.
use syntax::{
ast::{self, PathSegmentKind, VisibilityKind},
AstNode, WalkEvent,
};
pub fn expr_as_name_ref(expr: &ast::Expr) -> Option<ast::NameRef> {
if let ast::Expr::PathExpr(expr) = expr {
let path = expr.path()?;
let segment = path.segment()?;
let name_ref = segment.name_ref()?;
if path.qualifier().is_none() {
return Some(name_ref);
}
}
None
}
pub fn block_as_lone_tail(block: &ast::BlockExpr) -> Option<ast::Expr> {
block.statements().next().is_none().then(|| block.tail_expr()).flatten()
}
/// Preorder walk all the expression's child expressions.
pub fn walk_expr(expr: &ast::Expr, cb: &mut dyn FnMut(ast::Expr)) {
preorder_expr(expr, &mut |ev| {
if let WalkEvent::Enter(expr) = ev {
cb(expr);
}
false
})
}
/// Preorder walk all the expression's child expressions preserving events.
/// If the callback returns true on an [`WalkEvent::Enter`], the subtree of the expression will be skipped.
/// Note that the subtree may already be skipped due to the context analysis this function does.
pub fn preorder_expr(expr: &ast::Expr, cb: &mut dyn FnMut(WalkEvent<ast::Expr>) -> bool) {
let mut preorder = expr.syntax().preorder();
while let Some(event) = preorder.next() {
let node = match event {
WalkEvent::Enter(node) => node,
WalkEvent::Leave(node) => {
if let Some(expr) = ast::Expr::cast(node) {
cb(WalkEvent::Leave(expr));
}
continue;
}
};
match ast::Stmt::cast(node.clone()) {
// recursively walk the initializer, skipping potential const pat expressions
// let statements aren't usually nested too deeply so this is fine to recurse on
Some(ast::Stmt::LetStmt(l)) => {
if let Some(expr) = l.initializer() {
preorder_expr(&expr, cb);
}
preorder.skip_subtree();
}
// Don't skip subtree since we want to process the expression child next
Some(ast::Stmt::ExprStmt(_)) => (),
// This might be an expression
Some(ast::Stmt::Item(ast::Item::MacroCall(mcall))) => {
cb(WalkEvent::Enter(ast::Expr::MacroCall(mcall)));
preorder.skip_subtree();
}
// skip inner items which might have their own expressions
Some(ast::Stmt::Item(_)) => preorder.skip_subtree(),
None => {
// skip const args, those expressions are a different context
if ast::GenericArg::can_cast(node.kind()) {
preorder.skip_subtree();
} else if let Some(expr) = ast::Expr::cast(node) {
let is_different_context = match &expr {
ast::Expr::EffectExpr(effect) => {
matches!(
effect.effect(),
ast::Effect::Async(_) | ast::Effect::Try(_) | ast::Effect::Const(_)
)
}
ast::Expr::ClosureExpr(_) => true,
_ => false,
};
let skip = cb(WalkEvent::Enter(expr));
if skip || is_different_context {
preorder.skip_subtree();
}
}
}
}
}
}
/// Preorder walk all the expression's child patterns.
pub fn walk_patterns_in_expr(expr: &ast::Expr, cb: &mut dyn FnMut(ast::Pat)) {
let mut preorder = expr.syntax().preorder();
while let Some(event) = preorder.next() {
let node = match event {
WalkEvent::Enter(node) => node,
WalkEvent::Leave(_) => continue,
};
match ast::Stmt::cast(node.clone()) {
Some(ast::Stmt::LetStmt(l)) => {
if let Some(pat) = l.pat() {
walk_pat(&pat, cb);
}
if let Some(expr) = l.initializer() {
walk_patterns_in_expr(&expr, cb);
}
preorder.skip_subtree();
}
// Don't skip subtree since we want to process the expression child next
Some(ast::Stmt::ExprStmt(_)) => (),
// skip inner items which might have their own patterns
Some(ast::Stmt::Item(_)) => preorder.skip_subtree(),
None => {
// skip const args, those are a different context
if ast::GenericArg::can_cast(node.kind()) {
preorder.skip_subtree();
} else if let Some(expr) = ast::Expr::cast(node.clone()) {
let is_different_context = match &expr {
ast::Expr::EffectExpr(effect) => match effect.effect() {
ast::Effect::Async(_) | ast::Effect::Try(_) | ast::Effect::Const(_) => {
true
}
ast::Effect::Unsafe(_) | ast::Effect::Label(_) => false,
},
ast::Expr::ClosureExpr(_) => true,
_ => false,
};
if is_different_context {
preorder.skip_subtree();
}
} else if let Some(pat) = ast::Pat::cast(node) {
preorder.skip_subtree();
walk_pat(&pat, cb);
}
}
}
}
}
/// Preorder walk all the pattern's sub patterns.
pub fn walk_pat(pat: &ast::Pat, cb: &mut dyn FnMut(ast::Pat)) {
let mut preorder = pat.syntax().preorder();
while let Some(event) = preorder.next() {
let node = match event {
WalkEvent::Enter(node) => node,
WalkEvent::Leave(_) => continue,
};
let kind = node.kind();
match ast::Pat::cast(node) {
Some(pat @ ast::Pat::ConstBlockPat(_)) => {
preorder.skip_subtree();
cb(pat);
}
Some(pat) => {
cb(pat);
}
// skip const args
None if ast::GenericArg::can_cast(kind) => {
preorder.skip_subtree();
}
None => (),
}
}
}
/// Preorder walk all the type's sub types.
pub fn walk_ty(ty: &ast::Type, cb: &mut dyn FnMut(ast::Type)) {
let mut preorder = ty.syntax().preorder();
while let Some(event) = preorder.next() {
let node = match event {
WalkEvent::Enter(node) => node,
WalkEvent::Leave(_) => continue,
};
let kind = node.kind();
match ast::Type::cast(node) {
Some(ty @ ast::Type::MacroType(_)) => {
preorder.skip_subtree();
cb(ty)
}
Some(ty) => {
cb(ty);
}
// skip const args
None if ast::ConstArg::can_cast(kind) => {
preorder.skip_subtree();
}
None => (),
}
}
}
pub fn vis_eq(this: &ast::Visibility, other: &ast::Visibility) -> bool {
match (this.kind(), other.kind()) {
(VisibilityKind::In(this), VisibilityKind::In(other)) => {
stdx::iter_eq_by(this.segments(), other.segments(), |lhs, rhs| {
lhs.kind().zip(rhs.kind()).map_or(false, |it| match it {
(PathSegmentKind::CrateKw, PathSegmentKind::CrateKw)
| (PathSegmentKind::SelfKw, PathSegmentKind::SelfKw)
| (PathSegmentKind::SuperKw, PathSegmentKind::SuperKw) => true,
(PathSegmentKind::Name(lhs), PathSegmentKind::Name(rhs)) => {
lhs.text() == rhs.text()
}
_ => false,
})
})
}
(VisibilityKind::PubSelf, VisibilityKind::PubSelf)
| (VisibilityKind::PubSuper, VisibilityKind::PubSuper)
| (VisibilityKind::PubCrate, VisibilityKind::PubCrate)
| (VisibilityKind::Pub, VisibilityKind::Pub) => true,
_ => false,
}
}

View file

@ -34,6 +34,7 @@ use text_edit::{TextEdit, TextEditBuilder};
use crate::{
defs::Definition,
helpers::node_ext::expr_as_name_ref,
search::FileReference,
source_change::{FileSystemEdit, SourceChange},
RootDatabase,
@ -339,7 +340,7 @@ fn source_edit_from_name_ref(
if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) {
let rcf_name_ref = record_field.name_ref();
let rcf_expr = record_field.expr();
match &(rcf_name_ref, rcf_expr.and_then(|it| it.name_ref())) {
match &(rcf_name_ref, rcf_expr.and_then(|it| expr_as_name_ref(&it))) {
// field: init-expr, check if we can use a field init shorthand
(Some(field_name), Some(init)) => {
if field_name == name_ref {