mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
Remove inherent methods from ast node that carry semantic meaning
This commit is contained in:
parent
13da3d93f9
commit
151afdfe5c
15 changed files with 392 additions and 365 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
use ide_db::helpers::node_ext::vis_eq;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use syntax::{
|
use syntax::{
|
||||||
|
@ -198,7 +199,7 @@ where
|
||||||
fn eq_visibility(vis0: Option<ast::Visibility>, vis1: Option<ast::Visibility>) -> bool {
|
fn eq_visibility(vis0: Option<ast::Visibility>, vis1: Option<ast::Visibility>) -> bool {
|
||||||
match (vis0, vis1) {
|
match (vis0, vis1) {
|
||||||
(None, None) => true,
|
(None, None) => true,
|
||||||
(Some(vis0), Some(vis1)) => vis0.is_eq_to(&vis1),
|
(Some(vis0), Some(vis1)) => vis_eq(&vis0, &vis1),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use hir::Semantics;
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
base_db::FilePosition,
|
base_db::FilePosition,
|
||||||
defs::{Definition, NameClass, NameRefClass},
|
defs::{Definition, NameClass, NameRefClass},
|
||||||
helpers::{for_each_break_expr, for_each_tail_expr, pick_best_token},
|
helpers::{for_each_break_expr, for_each_tail_expr, node_ext::walk_expr, pick_best_token},
|
||||||
search::{FileReference, ReferenceAccess, SearchScope},
|
search::{FileReference, ReferenceAccess, SearchScope},
|
||||||
RootDatabase,
|
RootDatabase,
|
||||||
};
|
};
|
||||||
|
@ -122,7 +122,7 @@ fn highlight_exit_points(
|
||||||
) -> Option<Vec<HighlightedRange>> {
|
) -> Option<Vec<HighlightedRange>> {
|
||||||
let mut highlights = Vec::new();
|
let mut highlights = Vec::new();
|
||||||
let body = body?;
|
let body = body?;
|
||||||
body.walk(&mut |expr| match expr {
|
walk_expr(&body, &mut |expr| match expr {
|
||||||
ast::Expr::ReturnExpr(expr) => {
|
ast::Expr::ReturnExpr(expr) => {
|
||||||
if let Some(token) = expr.return_token() {
|
if let Some(token) = expr.return_token() {
|
||||||
highlights.push(HighlightedRange { access: None, range: token.text_range() });
|
highlights.push(HighlightedRange { access: None, range: token.text_range() });
|
||||||
|
@ -243,7 +243,7 @@ fn highlight_yield_points(token: SyntaxToken) -> Option<Vec<HighlightedRange>> {
|
||||||
let mut highlights =
|
let mut highlights =
|
||||||
vec![HighlightedRange { access: None, range: async_token?.text_range() }];
|
vec![HighlightedRange { access: None, range: async_token?.text_range() }];
|
||||||
if let Some(body) = body {
|
if let Some(body) = body {
|
||||||
body.walk(&mut |expr| {
|
walk_expr(&body, &mut |expr| {
|
||||||
if let ast::Expr::AwaitExpr(expr) = expr {
|
if let ast::Expr::AwaitExpr(expr) = expr {
|
||||||
if let Some(token) = expr.await_token() {
|
if let Some(token) = expr.await_token() {
|
||||||
highlights
|
highlights
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use ide_assists::utils::extract_trivial_expression;
|
use ide_assists::utils::extract_trivial_expression;
|
||||||
|
use ide_db::helpers::node_ext::expr_as_name_ref;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, AstNode, AstToken, IsString},
|
ast::{self, AstNode, AstToken, IsString},
|
||||||
|
@ -263,7 +264,7 @@ fn join_assignments(
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let lhs = bin_expr.lhs()?;
|
let lhs = bin_expr.lhs()?;
|
||||||
let name_ref = lhs.name_ref()?;
|
let name_ref = expr_as_name_ref(&lhs)?;
|
||||||
|
|
||||||
if name_ref.to_string() != let_ident_pat.syntax().to_string() {
|
if name_ref.to_string() != let_ident_pat.syntax().to_string() {
|
||||||
cov_mark::hit!(join_assignments_mismatch);
|
cov_mark::hit!(join_assignments_mismatch);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use hir::HirDisplay;
|
use hir::HirDisplay;
|
||||||
|
use ide_db::helpers::node_ext::walk_ty;
|
||||||
use syntax::ast::{self, AstNode, LetStmt, Param};
|
use syntax::ast::{self, AstNode, LetStmt, Param};
|
||||||
|
|
||||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||||
|
@ -46,7 +47,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
|
||||||
// Don't enable the assist if there is a type ascription without any placeholders
|
// Don't enable the assist if there is a type ascription without any placeholders
|
||||||
if let Some(ty) = &ascribed_ty {
|
if let Some(ty) = &ascribed_ty {
|
||||||
let mut contains_infer_ty = false;
|
let mut contains_infer_ty = false;
|
||||||
ty.walk(&mut |ty| contains_infer_ty |= matches!(ty, ast::Type::InferType(_)));
|
walk_ty(ty, &mut |ty| contains_infer_ty |= matches!(ty, ast::Type::InferType(_)));
|
||||||
if !contains_infer_ty {
|
if !contains_infer_ty {
|
||||||
cov_mark::hit!(add_explicit_type_not_applicable_if_ty_already_specified);
|
cov_mark::hit!(add_explicit_type_not_applicable_if_ty_already_specified);
|
||||||
return None;
|
return None;
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
use hir::{known, AsAssocItem, Semantics};
|
use hir::{known, AsAssocItem, Semantics};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
helpers::{for_each_tail_expr, FamousDefs},
|
helpers::{
|
||||||
|
for_each_tail_expr,
|
||||||
|
node_ext::{block_as_lone_tail, preorder_expr},
|
||||||
|
FamousDefs,
|
||||||
|
},
|
||||||
RootDatabase,
|
RootDatabase,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
@ -218,7 +222,7 @@ fn is_invalid_body(
|
||||||
expr: &ast::Expr,
|
expr: &ast::Expr,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut invalid = false;
|
let mut invalid = false;
|
||||||
expr.preorder(&mut |e| {
|
preorder_expr(expr, &mut |e| {
|
||||||
invalid |=
|
invalid |=
|
||||||
matches!(e, syntax::WalkEvent::Enter(ast::Expr::TryExpr(_) | ast::Expr::ReturnExpr(_)));
|
matches!(e, syntax::WalkEvent::Enter(ast::Expr::TryExpr(_) | ast::Expr::ReturnExpr(_)));
|
||||||
invalid
|
invalid
|
||||||
|
@ -252,7 +256,7 @@ fn block_is_none_variant(
|
||||||
block: &ast::BlockExpr,
|
block: &ast::BlockExpr,
|
||||||
none_variant: hir::Variant,
|
none_variant: hir::Variant,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
block.as_lone_tail().and_then(|e| match e {
|
block_as_lone_tail(block).and_then(|e| match e {
|
||||||
ast::Expr::PathExpr(pat) => match sema.resolve_path(&pat.path()?)? {
|
ast::Expr::PathExpr(pat) => match sema.resolve_path(&pat.path()?)? {
|
||||||
hir::PathResolution::Def(hir::ModuleDef::Variant(v)) => Some(v),
|
hir::PathResolution::Def(hir::ModuleDef::Variant(v)) => Some(v),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -5,6 +5,7 @@ use either::Either;
|
||||||
use hir::{HirDisplay, InFile, Local, Semantics, TypeInfo};
|
use hir::{HirDisplay, InFile, Local, Semantics, TypeInfo};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
defs::{Definition, NameRefClass},
|
defs::{Definition, NameRefClass},
|
||||||
|
helpers::node_ext::{preorder_expr, walk_expr, walk_pat, walk_patterns_in_expr},
|
||||||
search::{FileReference, ReferenceAccess, SearchScope},
|
search::{FileReference, ReferenceAccess, SearchScope},
|
||||||
RootDatabase,
|
RootDatabase,
|
||||||
};
|
};
|
||||||
|
@ -478,7 +479,7 @@ impl FunctionBody {
|
||||||
|
|
||||||
fn walk_expr(&self, cb: &mut dyn FnMut(ast::Expr)) {
|
fn walk_expr(&self, cb: &mut dyn FnMut(ast::Expr)) {
|
||||||
match self {
|
match self {
|
||||||
FunctionBody::Expr(expr) => expr.walk(cb),
|
FunctionBody::Expr(expr) => walk_expr(expr, cb),
|
||||||
FunctionBody::Span { parent, text_range } => {
|
FunctionBody::Span { parent, text_range } => {
|
||||||
parent
|
parent
|
||||||
.statements()
|
.statements()
|
||||||
|
@ -488,12 +489,12 @@ impl FunctionBody {
|
||||||
ast::Stmt::Item(_) => None,
|
ast::Stmt::Item(_) => None,
|
||||||
ast::Stmt::LetStmt(stmt) => stmt.initializer(),
|
ast::Stmt::LetStmt(stmt) => stmt.initializer(),
|
||||||
})
|
})
|
||||||
.for_each(|expr| expr.walk(cb));
|
.for_each(|expr| walk_expr(&expr, cb));
|
||||||
if let Some(expr) = parent
|
if let Some(expr) = parent
|
||||||
.tail_expr()
|
.tail_expr()
|
||||||
.filter(|it| text_range.contains_range(it.syntax().text_range()))
|
.filter(|it| text_range.contains_range(it.syntax().text_range()))
|
||||||
{
|
{
|
||||||
expr.walk(cb);
|
walk_expr(&expr, cb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -501,7 +502,7 @@ impl FunctionBody {
|
||||||
|
|
||||||
fn preorder_expr(&self, cb: &mut dyn FnMut(WalkEvent<ast::Expr>) -> bool) {
|
fn preorder_expr(&self, cb: &mut dyn FnMut(WalkEvent<ast::Expr>) -> bool) {
|
||||||
match self {
|
match self {
|
||||||
FunctionBody::Expr(expr) => expr.preorder(cb),
|
FunctionBody::Expr(expr) => preorder_expr(expr, cb),
|
||||||
FunctionBody::Span { parent, text_range } => {
|
FunctionBody::Span { parent, text_range } => {
|
||||||
parent
|
parent
|
||||||
.statements()
|
.statements()
|
||||||
|
@ -511,12 +512,12 @@ impl FunctionBody {
|
||||||
ast::Stmt::Item(_) => None,
|
ast::Stmt::Item(_) => None,
|
||||||
ast::Stmt::LetStmt(stmt) => stmt.initializer(),
|
ast::Stmt::LetStmt(stmt) => stmt.initializer(),
|
||||||
})
|
})
|
||||||
.for_each(|expr| expr.preorder(cb));
|
.for_each(|expr| preorder_expr(&expr, cb));
|
||||||
if let Some(expr) = parent
|
if let Some(expr) = parent
|
||||||
.tail_expr()
|
.tail_expr()
|
||||||
.filter(|it| text_range.contains_range(it.syntax().text_range()))
|
.filter(|it| text_range.contains_range(it.syntax().text_range()))
|
||||||
{
|
{
|
||||||
expr.preorder(cb);
|
preorder_expr(&expr, cb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -524,7 +525,7 @@ impl FunctionBody {
|
||||||
|
|
||||||
fn walk_pat(&self, cb: &mut dyn FnMut(ast::Pat)) {
|
fn walk_pat(&self, cb: &mut dyn FnMut(ast::Pat)) {
|
||||||
match self {
|
match self {
|
||||||
FunctionBody::Expr(expr) => expr.walk_patterns(cb),
|
FunctionBody::Expr(expr) => walk_patterns_in_expr(expr, cb),
|
||||||
FunctionBody::Span { parent, text_range } => {
|
FunctionBody::Span { parent, text_range } => {
|
||||||
parent
|
parent
|
||||||
.statements()
|
.statements()
|
||||||
|
@ -532,16 +533,16 @@ impl FunctionBody {
|
||||||
.for_each(|stmt| match stmt {
|
.for_each(|stmt| match stmt {
|
||||||
ast::Stmt::ExprStmt(expr_stmt) => {
|
ast::Stmt::ExprStmt(expr_stmt) => {
|
||||||
if let Some(expr) = expr_stmt.expr() {
|
if let Some(expr) = expr_stmt.expr() {
|
||||||
expr.walk_patterns(cb)
|
walk_patterns_in_expr(&expr, cb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Stmt::Item(_) => (),
|
ast::Stmt::Item(_) => (),
|
||||||
ast::Stmt::LetStmt(stmt) => {
|
ast::Stmt::LetStmt(stmt) => {
|
||||||
if let Some(pat) = stmt.pat() {
|
if let Some(pat) = stmt.pat() {
|
||||||
pat.walk(cb);
|
walk_pat(&pat, cb);
|
||||||
}
|
}
|
||||||
if let Some(expr) = stmt.initializer() {
|
if let Some(expr) = stmt.initializer() {
|
||||||
expr.walk_patterns(cb);
|
walk_patterns_in_expr(&expr, cb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -549,7 +550,7 @@ impl FunctionBody {
|
||||||
.tail_expr()
|
.tail_expr()
|
||||||
.filter(|it| text_range.contains_range(it.syntax().text_range()))
|
.filter(|it| text_range.contains_range(it.syntax().text_range()))
|
||||||
{
|
{
|
||||||
expr.walk_patterns(cb);
|
walk_patterns_in_expr(&expr, cb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use either::Either;
|
use either::Either;
|
||||||
|
use ide_db::helpers::node_ext::walk_ty;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, edit::IndentLevel, AstNode, GenericParamsOwner, NameOwner},
|
ast::{self, edit::IndentLevel, AstNode, GenericParamsOwner, NameOwner},
|
||||||
|
@ -120,7 +121,7 @@ fn collect_used_generics<'gp>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut generics = Vec::new();
|
let mut generics = Vec::new();
|
||||||
ty.walk(&mut |ty| match ty {
|
walk_ty(ty, &mut |ty| match ty {
|
||||||
ast::Type::PathType(ty) => {
|
ast::Type::PathType(ty) => {
|
||||||
if let Some(path) = ty.path() {
|
if let Some(path) = ty.path() {
|
||||||
if let Some(name_ref) = path.as_single_name_ref() {
|
if let Some(name_ref) = path.as_single_name_ref() {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
use ide_db::helpers::{for_each_tail_expr, FamousDefs};
|
use ide_db::helpers::{for_each_tail_expr, node_ext::walk_expr, FamousDefs};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, make, Expr},
|
ast::{self, make, Expr},
|
||||||
match_ast, AstNode,
|
match_ast, AstNode,
|
||||||
|
@ -54,7 +54,7 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext)
|
||||||
|
|
||||||
let mut exprs_to_wrap = Vec::new();
|
let mut exprs_to_wrap = Vec::new();
|
||||||
let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_wrap, e);
|
let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_wrap, e);
|
||||||
body.walk(&mut |expr| {
|
walk_expr(&body, &mut |expr| {
|
||||||
if let Expr::ReturnExpr(ret_expr) = expr {
|
if let Expr::ReturnExpr(ret_expr) = expr {
|
||||||
if let Some(ret_expr_arg) = &ret_expr.expr() {
|
if let Some(ret_expr_arg) = &ret_expr.expr() {
|
||||||
for_each_tail_expr(ret_expr_arg, tail_cb);
|
for_each_tail_expr(ret_expr_arg, tail_cb);
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
//! A module with ide helpers for high-level ide features.
|
//! A module with ide helpers for high-level ide features.
|
||||||
|
pub mod famous_defs;
|
||||||
|
pub mod generated_lints;
|
||||||
pub mod import_assets;
|
pub mod import_assets;
|
||||||
pub mod insert_use;
|
pub mod insert_use;
|
||||||
pub mod merge_imports;
|
pub mod merge_imports;
|
||||||
|
pub mod node_ext;
|
||||||
pub mod rust_doc;
|
pub mod rust_doc;
|
||||||
pub mod generated_lints;
|
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use base_db::FileId;
|
use base_db::FileId;
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait};
|
use hir::{ItemInNs, MacroDef, ModuleDef, Name, Semantics};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, make, LoopBodyOwner},
|
ast::{self, make, LoopBodyOwner},
|
||||||
AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxToken, TokenAtOffset, WalkEvent, T,
|
AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxToken, TokenAtOffset, WalkEvent, T,
|
||||||
|
@ -17,6 +19,8 @@ use syntax::{
|
||||||
|
|
||||||
use crate::RootDatabase;
|
use crate::RootDatabase;
|
||||||
|
|
||||||
|
pub use self::famous_defs::FamousDefs;
|
||||||
|
|
||||||
pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option<Name> {
|
pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option<Name> {
|
||||||
match item {
|
match item {
|
||||||
ItemInNs::Types(module_def_id) => ModuleDef::from(module_def_id).name(db),
|
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.
|
/// 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(
|
pub fn try_resolve_derive_input_at(
|
||||||
sema: &Semantics<RootDatabase>,
|
sema: &hir::Semantics<RootDatabase>,
|
||||||
derive_attr: &ast::Attr,
|
derive_attr: &ast::Attr,
|
||||||
cursor: &SyntaxToken,
|
cursor: &SyntaxToken,
|
||||||
) -> Option<MacroDef> {
|
) -> Option<MacroDef> {
|
||||||
|
@ -113,123 +117,6 @@ pub fn visit_file_defs(
|
||||||
module.impl_defs(db).into_iter().for_each(|impl_| cb(Either::Right(impl_)));
|
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)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub struct SnippetCap {
|
pub struct SnippetCap {
|
||||||
_private: (),
|
_private: (),
|
||||||
|
|
121
crates/ide_db/src/helpers/famous_defs.rs
Normal file
121
crates/ide_db/src/helpers/famous_defs.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,8 @@ use syntax::{
|
||||||
ted,
|
ted,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::helpers::node_ext::vis_eq;
|
||||||
|
|
||||||
/// What type of merges are allowed.
|
/// What type of merges are allowed.
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum MergeBehavior {
|
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 {
|
pub fn eq_visibility(vis0: Option<ast::Visibility>, vis1: Option<ast::Visibility>) -> bool {
|
||||||
match (vis0, vis1) {
|
match (vis0, vis1) {
|
||||||
(None, None) => true,
|
(None, None) => true,
|
||||||
(Some(vis0), Some(vis1)) => vis0.is_eq_to(&vis1),
|
(Some(vis0), Some(vis1)) => vis_eq(&vis0, &vis1),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
212
crates/ide_db/src/helpers/node_ext.rs
Normal file
212
crates/ide_db/src/helpers/node_ext.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,6 +34,7 @@ use text_edit::{TextEdit, TextEditBuilder};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
defs::Definition,
|
defs::Definition,
|
||||||
|
helpers::node_ext::expr_as_name_ref,
|
||||||
search::FileReference,
|
search::FileReference,
|
||||||
source_change::{FileSystemEdit, SourceChange},
|
source_change::{FileSystemEdit, SourceChange},
|
||||||
RootDatabase,
|
RootDatabase,
|
||||||
|
@ -339,7 +340,7 @@ fn source_edit_from_name_ref(
|
||||||
if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) {
|
if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) {
|
||||||
let rcf_name_ref = record_field.name_ref();
|
let rcf_name_ref = record_field.name_ref();
|
||||||
let rcf_expr = record_field.expr();
|
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
|
// field: init-expr, check if we can use a field init shorthand
|
||||||
(Some(field_name), Some(init)) => {
|
(Some(field_name), Some(init)) => {
|
||||||
if field_name == name_ref {
|
if field_name == name_ref {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Various extension methods to ast Expr Nodes, which are hard to code-generate.
|
//! Various extension methods to ast Expr Nodes, which are hard to code-generate.
|
||||||
|
//!
|
||||||
use rowan::WalkEvent;
|
//! These methods should only do simple, shallow tasks related to the syntax of the node itself.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
|
@ -28,139 +28,6 @@ impl ast::Expr {
|
||||||
| ast::Expr::EffectExpr(_)
|
| ast::Expr::EffectExpr(_)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name_ref(&self) -> Option<ast::NameRef> {
|
|
||||||
if let ast::Expr::PathExpr(expr) = self {
|
|
||||||
let path = expr.path()?;
|
|
||||||
let segment = path.segment()?;
|
|
||||||
let name_ref = segment.name_ref()?;
|
|
||||||
if path.qualifier().is_none() {
|
|
||||||
return Some(name_ref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Preorder walk all the expression's child expressions.
|
|
||||||
pub fn walk(&self, cb: &mut dyn FnMut(ast::Expr)) {
|
|
||||||
self.preorder(&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(&self, cb: &mut dyn FnMut(WalkEvent<ast::Expr>) -> bool) {
|
|
||||||
let mut preorder = self.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() {
|
|
||||||
expr.preorder(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(&self, cb: &mut dyn FnMut(ast::Pat)) {
|
|
||||||
let mut preorder = self.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() {
|
|
||||||
pat.walk(cb);
|
|
||||||
}
|
|
||||||
if let Some(expr) = l.initializer() {
|
|
||||||
expr.walk_patterns(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) => {
|
|
||||||
matches!(
|
|
||||||
effect.effect(),
|
|
||||||
ast::Effect::Async(_)
|
|
||||||
| ast::Effect::Try(_)
|
|
||||||
| ast::Effect::Const(_)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ast::Expr::ClosureExpr(_) => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
if is_different_context {
|
|
||||||
preorder.skip_subtree();
|
|
||||||
}
|
|
||||||
} else if let Some(pat) = ast::Pat::cast(node) {
|
|
||||||
preorder.skip_subtree();
|
|
||||||
pat.walk(cb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
@ -374,6 +241,7 @@ impl ast::Literal {
|
||||||
.and_then(|e| e.into_token())
|
.and_then(|e| e.into_token())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kind(&self) -> LiteralKind {
|
pub fn kind(&self) -> LiteralKind {
|
||||||
let token = self.token();
|
let token = self.token();
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
//! Various extension methods to ast Nodes, which are hard to code-generate.
|
//! Various extension methods to ast Nodes, which are hard to code-generate.
|
||||||
//! Extensions for various expressions live in a sibling `expr_extensions` module.
|
//! Extensions for various expressions live in a sibling `expr_extensions` module.
|
||||||
|
//!
|
||||||
|
//! These methods should only do simple, shallow tasks related to the syntax of the node itself.
|
||||||
|
|
||||||
use std::{borrow::Cow, fmt, iter::successors};
|
use std::{borrow::Cow, fmt, iter::successors};
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use parser::SyntaxKind;
|
use parser::SyntaxKind;
|
||||||
use rowan::{GreenNodeData, GreenTokenData, WalkEvent};
|
use rowan::{GreenNodeData, GreenTokenData};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
|
@ -56,66 +58,6 @@ impl ast::BlockExpr {
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.statements().next().is_none() && self.tail_expr().is_none()
|
self.statements().next().is_none() && self.tail_expr().is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_lone_tail(&self) -> Option<ast::Expr> {
|
|
||||||
self.statements().next().is_none().then(|| self.tail_expr()).flatten()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ast::Pat {
|
|
||||||
/// Preorder walk all the pattern's sub patterns.
|
|
||||||
pub fn walk(&self, cb: &mut dyn FnMut(ast::Pat)) {
|
|
||||||
let mut preorder = self.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 => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ast::Type {
|
|
||||||
/// Preorder walk all the type's sub types.
|
|
||||||
pub fn walk(&self, cb: &mut dyn FnMut(ast::Type)) {
|
|
||||||
let mut preorder = self.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 => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
|
@ -443,7 +385,15 @@ impl ast::RecordExprField {
|
||||||
if let Some(name_ref) = self.name_ref() {
|
if let Some(name_ref) = self.name_ref() {
|
||||||
return Some(name_ref);
|
return Some(name_ref);
|
||||||
}
|
}
|
||||||
self.expr()?.name_ref()
|
if let ast::Expr::PathExpr(expr) = self.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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -721,29 +671,6 @@ impl ast::Visibility {
|
||||||
None => VisibilityKind::Pub,
|
None => VisibilityKind::Pub,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_eq_to(&self, other: &Self) -> bool {
|
|
||||||
match (self.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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ast::LifetimeParam {
|
impl ast::LifetimeParam {
|
||||||
|
|
Loading…
Reference in a new issue