From fd1585a071718ef9c9fb44f88336608dd7e624a5 Mon Sep 17 00:00:00 2001 From: gfreezy Date: Mon, 25 Mar 2019 01:05:11 +0800 Subject: [PATCH 01/45] inline immutable local varialbe --- .../ra_assists/src/inline_local_variable.rs | 298 ++++++++++++++++++ crates/ra_assists/src/lib.rs | 2 + 2 files changed, 300 insertions(+) create mode 100644 crates/ra_assists/src/inline_local_variable.rs diff --git a/crates/ra_assists/src/inline_local_variable.rs b/crates/ra_assists/src/inline_local_variable.rs new file mode 100644 index 0000000000..ce0aafb27f --- /dev/null +++ b/crates/ra_assists/src/inline_local_variable.rs @@ -0,0 +1,298 @@ +use hir::db::HirDatabase; +use hir::source_binder::function_from_child_node; +use ra_syntax::{ast::{self, AstNode}, TextRange}; +use ra_syntax::ast::{PatKind, ExprKind}; + +use crate::{Assist, AssistCtx, AssistId}; +use crate::assist_ctx::AssistBuilder; + +pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx) -> Option { + let let_stmt = ctx.node_at_offset::()?; + let bind_pat = match let_stmt.pat()?.kind() { + PatKind::BindPat(pat) => pat, + _ => return None, + }; + if bind_pat.is_mutable() { + return None; + } + let initializer = let_stmt.initializer()?; + let wrap_in_parens = match initializer.kind() { + ExprKind::LambdaExpr(_) => true, + ExprKind::IfExpr(_) => true, + ExprKind::LoopExpr(_) => true, + ExprKind::ForExpr(_) => true, + ExprKind::WhileExpr(_) => true, + ExprKind::ContinueExpr(_) => true, + ExprKind::BreakExpr(_) => true, + ExprKind::Label(_) => true, + ExprKind::ReturnExpr(_) => true, + ExprKind::MatchExpr(_) => true, + ExprKind::StructLit(_) => true, + ExprKind::CastExpr(_) => true, + ExprKind::PrefixExpr(_) => true, + ExprKind::RangeExpr(_) => true, + ExprKind::BinExpr(_) => true, + ExprKind::CallExpr(_) => false, + ExprKind::IndexExpr(_) => false, + ExprKind::MethodCallExpr(_) => false, + ExprKind::FieldExpr(_) => false, + ExprKind::TryExpr(_) => false, + ExprKind::RefExpr(_) => false, + ExprKind::Literal(_) => false, + ExprKind::TupleExpr(_) => false, + ExprKind::ArrayExpr(_) => false, + ExprKind::ParenExpr(_) => false, + ExprKind::PathExpr(_) => false, + ExprKind::BlockExpr(_) => false, + }; + + let delete_range = if let Some(whitespace) = + let_stmt.syntax().next_sibling().and_then(ast::Whitespace::cast) + { + TextRange::from_to(let_stmt.syntax().range().start(), whitespace.syntax().range().end()) + } else { + let_stmt.syntax().range() + }; + + let init_str = if wrap_in_parens { + format!("({})", initializer.syntax().text().to_string()) + } else { + initializer.syntax().text().to_string() + }; + let function = function_from_child_node(ctx.db, ctx.frange.file_id, bind_pat.syntax())?; + let scope = function.scopes(ctx.db); + let refs = scope.find_all_refs(bind_pat); + + ctx.add_action( + AssistId("inline_local_variable"), + "inline local variable", + move |edit: &mut AssistBuilder| { + edit.delete(delete_range); + for desc in refs { + edit.replace(desc.range, init_str.clone()) + } + edit.set_cursor(delete_range.start()) + }, + ); + + ctx.build() +} + +#[cfg(test)] +mod tests { + use crate::helpers::{check_assist, check_assist_not_applicable}; + + use super::*; + + #[test] + fn test_inline_let_bind_literal_expr() { + check_assist( + inline_local_varialbe, + " +fn bar(a: usize) {} +fn foo() { + let a<|> = 1; + a + 1; + if a > 10 { + } + + while a > 10 { + + } + let b = a * 10; + bar(a); +}", + " +fn bar(a: usize) {} +fn foo() { + <|>1 + 1; + if 1 > 10 { + } + + while 1 > 10 { + + } + let b = 1 * 10; + bar(1); +}", + ); + } + + #[test] + fn test_inline_let_bind_bin_expr() { + check_assist( + inline_local_varialbe, + " +fn bar(a: usize) {} +fn foo() { + let a<|> = 1 + 1; + a + 1; + if a > 10 { + } + + while a > 10 { + + } + let b = a * 10; + bar(a); +}", + " +fn bar(a: usize) {} +fn foo() { + <|>(1 + 1) + 1; + if (1 + 1) > 10 { + } + + while (1 + 1) > 10 { + + } + let b = (1 + 1) * 10; + bar((1 + 1)); +}", + ); + } + + #[test] + fn test_inline_let_bind_function_call_expr() { + check_assist( + inline_local_varialbe, + " +fn bar(a: usize) {} +fn foo() { + let a<|> = bar(1); + a + 1; + if a > 10 { + } + + while a > 10 { + + } + let b = a * 10; + bar(a); +}", + " +fn bar(a: usize) {} +fn foo() { + <|>bar(1) + 1; + if bar(1) > 10 { + } + + while bar(1) > 10 { + + } + let b = bar(1) * 10; + bar(bar(1)); +}", + ); + } + + #[test] + fn test_inline_let_bind_cast_expr() { + check_assist( + inline_local_varialbe, + " +fn bar(a: usize): usize { a } +fn foo() { + let a<|> = bar(1) as u64; + a + 1; + if a > 10 { + } + + while a > 10 { + + } + let b = a * 10; + bar(a); +}", + " +fn bar(a: usize): usize { a } +fn foo() { + <|>(bar(1) as u64) + 1; + if (bar(1) as u64) > 10 { + } + + while (bar(1) as u64) > 10 { + + } + let b = (bar(1) as u64) * 10; + bar((bar(1) as u64)); +}", + ); + } + + #[test] + fn test_inline_let_bind_block_expr() { + check_assist( + inline_local_varialbe, + " +fn foo() { + let a<|> = { 10 + 1 }; + a + 1; + if a > 10 { + } + + while a > 10 { + + } + let b = a * 10; + bar(a); +}", + " +fn foo() { + <|>{ 10 + 1 } + 1; + if { 10 + 1 } > 10 { + } + + while { 10 + 1 } > 10 { + + } + let b = { 10 + 1 } * 10; + bar({ 10 + 1 }); +}", + ); + } + + #[test] + fn test_inline_let_bind_paren_expr() { + check_assist( + inline_local_varialbe, + " +fn foo() { + let a<|> = ( 10 + 1 ); + a + 1; + if a > 10 { + } + + while a > 10 { + + } + let b = a * 10; + bar(a); +}", + " +fn foo() { + <|>( 10 + 1 ) + 1; + if ( 10 + 1 ) > 10 { + } + + while ( 10 + 1 ) > 10 { + + } + let b = ( 10 + 1 ) * 10; + bar(( 10 + 1 )); +}", + ); + } + + #[test] + fn test_not_inline_mut_variable() { + check_assist_not_applicable( + inline_local_varialbe, + " +fn foo() { + let mut a<|> = 1 + 1; + a + 1; +}", + ); + } +} diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index fc36e8cc97..378470ac82 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -92,6 +92,7 @@ mod change_visibility; mod fill_match_arms; mod fill_struct_fields; mod introduce_variable; +mod inline_local_variable; mod replace_if_let_with_match; mod split_import; mod remove_dbg; @@ -113,6 +114,7 @@ fn all_assists() -> &'static [fn(AssistCtx) -> Option Date: Thu, 21 Mar 2019 22:13:11 +0300 Subject: [PATCH 02/45] diagnostics --- crates/ra_hir/src/code_model_api.rs | 5 +++++ crates/ra_hir/src/diagnostics.rs | 6 ++++++ crates/ra_hir/src/expr.rs | 15 ++++++++++++-- crates/ra_hir/src/lib.rs | 1 + crates/ra_hir/src/ty/infer.rs | 23 ++++++++++++++++++--- crates/ra_ide_api/src/diagnostics.rs | 30 +++++++++++++++++++++++++++- 6 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 crates/ra_hir/src/diagnostics.rs diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 45fa4cd110..58481e715e 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -17,6 +17,7 @@ use crate::{ ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId}, impl_block::ImplBlock, resolve::Resolver, + diagnostics::FunctionDiagnostic, }; /// hir::Crate describes a single crate. It's the main interface with which @@ -519,6 +520,10 @@ impl Function { let r = if !p.params.is_empty() { r.push_generic_params_scope(p) } else { r }; r } + + pub fn diagnostics(&self, db: &impl HirDatabase) -> Vec { + self.infer(db).diagnostics() + } } impl Docs for Function { diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs new file mode 100644 index 0000000000..82aff9cee0 --- /dev/null +++ b/crates/ra_hir/src/diagnostics.rs @@ -0,0 +1,6 @@ +use crate::{expr::ExprId}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum FunctionDiagnostic { + NoSuchField { expr: ExprId, field: usize }, +} diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index c37fd0454b..31af5d2410 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -5,7 +5,7 @@ use rustc_hash::FxHashMap; use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; use ra_syntax::{ - SyntaxNodePtr, AstNode, + SyntaxNodePtr, AstPtr, AstNode, ast::{self, LoopBodyOwner, ArgListOwner, NameOwner, LiteralFlavor, TypeAscriptionOwner} }; @@ -54,6 +54,7 @@ pub struct BodySourceMap { expr_map_back: ArenaMap, pat_map: FxHashMap, pat_map_back: ArenaMap, + field_map: FxHashMap<(ExprId, usize), AstPtr>, } impl Body { @@ -138,6 +139,10 @@ impl BodySourceMap { pub fn node_pat(&self, node: &ast::Pat) -> Option { self.pat_map.get(&SyntaxNodePtr::new(node.syntax())).cloned() } + + pub fn field_syntax(&self, expr: ExprId, field: usize) -> Option> { + self.field_map.get(&(expr, field)).cloned() + } } #[derive(Debug, Clone, Eq, PartialEq)] @@ -629,8 +634,10 @@ impl ExprCollector { } ast::ExprKind::StructLit(e) => { let path = e.path().and_then(Path::from_ast); + let mut field_ptrs = Vec::new(); let fields = if let Some(nfl) = e.named_field_list() { nfl.fields() + .inspect(|field| field_ptrs.push(AstPtr::new(*field))) .map(|field| StructLitField { name: field .name_ref() @@ -657,7 +664,11 @@ impl ExprCollector { Vec::new() }; let spread = e.spread().map(|s| self.collect_expr(s)); - self.alloc_expr(Expr::StructLit { path, fields, spread }, syntax_ptr) + let res = self.alloc_expr(Expr::StructLit { path, fields, spread }, syntax_ptr); + for (i, ptr) in field_ptrs.into_iter().enumerate() { + self.source_map.field_map.insert((res, i), ptr); + } + res } ast::ExprKind::FieldExpr(e) => { let expr = self.collect_expr_opt(e.expr()); diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index a89c916f88..390aef0a9c 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -35,6 +35,7 @@ mod expr; mod generics; mod docs; mod resolve; +pub mod diagnostics; mod code_model_api; mod code_model_impl; diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index cff7e74819..269b5162e3 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -36,7 +36,8 @@ use crate::{ path::{GenericArgs, GenericArg}, adt::VariantDef, resolve::{Resolver, Resolution}, - nameres::Namespace + nameres::Namespace, + diagnostics::FunctionDiagnostic, }; use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor}; @@ -96,6 +97,7 @@ pub struct InferenceResult { field_resolutions: FxHashMap, /// For each associated item record what it resolves to assoc_resolutions: FxHashMap, + diagnostics: Vec, pub(super) type_of_expr: ArenaMap, pub(super) type_of_pat: ArenaMap, } @@ -113,6 +115,9 @@ impl InferenceResult { pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option { self.assoc_resolutions.get(&id.into()).map(|it| *it) } + pub(crate) fn diagnostics(&self) -> Vec { + self.diagnostics.clone() + } } impl Index for InferenceResult { @@ -143,6 +148,7 @@ struct InferenceContext<'a, D: HirDatabase> { assoc_resolutions: FxHashMap, type_of_expr: ArenaMap, type_of_pat: ArenaMap, + diagnostics: Vec, /// The return type of the function being inferred. return_ty: Ty, } @@ -155,6 +161,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { assoc_resolutions: FxHashMap::default(), type_of_expr: ArenaMap::default(), type_of_pat: ArenaMap::default(), + diagnostics: Vec::default(), var_unification_table: InPlaceUnificationTable::new(), return_ty: Ty::Unknown, // set in collect_fn_signature db, @@ -181,6 +188,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { assoc_resolutions: self.assoc_resolutions, type_of_expr: expr_types, type_of_pat: pat_types, + diagnostics: self.diagnostics, } } @@ -915,9 +923,18 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Expr::StructLit { path, fields, spread } => { let (ty, def_id) = self.resolve_variant(path.as_ref()); let substs = ty.substs().unwrap_or_else(Substs::empty); - for field in fields { + for (field_idx, field) in fields.into_iter().enumerate() { let field_ty = def_id - .and_then(|it| it.field(self.db, &field.name)) + .and_then(|it| match it.field(self.db, &field.name) { + Some(field) => Some(field), + None => { + self.diagnostics.push(FunctionDiagnostic::NoSuchField { + expr: tgt_expr, + field: field_idx, + }); + None + } + }) .map_or(Ty::Unknown, |field| field.ty(self.db)) .subst(&substs); self.infer_expr(field.expr, &Expectation::has_type(field_ty)); diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 156f28ca33..f662f7e2f1 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -3,7 +3,7 @@ use hir::{Problem, source_binder}; use ra_db::SourceDatabase; use ra_syntax::{ Location, SourceFile, SyntaxKind, TextRange, SyntaxNode, - ast::{self, AstNode}, + ast::{self, AstNode, NameOwner}, }; use ra_text_edit::{TextEdit, TextEditBuilder}; @@ -134,6 +134,13 @@ fn check_module( file_id: FileId, module: hir::Module, ) { + for decl in module.declarations(db) { + match decl { + hir::ModuleDef::Function(f) => check_function(acc, db, f), + _ => (), + } + } + let source_root = db.file_source_root(file_id); for (name_node, problem) in module.problems(db) { let diag = match problem { @@ -153,6 +160,27 @@ fn check_module( } } +fn check_function(acc: &mut Vec, db: &RootDatabase, function: hir::Function) { + let (_file_id, fn_def) = function.source(db); + let source_file = fn_def.syntax().ancestors().find_map(ast::SourceFile::cast).unwrap(); + let source_map = function.body_source_map(db); + for d in function.diagnostics(db) { + match d { + hir::diagnostics::FunctionDiagnostic::NoSuchField { expr, field } => { + if let Some(field) = source_map.field_syntax(expr, field) { + let field = field.to_node(&source_file); + acc.push(Diagnostic { + message: "no such field".into(), + range: field.syntax().range(), + severity: Severity::Error, + fix: None, + }) + } + } + } + } +} + #[cfg(test)] mod tests { use test_utils::assert_eq_text; From fcca35969dd7c63a83ee34c4ce7d54cefdb72bbe Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 23 Mar 2019 16:28:47 +0300 Subject: [PATCH 03/45] allow dyn diagnostics --- crates/ra_hir/src/code_model_api.rs | 8 ++-- crates/ra_hir/src/diagnostics.rs | 62 ++++++++++++++++++++++++++-- crates/ra_hir/src/expr.rs | 4 +- crates/ra_hir/src/ty/infer.rs | 39 ++++++++++++++--- crates/ra_ide_api/src/diagnostics.rs | 26 ++++-------- crates/ra_syntax/src/ptr.rs | 6 +++ 6 files changed, 112 insertions(+), 33 deletions(-) diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 58481e715e..a37d960a14 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -17,7 +17,7 @@ use crate::{ ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId}, impl_block::ImplBlock, resolve::Resolver, - diagnostics::FunctionDiagnostic, + diagnostics::Diagnostics, }; /// hir::Crate describes a single crate. It's the main interface with which @@ -521,8 +521,10 @@ impl Function { r } - pub fn diagnostics(&self, db: &impl HirDatabase) -> Vec { - self.infer(db).diagnostics() + pub fn diagnostics(&self, db: &impl HirDatabase) -> Diagnostics { + let mut res = Diagnostics::default(); + self.infer(db).add_diagnostics(db, *self, &mut res); + res } } diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs index 82aff9cee0..46a3fdd479 100644 --- a/crates/ra_hir/src/diagnostics.rs +++ b/crates/ra_hir/src/diagnostics.rs @@ -1,6 +1,60 @@ -use crate::{expr::ExprId}; +use std::{fmt, any::Any}; -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum FunctionDiagnostic { - NoSuchField { expr: ExprId, field: usize }, +use ra_syntax::{SyntaxNodePtr, AstPtr, ast}; + +use crate::HirFileId; + +pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { + fn file(&self) -> HirFileId; + fn syntax_node(&self) -> SyntaxNodePtr; + fn message(&self) -> String; + fn as_any(&self) -> &(Any + Send + 'static); +} + +impl dyn Diagnostic { + pub fn downcast_ref(&self) -> Option<&D> { + self.as_any().downcast_ref() + } +} + +#[derive(Debug, Default)] +pub struct Diagnostics { + data: Vec>, +} + +impl Diagnostics { + pub fn push(&mut self, d: impl Diagnostic) { + self.data.push(Box::new(d)) + } + + pub fn iter<'a>(&'a self) -> impl Iterator + 'a { + self.data.iter().map(|it| it.as_ref()) + } +} + +#[derive(Debug)] +pub struct NoSuchField { + pub(crate) file: HirFileId, + pub(crate) field: AstPtr, +} + +impl NoSuchField { + pub fn field(&self) -> AstPtr { + self.field + } +} + +impl Diagnostic for NoSuchField { + fn file(&self) -> HirFileId { + self.file + } + fn syntax_node(&self) -> SyntaxNodePtr { + self.field.into() + } + fn message(&self) -> String { + "no such field".to_string() + } + fn as_any(&self) -> &(Any + Send + 'static) { + self + } } diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index 31af5d2410..a85422955b 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -140,8 +140,8 @@ impl BodySourceMap { self.pat_map.get(&SyntaxNodePtr::new(node.syntax())).cloned() } - pub fn field_syntax(&self, expr: ExprId, field: usize) -> Option> { - self.field_map.get(&(expr, field)).cloned() + pub fn field_syntax(&self, expr: ExprId, field: usize) -> AstPtr { + self.field_map[&(expr, field)].clone() } } diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 269b5162e3..02708ba0f4 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -37,7 +37,8 @@ use crate::{ adt::VariantDef, resolve::{Resolver, Resolution}, nameres::Namespace, - diagnostics::FunctionDiagnostic, + ty::infer::diagnostics::InferenceDiagnostic, + diagnostics::Diagnostics, }; use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor}; @@ -97,7 +98,7 @@ pub struct InferenceResult { field_resolutions: FxHashMap, /// For each associated item record what it resolves to assoc_resolutions: FxHashMap, - diagnostics: Vec, + diagnostics: Vec, pub(super) type_of_expr: ArenaMap, pub(super) type_of_pat: ArenaMap, } @@ -115,8 +116,13 @@ impl InferenceResult { pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option { self.assoc_resolutions.get(&id.into()).map(|it| *it) } - pub(crate) fn diagnostics(&self) -> Vec { - self.diagnostics.clone() + pub(crate) fn add_diagnostics( + &self, + db: &impl HirDatabase, + owner: Function, + diagnostics: &mut Diagnostics, + ) { + self.diagnostics.iter().for_each(|it| it.add_to(db, owner, diagnostics)) } } @@ -148,7 +154,7 @@ struct InferenceContext<'a, D: HirDatabase> { assoc_resolutions: FxHashMap, type_of_expr: ArenaMap, type_of_pat: ArenaMap, - diagnostics: Vec, + diagnostics: Vec, /// The return type of the function being inferred. return_ty: Ty, } @@ -928,7 +934,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { .and_then(|it| match it.field(self.db, &field.name) { Some(field) => Some(field), None => { - self.diagnostics.push(FunctionDiagnostic::NoSuchField { + self.diagnostics.push(InferenceDiagnostic::NoSuchField { expr: tgt_expr, field: field_idx, }); @@ -1261,3 +1267,24 @@ impl Expectation { Expectation { ty: Ty::Unknown } } } + +mod diagnostics { + use crate::{expr::ExprId, diagnostics::{Diagnostics, NoSuchField}, HirDatabase, Function}; + + #[derive(Debug, PartialEq, Eq, Clone)] + pub(super) enum InferenceDiagnostic { + NoSuchField { expr: ExprId, field: usize }, + } + + impl InferenceDiagnostic { + pub(super) fn add_to(&self, db: &impl HirDatabase, owner: Function, acc: &mut Diagnostics) { + match self { + InferenceDiagnostic::NoSuchField { expr, field } => { + let (file, _) = owner.source(db); + let field = owner.body_source_map(db).field_syntax(*expr, *field); + acc.push(NoSuchField { file, field }) + } + } + } + } +} diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index f662f7e2f1..943fd2f539 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -3,7 +3,7 @@ use hir::{Problem, source_binder}; use ra_db::SourceDatabase; use ra_syntax::{ Location, SourceFile, SyntaxKind, TextRange, SyntaxNode, - ast::{self, AstNode, NameOwner}, + ast::{self, AstNode}, }; use ra_text_edit::{TextEdit, TextEditBuilder}; @@ -161,23 +161,13 @@ fn check_module( } fn check_function(acc: &mut Vec, db: &RootDatabase, function: hir::Function) { - let (_file_id, fn_def) = function.source(db); - let source_file = fn_def.syntax().ancestors().find_map(ast::SourceFile::cast).unwrap(); - let source_map = function.body_source_map(db); - for d in function.diagnostics(db) { - match d { - hir::diagnostics::FunctionDiagnostic::NoSuchField { expr, field } => { - if let Some(field) = source_map.field_syntax(expr, field) { - let field = field.to_node(&source_file); - acc.push(Diagnostic { - message: "no such field".into(), - range: field.syntax().range(), - severity: Severity::Error, - fix: None, - }) - } - } - } + for d in function.diagnostics(db).iter() { + acc.push(Diagnostic { + message: d.message(), + range: d.syntax_node().range(), + severity: Severity::Error, + fix: None, + }) } } diff --git a/crates/ra_syntax/src/ptr.rs b/crates/ra_syntax/src/ptr.rs index aae590cb62..d8de1c4c1b 100644 --- a/crates/ra_syntax/src/ptr.rs +++ b/crates/ra_syntax/src/ptr.rs @@ -64,6 +64,12 @@ impl AstPtr { } } +impl From> for SyntaxNodePtr { + fn from(ptr: AstPtr) -> SyntaxNodePtr { + ptr.raw + } +} + #[test] fn test_local_syntax_ptr() { use crate::{ast, AstNode}; From 3fb88e95aa5e122a521beec766d5b1264ca4de3b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 23 Mar 2019 18:35:14 +0300 Subject: [PATCH 04/45] switch modules to new diagnostics --- crates/ra_hir/src/code_model_api.rs | 18 ++---- crates/ra_hir/src/code_model_impl/module.rs | 19 +----- crates/ra_hir/src/diagnostics.rs | 45 +++++++++++--- crates/ra_hir/src/lib.rs | 2 +- crates/ra_hir/src/nameres.rs | 65 +++++++++++++------- crates/ra_hir/src/nameres/collector.rs | 63 +++++++++----------- crates/ra_hir/src/ty/infer.rs | 13 ++-- crates/ra_ide_api/src/diagnostics.rs | 66 ++++++++++----------- 8 files changed, 156 insertions(+), 135 deletions(-) diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index a37d960a14..03b1acef3b 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -1,8 +1,7 @@ use std::sync::Arc; -use relative_path::RelativePathBuf; use ra_db::{CrateId, SourceRootId, Edition}; -use ra_syntax::{ast::self, TreeArc, SyntaxNode}; +use ra_syntax::{ast::self, TreeArc}; use crate::{ Name, ScopesWithSourceMap, Ty, HirFileId, @@ -96,11 +95,6 @@ pub enum ModuleSource { Module(TreeArc), } -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -pub enum Problem { - UnresolvedModule { candidate: RelativePathBuf }, -} - impl Module { /// Name of this module. pub fn name(&self, db: &impl HirDatabase) -> Option { @@ -172,8 +166,8 @@ impl Module { db.crate_def_map(self.krate)[self.module_id].scope.clone() } - pub fn problems(&self, db: &impl HirDatabase) -> Vec<(TreeArc, Problem)> { - self.problems_impl(db) + pub fn diagnostics(&self, db: &impl HirDatabase, sink: &mut Diagnostics) { + db.crate_def_map(self.krate).add_diagnostics(db, self.module_id, sink); } pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { @@ -521,10 +515,8 @@ impl Function { r } - pub fn diagnostics(&self, db: &impl HirDatabase) -> Diagnostics { - let mut res = Diagnostics::default(); - self.infer(db).add_diagnostics(db, *self, &mut res); - res + pub fn diagnostics(&self, db: &impl HirDatabase, sink: &mut Diagnostics) { + self.infer(db).add_diagnostics(db, *self, sink); } } diff --git a/crates/ra_hir/src/code_model_impl/module.rs b/crates/ra_hir/src/code_model_impl/module.rs index 52a33e981f..14237060cf 100644 --- a/crates/ra_hir/src/code_model_impl/module.rs +++ b/crates/ra_hir/src/code_model_impl/module.rs @@ -1,8 +1,8 @@ use ra_db::FileId; -use ra_syntax::{ast, SyntaxNode, TreeArc, AstNode}; +use ra_syntax::{ast, TreeArc, AstNode}; use crate::{ - Module, ModuleSource, Problem, Name, + Module, ModuleSource, Name, nameres::{CrateModuleId, ImportId}, HirDatabase, DefDatabase, HirFileId, SourceItemId, @@ -108,19 +108,4 @@ impl Module { let parent_id = def_map[self.module_id].parent?; Some(self.with_module_id(parent_id)) } - - pub(crate) fn problems_impl( - &self, - db: &impl HirDatabase, - ) -> Vec<(TreeArc, Problem)> { - let def_map = db.crate_def_map(self.krate); - let (my_file_id, _) = self.definition_source(db); - // FIXME: not entirely corret filterint by module - def_map - .problems() - .iter() - .filter(|(source_item_id, _problem)| my_file_id == source_item_id.file_id) - .map(|(source_item_id, problem)| (db.file_item(*source_item_id), problem.clone())) - .collect() - } } diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs index 46a3fdd479..e8bb1ed92e 100644 --- a/crates/ra_hir/src/diagnostics.rs +++ b/crates/ra_hir/src/diagnostics.rs @@ -3,7 +3,20 @@ use std::{fmt, any::Any}; use ra_syntax::{SyntaxNodePtr, AstPtr, ast}; use crate::HirFileId; +use relative_path::RelativePathBuf; +/// Diagnostic defines hir API for errors and warnings. +/// +/// It is used as a `dyn` object, which you can downcast to a concrete +/// diagnostic. Diagnostics are structured, meaning that they include rich +/// information which can be used by IDE to create fixes. Diagnostics are +/// expressed in terms of macro-expanded syntax tree nodes (so, it's a bad idea +/// to diagnostic in a salsa value). +/// +/// Internally, various subsystems of hir produce diagnostics specific to a +/// subsytem (typically, an `enum`), which are safe to store in salsa but do not +/// include source locations. Such internal diagnostic are transformed into an +/// instance of `Diagnostic` on demand. pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { fn file(&self) -> HirFileId; fn syntax_node(&self) -> SyntaxNodePtr; @@ -34,14 +47,8 @@ impl Diagnostics { #[derive(Debug)] pub struct NoSuchField { - pub(crate) file: HirFileId, - pub(crate) field: AstPtr, -} - -impl NoSuchField { - pub fn field(&self) -> AstPtr { - self.field - } + pub file: HirFileId, + pub field: AstPtr, } impl Diagnostic for NoSuchField { @@ -58,3 +65,25 @@ impl Diagnostic for NoSuchField { self } } + +#[derive(Debug)] +pub struct UnresolvedModule { + pub file: HirFileId, + pub decl: AstPtr, + pub candidate: RelativePathBuf, +} + +impl Diagnostic for UnresolvedModule { + fn file(&self) -> HirFileId { + self.file + } + fn syntax_node(&self) -> SyntaxNodePtr { + self.decl.into() + } + fn message(&self) -> String { + "unresolved module".to_string() + } + fn as_any(&self) -> &(Any + Send + 'static) { + self + } +} diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 390aef0a9c..ce54d76084 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -64,7 +64,7 @@ pub use self::{ pub use self::code_model_api::{ Crate, CrateDependency, - Module, ModuleDef, ModuleSource, Problem, + Module, ModuleDef, ModuleSource, Struct, Enum, EnumVariant, Function, FnSignature, StructField, FieldSource, diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index d361cf9e62..416f114b40 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -56,14 +56,17 @@ mod tests; use std::sync::Arc; use rustc_hash::FxHashMap; +use relative_path::RelativePathBuf; use ra_arena::{Arena, RawId, impl_arena_id}; use ra_db::{FileId, Edition}; +use ra_syntax::{AstNode, AstPtr, ast}; use test_utils::tested_by; use crate::{ - ModuleDef, Name, Crate, Module, Problem, + ModuleDef, Name, Crate, Module, DefDatabase, Path, PathKind, HirFileId, ids::{SourceItemId, SourceFileItemId, MacroCallId}, + diagnostics::{Diagnostics, UnresolvedModule}, }; pub(crate) use self::raw::{RawItems, ImportId, ImportSourceMap}; @@ -85,7 +88,7 @@ pub struct CrateDefMap { macros: Arena, public_macros: FxHashMap, macro_resolutions: FxHashMap, - problems: CrateDefMapProblems, + diagnostics: Vec, } impl std::ops::Index for CrateDefMap { @@ -125,21 +128,6 @@ pub(crate) struct ModuleData { pub(crate) definition: Option, } -#[derive(Default, Debug, PartialEq, Eq)] -pub(crate) struct CrateDefMapProblems { - problems: Vec<(SourceItemId, Problem)>, -} - -impl CrateDefMapProblems { - fn add(&mut self, source_item_id: SourceItemId, problem: Problem) { - self.problems.push((source_item_id, problem)) - } - - pub(crate) fn iter<'a>(&'a self) -> impl Iterator + 'a { - self.problems.iter().map(|(s, p)| (s, p)) - } -} - #[derive(Debug, Default, PartialEq, Eq, Clone)] pub struct ModuleScope { items: FxHashMap, @@ -212,7 +200,7 @@ impl CrateDefMap { macros: Arena::default(), public_macros: FxHashMap::default(), macro_resolutions: FxHashMap::default(), - problems: CrateDefMapProblems::default(), + diagnostics: Vec::new(), } }; let def_map = collector::collect_defs(db, def_map); @@ -224,10 +212,6 @@ impl CrateDefMap { self.root } - pub(crate) fn problems(&self) -> &CrateDefMapProblems { - &self.problems - } - pub(crate) fn mk_module(&self, module_id: CrateModuleId) -> Module { Module { krate: self.krate, module_id } } @@ -240,6 +224,15 @@ impl CrateDefMap { &self.extern_prelude } + pub(crate) fn add_diagnostics( + &self, + db: &impl DefDatabase, + module: CrateModuleId, + sink: &mut Diagnostics, + ) { + self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink)) + } + pub(crate) fn resolve_macro( &self, macro_call_id: MacroCallId, @@ -452,3 +445,31 @@ impl CrateDefMap { } } } + +#[derive(Debug, PartialEq, Eq)] +enum DefDiagnostic { + UnresolvedModule { + module: CrateModuleId, + declaration: SourceItemId, + candidate: RelativePathBuf, + }, +} + +impl DefDiagnostic { + fn add_to(&self, db: &impl DefDatabase, target_module: CrateModuleId, sink: &mut Diagnostics) { + match self { + DefDiagnostic::UnresolvedModule { module, declaration, candidate } => { + if *module != target_module { + return; + } + let syntax = db.file_item(*declaration); + let decl = ast::Module::cast(&syntax).unwrap(); + sink.push(UnresolvedModule { + file: declaration.file_id, + decl: AstPtr::new(&decl), + candidate: candidate.clone(), + }) + } + } + } +} diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index c5b73cfbee..bc6bc5e7fd 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -6,9 +6,9 @@ use ra_db::FileId; use crate::{ Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias, - DefDatabase, HirFileId, Name, Path, Problem, Crate, + DefDatabase, HirFileId, Name, Path, Crate, KnownName, - nameres::{Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode, raw}, + nameres::{Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode, raw, DefDiagnostic}, ids::{AstItemDef, LocationCtx, MacroCallLoc, SourceItemId, MacroCallId}, }; @@ -405,25 +405,27 @@ where raw::ModuleData::Declaration { name, source_item_id } => { let source_item_id = source_item_id.with_file_id(self.file_id); let is_root = self.def_collector.def_map.modules[self.module_id].parent.is_none(); - let (file_ids, problem) = - resolve_submodule(self.def_collector.db, self.file_id, name, is_root); - - if let Some(problem) = problem { - self.def_collector.def_map.problems.add(source_item_id, problem) - } - - if let Some(&file_id) = file_ids.first() { - let module_id = - self.push_child_module(name.clone(), source_item_id, Some(file_id)); - let raw_items = self.def_collector.db.raw_items(file_id); - ModCollector { - def_collector: &mut *self.def_collector, - module_id, - file_id: file_id.into(), - raw_items: &raw_items, + match resolve_submodule(self.def_collector.db, self.file_id, name, is_root) { + Ok(file_id) => { + let module_id = + self.push_child_module(name.clone(), source_item_id, Some(file_id)); + let raw_items = self.def_collector.db.raw_items(file_id); + ModCollector { + def_collector: &mut *self.def_collector, + module_id, + file_id: file_id.into(), + raw_items: &raw_items, + } + .collect(raw_items.items()) } - .collect(raw_items.items()) - } + Err(candidate) => self.def_collector.def_map.diagnostics.push( + DefDiagnostic::UnresolvedModule { + module: self.module_id, + declaration: source_item_id, + candidate, + }, + ), + }; } } } @@ -524,7 +526,7 @@ fn resolve_submodule( file_id: HirFileId, name: &Name, is_root: bool, -) -> (Vec, Option) { +) -> Result { // FIXME: handle submodules of inline modules properly let file_id = file_id.original_file(db); let source_root_id = db.file_source_root(file_id); @@ -545,17 +547,10 @@ fn resolve_submodule( candidates.push(file_dir_mod.clone()); }; let sr = db.source_root(source_root_id); - let points_to = candidates - .into_iter() - .filter_map(|path| sr.files.get(&path)) - .map(|&it| it) - .collect::>(); - let problem = if points_to.is_empty() { - Some(Problem::UnresolvedModule { - candidate: if is_dir_owner { file_mod } else { file_dir_mod }, - }) - } else { - None - }; - (points_to, problem) + let mut points_to = candidates.into_iter().filter_map(|path| sr.files.get(&path)).map(|&it| it); + // FIXME: handle ambiguity + match points_to.next() { + Some(file_id) => Ok(file_id), + None => Err(if is_dir_owner { file_mod } else { file_dir_mod }), + } } diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 02708ba0f4..6dc3edc7a1 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -120,9 +120,9 @@ impl InferenceResult { &self, db: &impl HirDatabase, owner: Function, - diagnostics: &mut Diagnostics, + sink: &mut Diagnostics, ) { - self.diagnostics.iter().for_each(|it| it.add_to(db, owner, diagnostics)) + self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink)) } } @@ -1277,12 +1277,17 @@ mod diagnostics { } impl InferenceDiagnostic { - pub(super) fn add_to(&self, db: &impl HirDatabase, owner: Function, acc: &mut Diagnostics) { + pub(super) fn add_to( + &self, + db: &impl HirDatabase, + owner: Function, + sink: &mut Diagnostics, + ) { match self { InferenceDiagnostic::NoSuchField { expr, field } => { let (file, _) = owner.source(db); let field = owner.body_source_map(db).field_syntax(*expr, *field); - acc.push(NoSuchField { file, field }) + sink.push(NoSuchField { file, field }) } } } diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 943fd2f539..254342c0a6 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -1,5 +1,5 @@ use itertools::Itertools; -use hir::{Problem, source_binder}; +use hir::{source_binder, diagnostics::Diagnostic as _}; use ra_db::SourceDatabase; use ra_syntax::{ Location, SourceFile, SyntaxKind, TextRange, SyntaxNode, @@ -28,7 +28,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec } if let Some(m) = source_binder::module_from_file_id(db, file_id) { - check_module(&mut res, db, file_id, m); + check_module(&mut res, db, m); }; res } @@ -128,46 +128,40 @@ fn check_struct_shorthand_initialization( Some(()) } -fn check_module( - acc: &mut Vec, - db: &RootDatabase, - file_id: FileId, - module: hir::Module, -) { +fn check_module(acc: &mut Vec, db: &RootDatabase, module: hir::Module) { + let mut diagnostics = hir::diagnostics::Diagnostics::default(); + module.diagnostics(db, &mut diagnostics); for decl in module.declarations(db) { match decl { - hir::ModuleDef::Function(f) => check_function(acc, db, f), + hir::ModuleDef::Function(f) => f.diagnostics(db, &mut diagnostics), _ => (), } } - let source_root = db.file_source_root(file_id); - for (name_node, problem) in module.problems(db) { - let diag = match problem { - Problem::UnresolvedModule { candidate } => { - let create_file = - FileSystemEdit::CreateFile { source_root, path: candidate.clone() }; - let fix = SourceChange::file_system_edit("create module", create_file); - Diagnostic { - range: name_node.range(), - message: "unresolved module".to_string(), - severity: Severity::Error, - fix: Some(fix), - } - } - }; - acc.push(diag) - } -} - -fn check_function(acc: &mut Vec, db: &RootDatabase, function: hir::Function) { - for d in function.diagnostics(db).iter() { - acc.push(Diagnostic { - message: d.message(), - range: d.syntax_node().range(), - severity: Severity::Error, - fix: None, - }) + for d in diagnostics.iter() { + if let Some(d) = d.downcast_ref::() { + let source_root = db.file_source_root(d.file().original_file(db)); + let create_file = FileSystemEdit::CreateFile { source_root, path: d.candidate.clone() }; + let fix = SourceChange { + label: "create module".to_string(), + source_file_edits: Vec::new(), + file_system_edits: vec![create_file], + cursor_position: None, + }; + acc.push(Diagnostic { + range: d.syntax_node().range(), + message: d.message(), + severity: Severity::Error, + fix: Some(fix), + }) + } else { + acc.push(Diagnostic { + message: d.message(), + range: d.syntax_node().range(), + severity: Severity::Error, + fix: None, + }) + } } } From 79df62bc742afa33dcf5bedefd60860ca296b9da Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 23 Mar 2019 20:41:59 +0300 Subject: [PATCH 05/45] cleanup --- crates/ra_hir/src/code_model_api.rs | 6 +-- crates/ra_hir/src/diagnostics.rs | 28 +++++------ crates/ra_hir/src/nameres.rs | 66 ++++++++++++++++---------- crates/ra_hir/src/nameres/collector.rs | 9 ++-- crates/ra_hir/src/ty/infer.rs | 8 ++-- crates/ra_ide_api/src/diagnostics.rs | 4 +- 6 files changed, 70 insertions(+), 51 deletions(-) diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 03b1acef3b..bc0f74c896 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -16,7 +16,7 @@ use crate::{ ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId}, impl_block::ImplBlock, resolve::Resolver, - diagnostics::Diagnostics, + diagnostics::DiagnosticSink, }; /// hir::Crate describes a single crate. It's the main interface with which @@ -166,7 +166,7 @@ impl Module { db.crate_def_map(self.krate)[self.module_id].scope.clone() } - pub fn diagnostics(&self, db: &impl HirDatabase, sink: &mut Diagnostics) { + pub fn diagnostics(&self, db: &impl HirDatabase, sink: &mut DiagnosticSink) { db.crate_def_map(self.krate).add_diagnostics(db, self.module_id, sink); } @@ -515,7 +515,7 @@ impl Function { r } - pub fn diagnostics(&self, db: &impl HirDatabase, sink: &mut Diagnostics) { + pub fn diagnostics(&self, db: &impl HirDatabase, sink: &mut DiagnosticSink) { self.infer(db).add_diagnostics(db, *self, sink); } } diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs index e8bb1ed92e..d6b28159e9 100644 --- a/crates/ra_hir/src/diagnostics.rs +++ b/crates/ra_hir/src/diagnostics.rs @@ -8,19 +8,19 @@ use relative_path::RelativePathBuf; /// Diagnostic defines hir API for errors and warnings. /// /// It is used as a `dyn` object, which you can downcast to a concrete -/// diagnostic. Diagnostics are structured, meaning that they include rich -/// information which can be used by IDE to create fixes. Diagnostics are +/// diagnostic. DiagnosticSink are structured, meaning that they include rich +/// information which can be used by IDE to create fixes. DiagnosticSink are /// expressed in terms of macro-expanded syntax tree nodes (so, it's a bad idea /// to diagnostic in a salsa value). /// /// Internally, various subsystems of hir produce diagnostics specific to a -/// subsytem (typically, an `enum`), which are safe to store in salsa but do not +/// subsystem (typically, an `enum`), which are safe to store in salsa but do not /// include source locations. Such internal diagnostic are transformed into an /// instance of `Diagnostic` on demand. pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { + fn message(&self) -> String; fn file(&self) -> HirFileId; fn syntax_node(&self) -> SyntaxNodePtr; - fn message(&self) -> String; fn as_any(&self) -> &(Any + Send + 'static); } @@ -31,17 +31,17 @@ impl dyn Diagnostic { } #[derive(Debug, Default)] -pub struct Diagnostics { +pub struct DiagnosticSink { data: Vec>, } -impl Diagnostics { +impl DiagnosticSink { pub fn push(&mut self, d: impl Diagnostic) { self.data.push(Box::new(d)) } - pub fn iter<'a>(&'a self) -> impl Iterator + 'a { - self.data.iter().map(|it| it.as_ref()) + pub fn into_diagnostics(self) -> Vec> { + self.data } } @@ -52,15 +52,15 @@ pub struct NoSuchField { } impl Diagnostic for NoSuchField { + fn message(&self) -> String { + "no such field".to_string() + } fn file(&self) -> HirFileId { self.file } fn syntax_node(&self) -> SyntaxNodePtr { self.field.into() } - fn message(&self) -> String { - "no such field".to_string() - } fn as_any(&self) -> &(Any + Send + 'static) { self } @@ -74,15 +74,15 @@ pub struct UnresolvedModule { } impl Diagnostic for UnresolvedModule { + fn message(&self) -> String { + "unresolved module".to_string() + } fn file(&self) -> HirFileId { self.file } fn syntax_node(&self) -> SyntaxNodePtr { self.decl.into() } - fn message(&self) -> String { - "unresolved module".to_string() - } fn as_any(&self) -> &(Any + Send + 'static) { self } diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 416f114b40..56ed872d59 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -56,17 +56,16 @@ mod tests; use std::sync::Arc; use rustc_hash::FxHashMap; -use relative_path::RelativePathBuf; use ra_arena::{Arena, RawId, impl_arena_id}; use ra_db::{FileId, Edition}; -use ra_syntax::{AstNode, AstPtr, ast}; use test_utils::tested_by; use crate::{ ModuleDef, Name, Crate, Module, DefDatabase, Path, PathKind, HirFileId, ids::{SourceItemId, SourceFileItemId, MacroCallId}, - diagnostics::{Diagnostics, UnresolvedModule}, + diagnostics::DiagnosticSink, + nameres::diagnostics::DefDiagnostic, }; pub(crate) use self::raw::{RawItems, ImportId, ImportSourceMap}; @@ -228,7 +227,7 @@ impl CrateDefMap { &self, db: &impl DefDatabase, module: CrateModuleId, - sink: &mut Diagnostics, + sink: &mut DiagnosticSink, ) { self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink)) } @@ -446,30 +445,47 @@ impl CrateDefMap { } } -#[derive(Debug, PartialEq, Eq)] -enum DefDiagnostic { - UnresolvedModule { - module: CrateModuleId, - declaration: SourceItemId, - candidate: RelativePathBuf, - }, -} +mod diagnostics { + use relative_path::RelativePathBuf; + use ra_syntax::{AstPtr, AstNode, ast}; -impl DefDiagnostic { - fn add_to(&self, db: &impl DefDatabase, target_module: CrateModuleId, sink: &mut Diagnostics) { - match self { - DefDiagnostic::UnresolvedModule { module, declaration, candidate } => { - if *module != target_module { - return; + use crate::{ + SourceItemId, DefDatabase, + nameres::CrateModuleId, + diagnostics::{DiagnosticSink, UnresolvedModule}, +}; + + #[derive(Debug, PartialEq, Eq)] + pub(super) enum DefDiagnostic { + UnresolvedModule { + module: CrateModuleId, + declaration: SourceItemId, + candidate: RelativePathBuf, + }, + } + + impl DefDiagnostic { + pub(super) fn add_to( + &self, + db: &impl DefDatabase, + target_module: CrateModuleId, + sink: &mut DiagnosticSink, + ) { + match self { + DefDiagnostic::UnresolvedModule { module, declaration, candidate } => { + if *module != target_module { + return; + } + let syntax = db.file_item(*declaration); + let decl = ast::Module::cast(&syntax).unwrap(); + sink.push(UnresolvedModule { + file: declaration.file_id, + decl: AstPtr::new(&decl), + candidate: candidate.clone(), + }) } - let syntax = db.file_item(*declaration); - let decl = ast::Module::cast(&syntax).unwrap(); - sink.push(UnresolvedModule { - file: declaration.file_id, - decl: AstPtr::new(&decl), - candidate: candidate.clone(), - }) } } } + } diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index bc6bc5e7fd..8830b4624a 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -8,12 +8,15 @@ use crate::{ Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias, DefDatabase, HirFileId, Name, Path, Crate, KnownName, - nameres::{Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode, raw, DefDiagnostic}, + nameres::{ + Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode, + CrateDefMap, CrateModuleId, ModuleData, CrateMacroId, + diagnostics::DefDiagnostic, + raw, + }, ids::{AstItemDef, LocationCtx, MacroCallLoc, SourceItemId, MacroCallId}, }; -use super::{CrateDefMap, CrateModuleId, ModuleData, CrateMacroId}; - pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { // populate external prelude for dep in def_map.krate.dependencies(db) { diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 6dc3edc7a1..5fd602a9e6 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -38,7 +38,7 @@ use crate::{ resolve::{Resolver, Resolution}, nameres::Namespace, ty::infer::diagnostics::InferenceDiagnostic, - diagnostics::Diagnostics, + diagnostics::DiagnosticSink, }; use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor}; @@ -120,7 +120,7 @@ impl InferenceResult { &self, db: &impl HirDatabase, owner: Function, - sink: &mut Diagnostics, + sink: &mut DiagnosticSink, ) { self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink)) } @@ -1269,7 +1269,7 @@ impl Expectation { } mod diagnostics { - use crate::{expr::ExprId, diagnostics::{Diagnostics, NoSuchField}, HirDatabase, Function}; + use crate::{expr::ExprId, diagnostics::{DiagnosticSink, NoSuchField}, HirDatabase, Function}; #[derive(Debug, PartialEq, Eq, Clone)] pub(super) enum InferenceDiagnostic { @@ -1281,7 +1281,7 @@ mod diagnostics { &self, db: &impl HirDatabase, owner: Function, - sink: &mut Diagnostics, + sink: &mut DiagnosticSink, ) { match self { InferenceDiagnostic::NoSuchField { expr, field } => { diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 254342c0a6..1395cede21 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -129,7 +129,7 @@ fn check_struct_shorthand_initialization( } fn check_module(acc: &mut Vec, db: &RootDatabase, module: hir::Module) { - let mut diagnostics = hir::diagnostics::Diagnostics::default(); + let mut diagnostics = hir::diagnostics::DiagnosticSink::default(); module.diagnostics(db, &mut diagnostics); for decl in module.declarations(db) { match decl { @@ -138,7 +138,7 @@ fn check_module(acc: &mut Vec, db: &RootDatabase, module: hir::Modul } } - for d in diagnostics.iter() { + for d in diagnostics.into_diagnostics().iter() { if let Some(d) = d.downcast_ref::() { let source_root = db.file_source_root(d.file().original_file(db)); let create_file = FileSystemEdit::CreateFile { source_root, path: d.candidate.clone() }; From 45fbab2b1ac02dab971d245c45c2404494cb3e03 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 23 Mar 2019 21:17:05 +0300 Subject: [PATCH 06/45] check impls as well --- crates/ra_hir/src/nameres.rs | 2 +- crates/ra_ide_api/src/diagnostics.rs | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 56ed872d59..cdf5023687 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -192,7 +192,7 @@ impl CrateDefMap { CrateDefMap { krate, edition, - extern_prelude: FxHashMap::default(), + xextern_prelude: FxHashMap::default(), prelude: None, root, modules, diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 1395cede21..e03dcaa8fe 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -138,6 +138,15 @@ fn check_module(acc: &mut Vec, db: &RootDatabase, module: hir::Modul } } + for impl_block in module.impl_blocks(db) { + for item in impl_block.items(db) { + match item { + hir::ImplItem::Method(f) => f.diagnostics(db, &mut diagnostics), + _ => (), + } + } + } + for d in diagnostics.into_diagnostics().iter() { if let Some(d) = d.downcast_ref::() { let source_root = db.file_source_root(d.file().original_file(db)); From 7ee2887d1ec129afed80845c2361ce35f1a0c013 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 23 Mar 2019 21:20:49 +0300 Subject: [PATCH 07/45] fixes --- crates/ra_hir/src/nameres.rs | 2 +- crates/ra_ide_api/src/diagnostics.rs | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index cdf5023687..56ed872d59 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -192,7 +192,7 @@ impl CrateDefMap { CrateDefMap { krate, edition, - xextern_prelude: FxHashMap::default(), + extern_prelude: FxHashMap::default(), prelude: None, root, modules, diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index e03dcaa8fe..5b1a90c82b 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -1,10 +1,9 @@ use itertools::Itertools; -use hir::{source_binder, diagnostics::Diagnostic as _}; +use hir::{source_binder, diagnostics::{Diagnostic as _, DiagnosticSink}}; use ra_db::SourceDatabase; use ra_syntax::{ Location, SourceFile, SyntaxKind, TextRange, SyntaxNode, ast::{self, AstNode}, - }; use ra_text_edit::{TextEdit, TextEditBuilder}; @@ -129,7 +128,7 @@ fn check_struct_shorthand_initialization( } fn check_module(acc: &mut Vec, db: &RootDatabase, module: hir::Module) { - let mut diagnostics = hir::diagnostics::DiagnosticSink::default(); + let mut diagnostics = DiagnosticSink::default(); module.diagnostics(db, &mut diagnostics); for decl in module.declarations(db) { match decl { From c7ffd939f670a1cba5bf415759b43e63700761a7 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 24 Mar 2019 10:21:36 +0300 Subject: [PATCH 08/45] more enterprisey diagnostics setup --- crates/ra_hir/src/code_model_api.rs | 16 ++++++ crates/ra_hir/src/diagnostics.rs | 39 ++++++++++---- crates/ra_ide_api/src/diagnostics.rs | 79 +++++++++++----------------- 3 files changed, 75 insertions(+), 59 deletions(-) diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index bc0f74c896..5437133b87 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -168,6 +168,22 @@ impl Module { pub fn diagnostics(&self, db: &impl HirDatabase, sink: &mut DiagnosticSink) { db.crate_def_map(self.krate).add_diagnostics(db, self.module_id, sink); + for decl in self.declarations(db) { + match decl { + crate::ModuleDef::Function(f) => f.diagnostics(db, sink), + crate::ModuleDef::Module(f) => f.diagnostics(db, sink), + _ => (), + } + } + + for impl_block in self.impl_blocks(db) { + for item in impl_block.items(db) { + match item { + crate::ImplItem::Method(f) => f.diagnostics(db, sink), + _ => (), + } + } + } } pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs index d6b28159e9..a6ca68d863 100644 --- a/crates/ra_hir/src/diagnostics.rs +++ b/crates/ra_hir/src/diagnostics.rs @@ -1,9 +1,9 @@ use std::{fmt, any::Any}; use ra_syntax::{SyntaxNodePtr, AstPtr, ast}; +use relative_path::RelativePathBuf; use crate::HirFileId; -use relative_path::RelativePathBuf; /// Diagnostic defines hir API for errors and warnings. /// @@ -21,7 +21,7 @@ pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { fn message(&self) -> String; fn file(&self) -> HirFileId; fn syntax_node(&self) -> SyntaxNodePtr; - fn as_any(&self) -> &(Any + Send + 'static); + fn as_any(&self) -> &(dyn Any + Send + 'static); } impl dyn Diagnostic { @@ -30,18 +30,37 @@ impl dyn Diagnostic { } } -#[derive(Debug, Default)] -pub struct DiagnosticSink { - data: Vec>, +pub struct DiagnosticSink<'a> { + callbacks: Vec Result<(), ()> + 'a>>, + default_callback: Box, } -impl DiagnosticSink { - pub fn push(&mut self, d: impl Diagnostic) { - self.data.push(Box::new(d)) +impl<'a> DiagnosticSink<'a> { + pub fn new(cb: impl FnMut(&dyn Diagnostic) + 'a) -> DiagnosticSink<'a> { + DiagnosticSink { callbacks: Vec::new(), default_callback: Box::new(cb) } } - pub fn into_diagnostics(self) -> Vec> { - self.data + pub fn on(mut self, mut cb: F) -> DiagnosticSink<'a> { + let cb = move |diag: &dyn Diagnostic| match diag.downcast_ref::() { + Some(d) => { + cb(d); + Ok(()) + } + None => Err(()), + }; + self.callbacks.push(Box::new(cb)); + self + } + + pub(crate) fn push(&mut self, d: impl Diagnostic) { + let d: &dyn Diagnostic = &d; + for cb in self.callbacks.iter_mut() { + match cb(d) { + Ok(()) => return, + Err(()) => (), + } + } + (self.default_callback)(d) } } diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 5b1a90c82b..bf77c9ab12 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -1,3 +1,5 @@ +use std::cell::RefCell; + use itertools::Itertools; use hir::{source_binder, diagnostics::{Diagnostic as _, DiagnosticSink}}; use ra_db::SourceDatabase; @@ -25,11 +27,36 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec check_unnecessary_braces_in_use_statement(&mut res, file_id, node); check_struct_shorthand_initialization(&mut res, file_id, node); } - + let res = RefCell::new(res); + let mut sink = DiagnosticSink::new(|d| { + res.borrow_mut().push(Diagnostic { + message: d.message(), + range: d.syntax_node().range(), + severity: Severity::Error, + fix: None, + }) + }) + .on::(|d| { + let source_root = db.file_source_root(d.file().original_file(db)); + let create_file = FileSystemEdit::CreateFile { source_root, path: d.candidate.clone() }; + let fix = SourceChange { + label: "create module".to_string(), + source_file_edits: Vec::new(), + file_system_edits: vec![create_file], + cursor_position: None, + }; + res.borrow_mut().push(Diagnostic { + range: d.syntax_node().range(), + message: d.message(), + severity: Severity::Error, + fix: Some(fix), + }) + }); if let Some(m) = source_binder::module_from_file_id(db, file_id) { - check_module(&mut res, db, m); + m.diagnostics(db, &mut sink); }; - res + drop(sink); + res.into_inner() } fn syntax_errors(acc: &mut Vec, source_file: &SourceFile) { @@ -127,52 +154,6 @@ fn check_struct_shorthand_initialization( Some(()) } -fn check_module(acc: &mut Vec, db: &RootDatabase, module: hir::Module) { - let mut diagnostics = DiagnosticSink::default(); - module.diagnostics(db, &mut diagnostics); - for decl in module.declarations(db) { - match decl { - hir::ModuleDef::Function(f) => f.diagnostics(db, &mut diagnostics), - _ => (), - } - } - - for impl_block in module.impl_blocks(db) { - for item in impl_block.items(db) { - match item { - hir::ImplItem::Method(f) => f.diagnostics(db, &mut diagnostics), - _ => (), - } - } - } - - for d in diagnostics.into_diagnostics().iter() { - if let Some(d) = d.downcast_ref::() { - let source_root = db.file_source_root(d.file().original_file(db)); - let create_file = FileSystemEdit::CreateFile { source_root, path: d.candidate.clone() }; - let fix = SourceChange { - label: "create module".to_string(), - source_file_edits: Vec::new(), - file_system_edits: vec![create_file], - cursor_position: None, - }; - acc.push(Diagnostic { - range: d.syntax_node().range(), - message: d.message(), - severity: Severity::Error, - fix: Some(fix), - }) - } else { - acc.push(Diagnostic { - message: d.message(), - range: d.syntax_node().range(), - severity: Severity::Error, - fix: None, - }) - } - } -} - #[cfg(test)] mod tests { use test_utils::assert_eq_text; From 4c4a714328490d7f2626272663827fd51dfab0bd Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 24 Mar 2019 11:39:47 +0300 Subject: [PATCH 09/45] test diagnostics --- crates/ra_hir/src/mock.rs | 20 +++++++++++++++++++- crates/ra_hir/src/nameres/tests.rs | 18 ++++++++++++++++++ crates/ra_hir/src/ty/tests.rs | 24 ++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index 10d4c1b8cc..08f927bb46 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs @@ -9,7 +9,7 @@ use relative_path::RelativePathBuf; use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset}; use rustc_hash::FxHashMap; -use crate::{db, HirInterner}; +use crate::{db, HirInterner, diagnostics::DiagnosticSink, DefDatabase}; pub const WORKSPACE: SourceRootId = SourceRootId(0); @@ -70,6 +70,24 @@ impl MockDatabase { self.set_crate_graph(Arc::new(crate_graph)) } + pub fn diagnostics(&self) -> String { + let mut buf = String::from("\n"); + let mut files: Vec = self.files.values().map(|&it| it).collect(); + files.sort(); + for file in files { + let module = crate::source_binder::module_from_file_id(self, file).unwrap(); + module.diagnostics( + self, + &mut DiagnosticSink::new(|d| { + let source_file = self.hir_parse(d.file()); + let syntax_node = d.syntax_node().to_node(&source_file); + buf += &format!("{:?}: {}\n", syntax_node.text(), d.message()); + }), + ) + } + buf + } + fn from_fixture(fixture: &str) -> (MockDatabase, Option) { let mut db = MockDatabase::default(); diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index ac9b88520e..277b0757c4 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs @@ -552,3 +552,21 @@ foo: v "### ); } + +#[test] +fn unresolved_module_diagnostics() { + let diagnostics = MockDatabase::with_files( + r" + //- /lib.rs + mod foo; + mod bar; + //- /foo.rs + ", + ) + .diagnostics(); + + assert_snapshot_matches!(diagnostics, @r###" +"mod bar;": unresolved module +"### + ); +} diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 5d8ad4aa75..3aedba2431 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -2319,3 +2319,27 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() { assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events) } } + +#[test] +fn no_such_field_diagnostics() { + let diagnostics = MockDatabase::with_files( + r" + //- /lib.rs + struct S { foo: i32, bar: () } + impl S { + fn new() -> S { + S { + foo: 92, + baz: 62, + } + } + } + ", + ) + .diagnostics(); + + assert_snapshot_matches!(diagnostics, @r###" +"baz: 62": no such field +"### + ); +} From 5ce84f3cbc5d5a3106ea89460da4a3f473618f32 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 25 Mar 2019 09:40:49 +0300 Subject: [PATCH 10/45] tweak diagnostics API --- crates/ra_hir/src/diagnostics.rs | 17 ++++++++++++----- crates/ra_hir/src/mock.rs | 6 ++---- crates/ra_ide_api/src/diagnostics.rs | 4 ++-- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs index a6ca68d863..d6a51b8332 100644 --- a/crates/ra_hir/src/diagnostics.rs +++ b/crates/ra_hir/src/diagnostics.rs @@ -1,9 +1,9 @@ use std::{fmt, any::Any}; -use ra_syntax::{SyntaxNodePtr, AstPtr, ast}; +use ra_syntax::{SyntaxNodePtr, TreeArc, AstPtr, TextRange, ast, SyntaxNode}; use relative_path::RelativePathBuf; -use crate::HirFileId; +use crate::{HirFileId, HirDatabase}; /// Diagnostic defines hir API for errors and warnings. /// @@ -20,11 +20,18 @@ use crate::HirFileId; pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { fn message(&self) -> String; fn file(&self) -> HirFileId; - fn syntax_node(&self) -> SyntaxNodePtr; + fn syntax_node_ptr(&self) -> SyntaxNodePtr; + fn highlight_range(&self) -> TextRange { + self.syntax_node_ptr().range() + } fn as_any(&self) -> &(dyn Any + Send + 'static); } impl dyn Diagnostic { + pub fn syntax_node(&self, db: &impl HirDatabase) -> TreeArc { + let source_file = db.hir_parse(self.file()); + self.syntax_node_ptr().to_node(&source_file).to_owned() + } pub fn downcast_ref(&self) -> Option<&D> { self.as_any().downcast_ref() } @@ -77,7 +84,7 @@ impl Diagnostic for NoSuchField { fn file(&self) -> HirFileId { self.file } - fn syntax_node(&self) -> SyntaxNodePtr { + fn syntax_node_ptr(&self) -> SyntaxNodePtr { self.field.into() } fn as_any(&self) -> &(Any + Send + 'static) { @@ -99,7 +106,7 @@ impl Diagnostic for UnresolvedModule { fn file(&self) -> HirFileId { self.file } - fn syntax_node(&self) -> SyntaxNodePtr { + fn syntax_node_ptr(&self) -> SyntaxNodePtr { self.decl.into() } fn as_any(&self) -> &(Any + Send + 'static) { diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index 08f927bb46..aeab6b1800 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs @@ -9,7 +9,7 @@ use relative_path::RelativePathBuf; use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset}; use rustc_hash::FxHashMap; -use crate::{db, HirInterner, diagnostics::DiagnosticSink, DefDatabase}; +use crate::{db, HirInterner, diagnostics::DiagnosticSink}; pub const WORKSPACE: SourceRootId = SourceRootId(0); @@ -79,9 +79,7 @@ impl MockDatabase { module.diagnostics( self, &mut DiagnosticSink::new(|d| { - let source_file = self.hir_parse(d.file()); - let syntax_node = d.syntax_node().to_node(&source_file); - buf += &format!("{:?}: {}\n", syntax_node.text(), d.message()); + buf += &format!("{:?}: {}\n", d.syntax_node(self).text(), d.message()); }), ) } diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index bf77c9ab12..11391ef02e 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -31,7 +31,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec let mut sink = DiagnosticSink::new(|d| { res.borrow_mut().push(Diagnostic { message: d.message(), - range: d.syntax_node().range(), + range: d.highlight_range(), severity: Severity::Error, fix: None, }) @@ -46,7 +46,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec cursor_position: None, }; res.borrow_mut().push(Diagnostic { - range: d.syntax_node().range(), + range: d.highlight_range(), message: d.message(), severity: Severity::Error, fix: Some(fix), From e9af69d9db8856d79f0da7ae7da66169cc225aac Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 25 Mar 2019 10:56:55 +0300 Subject: [PATCH 11/45] simplify --- crates/ra_ide_api/src/diagnostics.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 11391ef02e..8411033229 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -39,12 +39,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec .on::(|d| { let source_root = db.file_source_root(d.file().original_file(db)); let create_file = FileSystemEdit::CreateFile { source_root, path: d.candidate.clone() }; - let fix = SourceChange { - label: "create module".to_string(), - source_file_edits: Vec::new(), - file_system_edits: vec![create_file], - cursor_position: None, - }; + let fix = SourceChange::file_system_edit("create module", create_file); res.borrow_mut().push(Diagnostic { range: d.highlight_range(), message: d.message(), From 36cb58f76d702d87f445ee7eefe26a0bc1ee3811 Mon Sep 17 00:00:00 2001 From: Sergey Parilin Date: Fri, 22 Mar 2019 15:54:26 +0300 Subject: [PATCH 12/45] structure moved to ra_ide_api ra_ide_api_light removed completely --- Cargo.lock | 18 ------------- crates/ra_cli/Cargo.toml | 1 - crates/ra_cli/src/main.rs | 2 +- crates/ra_ide_api/Cargo.toml | 1 - crates/ra_ide_api/src/lib.rs | 11 ++++---- .../src/snapshots/tests__file_structure.snap | 2 +- .../src/structure.rs | 0 crates/ra_ide_api_light/Cargo.toml | 26 ------------------- crates/ra_ide_api_light/src/lib.rs | 12 --------- docs/dev/architecture.md | 13 ---------- 10 files changed, 7 insertions(+), 79 deletions(-) rename crates/{ra_ide_api_light => ra_ide_api}/src/snapshots/tests__file_structure.snap (98%) rename crates/{ra_ide_api_light => ra_ide_api}/src/structure.rs (100%) delete mode 100644 crates/ra_ide_api_light/Cargo.toml delete mode 100644 crates/ra_ide_api_light/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 1b5de271e1..653eaa6c28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -929,7 +929,6 @@ dependencies = [ "ra_db 0.1.0", "ra_hir 0.1.0", "ra_ide_api 0.1.0", - "ra_ide_api_light 0.1.0", "ra_syntax 0.1.0", "tools 0.1.0", ] @@ -993,7 +992,6 @@ dependencies = [ "ra_db 0.1.0", "ra_fmt 0.1.0", "ra_hir 0.1.0", - "ra_ide_api_light 0.1.0", "ra_syntax 0.1.0", "ra_text_edit 0.1.0", "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1004,22 +1002,6 @@ dependencies = [ "unicase 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "ra_ide_api_light" -version = "0.1.0" -dependencies = [ - "insta 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "join_to_string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "proptest 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ra_fmt 0.1.0", - "ra_syntax 0.1.0", - "ra_text_edit 0.1.0", - "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "superslice 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "test_utils 0.1.0", -] - [[package]] name = "ra_lsp_server" version = "0.1.0" diff --git a/crates/ra_cli/Cargo.toml b/crates/ra_cli/Cargo.toml index 4c666f5569..467628236a 100644 --- a/crates/ra_cli/Cargo.toml +++ b/crates/ra_cli/Cargo.toml @@ -14,7 +14,6 @@ indicatif = "0.11.0" ra_syntax = { path = "../ra_syntax" } ra_ide_api = { path = "../ra_ide_api" } -ra_ide_api_light = { path = "../ra_ide_api_light" } tools = { path = "../tools" } ra_batch = { path = "../ra_batch" } ra_hir = { path = "../ra_hir" } diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs index 5285f1f28e..11f5541ebf 100644 --- a/crates/ra_cli/src/main.rs +++ b/crates/ra_cli/src/main.rs @@ -5,7 +5,7 @@ use std::{fs, io::Read, path::Path, time::Instant}; use clap::{App, Arg, SubCommand}; use join_to_string::join; use ra_ide_api::{Analysis, FileRange}; -use ra_ide_api_light::file_structure; +use ra_ide_api::file_structure; use ra_syntax::{SourceFile, TextRange, TreeArc, AstNode}; use tools::collect_tests; use flexi_logger::Logger; diff --git a/crates/ra_ide_api/Cargo.toml b/crates/ra_ide_api/Cargo.toml index c64226801f..45bab4e28b 100644 --- a/crates/ra_ide_api/Cargo.toml +++ b/crates/ra_ide_api/Cargo.toml @@ -20,7 +20,6 @@ jemallocator = { version = "0.1.9", optional = true } jemalloc-ctl = { version = "0.2.0", optional = true } ra_syntax = { path = "../ra_syntax" } -ra_ide_api_light = { path = "../ra_ide_api_light" } ra_text_edit = { path = "../ra_text_edit" } ra_db = { path = "../ra_db" } ra_fmt = { path = "../ra_fmt" } diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 8aa3eb088b..9063f78a9f 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs @@ -6,9 +6,6 @@ //! database, and the `ra_hir` crate, where majority of the analysis happens. //! However, IDE specific bits of the analysis (most notably completion) happen //! in this crate. -//! -//! The sibling `ra_ide_api_light` handles those bits of IDE functionality -//! which are restricted to a single file and need only syntax. // For proving that RootDatabase is RefUnwindSafe. #![recursion_limit = "128"] @@ -33,10 +30,11 @@ mod impls; mod assists; mod diagnostics; mod syntax_tree; -mod line_index; mod folding_ranges; +mod line_index; mod line_index_utils; mod join_lines; +mod structure; mod typing; mod matching_brace; @@ -72,9 +70,10 @@ pub use crate::{ line_index_utils::translate_offset_with_edit, folding_ranges::{Fold, FoldKind}, syntax_highlighting::HighlightedRange, + structure::{StructureNode, file_structure}, diagnostics::Severity, }; -pub use ra_ide_api_light::StructureNode; + pub use ra_db::{ Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId, Edition @@ -388,7 +387,7 @@ impl Analysis { /// file outline. pub fn file_structure(&self, file_id: FileId) -> Vec { let file = self.db.parse(file_id); - ra_ide_api_light::file_structure(&file) + structure::file_structure(&file) } /// Returns the set of folding ranges. diff --git a/crates/ra_ide_api_light/src/snapshots/tests__file_structure.snap b/crates/ra_ide_api/src/snapshots/tests__file_structure.snap similarity index 98% rename from crates/ra_ide_api_light/src/snapshots/tests__file_structure.snap rename to crates/ra_ide_api/src/snapshots/tests__file_structure.snap index 8e4184b31c..2efa8e22cb 100644 --- a/crates/ra_ide_api_light/src/snapshots/tests__file_structure.snap +++ b/crates/ra_ide_api/src/snapshots/tests__file_structure.snap @@ -1,7 +1,7 @@ --- created: "2019-02-05T22:03:50.763530100Z" creator: insta@0.6.1 -source: crates/ra_ide_api_light/src/structure.rs +source: crates/ra_ide_api/src/structure.rs expression: structure --- [ diff --git a/crates/ra_ide_api_light/src/structure.rs b/crates/ra_ide_api/src/structure.rs similarity index 100% rename from crates/ra_ide_api_light/src/structure.rs rename to crates/ra_ide_api/src/structure.rs diff --git a/crates/ra_ide_api_light/Cargo.toml b/crates/ra_ide_api_light/Cargo.toml deleted file mode 100644 index 4e69f5325b..0000000000 --- a/crates/ra_ide_api_light/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -edition = "2018" -name = "ra_ide_api_light" -version = "0.1.0" -authors = ["rust-analyzer developers"] -publish = false - -[dependencies] -itertools = "0.8.0" -superslice = "1.0.0" -join_to_string = "0.1.1" -rustc-hash = "1.0" - -ra_syntax = { path = "../ra_syntax" } -ra_text_edit = { path = "../ra_text_edit" } -ra_fmt = { path = "../ra_fmt" } - -[dev-dependencies] -test_utils = { path = "../test_utils" } -insta = "0.7.0" - -[dev-dependencies.proptest] -version = "0.9.0" -# Disable `fork` feature to allow compiling on webassembly -default-features = false -features = ["std", "bit-set", "break-dead-code"] diff --git a/crates/ra_ide_api_light/src/lib.rs b/crates/ra_ide_api_light/src/lib.rs deleted file mode 100644 index df7f144b62..0000000000 --- a/crates/ra_ide_api_light/src/lib.rs +++ /dev/null @@ -1,12 +0,0 @@ -//! This crate provides those IDE features which use only a single file. -//! -//! This usually means functions which take syntax tree as an input and produce -//! an edit or some auxiliary info. - -mod structure; - -use ra_syntax::TextRange; - -pub use crate::{ - structure::{file_structure, StructureNode}, -}; diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md index f990d5bf0e..890b18fcd7 100644 --- a/docs/dev/architecture.md +++ b/docs/dev/architecture.md @@ -130,19 +130,6 @@ APIs in this crate are IDE centric: they take text offsets as input and produce offsets and strings as output. This works on top of rich code model powered by `hir`. -### `crates/ra_ide_api_light` - -All IDE features which can be implemented if you only have access to a single -file. `ra_ide_api_light` could be used to enhance editing of Rust code without -the need to fiddle with build-systems, file synchronization and such. - -In a sense, `ra_ide_api_light` is just a bunch of pure functions which take a -syntax tree as input. - -The tests for `ra_ide_api_light` are `#[cfg(test)] mod tests` unit-tests spread -throughout its modules. - - ### `crates/ra_lsp_server` An LSP implementation which wraps `ra_ide_api` into a langauge server protocol. From 65a5c6859f65af043e8b96ebf0d898c645f2880e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 25 Mar 2019 14:12:03 +0300 Subject: [PATCH 13/45] :arrow_up: deps --- Cargo.lock | 75 ++++++++++++++++++++++++++---------------------------- 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 653eaa6c28..3bc5f261cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -76,15 +76,15 @@ dependencies = [ [[package]] name = "bit-set" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bit-vec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bit-vec" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -118,7 +118,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cargo_metadata" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -359,11 +359,11 @@ dependencies = [ [[package]] name = "flexi_logger" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -443,6 +443,11 @@ name = "glob" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "heck" version = "0.3.1" @@ -503,7 +508,7 @@ dependencies = [ [[package]] name = "insta" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -518,7 +523,7 @@ dependencies = [ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)", - "uuid 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -589,9 +594,6 @@ dependencies = [ name = "lazy_static" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "lazycell" @@ -605,7 +607,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "linked-hash-map" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -855,10 +857,10 @@ dependencies = [ [[package]] name = "proptest" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bit-set 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -922,7 +924,7 @@ version = "0.1.0" dependencies = [ "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "flexi_logger 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "flexi_logger 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "indicatif 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "join_to_string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "ra_batch 0.1.0", @@ -960,8 +962,8 @@ version = "0.1.0" dependencies = [ "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "ena 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "flexi_logger 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", - "insta 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "flexi_logger 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "insta 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "join_to_string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -980,14 +982,14 @@ name = "ra_ide_api" version = "0.1.0" dependencies = [ "fst 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "insta 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "insta 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "jemalloc-ctl 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "jemallocator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "join_to_string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proptest 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proptest 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "ra_assists 0.1.0", "ra_db 0.1.0", "ra_fmt 0.1.0", @@ -1009,7 +1011,7 @@ dependencies = [ "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "flexi_logger 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "flexi_logger 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "gen_lsp_server 0.1.0", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types 0.56.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1052,7 +1054,7 @@ dependencies = [ name = "ra_project_model" version = "0.1.0" dependencies = [ - "cargo_metadata 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cargo_metadata 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "ra_arena 0.1.0", @@ -1087,7 +1089,7 @@ dependencies = [ name = "ra_text_edit" version = "0.1.0" dependencies = [ - "proptest 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proptest 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "test_utils 0.1.0", "text_unit 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1418,7 +1420,7 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1460,11 +1462,6 @@ dependencies = [ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "spin" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "stable_deref_trait" version = "1.1.1" @@ -1758,7 +1755,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "uuid" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1836,7 +1833,7 @@ name = "yaml-rust" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] @@ -1849,14 +1846,14 @@ dependencies = [ "checksum backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "cd5a90e2b463010cd0e0ce9a11d4a9d5d58d9f41d4a6ba3dcaf9e68b466e88b4" "checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -"checksum bit-set 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6f1efcc46c18245a69c38fcc5cc650f16d3a59d034f3106e9ed63748f695730a" -"checksum bit-vec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4440d5cb623bb7390ae27fec0bb6c61111969860f8e3ae198bfa0663645e67cf" +"checksum bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e84c238982c4b1e1ee668d136c510c67a13465279c0cb367ea6baf6310620a80" +"checksum bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" -"checksum cargo_metadata 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bc796c7161c220089dfc7159e13324979181532850a237576b8fb907dd087c0d" +"checksum cargo_metadata 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "178d62b240c34223f265a4c1e275e37d62da163d421fc8d7f7e3ee340f803c57" "checksum cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "c9ce8bb087aacff865633f0bd5aeaed910fe2fe55b55f4739527f2e023a2e53d" "checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" "checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" @@ -1885,7 +1882,7 @@ dependencies = [ "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a2df5c1a8c4be27e7707789dc42ae65976e60b394afd293d1419ab915833e646" -"checksum flexi_logger 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4a7878fc9e06c948c6f9cddf571758e0c44786a509e646a094ef13ade3b1aab7" +"checksum flexi_logger 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "005c01dd6942ca46283b7304d14c6d04ec2c87a62f6e62e17c06fb812a574f4a" "checksum fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674" "checksum fsevent 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "c4bbbf71584aeed076100b5665ac14e3d85eeb31fdbb45fbd41ef9a682b5ec05" "checksum fsevent-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1a772d36c338d07a032d5375a36f15f9a7043bf0cb8ce7cee658e037c6032874" @@ -1895,6 +1892,7 @@ dependencies = [ "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" +"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum humansize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e" "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" @@ -1902,7 +1900,7 @@ dependencies = [ "checksum indicatif 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2c60da1c9abea75996b70a931bba6c750730399005b61ccd853cee50ef3d0d0c" "checksum inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40b54539f3910d6f84fbf9a643efd6e3aa6e4f001426c0329576128255994718" "checksum inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0" -"checksum insta 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be9f00370d23dc7bd32a4d4506b1a14fb922fa39c576c3300fd25ce5b5dab18f" +"checksum insta 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "03e7d88a87d342ce8bd698516151be43e6eb2e84b683db528696cb4a382f734a" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" @@ -1914,7 +1912,7 @@ dependencies = [ "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" "checksum libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "aab692d7759f5cd8c859e169db98ae5b52c924add2af5fbbca11d12fefb567c1" -"checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e" +"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" "checksum lsp-types 0.56.0 (registry+https://github.com/rust-lang/crates.io-index)" = "31954f2cf354421e6f99a48fdcfd5c3113c675a0db311960ffdac0b8d45cf09c" @@ -1942,7 +1940,7 @@ dependencies = [ "checksum pest_generator 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "63120576c4efd69615b5537d3d052257328a4ca82876771d6944424ccfd9f646" "checksum pest_meta 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5a3492a4ed208ffc247adcdcc7ba2a95be3104f58877d0d02f0df39bf3efb5e" "checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915" -"checksum proptest 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea66c78d75f2c6e9f304269eaef90899798daecc69f1a625d5a3dd793ff3522" +"checksum proptest 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24f5844db2f839e97e3021980975f6ebf8691d9b9b2ca67ed3feb38dc3edb52c" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1" "checksum ra_vfs 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1839e4e003d865b58b8b6c231aae6c463dfcd01bfbbddffbdb7662a7b5a627" @@ -1986,7 +1984,6 @@ dependencies = [ "checksum slug 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b3bc762e6a4b6c6fcaade73e77f9ebc6991b676f88bb2358bddb56560f073373" "checksum smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c4488ae950c49d403731982257768f48fada354a5203fe81f9bb6f43ca9002be" "checksum smol_str 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9af1035bc5d742ab6b7ab16713e41cc2ffe78cb474f6f43cd696b2d16052007e" -"checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum superslice 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f" @@ -2020,7 +2017,7 @@ dependencies = [ "checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" "checksum url_serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "74e7d099f1ee52f823d4bdd60c93c3602043c728f5db3b97bdb548467f7bddea" "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" -"checksum uuid 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0238db0c5b605dd1cf51de0f21766f97fba2645897024461d6a00c036819a768" +"checksum uuid 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "600ef8213e9f8a0ac1f876e470e90780ae0478eabce7f76aff41b0f4ef0fd5c0" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1" From 309716cffe93d065bcad0344b0f332425576c1e5 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 25 Mar 2019 14:28:04 +0300 Subject: [PATCH 14/45] move tests to where they belong --- crates/ra_hir/src/nameres/tests.rs | 1 + crates/ra_ide_api/src/diagnostics.rs | 31 +++++++++++++++++++ crates/ra_ide_api/tests/test/main.rs | 16 ---------- .../test__unresolved_module_diagnostic.snap | 28 ----------------- 4 files changed, 32 insertions(+), 44 deletions(-) delete mode 100644 crates/ra_ide_api/tests/test/snapshots/test__unresolved_module_diagnostic.snap diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index 277b0757c4..572bd1bf74 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs @@ -560,6 +560,7 @@ fn unresolved_module_diagnostics() { //- /lib.rs mod foo; mod bar; + mod baz {} //- /foo.rs ", ) diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 8411033229..5a78e94d82 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -152,6 +152,9 @@ fn check_struct_shorthand_initialization( #[cfg(test)] mod tests { use test_utils::assert_eq_text; + use insta::assert_debug_snapshot_matches; + + use crate::mock_analysis::single_file; use super::*; @@ -180,6 +183,34 @@ mod tests { assert_eq_text!(after, &actual); } + #[test] + fn test_unresolved_module_diagnostic() { + let (analysis, file_id) = single_file("mod foo;"); + let diagnostics = analysis.diagnostics(file_id).unwrap(); + assert_debug_snapshot_matches!(diagnostics, @r####"[ + Diagnostic { + message: "unresolved module", + range: [0; 8), + fix: Some( + SourceChange { + label: "create module", + source_file_edits: [], + file_system_edits: [ + CreateFile { + source_root: SourceRootId( + 0 + ), + path: "foo.rs" + } + ], + cursor_position: None + } + ), + severity: Error + } +]"####); + } + #[test] fn test_check_unnecessary_braces_in_use_statement() { check_not_applicable( diff --git a/crates/ra_ide_api/tests/test/main.rs b/crates/ra_ide_api/tests/test/main.rs index 0f0766f621..d4ff21c09c 100644 --- a/crates/ra_ide_api/tests/test/main.rs +++ b/crates/ra_ide_api/tests/test/main.rs @@ -1,4 +1,3 @@ -use insta::assert_debug_snapshot_matches; use ra_ide_api::{ mock_analysis::{single_file, single_file_with_position, single_file_with_range, MockAnalysis}, AnalysisChange, CrateGraph, Edition::Edition2018, Query, NavigationTarget, @@ -6,21 +5,6 @@ use ra_ide_api::{ }; use ra_syntax::SmolStr; -#[test] -fn test_unresolved_module_diagnostic() { - let (analysis, file_id) = single_file("mod foo;"); - let diagnostics = analysis.diagnostics(file_id).unwrap(); - assert_debug_snapshot_matches!("unresolved_module_diagnostic", &diagnostics); -} - -// FIXME: move this test to hir -#[test] -fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() { - let (analysis, file_id) = single_file("mod foo {}"); - let diagnostics = analysis.diagnostics(file_id).unwrap(); - assert!(diagnostics.is_empty()); -} - #[test] fn test_resolve_crate_root() { let mock = MockAnalysis::with_files( diff --git a/crates/ra_ide_api/tests/test/snapshots/test__unresolved_module_diagnostic.snap b/crates/ra_ide_api/tests/test/snapshots/test__unresolved_module_diagnostic.snap deleted file mode 100644 index 5bb9538922..0000000000 --- a/crates/ra_ide_api/tests/test/snapshots/test__unresolved_module_diagnostic.snap +++ /dev/null @@ -1,28 +0,0 @@ ---- -created: "2019-01-22T14:45:01.486985900+00:00" -creator: insta@0.4.0 -expression: "&diagnostics" -source: "crates\\ra_ide_api\\tests\\test\\main.rs" ---- -[ - Diagnostic { - message: "unresolved module", - range: [0; 8), - fix: Some( - SourceChange { - label: "create module", - source_file_edits: [], - file_system_edits: [ - CreateFile { - source_root: SourceRootId( - 0 - ), - path: "foo.rs" - } - ], - cursor_position: None - } - ), - severity: Error - } -] From 61315e4a891b29c76bdae58b7f1e74c88732c398 Mon Sep 17 00:00:00 2001 From: gfreezy Date: Mon, 25 Mar 2019 20:57:43 +0800 Subject: [PATCH 15/45] use | instead of multiple match arms --- .../ra_assists/src/inline_local_variable.rs | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/crates/ra_assists/src/inline_local_variable.rs b/crates/ra_assists/src/inline_local_variable.rs index ce0aafb27f..bd3cdb970c 100644 --- a/crates/ra_assists/src/inline_local_variable.rs +++ b/crates/ra_assists/src/inline_local_variable.rs @@ -17,33 +17,33 @@ pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx) -> Opt } let initializer = let_stmt.initializer()?; let wrap_in_parens = match initializer.kind() { - ExprKind::LambdaExpr(_) => true, - ExprKind::IfExpr(_) => true, - ExprKind::LoopExpr(_) => true, - ExprKind::ForExpr(_) => true, - ExprKind::WhileExpr(_) => true, - ExprKind::ContinueExpr(_) => true, - ExprKind::BreakExpr(_) => true, - ExprKind::Label(_) => true, - ExprKind::ReturnExpr(_) => true, - ExprKind::MatchExpr(_) => true, - ExprKind::StructLit(_) => true, - ExprKind::CastExpr(_) => true, - ExprKind::PrefixExpr(_) => true, - ExprKind::RangeExpr(_) => true, - ExprKind::BinExpr(_) => true, - ExprKind::CallExpr(_) => false, - ExprKind::IndexExpr(_) => false, - ExprKind::MethodCallExpr(_) => false, - ExprKind::FieldExpr(_) => false, - ExprKind::TryExpr(_) => false, - ExprKind::RefExpr(_) => false, - ExprKind::Literal(_) => false, - ExprKind::TupleExpr(_) => false, - ExprKind::ArrayExpr(_) => false, - ExprKind::ParenExpr(_) => false, - ExprKind::PathExpr(_) => false, - ExprKind::BlockExpr(_) => false, + ExprKind::LambdaExpr(_) + | ExprKind::IfExpr(_) + | ExprKind::LoopExpr(_) + | ExprKind::ForExpr(_) + | ExprKind::WhileExpr(_) + | ExprKind::ContinueExpr(_) + | ExprKind::BreakExpr(_) + | ExprKind::Label(_) + | ExprKind::ReturnExpr(_) + | ExprKind::MatchExpr(_) + | ExprKind::StructLit(_) + | ExprKind::CastExpr(_) + | ExprKind::PrefixExpr(_) + | ExprKind::RangeExpr(_) + | ExprKind::BinExpr(_) => true, + ExprKind::CallExpr(_) + | ExprKind::IndexExpr(_) + | ExprKind::MethodCallExpr(_) + | ExprKind::FieldExpr(_) + | ExprKind::TryExpr(_) + | ExprKind::RefExpr(_) + | ExprKind::Literal(_) + | ExprKind::TupleExpr(_) + | ExprKind::ArrayExpr(_) + | ExprKind::ParenExpr(_) + | ExprKind::PathExpr(_) + | ExprKind::BlockExpr(_) => false, }; let delete_range = if let Some(whitespace) = From 2ca1b2bc8fd14cb78c869551c35713603ca785f3 Mon Sep 17 00:00:00 2001 From: gfreezy Date: Mon, 25 Mar 2019 23:14:30 +0800 Subject: [PATCH 16/45] add desc and examples for inline variable action --- docs/user/features.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/user/features.md b/docs/user/features.md index b9d2aa84f8..59ee3dbd55 100644 --- a/docs/user/features.md +++ b/docs/user/features.md @@ -270,6 +270,21 @@ fn foo() { } ``` +- Inline local variable: + +```rust +// before: +fn foo() { + let a<|> = 1 + 1; + let b = a * 10; +} + +// after: +fn foo() { + let b = (1 + 1) * 10; +} +``` + -- Remove `dbg!` ```rust From 8cb3041a0cbd64b7ddc46fbf81a3c0088af7bcf2 Mon Sep 17 00:00:00 2001 From: gfreezy Date: Mon, 25 Mar 2019 23:15:52 +0800 Subject: [PATCH 17/45] markdown syntax --- docs/user/features.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/user/features.md b/docs/user/features.md index 59ee3dbd55..7173d88e92 100644 --- a/docs/user/features.md +++ b/docs/user/features.md @@ -210,7 +210,7 @@ fn main() { } ``` --- Fill struct fields +- Fill struct fields ```rust // before: @@ -285,7 +285,7 @@ fn foo() { } ``` --- Remove `dbg!` +- Remove `dbg!` ```rust // before: From e03189c1109573bec9600eb20efd9291d69f1d5c Mon Sep 17 00:00:00 2001 From: Wilco Kusee Date: Mon, 25 Mar 2019 21:03:32 +0100 Subject: [PATCH 18/45] Move ra_ide_api unit tests --- crates/ra_ide_api/src/parent_module.rs | 30 +- crates/ra_ide_api/src/references.rs | 48 +++- crates/ra_ide_api/src/symbol_index.rs | 58 ++++ crates/ra_ide_api/src/syntax_tree.rs | 257 +++++++++++++++++ crates/ra_ide_api/tests/test/main.rs | 376 ------------------------- 5 files changed, 391 insertions(+), 378 deletions(-) delete mode 100644 crates/ra_ide_api/tests/test/main.rs diff --git a/crates/ra_ide_api/src/parent_module.rs b/crates/ra_ide_api/src/parent_module.rs index 603c3db6ae..27788c9846 100644 --- a/crates/ra_ide_api/src/parent_module.rs +++ b/crates/ra_ide_api/src/parent_module.rs @@ -28,7 +28,11 @@ pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec { #[cfg(test)] mod tests { - use crate::mock_analysis::analysis_and_position; + use crate::{ + AnalysisChange, CrateGraph, + mock_analysis::{analysis_and_position, MockAnalysis}, + Edition::Edition2018, +}; #[test] fn test_resolve_parent_module() { @@ -59,4 +63,28 @@ mod tests { let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); nav.assert_match("baz MODULE FileId(1) [32; 44)"); } + + #[test] + fn test_resolve_crate_root() { + let mock = MockAnalysis::with_files( + " + //- /bar.rs + mod foo; + //- /foo.rs + // empty <|> + ", + ); + let root_file = mock.id_of("/bar.rs"); + let mod_file = mock.id_of("/foo.rs"); + let mut host = mock.analysis_host(); + assert!(host.analysis().crate_for(mod_file).unwrap().is_empty()); + + let mut crate_graph = CrateGraph::default(); + let crate_id = crate_graph.add_crate_root(root_file, Edition2018); + let mut change = AnalysisChange::new(); + change.set_crate_graph(crate_graph); + host.apply_change(change); + + assert_eq!(host.analysis().crate_for(mod_file).unwrap(), vec![crate_id]); + } } diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs index 22741445ad..20bbf11a38 100644 --- a/crates/ra_ide_api/src/references.rs +++ b/crates/ra_ide_api/src/references.rs @@ -216,9 +216,55 @@ mod tests { use crate::{ mock_analysis::single_file_with_position, mock_analysis::analysis_and_position, - FileId + FileId, ReferenceSearchResult }; + #[test] + fn test_find_all_refs_for_local() { + let code = r#" + fn main() { + let mut i = 1; + let j = 1; + i = i<|> + j; + + { + i = 0; + } + + i = 5; + }"#; + + let refs = get_all_refs(code); + assert_eq!(refs.len(), 5); + } + + #[test] + fn test_find_all_refs_for_param_inside() { + let code = r#" + fn foo(i : u32) -> u32 { + i<|> + }"#; + + let refs = get_all_refs(code); + assert_eq!(refs.len(), 2); + } + + #[test] + fn test_find_all_refs_for_fn_param() { + let code = r#" + fn foo(i<|> : u32) -> u32 { + i + }"#; + + let refs = get_all_refs(code); + assert_eq!(refs.len(), 2); + } + + fn get_all_refs(text: &str) -> ReferenceSearchResult { + let (analysis, position) = single_file_with_position(text); + analysis.find_all_refs(position).unwrap().unwrap() + } + #[test] fn test_rename_for_local() { test_rename( diff --git a/crates/ra_ide_api/src/symbol_index.rs b/crates/ra_ide_api/src/symbol_index.rs index 0978d164a4..0eadc4e719 100644 --- a/crates/ra_ide_api/src/symbol_index.rs +++ b/crates/ra_ide_api/src/symbol_index.rs @@ -270,3 +270,61 @@ fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option { container_name: None, }) } + +#[cfg(test)] +mod tests { + use ra_syntax::SmolStr; + use crate::{ + navigation_target::NavigationTarget, + mock_analysis::single_file, + Query, +}; + + #[test] + fn test_world_symbols_with_no_container() { + let code = r#" + enum FooInner { } + "#; + + let mut symbols = get_symbols_matching(code, "FooInner"); + + let s = symbols.pop().unwrap(); + + assert_eq!(s.name(), "FooInner"); + assert!(s.container_name().is_none()); + } + + #[test] + fn test_world_symbols_include_container_name() { + let code = r#" +fn foo() { + enum FooInner { } +} + "#; + + let mut symbols = get_symbols_matching(code, "FooInner"); + + let s = symbols.pop().unwrap(); + + assert_eq!(s.name(), "FooInner"); + assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); + + let code = r#" +mod foo { + struct FooInner; +} + "#; + + let mut symbols = get_symbols_matching(code, "FooInner"); + + let s = symbols.pop().unwrap(); + + assert_eq!(s.name(), "FooInner"); + assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); + } + + fn get_symbols_matching(text: &str, query: &str) -> Vec { + let (analysis, _) = single_file(text); + analysis.symbol_search(Query::new(query.into())).unwrap() + } +} diff --git a/crates/ra_ide_api/src/syntax_tree.rs b/crates/ra_ide_api/src/syntax_tree.rs index bbe9222b42..276f8a8c8a 100644 --- a/crates/ra_ide_api/src/syntax_tree.rs +++ b/crates/ra_ide_api/src/syntax_tree.rs @@ -85,3 +85,260 @@ fn syntax_tree_for_token(node: &T, text_range: TextRange) -> Option None } + +#[cfg(test)] +mod tests { + use crate::mock_analysis::{single_file, single_file_with_range}; + + #[test] + fn test_syntax_tree_without_range() { + // Basic syntax + let (analysis, file_id) = single_file(r#"fn foo() {}"#); + let syn = analysis.syntax_tree(file_id, None); + + assert_eq!( + syn.trim(), + r#" +SOURCE_FILE@[0; 11) + FN_DEF@[0; 11) + FN_KW@[0; 2) + WHITESPACE@[2; 3) + NAME@[3; 6) + IDENT@[3; 6) "foo" + PARAM_LIST@[6; 8) + L_PAREN@[6; 7) + R_PAREN@[7; 8) + WHITESPACE@[8; 9) + BLOCK@[9; 11) + L_CURLY@[9; 10) + R_CURLY@[10; 11) + "# + .trim() + ); + + let (analysis, file_id) = single_file( + r#" +fn test() { + assert!(" + fn foo() { + } + ", ""); +}"# + .trim(), + ); + let syn = analysis.syntax_tree(file_id, None); + + assert_eq!( + syn.trim(), + r#" +SOURCE_FILE@[0; 60) + FN_DEF@[0; 60) + FN_KW@[0; 2) + WHITESPACE@[2; 3) + NAME@[3; 7) + IDENT@[3; 7) "test" + PARAM_LIST@[7; 9) + L_PAREN@[7; 8) + R_PAREN@[8; 9) + WHITESPACE@[9; 10) + BLOCK@[10; 60) + L_CURLY@[10; 11) + WHITESPACE@[11; 16) + EXPR_STMT@[16; 58) + MACRO_CALL@[16; 57) + PATH@[16; 22) + PATH_SEGMENT@[16; 22) + NAME_REF@[16; 22) + IDENT@[16; 22) "assert" + EXCL@[22; 23) + TOKEN_TREE@[23; 57) + L_PAREN@[23; 24) + STRING@[24; 52) + COMMA@[52; 53) + WHITESPACE@[53; 54) + STRING@[54; 56) + R_PAREN@[56; 57) + SEMI@[57; 58) + WHITESPACE@[58; 59) + R_CURLY@[59; 60) + "# + .trim() + ); + } + + #[test] + fn test_syntax_tree_with_range() { + let (analysis, range) = single_file_with_range(r#"<|>fn foo() {}<|>"#.trim()); + let syn = analysis.syntax_tree(range.file_id, Some(range.range)); + + assert_eq!( + syn.trim(), + r#" +FN_DEF@[0; 11) + FN_KW@[0; 2) + WHITESPACE@[2; 3) + NAME@[3; 6) + IDENT@[3; 6) "foo" + PARAM_LIST@[6; 8) + L_PAREN@[6; 7) + R_PAREN@[7; 8) + WHITESPACE@[8; 9) + BLOCK@[9; 11) + L_CURLY@[9; 10) + R_CURLY@[10; 11) + "# + .trim() + ); + + let (analysis, range) = single_file_with_range( + r#"fn test() { + <|>assert!(" + fn foo() { + } + ", "");<|> +}"# + .trim(), + ); + let syn = analysis.syntax_tree(range.file_id, Some(range.range)); + + assert_eq!( + syn.trim(), + r#" +EXPR_STMT@[16; 58) + MACRO_CALL@[16; 57) + PATH@[16; 22) + PATH_SEGMENT@[16; 22) + NAME_REF@[16; 22) + IDENT@[16; 22) "assert" + EXCL@[22; 23) + TOKEN_TREE@[23; 57) + L_PAREN@[23; 24) + STRING@[24; 52) + COMMA@[52; 53) + WHITESPACE@[53; 54) + STRING@[54; 56) + R_PAREN@[56; 57) + SEMI@[57; 58) + "# + .trim() + ); + } + + #[test] + fn test_syntax_tree_inside_string() { + let (analysis, range) = single_file_with_range( + r#"fn test() { + assert!(" +<|>fn foo() { +}<|> +fn bar() { +} + ", ""); +}"# + .trim(), + ); + let syn = analysis.syntax_tree(range.file_id, Some(range.range)); + assert_eq!( + syn.trim(), + r#" +SOURCE_FILE@[0; 12) + FN_DEF@[0; 12) + FN_KW@[0; 2) + WHITESPACE@[2; 3) + NAME@[3; 6) + IDENT@[3; 6) "foo" + PARAM_LIST@[6; 8) + L_PAREN@[6; 7) + R_PAREN@[7; 8) + WHITESPACE@[8; 9) + BLOCK@[9; 12) + L_CURLY@[9; 10) + WHITESPACE@[10; 11) + R_CURLY@[11; 12) +"# + .trim() + ); + + // With a raw string + let (analysis, range) = single_file_with_range( + r###"fn test() { + assert!(r#" +<|>fn foo() { +}<|> +fn bar() { +} + "#, ""); +}"### + .trim(), + ); + let syn = analysis.syntax_tree(range.file_id, Some(range.range)); + assert_eq!( + syn.trim(), + r#" +SOURCE_FILE@[0; 12) + FN_DEF@[0; 12) + FN_KW@[0; 2) + WHITESPACE@[2; 3) + NAME@[3; 6) + IDENT@[3; 6) "foo" + PARAM_LIST@[6; 8) + L_PAREN@[6; 7) + R_PAREN@[7; 8) + WHITESPACE@[8; 9) + BLOCK@[9; 12) + L_CURLY@[9; 10) + WHITESPACE@[10; 11) + R_CURLY@[11; 12) +"# + .trim() + ); + + // With a raw string + let (analysis, range) = single_file_with_range( + r###"fn test() { + assert!(r<|>#" +fn foo() { +} +fn bar() { +}"<|>#, ""); +}"### + .trim(), + ); + let syn = analysis.syntax_tree(range.file_id, Some(range.range)); + assert_eq!( + syn.trim(), + r#" +SOURCE_FILE@[0; 25) + FN_DEF@[0; 12) + FN_KW@[0; 2) + WHITESPACE@[2; 3) + NAME@[3; 6) + IDENT@[3; 6) "foo" + PARAM_LIST@[6; 8) + L_PAREN@[6; 7) + R_PAREN@[7; 8) + WHITESPACE@[8; 9) + BLOCK@[9; 12) + L_CURLY@[9; 10) + WHITESPACE@[10; 11) + R_CURLY@[11; 12) + WHITESPACE@[12; 13) + FN_DEF@[13; 25) + FN_KW@[13; 15) + WHITESPACE@[15; 16) + NAME@[16; 19) + IDENT@[16; 19) "bar" + PARAM_LIST@[19; 21) + L_PAREN@[19; 20) + R_PAREN@[20; 21) + WHITESPACE@[21; 22) + BLOCK@[22; 25) + L_CURLY@[22; 23) + WHITESPACE@[23; 24) + R_CURLY@[24; 25) + +"# + .trim() + ); + } +} diff --git a/crates/ra_ide_api/tests/test/main.rs b/crates/ra_ide_api/tests/test/main.rs deleted file mode 100644 index d4ff21c09c..0000000000 --- a/crates/ra_ide_api/tests/test/main.rs +++ /dev/null @@ -1,376 +0,0 @@ -use ra_ide_api::{ - mock_analysis::{single_file, single_file_with_position, single_file_with_range, MockAnalysis}, - AnalysisChange, CrateGraph, Edition::Edition2018, Query, NavigationTarget, - ReferenceSearchResult, -}; -use ra_syntax::SmolStr; - -#[test] -fn test_resolve_crate_root() { - let mock = MockAnalysis::with_files( - " - //- /bar.rs - mod foo; - //- /foo.rs - // empty <|> - ", - ); - let root_file = mock.id_of("/bar.rs"); - let mod_file = mock.id_of("/foo.rs"); - let mut host = mock.analysis_host(); - assert!(host.analysis().crate_for(mod_file).unwrap().is_empty()); - - let mut crate_graph = CrateGraph::default(); - let crate_id = crate_graph.add_crate_root(root_file, Edition2018); - let mut change = AnalysisChange::new(); - change.set_crate_graph(crate_graph); - host.apply_change(change); - - assert_eq!(host.analysis().crate_for(mod_file).unwrap(), vec![crate_id]); -} - -fn get_all_refs(text: &str) -> ReferenceSearchResult { - let (analysis, position) = single_file_with_position(text); - analysis.find_all_refs(position).unwrap().unwrap() -} - -fn get_symbols_matching(text: &str, query: &str) -> Vec { - let (analysis, _) = single_file(text); - analysis.symbol_search(Query::new(query.into())).unwrap() -} - -#[test] -fn test_find_all_refs_for_local() { - let code = r#" - fn main() { - let mut i = 1; - let j = 1; - i = i<|> + j; - - { - i = 0; - } - - i = 5; - }"#; - - let refs = get_all_refs(code); - assert_eq!(refs.len(), 5); -} - -#[test] -fn test_find_all_refs_for_param_inside() { - let code = r#" - fn foo(i : u32) -> u32 { - i<|> - }"#; - - let refs = get_all_refs(code); - assert_eq!(refs.len(), 2); -} - -#[test] -fn test_find_all_refs_for_fn_param() { - let code = r#" - fn foo(i<|> : u32) -> u32 { - i - }"#; - - let refs = get_all_refs(code); - assert_eq!(refs.len(), 2); -} - -#[test] -fn test_world_symbols_with_no_container() { - let code = r#" - enum FooInner { } - "#; - - let mut symbols = get_symbols_matching(code, "FooInner"); - - let s = symbols.pop().unwrap(); - - assert_eq!(s.name(), "FooInner"); - assert!(s.container_name().is_none()); -} - -#[test] -fn test_world_symbols_include_container_name() { - let code = r#" -fn foo() { - enum FooInner { } -} - "#; - - let mut symbols = get_symbols_matching(code, "FooInner"); - - let s = symbols.pop().unwrap(); - - assert_eq!(s.name(), "FooInner"); - assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); - - let code = r#" -mod foo { - struct FooInner; -} - "#; - - let mut symbols = get_symbols_matching(code, "FooInner"); - - let s = symbols.pop().unwrap(); - - assert_eq!(s.name(), "FooInner"); - assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); -} - -#[test] -fn test_syntax_tree_without_range() { - // Basic syntax - let (analysis, file_id) = single_file(r#"fn foo() {}"#); - let syn = analysis.syntax_tree(file_id, None); - - assert_eq!( - syn.trim(), - r#" -SOURCE_FILE@[0; 11) - FN_DEF@[0; 11) - FN_KW@[0; 2) - WHITESPACE@[2; 3) - NAME@[3; 6) - IDENT@[3; 6) "foo" - PARAM_LIST@[6; 8) - L_PAREN@[6; 7) - R_PAREN@[7; 8) - WHITESPACE@[8; 9) - BLOCK@[9; 11) - L_CURLY@[9; 10) - R_CURLY@[10; 11) - "# - .trim() - ); - - let (analysis, file_id) = single_file( - r#" -fn test() { - assert!(" - fn foo() { - } - ", ""); -}"# - .trim(), - ); - let syn = analysis.syntax_tree(file_id, None); - - assert_eq!( - syn.trim(), - r#" -SOURCE_FILE@[0; 60) - FN_DEF@[0; 60) - FN_KW@[0; 2) - WHITESPACE@[2; 3) - NAME@[3; 7) - IDENT@[3; 7) "test" - PARAM_LIST@[7; 9) - L_PAREN@[7; 8) - R_PAREN@[8; 9) - WHITESPACE@[9; 10) - BLOCK@[10; 60) - L_CURLY@[10; 11) - WHITESPACE@[11; 16) - EXPR_STMT@[16; 58) - MACRO_CALL@[16; 57) - PATH@[16; 22) - PATH_SEGMENT@[16; 22) - NAME_REF@[16; 22) - IDENT@[16; 22) "assert" - EXCL@[22; 23) - TOKEN_TREE@[23; 57) - L_PAREN@[23; 24) - STRING@[24; 52) - COMMA@[52; 53) - WHITESPACE@[53; 54) - STRING@[54; 56) - R_PAREN@[56; 57) - SEMI@[57; 58) - WHITESPACE@[58; 59) - R_CURLY@[59; 60) - "# - .trim() - ); -} - -#[test] -fn test_syntax_tree_with_range() { - let (analysis, range) = single_file_with_range(r#"<|>fn foo() {}<|>"#.trim()); - let syn = analysis.syntax_tree(range.file_id, Some(range.range)); - - assert_eq!( - syn.trim(), - r#" -FN_DEF@[0; 11) - FN_KW@[0; 2) - WHITESPACE@[2; 3) - NAME@[3; 6) - IDENT@[3; 6) "foo" - PARAM_LIST@[6; 8) - L_PAREN@[6; 7) - R_PAREN@[7; 8) - WHITESPACE@[8; 9) - BLOCK@[9; 11) - L_CURLY@[9; 10) - R_CURLY@[10; 11) - "# - .trim() - ); - - let (analysis, range) = single_file_with_range( - r#"fn test() { - <|>assert!(" - fn foo() { - } - ", "");<|> -}"# - .trim(), - ); - let syn = analysis.syntax_tree(range.file_id, Some(range.range)); - - assert_eq!( - syn.trim(), - r#" -EXPR_STMT@[16; 58) - MACRO_CALL@[16; 57) - PATH@[16; 22) - PATH_SEGMENT@[16; 22) - NAME_REF@[16; 22) - IDENT@[16; 22) "assert" - EXCL@[22; 23) - TOKEN_TREE@[23; 57) - L_PAREN@[23; 24) - STRING@[24; 52) - COMMA@[52; 53) - WHITESPACE@[53; 54) - STRING@[54; 56) - R_PAREN@[56; 57) - SEMI@[57; 58) - "# - .trim() - ); -} - -#[test] -fn test_syntax_tree_inside_string() { - let (analysis, range) = single_file_with_range( - r#"fn test() { - assert!(" -<|>fn foo() { -}<|> -fn bar() { -} - ", ""); -}"# - .trim(), - ); - let syn = analysis.syntax_tree(range.file_id, Some(range.range)); - assert_eq!( - syn.trim(), - r#" -SOURCE_FILE@[0; 12) - FN_DEF@[0; 12) - FN_KW@[0; 2) - WHITESPACE@[2; 3) - NAME@[3; 6) - IDENT@[3; 6) "foo" - PARAM_LIST@[6; 8) - L_PAREN@[6; 7) - R_PAREN@[7; 8) - WHITESPACE@[8; 9) - BLOCK@[9; 12) - L_CURLY@[9; 10) - WHITESPACE@[10; 11) - R_CURLY@[11; 12) -"# - .trim() - ); - - // With a raw string - let (analysis, range) = single_file_with_range( - r###"fn test() { - assert!(r#" -<|>fn foo() { -}<|> -fn bar() { -} - "#, ""); -}"### - .trim(), - ); - let syn = analysis.syntax_tree(range.file_id, Some(range.range)); - assert_eq!( - syn.trim(), - r#" -SOURCE_FILE@[0; 12) - FN_DEF@[0; 12) - FN_KW@[0; 2) - WHITESPACE@[2; 3) - NAME@[3; 6) - IDENT@[3; 6) "foo" - PARAM_LIST@[6; 8) - L_PAREN@[6; 7) - R_PAREN@[7; 8) - WHITESPACE@[8; 9) - BLOCK@[9; 12) - L_CURLY@[9; 10) - WHITESPACE@[10; 11) - R_CURLY@[11; 12) -"# - .trim() - ); - - // With a raw string - let (analysis, range) = single_file_with_range( - r###"fn test() { - assert!(r<|>#" -fn foo() { -} -fn bar() { -}"<|>#, ""); -}"### - .trim(), - ); - let syn = analysis.syntax_tree(range.file_id, Some(range.range)); - assert_eq!( - syn.trim(), - r#" -SOURCE_FILE@[0; 25) - FN_DEF@[0; 12) - FN_KW@[0; 2) - WHITESPACE@[2; 3) - NAME@[3; 6) - IDENT@[3; 6) "foo" - PARAM_LIST@[6; 8) - L_PAREN@[6; 7) - R_PAREN@[7; 8) - WHITESPACE@[8; 9) - BLOCK@[9; 12) - L_CURLY@[9; 10) - WHITESPACE@[10; 11) - R_CURLY@[11; 12) - WHITESPACE@[12; 13) - FN_DEF@[13; 25) - FN_KW@[13; 15) - WHITESPACE@[15; 16) - NAME@[16; 19) - IDENT@[16; 19) "bar" - PARAM_LIST@[19; 21) - L_PAREN@[19; 20) - R_PAREN@[20; 21) - WHITESPACE@[21; 22) - BLOCK@[22; 25) - L_CURLY@[22; 23) - WHITESPACE@[23; 24) - R_CURLY@[24; 25) - -"# - .trim() - ); -} From c947c15ce1ec02261803f10568e4659e9396109e Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 24 Mar 2019 17:36:15 +0100 Subject: [PATCH 19/45] Basics for trait method resolution --- crates/ra_hir/src/code_model_api.rs | 13 +++++ crates/ra_hir/src/db.rs | 4 ++ crates/ra_hir/src/lib.rs | 1 + crates/ra_hir/src/nameres.rs | 8 ++- crates/ra_hir/src/resolve.rs | 17 ++++++- crates/ra_hir/src/traits.rs | 52 ++++++++++++++++++++ crates/ra_hir/src/ty/infer.rs | 3 +- crates/ra_hir/src/ty/method_resolution.rs | 60 +++++++++++++++++------ crates/ra_hir/src/ty/tests.rs | 12 ++--- crates/ra_syntax/src/ast/generated.rs | 6 ++- crates/ra_syntax/src/grammar.ron | 5 +- 11 files changed, 156 insertions(+), 25 deletions(-) create mode 100644 crates/ra_hir/src/traits.rs diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 5437133b87..88c13566c4 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -17,6 +17,7 @@ use crate::{ impl_block::ImplBlock, resolve::Resolver, diagnostics::DiagnosticSink, + traits::{TraitItem, TraitData}, }; /// hir::Crate describes a single crate. It's the main interface with which @@ -649,6 +650,18 @@ impl Trait { pub fn generic_params(&self, db: &impl DefDatabase) -> Arc { db.generic_params((*self).into()) } + + pub fn name(self, db: &impl DefDatabase) -> Option { + self.trait_data(db).name().clone() + } + + pub fn items(self, db: &impl DefDatabase) -> Vec { + self.trait_data(db).items().to_vec() + } + + pub(crate) fn trait_data(self, db: &impl DefDatabase) -> Arc { + db.trait_data(self) + } } impl Docs for Trait { diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index d3908f8acc..dd0bf6e343 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -14,6 +14,7 @@ use crate::{ impl_block::{ModuleImplBlocks, ImplSourceMap}, generics::{GenericParams, GenericDef}, type_ref::TypeRef, + traits::TraitData, Trait }; #[salsa::query_group(DefDatabaseStorage)] @@ -27,6 +28,9 @@ pub trait DefDatabase: SourceDatabase + AsRef { #[salsa::invoke(crate::adt::EnumData::enum_data_query)] fn enum_data(&self, e: Enum) -> Arc; + #[salsa::invoke(crate::traits::TraitData::trait_data_query)] + fn trait_data(&self, t: Trait) -> Arc; + #[salsa::invoke(crate::ids::SourceFileItems::file_items_query)] fn file_items(&self, file_id: HirFileId) -> Arc; diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index ce54d76084..974ebd8313 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -27,6 +27,7 @@ mod ids; mod name; mod nameres; mod adt; +mod traits; mod type_alias; mod type_ref; mod ty; diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 56ed872d59..c34aa4b50c 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -62,7 +62,7 @@ use test_utils::tested_by; use crate::{ ModuleDef, Name, Crate, Module, - DefDatabase, Path, PathKind, HirFileId, + DefDatabase, Path, PathKind, HirFileId, Trait, ids::{SourceItemId, SourceFileItemId, MacroCallId}, diagnostics::DiagnosticSink, nameres::diagnostics::DefDiagnostic, @@ -139,6 +139,12 @@ impl ModuleScope { pub fn get(&self, name: &Name) -> Option<&Resolution> { self.items.get(name) } + pub fn traits<'a>(&'a self) -> impl Iterator + 'a { + self.items.values().filter_map(|r| match r.def.take_types() { + Some(ModuleDef::Trait(t)) => Some(t), + _ => None, + }) + } } #[derive(Debug, Clone, PartialEq, Eq, Default)] diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index f28154517e..0f5031e762 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs @@ -11,7 +11,7 @@ use crate::{ generics::GenericParams, expr::{scope::{ExprScopes, ScopeId}, PatId, Body}, impl_block::ImplBlock, - path::Path, + path::Path, Trait }; #[derive(Debug, Clone, Default)] @@ -175,6 +175,21 @@ impl Resolver { names } + pub(crate) fn traits_in_scope<'a>(&'a self) -> impl Iterator + 'a { + // FIXME prelude + self.scopes + .iter() + .rev() + .flat_map(|scope| { + match scope { + Scope::ModuleScope(m) => Some(m.crate_def_map[m.module_id].scope.traits()), + _ => None, + } + .into_iter() + }) + .flat_map(|i| i) + } + fn module(&self) -> Option<(&CrateDefMap, CrateModuleId)> { self.scopes.iter().rev().find_map(|scope| match scope { Scope::ModuleScope(m) => Some((&*m.crate_def_map, m.module_id)), diff --git a/crates/ra_hir/src/traits.rs b/crates/ra_hir/src/traits.rs new file mode 100644 index 0000000000..725bdd5cb3 --- /dev/null +++ b/crates/ra_hir/src/traits.rs @@ -0,0 +1,52 @@ +//! HIR for trait definitions. + +use std::sync::Arc; + +use ra_syntax::ast::{self, NameOwner}; + +use crate::{Function, Const, TypeAlias, Name, DefDatabase, Trait, ids::LocationCtx, name::AsName}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TraitData { + name: Option, + items: Vec, +} + +impl TraitData { + pub(crate) fn trait_data_query(db: &impl DefDatabase, tr: Trait) -> Arc { + let (file_id, node) = tr.source(db); + let name = node.name().map(|n| n.as_name()); + let module = tr.module(db); + let ctx = LocationCtx::new(db, module, file_id); + let items = if let Some(item_list) = node.item_list() { + item_list + .impl_items() + .map(|item_node| match item_node.kind() { + ast::ImplItemKind::FnDef(it) => Function { id: ctx.to_def(it) }.into(), + ast::ImplItemKind::ConstDef(it) => Const { id: ctx.to_def(it) }.into(), + ast::ImplItemKind::TypeAliasDef(it) => TypeAlias { id: ctx.to_def(it) }.into(), + }) + .collect() + } else { + Vec::new() + }; + Arc::new(TraitData { name, items }) + } + + pub(crate) fn name(&self) -> &Option { + &self.name + } + + pub(crate) fn items(&self) -> &[TraitItem] { + &self.items + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum TraitItem { + Function(Function), + Const(Const), + TypeAlias(TypeAlias), + // Existential +} +impl_froms!(TraitItem: Function, Const, TypeAlias); diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 5fd602a9e6..5731153216 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -821,7 +821,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } Expr::MethodCall { receiver, args, method_name, generic_args } => { let receiver_ty = self.infer_expr(*receiver, &Expectation::none()); - let resolved = receiver_ty.clone().lookup_method(self.db, method_name); + let resolved = + receiver_ty.clone().lookup_method(self.db, method_name, &self.resolver); let (derefed_receiver_ty, method_ty, def_generics) = match resolved { Some((ty, func)) => { self.write_method_resolution(tgt_expr, func); diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index b1684acf96..708a435b4c 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -11,7 +11,7 @@ use crate::{ ids::TraitId, impl_block::{ImplId, ImplBlock, ImplItem}, ty::{Ty, TypeCtor}, - nameres::CrateModuleId, + nameres::CrateModuleId, resolve::Resolver, traits::TraitItem }; @@ -73,18 +73,18 @@ impl CrateImplBlocks { let target_ty = impl_block.target_ty(db); - if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) { - self.impls - .entry(target_ty_fp) - .or_insert_with(Vec::new) - .push((module.module_id, impl_id)); - } - if let Some(tr) = impl_block.target_trait(db) { self.impls_by_trait .entry(tr.id) .or_insert_with(Vec::new) .push((module.module_id, impl_id)); + } else { + if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) { + self.impls + .entry(target_ty_fp) + .or_insert_with(Vec::new) + .push((module.module_id, impl_id)); + } } } @@ -120,20 +120,52 @@ fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option { } impl Ty { - // FIXME: cache this as a query? - // - if so, what signature? (TyFingerprint, Name)? - // - or maybe cache all names and def_ids of methods per fingerprint? /// Look up the method with the given name, returning the actual autoderefed /// receiver type (but without autoref applied yet). - pub fn lookup_method(self, db: &impl HirDatabase, name: &Name) -> Option<(Ty, Function)> { - self.iterate_methods(db, |ty, f| { + pub fn lookup_method( + self, + db: &impl HirDatabase, + name: &Name, + resolver: &Resolver, + ) -> Option<(Ty, Function)> { + // FIXME: what has priority, an inherent method that needs autoderefs or a trait method? + let inherent_method = self.clone().iterate_methods(db, |ty, f| { let sig = f.signature(db); if sig.name() == name && sig.has_self_param() { Some((ty.clone(), f)) } else { None } - }) + }); + inherent_method.or_else(|| self.lookup_trait_method(db, name, resolver)) + } + + fn lookup_trait_method( + self, + db: &impl HirDatabase, + name: &Name, + resolver: &Resolver, + ) -> Option<(Ty, Function)> { + let mut candidates = Vec::new(); + for t in resolver.traits_in_scope() { + let data = t.trait_data(db); + for item in data.items() { + match item { + &TraitItem::Function(m) => { + let sig = m.signature(db); + if sig.name() == name && sig.has_self_param() { + candidates.push((t, m)); + } + } + _ => {} + } + } + } + // FIXME the implements check may result in other obligations or unifying variables? + candidates.retain(|(_t, _m)| /* self implements t */ true); + // FIXME what happens if there are still multiple potential candidates? + let (_chosen_trait, chosen_method) = candidates.first()?; + Some((self.clone(), *chosen_method)) } // This would be nicer if it just returned an iterator, but that runs into diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 3aedba2431..9710432661 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -1272,8 +1272,8 @@ fn test() { [241; 252) 'Struct::FOO': u32 [262; 263) 'y': u32 [266; 275) 'Enum::BAR': u32 -[285; 286) 'z': u32 -[289; 302) 'TraitTest::ID': u32"### +[285; 286) 'z': {unknown} +[289; 302) 'TraitTest::ID': {unknown}"### ); } @@ -1918,9 +1918,9 @@ fn test() { [110; 114) 'self': &{unknown} [170; 228) '{ ...i128 }': () [176; 178) 'S1': S1 -[176; 187) 'S1.method()': {unknown} +[176; 187) 'S1.method()': u32 [203; 205) 'S2': S2 -[203; 214) 'S2.method()': {unknown}"### +[203; 214) 'S2.method()': u32"### ); } @@ -1964,10 +1964,10 @@ mod bar_test { [169; 173) 'self': &{unknown} [300; 337) '{ ... }': () [310; 311) 'S': S -[310; 320) 'S.method()': {unknown} +[310; 320) 'S.method()': u32 [416; 454) '{ ... }': () [426; 427) 'S': S -[426; 436) 'S.method()': {unknown}"### +[426; 436) 'S.method()': i128"### ); } diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 54b72f8c57..47a37e4d19 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -4061,7 +4061,11 @@ impl ast::NameOwner for TraitDef {} impl ast::AttrsOwner for TraitDef {} impl ast::DocCommentsOwner for TraitDef {} impl ast::TypeParamsOwner for TraitDef {} -impl TraitDef {} +impl TraitDef { + pub fn item_list(&self) -> Option<&ItemList> { + super::child_opt(self) + } +} // TrueKw #[derive(Debug, PartialEq, Eq, Hash)] diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 4f8e19bd00..ad6d74162b 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -292,7 +292,10 @@ Grammar( ], options: [["variant_list", "EnumVariantList"]] ), "EnumVariantList": ( collections: [["variants", "EnumVariant"]] ), "EnumVariant": ( traits: ["NameOwner", "DocCommentsOwner", "AttrsOwner"], options: ["Expr"] ), - "TraitDef": ( traits: ["VisibilityOwner", "NameOwner", "AttrsOwner", "DocCommentsOwner", "TypeParamsOwner"] ), + "TraitDef": ( + traits: ["VisibilityOwner", "NameOwner", "AttrsOwner", "DocCommentsOwner", "TypeParamsOwner"], + options: ["ItemList"] + ), "Module": ( traits: ["VisibilityOwner", "NameOwner", "AttrsOwner", "DocCommentsOwner" ], options: [ "ItemList" ] From 0f7e4a7d2440e7e13a5cdf7e91f262426f0d0d18 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 24 Mar 2019 17:37:27 +0100 Subject: [PATCH 20/45] Implement a very naive implements check ... to make the infer_trait_method_simple test have the correct result. --- crates/ra_hir/src/db.rs | 5 +++- crates/ra_hir/src/ty.rs | 23 +++++++++++++--- crates/ra_hir/src/ty/method_resolution.rs | 32 ++++++++++++++++++----- crates/ra_hir/src/ty/tests.rs | 2 +- 4 files changed, 50 insertions(+), 12 deletions(-) diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index dd0bf6e343..143919cdcf 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -14,7 +14,7 @@ use crate::{ impl_block::{ModuleImplBlocks, ImplSourceMap}, generics::{GenericParams, GenericDef}, type_ref::TypeRef, - traits::TraitData, Trait + traits::TraitData, Trait, ty::TraitRef }; #[salsa::query_group(DefDatabaseStorage)] @@ -102,6 +102,9 @@ pub trait HirDatabase: DefDatabase { #[salsa::invoke(crate::ty::method_resolution::CrateImplBlocks::impls_in_crate_query)] fn impls_in_crate(&self, krate: Crate) -> Arc; + + #[salsa::invoke(crate::ty::method_resolution::implements)] + fn implements(&self, trait_ref: TraitRef) -> bool; } #[test] diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 7d25ade47b..d42c61e9d3 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -14,7 +14,7 @@ pub(crate) mod display; use std::sync::Arc; use std::{fmt, mem}; -use crate::{Name, AdtDef, type_ref::Mutability, db::HirDatabase}; +use crate::{Name, AdtDef, type_ref::Mutability, db::HirDatabase, Trait}; pub(crate) use lower::{TypableDef, CallableDef, type_for_def, type_for_field, callable_item_sig}; pub(crate) use infer::{infer, InferenceResult, InferTy}; @@ -91,7 +91,7 @@ pub enum TypeCtor { /// A nominal type with (maybe 0) type parameters. This might be a primitive /// type like `bool`, a struct, tuple, function pointer, reference or /// several other things. -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct ApplicationTy { pub ctor: TypeCtor, pub parameters: Substs, @@ -103,7 +103,7 @@ pub struct ApplicationTy { /// the same thing (but in a different way). /// /// This should be cheap to clone. -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug, Hash)] pub enum Ty { /// A nominal type with (maybe 0) type parameters. This might be a primitive /// type like `bool`, a struct, tuple, function pointer, reference or @@ -132,7 +132,7 @@ pub enum Ty { } /// A list of substitutions for generic parameters. -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct Substs(Arc<[Ty]>); impl Substs { @@ -169,6 +169,21 @@ impl Substs { } } +/// A trait with type parameters. This includes the `Self`, so this represents a concrete type implementing the trait. +/// Name to be bikeshedded: TraitBound? TraitImplements? +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub struct TraitRef { + /// FIXME name? + trait_: Trait, + substs: Substs, +} + +impl TraitRef { + pub fn self_ty(&self) -> &Ty { + &self.substs.0[0] + } +} + /// A function signature as seen by type inference: Several parameter types and /// one return type. #[derive(Clone, PartialEq, Eq, Debug)] diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 708a435b4c..146e8a02e7 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -8,12 +8,12 @@ use rustc_hash::FxHashMap; use crate::{ HirDatabase, Module, Crate, Name, Function, Trait, - ids::TraitId, impl_block::{ImplId, ImplBlock, ImplItem}, ty::{Ty, TypeCtor}, nameres::CrateModuleId, resolve::Resolver, traits::TraitItem }; +use super::{ TraitRef, Substs}; /// This is used as a key for indexing impls. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -38,7 +38,7 @@ pub struct CrateImplBlocks { /// To make sense of the CrateModuleIds, we need the source root. krate: Crate, impls: FxHashMap>, - impls_by_trait: FxHashMap>, + impls_by_trait: FxHashMap>, } impl CrateImplBlocks { @@ -56,8 +56,7 @@ impl CrateImplBlocks { &'a self, tr: &Trait, ) -> impl Iterator + 'a { - let id = tr.id; - self.impls_by_trait.get(&id).into_iter().flat_map(|i| i.iter()).map( + self.impls_by_trait.get(&tr).into_iter().flat_map(|i| i.iter()).map( move |(module_id, impl_id)| { let module = Module { krate: self.krate, module_id: *module_id }; ImplBlock::from_id(module, *impl_id) @@ -75,7 +74,7 @@ impl CrateImplBlocks { if let Some(tr) = impl_block.target_trait(db) { self.impls_by_trait - .entry(tr.id) + .entry(tr) .or_insert_with(Vec::new) .push((module.module_id, impl_id)); } else { @@ -109,6 +108,24 @@ impl CrateImplBlocks { } } +/// Rudimentary check whether an impl exists for a given type and trait; this +/// will actually be done by chalk. +pub(crate) fn implements(db: &impl HirDatabase, trait_ref: TraitRef) -> bool { + // FIXME use all trait impls in the whole crate graph + let krate = trait_ref.trait_.module(db).krate(db); + let krate = match krate { + Some(krate) => krate, + None => return false, + }; + let crate_impl_blocks = db.impls_in_crate(krate); + for impl_block in crate_impl_blocks.lookup_impl_blocks_for_trait(&trait_ref.trait_) { + if &impl_block.target_ty(db) == trait_ref.self_ty() { + return true; + } + } + false +} + fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option { match ty { Ty::Apply(a_ty) => match a_ty.ctor { @@ -162,7 +179,10 @@ impl Ty { } } // FIXME the implements check may result in other obligations or unifying variables? - candidates.retain(|(_t, _m)| /* self implements t */ true); + candidates.retain(|(t, _m)| { + let trait_ref = TraitRef { trait_: *t, substs: Substs::single(self.clone()) }; + db.implements(trait_ref) + }); // FIXME what happens if there are still multiple potential candidates? let (_chosen_trait, chosen_method) = candidates.first()?; Some((self.clone(), *chosen_method)) diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 9710432661..655f3c5223 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -1920,7 +1920,7 @@ fn test() { [176; 178) 'S1': S1 [176; 187) 'S1.method()': u32 [203; 205) 'S2': S2 -[203; 214) 'S2.method()': u32"### +[203; 214) 'S2.method()': i128"### ); } From 99711c1863fc712dc14ca61809055b283415acbe Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 25 Mar 2019 21:24:20 +0100 Subject: [PATCH 21/45] Clean up comments / use nicer Iterator methods --- crates/ra_hir/src/resolve.rs | 2 +- crates/ra_hir/src/ty/method_resolution.rs | 23 ++++++++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index 0f5031e762..2609585b1f 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs @@ -187,7 +187,7 @@ impl Resolver { } .into_iter() }) - .flat_map(|i| i) + .flatten() } fn module(&self) -> Option<(&CrateDefMap, CrateModuleId)> { diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 146e8a02e7..3ac8dc46b0 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -118,12 +118,8 @@ pub(crate) fn implements(db: &impl HirDatabase, trait_ref: TraitRef) -> bool { None => return false, }; let crate_impl_blocks = db.impls_in_crate(krate); - for impl_block in crate_impl_blocks.lookup_impl_blocks_for_trait(&trait_ref.trait_) { - if &impl_block.target_ty(db) == trait_ref.self_ty() { - return true; - } - } - false + let mut impl_blocks = crate_impl_blocks.lookup_impl_blocks_for_trait(&trait_ref.trait_); + impl_blocks.any(|impl_block| &impl_block.target_ty(db) == trait_ref.self_ty()) } fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option { @@ -145,7 +141,7 @@ impl Ty { name: &Name, resolver: &Resolver, ) -> Option<(Ty, Function)> { - // FIXME: what has priority, an inherent method that needs autoderefs or a trait method? + // FIXME: trait methods should be used before autoderefs let inherent_method = self.clone().iterate_methods(db, |ty, f| { let sig = f.signature(db); if sig.name() == name && sig.has_self_param() { @@ -178,12 +174,21 @@ impl Ty { } } } - // FIXME the implements check may result in other obligations or unifying variables? + // FIXME: + // - we might not actually be able to determine fully that the type + // implements the trait here; it's enough if we (well, Chalk) determine + // that it's possible. + // - when the trait method is picked, we need to register an + // 'obligation' somewhere so that we later check that it's really + // implemented + // - both points go for additional requirements from where clauses as + // well (in fact, the 'implements' condition could just be considered a + // 'where Self: Trait' clause) candidates.retain(|(t, _m)| { let trait_ref = TraitRef { trait_: *t, substs: Substs::single(self.clone()) }; db.implements(trait_ref) }); - // FIXME what happens if there are still multiple potential candidates? + // FIXME if there's multiple candidates here, that's an ambiguity error let (_chosen_trait, chosen_method) = candidates.first()?; Some((self.clone(), *chosen_method)) } From 12b5d4f795f69f7fa07051cdec7a1347d3aa7924 Mon Sep 17 00:00:00 2001 From: Marco Groppo Date: Mon, 25 Mar 2019 23:53:57 +0100 Subject: [PATCH 22/45] Assist to flip (some) binary expressions. This assist can flip the following operators: ==, !=, >, >=, <, <=. --- crates/ra_assists/src/flip_binexpr.rs | 149 ++++++++++++++++++++++ crates/ra_assists/src/flip_eq_operands.rs | 86 ------------- crates/ra_assists/src/lib.rs | 4 +- 3 files changed, 151 insertions(+), 88 deletions(-) create mode 100644 crates/ra_assists/src/flip_binexpr.rs delete mode 100644 crates/ra_assists/src/flip_eq_operands.rs diff --git a/crates/ra_assists/src/flip_binexpr.rs b/crates/ra_assists/src/flip_binexpr.rs new file mode 100644 index 0000000000..8a0737b556 --- /dev/null +++ b/crates/ra_assists/src/flip_binexpr.rs @@ -0,0 +1,149 @@ +use hir::db::HirDatabase; +use ra_syntax::ast::{AstNode, BinExpr, BinOp}; + +use crate::{AssistCtx, Assist, AssistId}; + +/// Flip binary comparison expressions (==, !=, >, >=, <, <=). +pub(crate) fn flip_binexpr(mut ctx: AssistCtx) -> Option { + let expr = ctx.node_at_offset::()?; + let lhs = expr.lhs()?.syntax(); + let rhs = expr.rhs()?.syntax(); + let op_range = expr.op()?.range(); + // The assist should be available only if the cursor is on the operator + let cursor_in_range = ctx.frange.range.is_subrange(&op_range); + // The assist should be available only for these binary operators + // (it should not change the meaning of the expression) + let allowed_ops = [ + BinOp::EqualityTest, + BinOp::NegatedEqualityTest, + BinOp::GreaterTest, + BinOp::GreaterEqualTest, + BinOp::LesserTest, + BinOp::LesserEqualTest, + ]; + let op_kind = expr.op_kind()?; + if !cursor_in_range || !allowed_ops.iter().any(|o| *o == op_kind) { + return None; + } + let new_op = match op_kind { + BinOp::GreaterTest => Some("<"), + BinOp::GreaterEqualTest => Some("<="), + BinOp::LesserTest => Some(">"), + BinOp::LesserEqualTest => Some(">="), + _ => None, + }; + ctx.add_action(AssistId("flip_binexpr"), "flip binary expression", |edit| { + edit.target(op_range); + if let Some(new_op) = new_op { + edit.replace(op_range, new_op); + } + edit.replace(lhs.range(), rhs.text()); + edit.replace(rhs.range(), lhs.text()); + }); + + ctx.build() +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::helpers::{check_assist, check_assist_target}; + + #[test] + fn flip_eq_operands_for_simple_stmt() { + check_assist( + flip_binexpr, + "fn f() { let res = 1 ==<|> 2; }", + "fn f() { let res = 2 ==<|> 1; }", + ) + } + + #[test] + fn flip_neq_operands_for_simple_stmt() { + check_assist( + flip_binexpr, + "fn f() { let res = 1 !=<|> 2; }", + "fn f() { let res = 2 !=<|> 1; }", + ) + } + + #[test] + fn flip_gt_operands_for_simple_stmt() { + check_assist( + flip_binexpr, + "fn f() { let res = 1 ><|> 2; }", + "fn f() { let res = 2 <<|> 1; }", + ) + } + + #[test] + fn flip_gteq_operands_for_simple_stmt() { + check_assist( + flip_binexpr, + "fn f() { let res = 1 >=<|> 2; }", + "fn f() { let res = 2 <=<|> 1; }", + ) + } + + #[test] + fn flip_lt_operands_for_simple_stmt() { + check_assist( + flip_binexpr, + "fn f() { let res = 1 <<|> 2; }", + "fn f() { let res = 2 ><|> 1; }", + ) + } + + #[test] + fn flip_lteq_operands_for_simple_stmt() { + check_assist( + flip_binexpr, + "fn f() { let res = 1 <=<|> 2; }", + "fn f() { let res = 2 >=<|> 1; }", + ) + } + + #[test] + fn flip_eq_operands_for_complex_stmt() { + check_assist( + flip_binexpr, + "fn f() { let res = (1 + 1) ==<|> (2 + 2); }", + "fn f() { let res = (2 + 2) ==<|> (1 + 1); }", + ) + } + + #[test] + fn flip_eq_operands_in_match_expr() { + check_assist( + flip_binexpr, + r#" + fn dyn_eq(&self, other: &dyn Diagnostic) -> bool { + match other.downcast_ref::() { + None => false, + Some(it) => it ==<|> self, + } + } + "#, + r#" + fn dyn_eq(&self, other: &dyn Diagnostic) -> bool { + match other.downcast_ref::() { + None => false, + Some(it) => self ==<|> it, + } + } + "#, + ) + } + + #[test] + fn flip_eq_operands_target() { + check_assist_target(flip_binexpr, "fn f() { let res = 1 ==<|> 2; }", "==") + } + + #[test] + fn flip_gt_operands_target() { + check_assist_target(flip_binexpr, "fn f() { let res = 1 ><|> 2; }", ">") + } + +} diff --git a/crates/ra_assists/src/flip_eq_operands.rs b/crates/ra_assists/src/flip_eq_operands.rs deleted file mode 100644 index df0bb689da..0000000000 --- a/crates/ra_assists/src/flip_eq_operands.rs +++ /dev/null @@ -1,86 +0,0 @@ -use hir::db::HirDatabase; -use ra_syntax::ast::{AstNode, BinExpr, BinOp}; - -use crate::{AssistCtx, Assist, AssistId}; - -pub(crate) fn flip_eq_operands(mut ctx: AssistCtx) -> Option { - let expr = ctx.node_at_offset::()?; - let lhs = expr.lhs()?.syntax(); - let rhs = expr.rhs()?.syntax(); - let op_range = expr.op()?.range(); - let cursor_in_range = ctx.frange.range.is_subrange(&op_range); - let allowed_ops = [BinOp::EqualityTest, BinOp::NegatedEqualityTest]; - let expr_op = expr.op_kind()?; - if !cursor_in_range || !allowed_ops.iter().any(|o| *o == expr_op) { - return None; - } - ctx.add_action(AssistId("flip_eq_operands"), "flip equality operands", |edit| { - edit.target(op_range); - edit.replace(lhs.range(), rhs.text()); - edit.replace(rhs.range(), lhs.text()); - }); - - ctx.build() -} - -#[cfg(test)] -mod tests { - use super::*; - - use crate::helpers::{check_assist, check_assist_target}; - - #[test] - fn flip_eq_operands_for_simple_stmt() { - check_assist( - flip_eq_operands, - "fn f() { let res = 1 ==<|> 2; }", - "fn f() { let res = 2 ==<|> 1; }", - ) - } - - #[test] - fn flip_neq_operands_for_simple_stmt() { - check_assist( - flip_eq_operands, - "fn f() { let res = 1 !=<|> 2; }", - "fn f() { let res = 2 !=<|> 1; }", - ) - } - - #[test] - fn flip_eq_operands_for_complex_stmt() { - check_assist( - flip_eq_operands, - "fn f() { let res = (1 + 1) ==<|> (2 + 2); }", - "fn f() { let res = (2 + 2) ==<|> (1 + 1); }", - ) - } - - #[test] - fn flip_eq_operands_in_match_expr() { - check_assist( - flip_eq_operands, - r#" - fn dyn_eq(&self, other: &dyn Diagnostic) -> bool { - match other.downcast_ref::() { - None => false, - Some(it) => it ==<|> self, - } - } - "#, - r#" - fn dyn_eq(&self, other: &dyn Diagnostic) -> bool { - match other.downcast_ref::() { - None => false, - Some(it) => self ==<|> it, - } - } - "#, - ) - } - - #[test] - fn flip_eq_operands_target() { - check_assist_target(flip_eq_operands, "fn f() { let res = 1 ==<|> 2; }", "==") - } -} diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 2e47b5215c..c1514f8e52 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -88,7 +88,7 @@ where mod add_derive; mod add_impl; mod flip_comma; -mod flip_eq_operands; +mod flip_binexpr; mod change_visibility; mod fill_match_arms; mod fill_struct_fields; @@ -108,7 +108,7 @@ fn all_assists() -> &'static [fn(AssistCtx) -> Option Date: Tue, 26 Mar 2019 13:09:39 +0300 Subject: [PATCH 23/45] store macro def inside macro id This solves the problem of "macro expansion can't call into name resolution, because name resolution calls back into macro expansion" Because we store macro def as a part of call id, macro expansion just knows the def! --- crates/ra_hir/src/code_model_impl/module.rs | 2 +- crates/ra_hir/src/db.rs | 9 +- crates/ra_hir/src/ids.rs | 24 ++++- crates/ra_hir/src/nameres.rs | 11 +-- crates/ra_hir/src/nameres/collector.rs | 93 +++++++------------ crates/ra_hir/src/nameres/raw.rs | 30 ++---- .../ra_hir/src/nameres/tests/incremental.rs | 23 ++--- 7 files changed, 73 insertions(+), 119 deletions(-) diff --git a/crates/ra_hir/src/code_model_impl/module.rs b/crates/ra_hir/src/code_model_impl/module.rs index 14237060cf..790e2b80f7 100644 --- a/crates/ra_hir/src/code_model_impl/module.rs +++ b/crates/ra_hir/src/code_model_impl/module.rs @@ -76,7 +76,7 @@ impl Module { import: ImportId, ) -> TreeArc { let (file_id, source) = self.definition_source(db); - let (_, source_map) = db.raw_items_with_source_map(file_id.original_file(db)); + let (_, source_map) = db.raw_items_with_source_map(file_id); source_map.get(&source, import) } diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 143919cdcf..6eb9161496 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use ra_syntax::{SyntaxNode, TreeArc, SourceFile}; -use ra_db::{SourceDatabase, salsa, FileId}; +use ra_db::{SourceDatabase, salsa}; use crate::{ HirFileId, SourceFileItems, SourceItemId, Crate, Module, HirInterner, @@ -38,10 +38,13 @@ pub trait DefDatabase: SourceDatabase + AsRef { fn file_item(&self, source_item_id: SourceItemId) -> TreeArc; #[salsa::invoke(RawItems::raw_items_query)] - fn raw_items(&self, file_id: FileId) -> Arc; + fn raw_items(&self, file_id: HirFileId) -> Arc; #[salsa::invoke(RawItems::raw_items_with_source_map_query)] - fn raw_items_with_source_map(&self, file_id: FileId) -> (Arc, Arc); + fn raw_items_with_source_map( + &self, + file_id: HirFileId, + ) -> (Arc, Arc); #[salsa::invoke(CrateDefMap::crate_def_map_query)] fn crate_def_map(&self, krate: Crate) -> Arc; diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 18401f8659..cf0566308f 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -7,6 +7,7 @@ use std::{ use ra_db::{LocationInterner, FileId}; use ra_syntax::{TreeArc, SyntaxNode, SourceFile, AstNode, SyntaxNodePtr, ast}; use ra_arena::{Arena, RawId, ArenaId, impl_arena_id}; +use mbe::MacroRules; use crate::{ Module, @@ -100,10 +101,7 @@ fn parse_macro(db: &impl DefDatabase, macro_call_id: MacroCallId) -> Option for HirFileId { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub(crate) enum MacroDefId { + MacroByExample { source_item_id: SourceItemId }, +} + +fn macro_def_query(db: &impl DefDatabase, id: MacroDefId) -> Option> { + let syntax_node = match id { + MacroDefId::MacroByExample { source_item_id } => db.file_item(source_item_id), + }; + let macro_call = ast::MacroCall::cast(&syntax_node).unwrap(); + let arg = macro_call.token_tree()?; + let (tt, _) = mbe::ast_to_token_tree(arg)?; + let rules = MacroRules::parse(&tt).ok()?; + Some(Arc::new(rules)) +} + /// `MacroCallId` identifies a particular macro invocation, like /// `println!("Hello, {}", world)`. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -134,7 +148,7 @@ impl_arena_id!(MacroCallId); #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct MacroCallLoc { - pub(crate) module: Module, + pub(crate) def: MacroDefId, pub(crate) source_item_id: SourceItemId, } diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index c34aa4b50c..93c11f271d 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -63,7 +63,7 @@ use test_utils::tested_by; use crate::{ ModuleDef, Name, Crate, Module, DefDatabase, Path, PathKind, HirFileId, Trait, - ids::{SourceItemId, SourceFileItemId, MacroCallId}, + ids::{SourceItemId, SourceFileItemId, MacroCallId, MacroDefId}, diagnostics::DiagnosticSink, nameres::diagnostics::DefDiagnostic, }; @@ -85,7 +85,7 @@ pub struct CrateDefMap { root: CrateModuleId, modules: Arena, macros: Arena, - public_macros: FxHashMap, + public_macros: FxHashMap, macro_resolutions: FxHashMap, diagnostics: Vec, } @@ -238,13 +238,6 @@ impl CrateDefMap { self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink)) } - pub(crate) fn resolve_macro( - &self, - macro_call_id: MacroCallId, - ) -> Option<(Crate, CrateMacroId)> { - self.macro_resolutions.get(¯o_call_id).map(|&it| it) - } - pub(crate) fn find_module_by_source( &self, file_id: HirFileId, diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index 8830b4624a..89300d2ecc 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -6,15 +6,15 @@ use ra_db::FileId; use crate::{ Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias, - DefDatabase, HirFileId, Name, Path, Crate, + DefDatabase, HirFileId, Name, Path, KnownName, nameres::{ Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode, - CrateDefMap, CrateModuleId, ModuleData, CrateMacroId, + CrateDefMap, CrateModuleId, ModuleData, diagnostics::DefDiagnostic, raw, }, - ids::{AstItemDef, LocationCtx, MacroCallLoc, SourceItemId, MacroCallId}, + ids::{AstItemDef, LocationCtx, MacroCallLoc, SourceItemId, MacroCallId, MacroDefId}, }; pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { @@ -51,8 +51,8 @@ struct DefCollector { def_map: CrateDefMap, glob_imports: FxHashMap>, unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>, - unexpanded_macros: Vec<(CrateModuleId, MacroCallId, Path, tt::Subtree)>, - global_macro_scope: FxHashMap, + unexpanded_macros: Vec<(CrateModuleId, SourceItemId, Path)>, + global_macro_scope: FxHashMap, } impl<'a, DB> DefCollector<&'a DB> @@ -62,7 +62,7 @@ where fn collect(&mut self) { let crate_graph = self.db.crate_graph(); let file_id = crate_graph.crate_root(self.def_map.krate.crate_id()); - let raw_items = self.db.raw_items(file_id); + let raw_items = self.db.raw_items(file_id.into()); let module_id = self.def_map.root; self.def_map.modules[module_id].definition = Some(file_id); ModCollector { @@ -93,14 +93,11 @@ where } } - fn define_macro(&mut self, name: Name, tt: &tt::Subtree, export: bool) { - if let Ok(rules) = mbe::MacroRules::parse(tt) { - let macro_id = self.def_map.macros.alloc(rules); - if export { - self.def_map.public_macros.insert(name.clone(), macro_id); - } - self.global_macro_scope.insert(name, macro_id); + fn define_macro(&mut self, name: Name, macro_id: MacroDefId, export: bool) { + if export { + self.def_map.public_macros.insert(name.clone(), macro_id); } + self.global_macro_scope.insert(name, macro_id); } fn resolve_imports(&mut self) -> ReachedFixedPoint { @@ -296,7 +293,7 @@ where let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); let mut resolved = Vec::new(); let mut res = ReachedFixedPoint::Yes; - macros.retain(|(module_id, call_id, path, tt)| { + macros.retain(|(module_id, source_item_id, path)| { if path.segments.len() != 2 { return true; } @@ -312,47 +309,24 @@ where res = ReachedFixedPoint::No; let def_map = self.db.crate_def_map(krate); if let Some(macro_id) = def_map.public_macros.get(&path.segments[1].name).cloned() { - resolved.push((*module_id, *call_id, (krate, macro_id), tt.clone())); + let call_id = + MacroCallLoc { def: macro_id, source_item_id: *source_item_id }.id(self.db); + resolved.push((*module_id, call_id)); } false }); - for (module_id, macro_call_id, macro_def_id, arg) in resolved { - self.collect_macro_expansion(module_id, macro_call_id, macro_def_id, arg); + for (module_id, macro_call_id) in resolved { + self.collect_macro_expansion(module_id, macro_call_id); } res } - fn collect_macro_expansion( - &mut self, - module_id: CrateModuleId, - macro_call_id: MacroCallId, - macro_def_id: (Crate, CrateMacroId), - macro_arg: tt::Subtree, - ) { - let (macro_krate, macro_id) = macro_def_id; - let dm; - let rules = if macro_krate == self.def_map.krate { - &self.def_map[macro_id] - } else { - dm = self.db.crate_def_map(macro_krate); - &dm[macro_id] - }; - if let Ok(expansion) = rules.expand(¯o_arg) { - self.def_map.macro_resolutions.insert(macro_call_id, macro_def_id); - // XXX: this **does not** go through a database, because we can't - // identify macro_call without adding the whole state of name resolution - // as a parameter to the query. - // - // So, we run the queries "manually" and we must ensure that - // `db.hir_parse(macro_call_id)` returns the same source_file. - let file_id: HirFileId = macro_call_id.into(); - let source_file = mbe::token_tree_to_ast_item_list(&expansion); - - let raw_items = raw::RawItems::from_source_file(&source_file, file_id); - ModCollector { def_collector: &mut *self, file_id, module_id, raw_items: &raw_items } - .collect(raw_items.items()) - } + fn collect_macro_expansion(&mut self, module_id: CrateModuleId, macro_call_id: MacroCallId) { + let file_id: HirFileId = macro_call_id.into(); + let raw_items = self.db.raw_items(file_id); + ModCollector { def_collector: &mut *self, file_id, module_id, raw_items: &raw_items } + .collect(raw_items.items()) } fn finish(self) -> CrateDefMap { @@ -412,7 +386,7 @@ where Ok(file_id) => { let module_id = self.push_child_module(name.clone(), source_item_id, Some(file_id)); - let raw_items = self.def_collector.db.raw_items(file_id); + let raw_items = self.def_collector.db.raw_items(file_id.into()); ModCollector { def_collector: &mut *self.def_collector, module_id, @@ -484,38 +458,33 @@ where // Case 1: macro rules, define a macro in crate-global mutable scope if is_macro_rules(&mac.path) { if let Some(name) = &mac.name { - self.def_collector.define_macro(name.clone(), &mac.arg, mac.export) + let macro_id = MacroDefId::MacroByExample { + source_item_id: mac.source_item_id.with_file_id(self.file_id), + }; + self.def_collector.define_macro(name.clone(), macro_id, mac.export) } return; } let source_item_id = SourceItemId { file_id: self.file_id, item_id: mac.source_item_id }; - let macro_call_id = MacroCallLoc { - module: Module { krate: self.def_collector.def_map.krate, module_id: self.module_id }, - source_item_id, - } - .id(self.def_collector.db); // Case 2: try to expand macro_rules from this crate, triggering // recursive item collection. if let Some(¯o_id) = mac.path.as_ident().and_then(|name| self.def_collector.global_macro_scope.get(name)) { - self.def_collector.collect_macro_expansion( - self.module_id, - macro_call_id, - (self.def_collector.def_map.krate, macro_id), - mac.arg.clone(), - ); + let macro_call_id = + MacroCallLoc { def: macro_id, source_item_id }.id(self.def_collector.db); + + self.def_collector.collect_macro_expansion(self.module_id, macro_call_id); return; } // Case 3: path to a macro from another crate, expand during name resolution self.def_collector.unexpanded_macros.push(( self.module_id, - macro_call_id, + source_item_id, mac.path.clone(), - mac.arg.clone(), )) } } diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index f8ba398ecf..7a516e556f 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -4,7 +4,6 @@ use std::{ }; use test_utils::tested_by; -use ra_db::FileId; use ra_arena::{Arena, impl_arena_id, RawId, map::ArenaMap}; use ra_syntax::{ AstNode, SourceFile, AstPtr, TreeArc, @@ -47,20 +46,20 @@ impl ImportSourceMap { } impl RawItems { - pub(crate) fn raw_items_query(db: &impl DefDatabase, file_id: FileId) -> Arc { + pub(crate) fn raw_items_query(db: &impl DefDatabase, file_id: HirFileId) -> Arc { db.raw_items_with_source_map(file_id).0 } pub(crate) fn raw_items_with_source_map_query( db: &impl DefDatabase, - file_id: FileId, + file_id: HirFileId, ) -> (Arc, Arc) { let mut collector = RawItemsCollector { raw_items: RawItems::default(), source_file_items: db.file_items(file_id.into()), source_map: ImportSourceMap::default(), }; - let source_file = db.parse(file_id); + let source_file = db.hir_parse(file_id); collector.process_module(None, &*source_file); (Arc::new(collector.raw_items), Arc::new(collector.source_map)) } @@ -68,19 +67,6 @@ impl RawItems { pub(crate) fn items(&self) -> &[RawItem] { &self.items } - - // We can't use queries during name resolution for fear of cycles, so this - // is a query-less variant of the above function. - pub(crate) fn from_source_file(source_file: &SourceFile, file_id: HirFileId) -> RawItems { - let source_file_items = SourceFileItems::from_source_file(source_file, file_id); - let mut collector = RawItemsCollector { - raw_items: RawItems::default(), - source_file_items: Arc::new(source_file_items), - source_map: ImportSourceMap::default(), - }; - collector.process_module(None, &*source_file); - collector.raw_items - } } impl Index for RawItems { @@ -173,7 +159,6 @@ pub(crate) struct MacroData { pub(crate) source_item_id: SourceFileItemId, pub(crate) path: Path, pub(crate) name: Option, - pub(crate) arg: tt::Subtree, pub(crate) export: bool, } @@ -291,18 +276,15 @@ impl RawItemsCollector { } fn add_macro(&mut self, current_module: Option, m: &ast::MacroCall) { - let (path, arg) = match ( - m.path().and_then(Path::from_ast), - m.token_tree().and_then(mbe::ast_to_token_tree), - ) { - (Some(path), Some((token_tree, _token_map))) => (path, token_tree), + let path = match m.path().and_then(Path::from_ast) { + Some(it) => it, _ => return, }; let name = m.name().map(|it| it.as_name()); let source_item_id = self.source_file_items.id_of_unchecked(m.syntax()); let export = m.has_atom_attr("macro_export"); - let m = self.raw_items.macros.alloc(MacroData { source_item_id, path, arg, name, export }); + let m = self.raw_items.macros.alloc(MacroData { source_item_id, path, name, export }); self.push_item(current_module, RawItem::Macro(m)); } diff --git a/crates/ra_hir/src/nameres/tests/incremental.rs b/crates/ra_hir/src/nameres/tests/incremental.rs index 6987819233..a059634e20 100644 --- a/crates/ra_hir/src/nameres/tests/incremental.rs +++ b/crates/ra_hir/src/nameres/tests/incremental.rs @@ -90,34 +90,27 @@ fn adding_inner_items_should_not_invalidate_def_map() { ); } -// It would be awesome to make this work, but it's unclear how #[test] -#[ignore] -fn typing_inside_a_function_inside_a_macro_should_not_invalidate_def_map() { +fn typing_inside_a_macro_should_not_invalidate_def_map() { check_def_map_is_not_recomputed( " //- /lib.rs + macro_rules! m { + ($ident:ident) => { + struct Foo; + } + } mod foo; - use crate::foo::bar::Baz; - //- /foo/mod.rs pub mod bar; //- /foo/bar.rs <|> - salsa::query_group! { - trait Baz { - fn foo() -> i32 { 1 + 1 } - } - } + m!(X); ", " - salsa::query_group! { - trait Baz { - fn foo() -> i32 { 92 } - } - } + m!(Y); ", ); } From e4646ce0d5bc232fb525a9330bc0c64d826fd241 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 26 Mar 2019 13:53:50 +0300 Subject: [PATCH 24/45] reduce visibility --- crates/ra_hir/src/nameres/raw.rs | 50 +++++++++++++++++--------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index 7a516e556f..ee0719ee0e 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -15,6 +15,10 @@ use crate::{ ids::{SourceFileItemId, SourceFileItems}, }; +/// `RawItems` is a set of top-level items in a file (except for impls). +/// +/// It is the input to name resolution algorithm. `RawItems` are not invalidated +/// on most edits. #[derive(Debug, Default, PartialEq, Eq)] pub struct RawItems { modules: Arena, @@ -31,11 +35,11 @@ pub struct ImportSourceMap { } impl ImportSourceMap { - pub(crate) fn insert(&mut self, import: ImportId, segment: &ast::PathSegment) { + fn insert(&mut self, import: ImportId, segment: &ast::PathSegment) { self.map.insert(import, AstPtr::new(segment)) } - pub fn get(&self, source: &ModuleSource, import: ImportId) -> TreeArc { + pub(crate) fn get(&self, source: &ModuleSource, import: ImportId) -> TreeArc { let file = match source { ModuleSource::SourceFile(file) => &*file, ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(), @@ -64,7 +68,7 @@ impl RawItems { (Arc::new(collector.raw_items), Arc::new(collector.source_map)) } - pub(crate) fn items(&self) -> &[RawItem] { + pub(super) fn items(&self) -> &[RawItem] { &self.items } } @@ -98,7 +102,7 @@ impl Index for RawItems { } #[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub(crate) enum RawItem { +pub(super) enum RawItem { Module(Module), Import(ImportId), Def(Def), @@ -106,11 +110,11 @@ pub(crate) enum RawItem { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub(crate) struct Module(RawId); +pub(super) struct Module(RawId); impl_arena_id!(Module); #[derive(Debug, PartialEq, Eq)] -pub(crate) enum ModuleData { +pub(super) enum ModuleData { Declaration { name: Name, source_item_id: SourceFileItemId }, Definition { name: Name, source_item_id: SourceFileItemId, items: Vec }, } @@ -121,26 +125,26 @@ impl_arena_id!(ImportId); #[derive(Debug, Clone, PartialEq, Eq)] pub struct ImportData { - pub(crate) path: Path, - pub(crate) alias: Option, - pub(crate) is_glob: bool, - pub(crate) is_prelude: bool, - pub(crate) is_extern_crate: bool, + pub(super) path: Path, + pub(super) alias: Option, + pub(super) is_glob: bool, + pub(super) is_prelude: bool, + pub(super) is_extern_crate: bool, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub(crate) struct Def(RawId); +pub(super) struct Def(RawId); impl_arena_id!(Def); #[derive(Debug, PartialEq, Eq)] -pub(crate) struct DefData { - pub(crate) source_item_id: SourceFileItemId, - pub(crate) name: Name, - pub(crate) kind: DefKind, +pub(super) struct DefData { + pub(super) source_item_id: SourceFileItemId, + pub(super) name: Name, + pub(super) kind: DefKind, } #[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub(crate) enum DefKind { +pub(super) enum DefKind { Function, Struct, Enum, @@ -151,15 +155,15 @@ pub(crate) enum DefKind { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub(crate) struct Macro(RawId); +pub(super) struct Macro(RawId); impl_arena_id!(Macro); #[derive(Debug, PartialEq, Eq)] -pub(crate) struct MacroData { - pub(crate) source_item_id: SourceFileItemId, - pub(crate) path: Path, - pub(crate) name: Option, - pub(crate) export: bool, +pub(super) struct MacroData { + pub(super) source_item_id: SourceFileItemId, + pub(super) path: Path, + pub(super) name: Option, + pub(super) export: bool, } struct RawItemsCollector { From 274990bc83084089918b0197767d2a1cea697b85 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 26 Mar 2019 13:54:23 +0300 Subject: [PATCH 25/45] :arrow_up: vfs --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3bc5f261cf..00f2338c9e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -913,7 +913,7 @@ dependencies = [ "ra_hir 0.1.0", "ra_project_model 0.1.0", "ra_syntax 0.1.0", - "ra_vfs 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ra_vfs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "test_utils 0.1.0", ] @@ -1021,7 +1021,7 @@ dependencies = [ "ra_project_model 0.1.0", "ra_syntax 0.1.0", "ra_text_edit 0.1.0", - "ra_vfs 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ra_vfs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1103,7 +1103,7 @@ dependencies = [ [[package]] name = "ra_vfs" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1943,7 +1943,7 @@ dependencies = [ "checksum proptest 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24f5844db2f839e97e3021980975f6ebf8691d9b9b2ca67ed3feb38dc3edb52c" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1" -"checksum ra_vfs 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1839e4e003d865b58b8b6c231aae6c463dfcd01bfbbddffbdb7662a7b5a627" +"checksum ra_vfs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d75d08da053ec832676686c72dfe509fdd1e807191a50ac79087466ffefccb1c" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" From e231277ab2f1ebb3040457e89b92540e599dbb3e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 26 Mar 2019 14:13:17 +0300 Subject: [PATCH 26/45] make macro parsing a query --- crates/ra_hir/src/db.rs | 5 ++++- crates/ra_hir/src/ids.rs | 6 +++--- crates/ra_hir/src/lib.rs | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 6eb9161496..492814cbbc 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -4,7 +4,7 @@ use ra_syntax::{SyntaxNode, TreeArc, SourceFile}; use ra_db::{SourceDatabase, salsa}; use crate::{ - HirFileId, SourceFileItems, SourceItemId, Crate, Module, HirInterner, + HirFileId, MacroDefId, SourceFileItems, SourceItemId, Crate, Module, HirInterner, Function, FnSignature, ExprScopes, TypeAlias, Struct, Enum, StructField, Const, ConstSignature, Static, @@ -19,6 +19,9 @@ use crate::{ #[salsa::query_group(DefDatabaseStorage)] pub trait DefDatabase: SourceDatabase + AsRef { + #[salsa::invoke(crate::ids::macro_def_query)] + fn macro_def(&self, macro_id: MacroDefId) -> Option>; + #[salsa::invoke(HirFileId::hir_parse)] fn hir_parse(&self, file_id: HirFileId) -> TreeArc; diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index cf0566308f..bac7b9e46b 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -101,7 +101,7 @@ fn parse_macro(db: &impl DefDatabase, macro_call_id: MacroCallId) -> Option for HirFileId { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub(crate) enum MacroDefId { +pub enum MacroDefId { MacroByExample { source_item_id: SourceItemId }, } -fn macro_def_query(db: &impl DefDatabase, id: MacroDefId) -> Option> { +pub(crate) fn macro_def_query(db: &impl DefDatabase, id: MacroDefId) -> Option> { let syntax_node = match id { MacroDefId::MacroByExample { source_item_id } => db.file_item(source_item_id), }; diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 974ebd8313..87bc8009d3 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -53,7 +53,7 @@ use crate::{ pub use self::{ path::{Path, PathKind}, name::Name, - ids::{HirFileId, MacroCallId, MacroCallLoc, HirInterner}, + ids::{HirFileId, MacroDefId, MacroCallId, MacroCallLoc, HirInterner}, nameres::{PerNs, Namespace}, ty::{Ty, ApplicationTy, TypeCtor, Substs, display::HirDisplay}, impl_block::{ImplBlock, ImplItem}, From 0b820cacab020993b6e1667f491289122f03de04 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 26 Mar 2019 14:40:34 +0300 Subject: [PATCH 27/45] move source_id to a separate file --- crates/ra_hir/src/db.rs | 4 +- crates/ra_hir/src/ids.rs | 114 +------------------------ crates/ra_hir/src/lib.rs | 4 +- crates/ra_hir/src/nameres.rs | 3 +- crates/ra_hir/src/nameres/collector.rs | 4 +- crates/ra_hir/src/nameres/raw.rs | 2 +- crates/ra_hir/src/source_binder.rs | 4 +- crates/ra_hir/src/source_id.rs | 113 ++++++++++++++++++++++++ 8 files changed, 128 insertions(+), 120 deletions(-) create mode 100644 crates/ra_hir/src/source_id.rs diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 492814cbbc..3296b9b31d 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -34,10 +34,10 @@ pub trait DefDatabase: SourceDatabase + AsRef { #[salsa::invoke(crate::traits::TraitData::trait_data_query)] fn trait_data(&self, t: Trait) -> Arc; - #[salsa::invoke(crate::ids::SourceFileItems::file_items_query)] + #[salsa::invoke(crate::source_id::SourceFileItems::file_items_query)] fn file_items(&self, file_id: HirFileId) -> Arc; - #[salsa::invoke(crate::ids::SourceFileItems::file_item_query)] + #[salsa::invoke(crate::source_id::SourceFileItems::file_item_query)] fn file_item(&self, source_item_id: SourceItemId) -> TreeArc; #[salsa::invoke(RawItems::raw_items_query)] diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index bac7b9e46b..e73dd5d21d 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -5,13 +5,12 @@ use std::{ }; use ra_db::{LocationInterner, FileId}; -use ra_syntax::{TreeArc, SyntaxNode, SourceFile, AstNode, SyntaxNodePtr, ast}; -use ra_arena::{Arena, RawId, ArenaId, impl_arena_id}; +use ra_syntax::{TreeArc, SourceFile, AstNode, ast}; +use ra_arena::{RawId, ArenaId, impl_arena_id}; use mbe::MacroRules; use crate::{ - Module, - DefDatabase, + Module, DefDatabase, SourceItemId, SourceFileItemId, }; #[derive(Debug, Default)] @@ -304,110 +303,3 @@ impl AstItemDef for TypeId { &interner.types } } - -/// Identifier of item within a specific file. This is stable over reparses, so -/// it's OK to use it as a salsa key/value. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct SourceFileItemId(RawId); -impl_arena_id!(SourceFileItemId); - -impl SourceFileItemId { - pub(crate) fn with_file_id(self, file_id: HirFileId) -> SourceItemId { - SourceItemId { file_id, item_id: self } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct SourceItemId { - pub(crate) file_id: HirFileId, - pub(crate) item_id: SourceFileItemId, -} - -/// Maps items' `SyntaxNode`s to `SourceFileItemId`s and back. -#[derive(Debug, PartialEq, Eq)] -pub struct SourceFileItems { - file_id: HirFileId, - arena: Arena, -} - -impl SourceFileItems { - pub(crate) fn file_items_query( - db: &impl DefDatabase, - file_id: HirFileId, - ) -> Arc { - let source_file = db.hir_parse(file_id); - Arc::new(SourceFileItems::from_source_file(&source_file, file_id)) - } - - pub(crate) fn file_item_query( - db: &impl DefDatabase, - source_item_id: SourceItemId, - ) -> TreeArc { - let source_file = db.hir_parse(source_item_id.file_id); - db.file_items(source_item_id.file_id)[source_item_id.item_id] - .to_node(&source_file) - .to_owned() - } - - pub(crate) fn from_source_file( - source_file: &SourceFile, - file_id: HirFileId, - ) -> SourceFileItems { - let mut res = SourceFileItems { file_id, arena: Arena::default() }; - // By walking the tree in bread-first order we make sure that parents - // get lower ids then children. That is, adding a new child does not - // change parent's id. This means that, say, adding a new function to a - // trait does not change ids of top-level items, which helps caching. - bfs(source_file.syntax(), |it| { - if let Some(module_item) = ast::ModuleItem::cast(it) { - res.alloc(module_item.syntax()); - } else if let Some(macro_call) = ast::MacroCall::cast(it) { - res.alloc(macro_call.syntax()); - } - }); - res - } - - fn alloc(&mut self, item: &SyntaxNode) -> SourceFileItemId { - self.arena.alloc(SyntaxNodePtr::new(item)) - } - pub(crate) fn id_of(&self, file_id: HirFileId, item: &SyntaxNode) -> SourceFileItemId { - assert_eq!( - self.file_id, file_id, - "SourceFileItems: wrong file, expected {:?}, got {:?}", - self.file_id, file_id - ); - self.id_of_unchecked(item) - } - pub(crate) fn id_of_unchecked(&self, item: &SyntaxNode) -> SourceFileItemId { - let ptr = SyntaxNodePtr::new(item); - if let Some((id, _)) = self.arena.iter().find(|(_id, i)| **i == ptr) { - return id; - } - panic!( - "Can't find {:?} in SourceFileItems:\n{:?}", - item, - self.arena.iter().map(|(_id, i)| i).collect::>(), - ); - } -} - -impl std::ops::Index for SourceFileItems { - type Output = SyntaxNodePtr; - fn index(&self, idx: SourceFileItemId) -> &SyntaxNodePtr { - &self.arena[idx] - } -} - -/// Walks the subtree in bfs order, calling `f` for each node. -fn bfs(node: &SyntaxNode, mut f: impl FnMut(&SyntaxNode)) { - let mut curr_layer = vec![node]; - let mut next_layer = vec![]; - while !curr_layer.is_empty() { - curr_layer.drain(..).for_each(|node| { - next_layer.extend(node.children()); - f(node); - }); - std::mem::swap(&mut curr_layer, &mut next_layer); - } -} diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 87bc8009d3..ac2585de0f 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -23,6 +23,7 @@ pub mod mock; mod path; pub mod source_binder; +mod source_id; mod ids; mod name; mod nameres; @@ -47,12 +48,13 @@ mod marks; use crate::{ db::{HirDatabase, DefDatabase}, name::{AsName, KnownName}, - ids::{SourceItemId, SourceFileItems}, + source_id::SourceFileItemId, }; pub use self::{ path::{Path, PathKind}, name::Name, + source_id::{SourceFileItems, SourceItemId}, ids::{HirFileId, MacroDefId, MacroCallId, MacroCallLoc, HirInterner}, nameres::{PerNs, Namespace}, ty::{Ty, ApplicationTy, TypeCtor, Substs, display::HirDisplay}, diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 93c11f271d..d8fa2383f5 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -63,7 +63,8 @@ use test_utils::tested_by; use crate::{ ModuleDef, Name, Crate, Module, DefDatabase, Path, PathKind, HirFileId, Trait, - ids::{SourceItemId, SourceFileItemId, MacroCallId, MacroDefId}, + SourceItemId, SourceFileItemId, + ids::{MacroCallId, MacroDefId}, diagnostics::DiagnosticSink, nameres::diagnostics::DefDiagnostic, }; diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index 89300d2ecc..4fb2981550 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -6,7 +6,7 @@ use ra_db::FileId; use crate::{ Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias, - DefDatabase, HirFileId, Name, Path, + DefDatabase, HirFileId, Name, Path, SourceItemId, KnownName, nameres::{ Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode, @@ -14,7 +14,7 @@ use crate::{ diagnostics::DefDiagnostic, raw, }, - ids::{AstItemDef, LocationCtx, MacroCallLoc, SourceItemId, MacroCallId, MacroDefId}, + ids::{AstItemDef, LocationCtx, MacroCallLoc, MacroCallId, MacroDefId}, }; pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index ee0719ee0e..f320046010 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -12,7 +12,7 @@ use ra_syntax::{ use crate::{ DefDatabase, Name, AsName, Path, HirFileId, ModuleSource, - ids::{SourceFileItemId, SourceFileItems}, + SourceFileItemId, SourceFileItems, }; /// `RawItems` is a set of top-level items in a file (except for impls). diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 3645b73b44..6e157007f4 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -13,9 +13,9 @@ use ra_syntax::{ }; use crate::{ - HirDatabase, Function, Struct, Enum, + HirDatabase, Function, Struct, Enum, SourceFileItemId, AsName, Module, HirFileId, Crate, Trait, Resolver, - ids::{LocationCtx, SourceFileItemId}, + ids::LocationCtx, expr }; diff --git a/crates/ra_hir/src/source_id.rs b/crates/ra_hir/src/source_id.rs new file mode 100644 index 0000000000..f961adf8bd --- /dev/null +++ b/crates/ra_hir/src/source_id.rs @@ -0,0 +1,113 @@ +use std::sync::Arc; + +use ra_arena::{Arena, RawId, impl_arena_id}; +use ra_syntax::{SyntaxNodePtr, TreeArc, SyntaxNode, SourceFile, AstNode, ast}; + +use crate::{HirFileId, DefDatabase}; + +/// Identifier of item within a specific file. This is stable over reparses, so +/// it's OK to use it as a salsa key/value. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct SourceFileItemId(RawId); +impl_arena_id!(SourceFileItemId); + +impl SourceFileItemId { + pub(crate) fn with_file_id(self, file_id: HirFileId) -> SourceItemId { + SourceItemId { file_id, item_id: self } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct SourceItemId { + pub(crate) file_id: HirFileId, + pub(crate) item_id: SourceFileItemId, +} + +/// Maps items' `SyntaxNode`s to `SourceFileItemId`s and back. +#[derive(Debug, PartialEq, Eq)] +pub struct SourceFileItems { + file_id: HirFileId, + arena: Arena, +} + +impl SourceFileItems { + pub(crate) fn file_items_query( + db: &impl DefDatabase, + file_id: HirFileId, + ) -> Arc { + let source_file = db.hir_parse(file_id); + Arc::new(SourceFileItems::from_source_file(&source_file, file_id)) + } + + pub(crate) fn file_item_query( + db: &impl DefDatabase, + source_item_id: SourceItemId, + ) -> TreeArc { + let source_file = db.hir_parse(source_item_id.file_id); + db.file_items(source_item_id.file_id)[source_item_id.item_id] + .to_node(&source_file) + .to_owned() + } + + pub(crate) fn from_source_file( + source_file: &SourceFile, + file_id: HirFileId, + ) -> SourceFileItems { + let mut res = SourceFileItems { file_id, arena: Arena::default() }; + // By walking the tree in bread-first order we make sure that parents + // get lower ids then children. That is, adding a new child does not + // change parent's id. This means that, say, adding a new function to a + // trait does not change ids of top-level items, which helps caching. + bfs(source_file.syntax(), |it| { + if let Some(module_item) = ast::ModuleItem::cast(it) { + res.alloc(module_item.syntax()); + } else if let Some(macro_call) = ast::MacroCall::cast(it) { + res.alloc(macro_call.syntax()); + } + }); + res + } + + fn alloc(&mut self, item: &SyntaxNode) -> SourceFileItemId { + self.arena.alloc(SyntaxNodePtr::new(item)) + } + pub(crate) fn id_of(&self, file_id: HirFileId, item: &SyntaxNode) -> SourceFileItemId { + assert_eq!( + self.file_id, file_id, + "SourceFileItems: wrong file, expected {:?}, got {:?}", + self.file_id, file_id + ); + self.id_of_unchecked(item) + } + pub(crate) fn id_of_unchecked(&self, item: &SyntaxNode) -> SourceFileItemId { + let ptr = SyntaxNodePtr::new(item); + if let Some((id, _)) = self.arena.iter().find(|(_id, i)| **i == ptr) { + return id; + } + panic!( + "Can't find {:?} in SourceFileItems:\n{:?}", + item, + self.arena.iter().map(|(_id, i)| i).collect::>(), + ); + } +} + +impl std::ops::Index for SourceFileItems { + type Output = SyntaxNodePtr; + fn index(&self, idx: SourceFileItemId) -> &SyntaxNodePtr { + &self.arena[idx] + } +} + +/// Walks the subtree in bfs order, calling `f` for each node. +fn bfs(node: &SyntaxNode, mut f: impl FnMut(&SyntaxNode)) { + let mut curr_layer = vec![node]; + let mut next_layer = vec![]; + while !curr_layer.is_empty() { + curr_layer.drain(..).for_each(|node| { + next_layer.extend(node.children()); + f(node); + }); + std::mem::swap(&mut curr_layer, &mut next_layer); + } +} From f6f2d69bb8318815ff87cd1bfe504fe5aa09ed63 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 26 Mar 2019 14:54:05 +0300 Subject: [PATCH 28/45] simplify --- crates/ra_hir/src/nameres.rs | 6 ++---- crates/ra_hir/src/source_binder.rs | 6 +++--- crates/ra_hir/src/source_id.rs | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index d8fa2383f5..36ef615a3a 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -61,9 +61,8 @@ use ra_db::{FileId, Edition}; use test_utils::tested_by; use crate::{ - ModuleDef, Name, Crate, Module, + ModuleDef, Name, Crate, Module, SourceItemId, DefDatabase, Path, PathKind, HirFileId, Trait, - SourceItemId, SourceFileItemId, ids::{MacroCallId, MacroDefId}, diagnostics::DiagnosticSink, nameres::diagnostics::DefDiagnostic, @@ -242,9 +241,8 @@ impl CrateDefMap { pub(crate) fn find_module_by_source( &self, file_id: HirFileId, - decl_id: Option, + decl_id: Option, ) -> Option { - let decl_id = decl_id.map(|it| it.with_file_id(file_id)); let (module_id, _module_data) = self.modules.iter().find(|(_module_id, module_data)| { if decl_id.is_some() { module_data.declaration == decl_id diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 6e157007f4..db9e3a22e7 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -13,7 +13,7 @@ use ra_syntax::{ }; use crate::{ - HirDatabase, Function, Struct, Enum, SourceFileItemId, + HirDatabase, Function, Struct, Enum, SourceItemId, AsName, Module, HirFileId, Crate, Trait, Resolver, ids::LocationCtx, expr @@ -55,7 +55,7 @@ fn module_from_inline( assert!(!module.has_semi()); let file_id = file_id.into(); let file_items = db.file_items(file_id); - let item_id = file_items.id_of(file_id, module.syntax()); + let item_id = file_items.id_of(file_id, module.syntax()).with_file_id(file_id); module_from_source(db, file_id, Some(item_id)) } @@ -75,7 +75,7 @@ pub fn module_from_child_node( fn module_from_source( db: &impl HirDatabase, file_id: HirFileId, - decl_id: Option, + decl_id: Option, ) -> Option { let source_root_id = db.file_source_root(file_id.as_original_file()); db.source_root_crates(source_root_id).iter().map(|&crate_id| Crate { crate_id }).find_map( diff --git a/crates/ra_hir/src/source_id.rs b/crates/ra_hir/src/source_id.rs index f961adf8bd..62707ba6ac 100644 --- a/crates/ra_hir/src/source_id.rs +++ b/crates/ra_hir/src/source_id.rs @@ -8,7 +8,7 @@ use crate::{HirFileId, DefDatabase}; /// Identifier of item within a specific file. This is stable over reparses, so /// it's OK to use it as a salsa key/value. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct SourceFileItemId(RawId); +pub(crate) struct SourceFileItemId(RawId); impl_arena_id!(SourceFileItemId); impl SourceFileItemId { From b1c865fd5aadfa80e46a5c37ef5e0574d289a882 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 26 Mar 2019 15:38:46 +0300 Subject: [PATCH 29/45] add links to readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 3a0c9dee16..8df9912467 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,12 @@ We are on the rust-lang Zulip! https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frls-2.2E0 +## Quick Links + +* Work List: https://paper.dropbox.com/doc/RLS-2.0-work-list--AZ3BgHKKCtqszbsi3gi6sjchAQ-42vbnxzuKq2lKwW0mkn8Y +* API docs: https://rust-analyzer.github.io/rust-analyzer/ra_ide_api/index.html +* CI: https://travis-ci.org/rust-analyzer/rust-analyzer + ## License Rust analyzer is primarily distributed under the terms of both the MIT From 6e5562224ef1d67d51d529b76cf89ed14ed81ecb Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 26 Mar 2019 16:47:52 +0300 Subject: [PATCH 30/45] remove some dead code --- crates/ra_hir/src/nameres.rs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 36ef615a3a..e962bbd311 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -63,7 +63,7 @@ use test_utils::tested_by; use crate::{ ModuleDef, Name, Crate, Module, SourceItemId, DefDatabase, Path, PathKind, HirFileId, Trait, - ids::{MacroCallId, MacroDefId}, + ids::MacroDefId, diagnostics::DiagnosticSink, nameres::diagnostics::DefDiagnostic, }; @@ -84,9 +84,7 @@ pub struct CrateDefMap { extern_prelude: FxHashMap, root: CrateModuleId, modules: Arena, - macros: Arena, public_macros: FxHashMap, - macro_resolutions: FxHashMap, diagnostics: Vec, } @@ -97,18 +95,6 @@ impl std::ops::Index for CrateDefMap { } } -impl std::ops::Index for CrateDefMap { - type Output = mbe::MacroRules; - fn index(&self, id: CrateMacroId) -> &mbe::MacroRules { - &self.macros[id] - } -} - -/// An ID of a macro, **local** to a specific crate -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub(crate) struct CrateMacroId(RawId); -impl_arena_id!(CrateMacroId); - /// An ID of a module, **local** to a specific crate #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub(crate) struct CrateModuleId(RawId); @@ -202,9 +188,7 @@ impl CrateDefMap { prelude: None, root, modules, - macros: Arena::default(), public_macros: FxHashMap::default(), - macro_resolutions: FxHashMap::default(), diagnostics: Vec::new(), } }; From fb8b354dcc837d5eb9b81fc205e4282a203df177 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 26 Mar 2019 17:25:14 +0300 Subject: [PATCH 31/45] add typed ids --- crates/ra_hir/src/code_model_impl/module.rs | 18 ++++---- crates/ra_hir/src/lib.rs | 2 +- crates/ra_hir/src/nameres.rs | 20 ++++----- crates/ra_hir/src/nameres/collector.rs | 22 +++++---- crates/ra_hir/src/nameres/raw.rs | 13 +++--- crates/ra_hir/src/source_binder.rs | 8 ++-- crates/ra_hir/src/source_id.rs | 50 ++++++++++++++++++++- 7 files changed, 88 insertions(+), 45 deletions(-) diff --git a/crates/ra_hir/src/code_model_impl/module.rs b/crates/ra_hir/src/code_model_impl/module.rs index 790e2b80f7..0edb8ade5b 100644 --- a/crates/ra_hir/src/code_model_impl/module.rs +++ b/crates/ra_hir/src/code_model_impl/module.rs @@ -1,18 +1,18 @@ use ra_db::FileId; -use ra_syntax::{ast, TreeArc, AstNode}; +use ra_syntax::{ast, TreeArc}; use crate::{ - Module, ModuleSource, Name, + Module, ModuleSource, Name, AstId, nameres::{CrateModuleId, ImportId}, HirDatabase, DefDatabase, - HirFileId, SourceItemId, + HirFileId, }; impl ModuleSource { pub(crate) fn new( db: &impl DefDatabase, file_id: Option, - decl_id: Option, + decl_id: Option>, ) -> ModuleSource { match (file_id, decl_id) { (Some(file_id), _) => { @@ -20,8 +20,7 @@ impl ModuleSource { ModuleSource::SourceFile(source_file) } (None, Some(item_id)) => { - let module = db.file_item(item_id); - let module = ast::Module::cast(&*module).unwrap(); + let module = item_id.to_node(db); assert!(module.item_list().is_some(), "expected inline module"); ModuleSource::Module(module.to_owned()) } @@ -55,7 +54,7 @@ impl Module { let decl_id = def_map[self.module_id].declaration; let file_id = def_map[self.module_id].definition; let module_source = ModuleSource::new(db, file_id, decl_id); - let file_id = file_id.map(HirFileId::from).unwrap_or_else(|| decl_id.unwrap().file_id); + let file_id = file_id.map(HirFileId::from).unwrap_or_else(|| decl_id.unwrap().file_id()); (file_id, module_source) } @@ -65,9 +64,8 @@ impl Module { ) -> Option<(HirFileId, TreeArc)> { let def_map = db.crate_def_map(self.krate); let decl = def_map[self.module_id].declaration?; - let syntax_node = db.file_item(decl); - let ast = ast::Module::cast(&syntax_node).unwrap().to_owned(); - Some((decl.file_id, ast)) + let ast = decl.to_node(db); + Some((decl.file_id(), ast)) } pub(crate) fn import_source_impl( diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index ac2585de0f..3fc08c55ca 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -48,7 +48,7 @@ mod marks; use crate::{ db::{HirDatabase, DefDatabase}, name::{AsName, KnownName}, - source_id::SourceFileItemId, + source_id::{SourceFileItemId, FileAstId, AstId}, }; pub use self::{ diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index e962bbd311..67b9d69860 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -59,13 +59,15 @@ use rustc_hash::FxHashMap; use ra_arena::{Arena, RawId, impl_arena_id}; use ra_db::{FileId, Edition}; use test_utils::tested_by; +use ra_syntax::ast; use crate::{ - ModuleDef, Name, Crate, Module, SourceItemId, + ModuleDef, Name, Crate, Module, DefDatabase, Path, PathKind, HirFileId, Trait, ids::MacroDefId, diagnostics::DiagnosticSink, nameres::diagnostics::DefDiagnostic, + AstId, }; pub(crate) use self::raw::{RawItems, ImportId, ImportSourceMap}; @@ -106,7 +108,7 @@ pub(crate) struct ModuleData { pub(crate) children: FxHashMap, pub(crate) scope: ModuleScope, /// None for root - pub(crate) declaration: Option, + pub(crate) declaration: Option>, /// None for inline modules. /// /// Note that non-inline modules, by definition, live inside non-macro file. @@ -225,7 +227,7 @@ impl CrateDefMap { pub(crate) fn find_module_by_source( &self, file_id: HirFileId, - decl_id: Option, + decl_id: Option>, ) -> Option { let (module_id, _module_data) = self.modules.iter().find(|(_module_id, module_data)| { if decl_id.is_some() { @@ -429,10 +431,10 @@ impl CrateDefMap { mod diagnostics { use relative_path::RelativePathBuf; - use ra_syntax::{AstPtr, AstNode, ast}; + use ra_syntax::{AstPtr, ast}; use crate::{ - SourceItemId, DefDatabase, + AstId, DefDatabase, nameres::CrateModuleId, diagnostics::{DiagnosticSink, UnresolvedModule}, }; @@ -441,7 +443,7 @@ mod diagnostics { pub(super) enum DefDiagnostic { UnresolvedModule { module: CrateModuleId, - declaration: SourceItemId, + declaration: AstId, candidate: RelativePathBuf, }, } @@ -458,10 +460,9 @@ mod diagnostics { if *module != target_module { return; } - let syntax = db.file_item(*declaration); - let decl = ast::Module::cast(&syntax).unwrap(); + let decl = declaration.to_node(db); sink.push(UnresolvedModule { - file: declaration.file_id, + file: declaration.file_id(), decl: AstPtr::new(&decl), candidate: candidate.clone(), }) @@ -469,5 +470,4 @@ mod diagnostics { } } } - } diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index 4fb2981550..e6fd8632ae 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -3,6 +3,7 @@ use rustc_hash::FxHashMap; use relative_path::RelativePathBuf; use test_utils::tested_by; use ra_db::FileId; +use ra_syntax::ast; use crate::{ Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias, @@ -15,6 +16,7 @@ use crate::{ raw, }, ids::{AstItemDef, LocationCtx, MacroCallLoc, MacroCallId, MacroDefId}, + AstId, }; pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { @@ -364,12 +366,9 @@ where fn collect_module(&mut self, module: &raw::ModuleData) { match module { // inline module, just recurse - raw::ModuleData::Definition { name, items, source_item_id } => { - let module_id = self.push_child_module( - name.clone(), - source_item_id.with_file_id(self.file_id), - None, - ); + raw::ModuleData::Definition { name, items, ast_id } => { + let module_id = + self.push_child_module(name.clone(), ast_id.with_file_id(self.file_id), None); ModCollector { def_collector: &mut *self.def_collector, module_id, @@ -379,13 +378,12 @@ where .collect(&*items); } // out of line module, resovle, parse and recurse - raw::ModuleData::Declaration { name, source_item_id } => { - let source_item_id = source_item_id.with_file_id(self.file_id); + raw::ModuleData::Declaration { name, ast_id } => { + let ast_id = ast_id.with_file_id(self.file_id); let is_root = self.def_collector.def_map.modules[self.module_id].parent.is_none(); match resolve_submodule(self.def_collector.db, self.file_id, name, is_root) { Ok(file_id) => { - let module_id = - self.push_child_module(name.clone(), source_item_id, Some(file_id)); + let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id)); let raw_items = self.def_collector.db.raw_items(file_id.into()); ModCollector { def_collector: &mut *self.def_collector, @@ -398,7 +396,7 @@ where Err(candidate) => self.def_collector.def_map.diagnostics.push( DefDiagnostic::UnresolvedModule { module: self.module_id, - declaration: source_item_id, + declaration: ast_id, candidate, }, ), @@ -410,7 +408,7 @@ where fn push_child_module( &mut self, name: Name, - declaration: SourceItemId, + declaration: AstId, definition: Option, ) -> CrateModuleId { let modules = &mut self.def_collector.def_map.modules; diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index f320046010..09acd5a98e 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -12,7 +12,7 @@ use ra_syntax::{ use crate::{ DefDatabase, Name, AsName, Path, HirFileId, ModuleSource, - SourceFileItemId, SourceFileItems, + SourceFileItemId, SourceFileItems, FileAstId, }; /// `RawItems` is a set of top-level items in a file (except for impls). @@ -115,8 +115,8 @@ impl_arena_id!(Module); #[derive(Debug, PartialEq, Eq)] pub(super) enum ModuleData { - Declaration { name: Name, source_item_id: SourceFileItemId }, - Definition { name: Name, source_item_id: SourceFileItemId, items: Vec }, + Declaration { name: Name, ast_id: FileAstId }, + Definition { name: Name, ast_id: FileAstId, items: Vec }, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -221,10 +221,9 @@ impl RawItemsCollector { Some(it) => it.as_name(), None => return, }; - let source_item_id = self.source_file_items.id_of_unchecked(module.syntax()); + let ast_id = self.source_file_items.ast_id(module); if module.has_semi() { - let item = - self.raw_items.modules.alloc(ModuleData::Declaration { name, source_item_id }); + let item = self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id }); self.push_item(current_module, RawItem::Module(item)); return; } @@ -232,7 +231,7 @@ impl RawItemsCollector { if let Some(item_list) = module.item_list() { let item = self.raw_items.modules.alloc(ModuleData::Definition { name, - source_item_id, + ast_id, items: Vec::new(), }); self.process_module(Some(item), item_list); diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index db9e3a22e7..f9d2d0247a 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -13,10 +13,10 @@ use ra_syntax::{ }; use crate::{ - HirDatabase, Function, Struct, Enum, SourceItemId, + HirDatabase, Function, Struct, Enum, AsName, Module, HirFileId, Crate, Trait, Resolver, ids::LocationCtx, - expr + expr, AstId }; /// Locates the module by `FileId`. Picks topmost module in the file. @@ -55,7 +55,7 @@ fn module_from_inline( assert!(!module.has_semi()); let file_id = file_id.into(); let file_items = db.file_items(file_id); - let item_id = file_items.id_of(file_id, module.syntax()).with_file_id(file_id); + let item_id = file_items.ast_id(module).with_file_id(file_id); module_from_source(db, file_id, Some(item_id)) } @@ -75,7 +75,7 @@ pub fn module_from_child_node( fn module_from_source( db: &impl HirDatabase, file_id: HirFileId, - decl_id: Option, + decl_id: Option>, ) -> Option { let source_root_id = db.file_source_root(file_id.as_original_file()); db.source_root_crates(source_root_id).iter().map(|&crate_id| Crate { crate_id }).find_map( diff --git a/crates/ra_hir/src/source_id.rs b/crates/ra_hir/src/source_id.rs index 62707ba6ac..881cc590ea 100644 --- a/crates/ra_hir/src/source_id.rs +++ b/crates/ra_hir/src/source_id.rs @@ -1,10 +1,55 @@ -use std::sync::Arc; +use std::{marker::PhantomData, sync::Arc}; use ra_arena::{Arena, RawId, impl_arena_id}; use ra_syntax::{SyntaxNodePtr, TreeArc, SyntaxNode, SourceFile, AstNode, ast}; use crate::{HirFileId, DefDatabase}; +#[derive(Debug, PartialEq, Eq)] +pub(crate) struct AstId { + file_id: HirFileId, + file_ast_id: FileAstId, +} + +impl Clone for AstId { + fn clone(&self) -> AstId { + *self + } +} + +impl Copy for AstId {} + +impl AstId { + pub(crate) fn file_id(&self) -> HirFileId { + self.file_id + } + + pub(crate) fn to_node(&self, db: &impl DefDatabase) -> TreeArc { + let syntax_node = db.file_item(self.file_ast_id.raw.with_file_id(self.file_id)); + N::cast(&syntax_node).unwrap().to_owned() + } +} + +#[derive(Debug, PartialEq, Eq)] +pub(crate) struct FileAstId { + raw: SourceFileItemId, + _ty: PhantomData, +} + +impl Clone for FileAstId { + fn clone(&self) -> FileAstId { + *self + } +} + +impl Copy for FileAstId {} + +impl FileAstId { + pub(crate) fn with_file_id(self, file_id: HirFileId) -> AstId { + AstId { file_id, file_ast_id: self } + } +} + /// Identifier of item within a specific file. This is stable over reparses, so /// it's OK to use it as a salsa key/value. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -90,6 +135,9 @@ impl SourceFileItems { self.arena.iter().map(|(_id, i)| i).collect::>(), ); } + pub(crate) fn ast_id(&self, item: &N) -> FileAstId { + FileAstId { raw: self.id_of_unchecked(item.syntax()), _ty: PhantomData } + } } impl std::ops::Index for SourceFileItems { From 071a19537d4399fd04d1e9594ab7878502a12d21 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 26 Mar 2019 18:03:17 +0300 Subject: [PATCH 32/45] strongy-typed ids for macros --- crates/ra_hir/src/ids.rs | 18 ++++++------------ crates/ra_hir/src/nameres/collector.rs | 24 ++++++++---------------- crates/ra_hir/src/nameres/raw.rs | 6 +++--- crates/ra_hir/src/source_id.rs | 4 ++-- 4 files changed, 19 insertions(+), 33 deletions(-) diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index e73dd5d21d..b503e0ee59 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -10,7 +10,7 @@ use ra_arena::{RawId, ArenaId, impl_arena_id}; use mbe::MacroRules; use crate::{ - Module, DefDatabase, SourceItemId, SourceFileItemId, + Module, DefDatabase, SourceItemId, SourceFileItemId, AstId, }; #[derive(Debug, Default)] @@ -68,7 +68,7 @@ impl HirFileId { HirFileIdRepr::File(file_id) => file_id, HirFileIdRepr::Macro(macro_call_id) => { let loc = macro_call_id.loc(db); - loc.source_item_id.file_id.original_file(db) + loc.ast_id.file_id().original_file(db) } } } @@ -96,8 +96,7 @@ impl HirFileId { fn parse_macro(db: &impl DefDatabase, macro_call_id: MacroCallId) -> Option> { let loc = macro_call_id.loc(db); - let syntax = db.file_item(loc.source_item_id); - let macro_call = ast::MacroCall::cast(&syntax).unwrap(); + let macro_call = loc.ast_id.to_node(db); let (macro_arg, _) = macro_call.token_tree().and_then(mbe::ast_to_token_tree)?; let macro_rules = db.macro_def(loc.def)?; @@ -124,15 +123,10 @@ impl From for HirFileId { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum MacroDefId { - MacroByExample { source_item_id: SourceItemId }, -} +pub struct MacroDefId(pub(crate) AstId); pub(crate) fn macro_def_query(db: &impl DefDatabase, id: MacroDefId) -> Option> { - let syntax_node = match id { - MacroDefId::MacroByExample { source_item_id } => db.file_item(source_item_id), - }; - let macro_call = ast::MacroCall::cast(&syntax_node).unwrap(); + let macro_call = id.0.to_node(db); let arg = macro_call.token_tree()?; let (tt, _) = mbe::ast_to_token_tree(arg)?; let rules = MacroRules::parse(&tt).ok()?; @@ -148,7 +142,7 @@ impl_arena_id!(MacroCallId); #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct MacroCallLoc { pub(crate) def: MacroDefId, - pub(crate) source_item_id: SourceItemId, + pub(crate) ast_id: AstId, } impl MacroCallId { diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index e6fd8632ae..b5f02ab803 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -7,7 +7,7 @@ use ra_syntax::ast; use crate::{ Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias, - DefDatabase, HirFileId, Name, Path, SourceItemId, + DefDatabase, HirFileId, Name, Path, KnownName, nameres::{ Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode, @@ -53,7 +53,7 @@ struct DefCollector { def_map: CrateDefMap, glob_imports: FxHashMap>, unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>, - unexpanded_macros: Vec<(CrateModuleId, SourceItemId, Path)>, + unexpanded_macros: Vec<(CrateModuleId, AstId, Path)>, global_macro_scope: FxHashMap, } @@ -295,7 +295,7 @@ where let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); let mut resolved = Vec::new(); let mut res = ReachedFixedPoint::Yes; - macros.retain(|(module_id, source_item_id, path)| { + macros.retain(|(module_id, ast_id, path)| { if path.segments.len() != 2 { return true; } @@ -311,8 +311,7 @@ where res = ReachedFixedPoint::No; let def_map = self.db.crate_def_map(krate); if let Some(macro_id) = def_map.public_macros.get(&path.segments[1].name).cloned() { - let call_id = - MacroCallLoc { def: macro_id, source_item_id: *source_item_id }.id(self.db); + let call_id = MacroCallLoc { def: macro_id, ast_id: *ast_id }.id(self.db); resolved.push((*module_id, call_id)); } false @@ -456,34 +455,27 @@ where // Case 1: macro rules, define a macro in crate-global mutable scope if is_macro_rules(&mac.path) { if let Some(name) = &mac.name { - let macro_id = MacroDefId::MacroByExample { - source_item_id: mac.source_item_id.with_file_id(self.file_id), - }; + let macro_id = MacroDefId(mac.ast_id.with_file_id(self.file_id)); self.def_collector.define_macro(name.clone(), macro_id, mac.export) } return; } - let source_item_id = SourceItemId { file_id: self.file_id, item_id: mac.source_item_id }; + let ast_id = mac.ast_id.with_file_id(self.file_id); // Case 2: try to expand macro_rules from this crate, triggering // recursive item collection. if let Some(¯o_id) = mac.path.as_ident().and_then(|name| self.def_collector.global_macro_scope.get(name)) { - let macro_call_id = - MacroCallLoc { def: macro_id, source_item_id }.id(self.def_collector.db); + let macro_call_id = MacroCallLoc { def: macro_id, ast_id }.id(self.def_collector.db); self.def_collector.collect_macro_expansion(self.module_id, macro_call_id); return; } // Case 3: path to a macro from another crate, expand during name resolution - self.def_collector.unexpanded_macros.push(( - self.module_id, - source_item_id, - mac.path.clone(), - )) + self.def_collector.unexpanded_macros.push((self.module_id, ast_id, mac.path.clone())) } } diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index 09acd5a98e..684bd1d50e 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -160,7 +160,7 @@ impl_arena_id!(Macro); #[derive(Debug, PartialEq, Eq)] pub(super) struct MacroData { - pub(super) source_item_id: SourceFileItemId, + pub(super) ast_id: FileAstId, pub(super) path: Path, pub(super) name: Option, pub(super) export: bool, @@ -285,9 +285,9 @@ impl RawItemsCollector { }; let name = m.name().map(|it| it.as_name()); - let source_item_id = self.source_file_items.id_of_unchecked(m.syntax()); + let ast_id = self.source_file_items.ast_id(m); let export = m.has_atom_attr("macro_export"); - let m = self.raw_items.macros.alloc(MacroData { source_item_id, path, name, export }); + let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export }); self.push_item(current_module, RawItem::Macro(m)); } diff --git a/crates/ra_hir/src/source_id.rs b/crates/ra_hir/src/source_id.rs index 881cc590ea..1dadd76c51 100644 --- a/crates/ra_hir/src/source_id.rs +++ b/crates/ra_hir/src/source_id.rs @@ -5,7 +5,7 @@ use ra_syntax::{SyntaxNodePtr, TreeArc, SyntaxNode, SourceFile, AstNode, ast}; use crate::{HirFileId, DefDatabase}; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Hash)] pub(crate) struct AstId { file_id: HirFileId, file_ast_id: FileAstId, @@ -30,7 +30,7 @@ impl AstId { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Hash)] pub(crate) struct FileAstId { raw: SourceFileItemId, _ty: PhantomData, From 8f324773127c733b12d1c5ee98a3d9c6a5360db0 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 26 Mar 2019 18:27:22 +0300 Subject: [PATCH 33/45] more type safety --- crates/ra_hir/src/ids.rs | 32 +++++++---------- crates/ra_hir/src/lib.rs | 2 +- crates/ra_hir/src/nameres/collector.rs | 23 ++++++------ crates/ra_hir/src/nameres/raw.rs | 48 ++++++++++++++++---------- crates/ra_hir/src/source_id.rs | 40 ++++++++++++++------- 5 files changed, 82 insertions(+), 63 deletions(-) diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index b503e0ee59..c2df5ce00a 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -1,5 +1,4 @@ use std::{ - marker::PhantomData, hash::{Hash, Hasher}, sync::Arc, }; @@ -10,7 +9,7 @@ use ra_arena::{RawId, ArenaId, impl_arena_id}; use mbe::MacroRules; use crate::{ - Module, DefDatabase, SourceItemId, SourceFileItemId, AstId, + Module, DefDatabase, AstId, FileAstId, }; #[derive(Debug, Default)] @@ -123,6 +122,7 @@ impl From for HirFileId { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct MacroDefId(pub(crate) AstId); pub(crate) fn macro_def_query(db: &impl DefDatabase, id: MacroDefId) -> Option> { @@ -161,26 +161,25 @@ impl MacroCallLoc { #[derive(Debug)] pub struct ItemLoc { pub(crate) module: Module, - raw: SourceItemId, - _ty: PhantomData, + ast_id: AstId, } impl PartialEq for ItemLoc { fn eq(&self, other: &Self) -> bool { - self.module == other.module && self.raw == other.raw + self.module == other.module && self.ast_id == other.ast_id } } impl Eq for ItemLoc {} impl Hash for ItemLoc { fn hash(&self, hasher: &mut H) { self.module.hash(hasher); - self.raw.hash(hasher); + self.ast_id.hash(hasher); } } impl Clone for ItemLoc { fn clone(&self) -> ItemLoc { - ItemLoc { module: self.module, raw: self.raw, _ty: PhantomData } + ItemLoc { module: self.module, ast_id: self.ast_id } } } @@ -208,25 +207,18 @@ pub(crate) trait AstItemDef: ArenaId + Clone { fn interner(interner: &HirInterner) -> &LocationInterner, Self>; fn from_ast(ctx: LocationCtx<&impl DefDatabase>, ast: &N) -> Self { let items = ctx.db.file_items(ctx.file_id); - let item_id = items.id_of(ctx.file_id, ast.syntax()); - Self::from_source_item_id_unchecked(ctx, item_id) + let item_id = items.ast_id(ast); + Self::from_ast_id(ctx, item_id) } - fn from_source_item_id_unchecked( - ctx: LocationCtx<&impl DefDatabase>, - item_id: SourceFileItemId, - ) -> Self { - let raw = SourceItemId { file_id: ctx.file_id, item_id }; - let loc = ItemLoc { module: ctx.module, raw, _ty: PhantomData }; - + fn from_ast_id(ctx: LocationCtx<&impl DefDatabase>, ast_id: FileAstId) -> Self { + let loc = ItemLoc { module: ctx.module, ast_id: ast_id.with_file_id(ctx.file_id) }; Self::interner(ctx.db.as_ref()).loc2id(&loc) } fn source(self, db: &impl DefDatabase) -> (HirFileId, TreeArc) { let int = Self::interner(db.as_ref()); let loc = int.id2loc(self); - let syntax = db.file_item(loc.raw); - let ast = - N::cast(&syntax).unwrap_or_else(|| panic!("invalid ItemLoc: {:?}", loc.raw)).to_owned(); - (loc.raw.file_id, ast) + let ast = loc.ast_id.to_node(db); + (loc.ast_id.file_id(), ast) } fn module(self, db: &impl DefDatabase) -> Module { let int = Self::interner(db.as_ref()); diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 3fc08c55ca..b1f388b065 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -48,7 +48,7 @@ mod marks; use crate::{ db::{HirDatabase, DefDatabase}, name::{AsName, KnownName}, - source_id::{SourceFileItemId, FileAstId, AstId}, + source_id::{FileAstId, AstId}, }; pub use self::{ diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index b5f02ab803..39cadc94aa 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -429,23 +429,24 @@ where fn define_def(&mut self, def: &raw::DefData) { let module = Module { krate: self.def_collector.def_map.krate, module_id: self.module_id }; let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id.into()); - macro_rules! id { - () => { - AstItemDef::from_source_item_id_unchecked(ctx, def.source_item_id) + + macro_rules! def { + ($kind:ident, $ast_id:ident) => { + $kind { id: AstItemDef::from_ast_id(ctx, $ast_id) }.into() }; } let name = def.name.clone(); let def: PerNs = match def.kind { - raw::DefKind::Function => PerNs::values(Function { id: id!() }.into()), - raw::DefKind::Struct => { - let s = Struct { id: id!() }.into(); + raw::DefKind::Function(ast_id) => PerNs::values(def!(Function, ast_id)), + raw::DefKind::Struct(ast_id) => { + let s = def!(Struct, ast_id); PerNs::both(s, s) } - raw::DefKind::Enum => PerNs::types(Enum { id: id!() }.into()), - raw::DefKind::Const => PerNs::values(Const { id: id!() }.into()), - raw::DefKind::Static => PerNs::values(Static { id: id!() }.into()), - raw::DefKind::Trait => PerNs::types(Trait { id: id!() }.into()), - raw::DefKind::TypeAlias => PerNs::types(TypeAlias { id: id!() }.into()), + raw::DefKind::Enum(ast_id) => PerNs::types(def!(Enum, ast_id)), + raw::DefKind::Const(ast_id) => PerNs::values(def!(Const, ast_id)), + raw::DefKind::Static(ast_id) => PerNs::values(def!(Static, ast_id)), + raw::DefKind::Trait(ast_id) => PerNs::types(def!(Trait, ast_id)), + raw::DefKind::TypeAlias(ast_id) => PerNs::types(def!(TypeAlias, ast_id)), }; let resolution = Resolution { def, import: None }; self.def_collector.update(self.module_id, None, &[(name, resolution)]) diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index 684bd1d50e..984478adcd 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -12,7 +12,7 @@ use ra_syntax::{ use crate::{ DefDatabase, Name, AsName, Path, HirFileId, ModuleSource, - SourceFileItemId, SourceFileItems, FileAstId, + SourceFileItems, FileAstId, }; /// `RawItems` is a set of top-level items in a file (except for impls). @@ -138,20 +138,19 @@ impl_arena_id!(Def); #[derive(Debug, PartialEq, Eq)] pub(super) struct DefData { - pub(super) source_item_id: SourceFileItemId, pub(super) name: Name, pub(super) kind: DefKind, } #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub(super) enum DefKind { - Function, - Struct, - Enum, - Const, - Static, - Trait, - TypeAlias, + Function(FileAstId), + Struct(FileAstId), + Enum(FileAstId), + Const(FileAstId), + Static(FileAstId), + Trait(FileAstId), + TypeAlias(FileAstId), } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -200,18 +199,31 @@ impl RawItemsCollector { // impls don't participate in name resolution return; } - ast::ModuleItemKind::StructDef(it) => (DefKind::Struct, it.name()), - ast::ModuleItemKind::EnumDef(it) => (DefKind::Enum, it.name()), - ast::ModuleItemKind::FnDef(it) => (DefKind::Function, it.name()), - ast::ModuleItemKind::TraitDef(it) => (DefKind::Trait, it.name()), - ast::ModuleItemKind::TypeAliasDef(it) => (DefKind::TypeAlias, it.name()), - ast::ModuleItemKind::ConstDef(it) => (DefKind::Const, it.name()), - ast::ModuleItemKind::StaticDef(it) => (DefKind::Static, it.name()), + ast::ModuleItemKind::StructDef(it) => { + (DefKind::Struct(self.source_file_items.ast_id(it)), it.name()) + } + ast::ModuleItemKind::EnumDef(it) => { + (DefKind::Enum(self.source_file_items.ast_id(it)), it.name()) + } + ast::ModuleItemKind::FnDef(it) => { + (DefKind::Function(self.source_file_items.ast_id(it)), it.name()) + } + ast::ModuleItemKind::TraitDef(it) => { + (DefKind::Trait(self.source_file_items.ast_id(it)), it.name()) + } + ast::ModuleItemKind::TypeAliasDef(it) => { + (DefKind::TypeAlias(self.source_file_items.ast_id(it)), it.name()) + } + ast::ModuleItemKind::ConstDef(it) => { + (DefKind::Const(self.source_file_items.ast_id(it)), it.name()) + } + ast::ModuleItemKind::StaticDef(it) => { + (DefKind::Static(self.source_file_items.ast_id(it)), it.name()) + } }; if let Some(name) = name { let name = name.as_name(); - let source_item_id = self.source_file_items.id_of_unchecked(item.syntax()); - let def = self.raw_items.defs.alloc(DefData { name, kind, source_item_id }); + let def = self.raw_items.defs.alloc(DefData { name, kind }); self.push_item(current_module, RawItem::Def(def)) } } diff --git a/crates/ra_hir/src/source_id.rs b/crates/ra_hir/src/source_id.rs index 1dadd76c51..2c855897a3 100644 --- a/crates/ra_hir/src/source_id.rs +++ b/crates/ra_hir/src/source_id.rs @@ -1,11 +1,11 @@ -use std::{marker::PhantomData, sync::Arc}; +use std::{marker::PhantomData, sync::Arc, hash::{Hash, Hasher}}; use ra_arena::{Arena, RawId, impl_arena_id}; use ra_syntax::{SyntaxNodePtr, TreeArc, SyntaxNode, SourceFile, AstNode, ast}; use crate::{HirFileId, DefDatabase}; -#[derive(Debug, PartialEq, Eq, Hash)] +#[derive(Debug)] pub(crate) struct AstId { file_id: HirFileId, file_ast_id: FileAstId, @@ -16,9 +16,20 @@ impl Clone for AstId { *self } } - impl Copy for AstId {} +impl PartialEq for AstId { + fn eq(&self, other: &Self) -> bool { + (self.file_id, self.file_ast_id) == (other.file_id, other.file_ast_id) + } +} +impl Eq for AstId {} +impl Hash for AstId { + fn hash(&self, hasher: &mut H) { + (self.file_id, self.file_ast_id).hash(hasher); + } +} + impl AstId { pub(crate) fn file_id(&self) -> HirFileId { self.file_id @@ -30,7 +41,7 @@ impl AstId { } } -#[derive(Debug, PartialEq, Eq, Hash)] +#[derive(Debug)] pub(crate) struct FileAstId { raw: SourceFileItemId, _ty: PhantomData, @@ -41,9 +52,20 @@ impl Clone for FileAstId { *self } } - impl Copy for FileAstId {} +impl PartialEq for FileAstId { + fn eq(&self, other: &Self) -> bool { + self.raw == other.raw + } +} +impl Eq for FileAstId {} +impl Hash for FileAstId { + fn hash(&self, hasher: &mut H) { + self.raw.hash(hasher); + } +} + impl FileAstId { pub(crate) fn with_file_id(self, file_id: HirFileId) -> AstId { AstId { file_id, file_ast_id: self } @@ -116,14 +138,6 @@ impl SourceFileItems { fn alloc(&mut self, item: &SyntaxNode) -> SourceFileItemId { self.arena.alloc(SyntaxNodePtr::new(item)) } - pub(crate) fn id_of(&self, file_id: HirFileId, item: &SyntaxNode) -> SourceFileItemId { - assert_eq!( - self.file_id, file_id, - "SourceFileItems: wrong file, expected {:?}, got {:?}", - self.file_id, file_id - ); - self.id_of_unchecked(item) - } pub(crate) fn id_of_unchecked(&self, item: &SyntaxNode) -> SourceFileItemId { let ptr = SyntaxNodePtr::new(item); if let Some((id, _)) = self.arena.iter().find(|(_id, i)| **i == ptr) { From cffa3f960e401ac8100fd450b32c336c93181959 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 26 Mar 2019 18:32:46 +0300 Subject: [PATCH 34/45] make stuff private --- crates/ra_hir/src/source_id.rs | 40 ++++++++++++---------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/crates/ra_hir/src/source_id.rs b/crates/ra_hir/src/source_id.rs index 2c855897a3..04b7bb7b3e 100644 --- a/crates/ra_hir/src/source_id.rs +++ b/crates/ra_hir/src/source_id.rs @@ -36,7 +36,9 @@ impl AstId { } pub(crate) fn to_node(&self, db: &impl DefDatabase) -> TreeArc { - let syntax_node = db.file_item(self.file_ast_id.raw.with_file_id(self.file_id)); + let source_item_id = + SourceItemId { file_id: self.file_id(), item_id: self.file_ast_id.raw }; + let syntax_node = db.file_item(source_item_id); N::cast(&syntax_node).unwrap().to_owned() } } @@ -75,19 +77,13 @@ impl FileAstId { /// Identifier of item within a specific file. This is stable over reparses, so /// it's OK to use it as a salsa key/value. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub(crate) struct SourceFileItemId(RawId); +struct SourceFileItemId(RawId); impl_arena_id!(SourceFileItemId); -impl SourceFileItemId { - pub(crate) fn with_file_id(self, file_id: HirFileId) -> SourceItemId { - SourceItemId { file_id, item_id: self } - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct SourceItemId { - pub(crate) file_id: HirFileId, - pub(crate) item_id: SourceFileItemId, + file_id: HirFileId, + item_id: SourceFileItemId, } /// Maps items' `SyntaxNode`s to `SourceFileItemId`s and back. @@ -111,15 +107,16 @@ impl SourceFileItems { source_item_id: SourceItemId, ) -> TreeArc { let source_file = db.hir_parse(source_item_id.file_id); - db.file_items(source_item_id.file_id)[source_item_id.item_id] + db.file_items(source_item_id.file_id).arena[source_item_id.item_id] .to_node(&source_file) .to_owned() } - pub(crate) fn from_source_file( - source_file: &SourceFile, - file_id: HirFileId, - ) -> SourceFileItems { + pub(crate) fn ast_id(&self, item: &N) -> FileAstId { + FileAstId { raw: self.id_of_unchecked(item.syntax()), _ty: PhantomData } + } + + fn from_source_file(source_file: &SourceFile, file_id: HirFileId) -> SourceFileItems { let mut res = SourceFileItems { file_id, arena: Arena::default() }; // By walking the tree in bread-first order we make sure that parents // get lower ids then children. That is, adding a new child does not @@ -138,7 +135,8 @@ impl SourceFileItems { fn alloc(&mut self, item: &SyntaxNode) -> SourceFileItemId { self.arena.alloc(SyntaxNodePtr::new(item)) } - pub(crate) fn id_of_unchecked(&self, item: &SyntaxNode) -> SourceFileItemId { + + fn id_of_unchecked(&self, item: &SyntaxNode) -> SourceFileItemId { let ptr = SyntaxNodePtr::new(item); if let Some((id, _)) = self.arena.iter().find(|(_id, i)| **i == ptr) { return id; @@ -149,16 +147,6 @@ impl SourceFileItems { self.arena.iter().map(|(_id, i)| i).collect::>(), ); } - pub(crate) fn ast_id(&self, item: &N) -> FileAstId { - FileAstId { raw: self.id_of_unchecked(item.syntax()), _ty: PhantomData } - } -} - -impl std::ops::Index for SourceFileItems { - type Output = SyntaxNodePtr; - fn index(&self, idx: SourceFileItemId) -> &SyntaxNodePtr { - &self.arena[idx] - } } /// Walks the subtree in bfs order, calling `f` for each node. From b17217b34afbdbdc6b1d8ec480fcf06ec4bd587e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 26 Mar 2019 18:57:57 +0300 Subject: [PATCH 35/45] simplify --- crates/ra_hir/src/db.rs | 2 +- crates/ra_hir/src/ids.rs | 7 ++++--- crates/ra_hir/src/source_id.rs | 33 ++++++++++++++++----------------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 3296b9b31d..a7bbaefb89 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -22,7 +22,7 @@ pub trait DefDatabase: SourceDatabase + AsRef { #[salsa::invoke(crate::ids::macro_def_query)] fn macro_def(&self, macro_id: MacroDefId) -> Option>; - #[salsa::invoke(HirFileId::hir_parse)] + #[salsa::invoke(HirFileId::hir_parse_query)] fn hir_parse(&self, file_id: HirFileId) -> TreeArc; #[salsa::invoke(crate::adt::StructData::struct_data_query)] diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index c2df5ce00a..81b3cfd229 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -82,7 +82,10 @@ impl HirFileId { } } - pub(crate) fn hir_parse(db: &impl DefDatabase, file_id: HirFileId) -> TreeArc { + pub(crate) fn hir_parse_query( + db: &impl DefDatabase, + file_id: HirFileId, + ) -> TreeArc { match file_id.0 { HirFileIdRepr::File(file_id) => db.parse(file_id), HirFileIdRepr::Macro(macro_call_id) => { @@ -122,7 +125,6 @@ impl From for HirFileId { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub struct MacroDefId(pub(crate) AstId); pub(crate) fn macro_def_query(db: &impl DefDatabase, id: MacroDefId) -> Option> { @@ -152,7 +154,6 @@ impl MacroCallId { } impl MacroCallLoc { - #[allow(unused)] pub(crate) fn id(&self, db: &impl AsRef) -> MacroCallId { db.as_ref().macros.loc2id(&self) } diff --git a/crates/ra_hir/src/source_id.rs b/crates/ra_hir/src/source_id.rs index 04b7bb7b3e..fb71417aff 100644 --- a/crates/ra_hir/src/source_id.rs +++ b/crates/ra_hir/src/source_id.rs @@ -5,6 +5,7 @@ use ra_syntax::{SyntaxNodePtr, TreeArc, SyntaxNode, SourceFile, AstNode, ast}; use crate::{HirFileId, DefDatabase}; +/// `AstId` points to an AST node in any file #[derive(Debug)] pub(crate) struct AstId { file_id: HirFileId, @@ -43,6 +44,7 @@ impl AstId { } } +/// `AstId` points to an AST node in a specific file. #[derive(Debug)] pub(crate) struct FileAstId { raw: SourceFileItemId, @@ -89,7 +91,6 @@ pub struct SourceItemId { /// Maps items' `SyntaxNode`s to `SourceFileItemId`s and back. #[derive(Debug, PartialEq, Eq)] pub struct SourceFileItems { - file_id: HirFileId, arena: Arena, } @@ -99,7 +100,7 @@ impl SourceFileItems { file_id: HirFileId, ) -> Arc { let source_file = db.hir_parse(file_id); - Arc::new(SourceFileItems::from_source_file(&source_file, file_id)) + Arc::new(SourceFileItems::from_source_file(&source_file)) } pub(crate) fn file_item_query( @@ -113,11 +114,21 @@ impl SourceFileItems { } pub(crate) fn ast_id(&self, item: &N) -> FileAstId { - FileAstId { raw: self.id_of_unchecked(item.syntax()), _ty: PhantomData } + let ptr = SyntaxNodePtr::new(item.syntax()); + let raw = match self.arena.iter().find(|(_id, i)| **i == ptr) { + Some((it, _)) => it, + None => panic!( + "Can't find {:?} in SourceFileItems:\n{:?}", + item.syntax(), + self.arena.iter().map(|(_id, i)| i).collect::>(), + ), + }; + + FileAstId { raw, _ty: PhantomData } } - fn from_source_file(source_file: &SourceFile, file_id: HirFileId) -> SourceFileItems { - let mut res = SourceFileItems { file_id, arena: Arena::default() }; + fn from_source_file(source_file: &SourceFile) -> SourceFileItems { + let mut res = SourceFileItems { arena: Arena::default() }; // By walking the tree in bread-first order we make sure that parents // get lower ids then children. That is, adding a new child does not // change parent's id. This means that, say, adding a new function to a @@ -135,18 +146,6 @@ impl SourceFileItems { fn alloc(&mut self, item: &SyntaxNode) -> SourceFileItemId { self.arena.alloc(SyntaxNodePtr::new(item)) } - - fn id_of_unchecked(&self, item: &SyntaxNode) -> SourceFileItemId { - let ptr = SyntaxNodePtr::new(item); - if let Some((id, _)) = self.arena.iter().find(|(_id, i)| **i == ptr) { - return id; - } - panic!( - "Can't find {:?} in SourceFileItems:\n{:?}", - item, - self.arena.iter().map(|(_id, i)| i).collect::>(), - ); - } } /// Walks the subtree in bfs order, calling `f` for each node. From e28db444dfdc797002bd3561940cde2659b831de Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 26 Mar 2019 19:00:11 +0300 Subject: [PATCH 36/45] rename --- crates/ra_hir/src/db.rs | 10 +++--- crates/ra_hir/src/ids.rs | 2 +- crates/ra_hir/src/lib.rs | 2 +- crates/ra_hir/src/nameres/raw.rs | 24 ++++++------- crates/ra_hir/src/source_binder.rs | 4 +-- crates/ra_hir/src/source_id.rs | 54 ++++++++++++------------------ crates/ra_ide_api/src/change.rs | 4 +-- 7 files changed, 44 insertions(+), 56 deletions(-) diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index a7bbaefb89..1470058480 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -4,7 +4,7 @@ use ra_syntax::{SyntaxNode, TreeArc, SourceFile}; use ra_db::{SourceDatabase, salsa}; use crate::{ - HirFileId, MacroDefId, SourceFileItems, SourceItemId, Crate, Module, HirInterner, + HirFileId, MacroDefId, AstIdMap, ErasedFileAstId, Crate, Module, HirInterner, Function, FnSignature, ExprScopes, TypeAlias, Struct, Enum, StructField, Const, ConstSignature, Static, @@ -34,11 +34,11 @@ pub trait DefDatabase: SourceDatabase + AsRef { #[salsa::invoke(crate::traits::TraitData::trait_data_query)] fn trait_data(&self, t: Trait) -> Arc; - #[salsa::invoke(crate::source_id::SourceFileItems::file_items_query)] - fn file_items(&self, file_id: HirFileId) -> Arc; + #[salsa::invoke(crate::source_id::AstIdMap::ast_id_map_query)] + fn ast_id_map(&self, file_id: HirFileId) -> Arc; - #[salsa::invoke(crate::source_id::SourceFileItems::file_item_query)] - fn file_item(&self, source_item_id: SourceItemId) -> TreeArc; + #[salsa::invoke(crate::source_id::AstIdMap::file_item_query)] + fn ast_id_to_node(&self, file_id: HirFileId, ast_id: ErasedFileAstId) -> TreeArc; #[salsa::invoke(RawItems::raw_items_query)] fn raw_items(&self, file_id: HirFileId) -> Arc; diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 81b3cfd229..d8a25e2466 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -207,7 +207,7 @@ impl<'a, DB: DefDatabase> LocationCtx<&'a DB> { pub(crate) trait AstItemDef: ArenaId + Clone { fn interner(interner: &HirInterner) -> &LocationInterner, Self>; fn from_ast(ctx: LocationCtx<&impl DefDatabase>, ast: &N) -> Self { - let items = ctx.db.file_items(ctx.file_id); + let items = ctx.db.ast_id_map(ctx.file_id); let item_id = items.ast_id(ast); Self::from_ast_id(ctx, item_id) } diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index b1f388b065..7c603bbd3e 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -54,7 +54,7 @@ use crate::{ pub use self::{ path::{Path, PathKind}, name::Name, - source_id::{SourceFileItems, SourceItemId}, + source_id::{AstIdMap, ErasedFileAstId}, ids::{HirFileId, MacroDefId, MacroCallId, MacroCallLoc, HirInterner}, nameres::{PerNs, Namespace}, ty::{Ty, ApplicationTy, TypeCtor, Substs, display::HirDisplay}, diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index 984478adcd..0936229acf 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -12,7 +12,7 @@ use ra_syntax::{ use crate::{ DefDatabase, Name, AsName, Path, HirFileId, ModuleSource, - SourceFileItems, FileAstId, + AstIdMap, FileAstId, }; /// `RawItems` is a set of top-level items in a file (except for impls). @@ -60,7 +60,7 @@ impl RawItems { ) -> (Arc, Arc) { let mut collector = RawItemsCollector { raw_items: RawItems::default(), - source_file_items: db.file_items(file_id.into()), + source_ast_id_map: db.ast_id_map(file_id.into()), source_map: ImportSourceMap::default(), }; let source_file = db.hir_parse(file_id); @@ -167,7 +167,7 @@ pub(super) struct MacroData { struct RawItemsCollector { raw_items: RawItems, - source_file_items: Arc, + source_ast_id_map: Arc, source_map: ImportSourceMap, } @@ -200,25 +200,25 @@ impl RawItemsCollector { return; } ast::ModuleItemKind::StructDef(it) => { - (DefKind::Struct(self.source_file_items.ast_id(it)), it.name()) + (DefKind::Struct(self.source_ast_id_map.ast_id(it)), it.name()) } ast::ModuleItemKind::EnumDef(it) => { - (DefKind::Enum(self.source_file_items.ast_id(it)), it.name()) + (DefKind::Enum(self.source_ast_id_map.ast_id(it)), it.name()) } ast::ModuleItemKind::FnDef(it) => { - (DefKind::Function(self.source_file_items.ast_id(it)), it.name()) + (DefKind::Function(self.source_ast_id_map.ast_id(it)), it.name()) } ast::ModuleItemKind::TraitDef(it) => { - (DefKind::Trait(self.source_file_items.ast_id(it)), it.name()) + (DefKind::Trait(self.source_ast_id_map.ast_id(it)), it.name()) } ast::ModuleItemKind::TypeAliasDef(it) => { - (DefKind::TypeAlias(self.source_file_items.ast_id(it)), it.name()) + (DefKind::TypeAlias(self.source_ast_id_map.ast_id(it)), it.name()) } ast::ModuleItemKind::ConstDef(it) => { - (DefKind::Const(self.source_file_items.ast_id(it)), it.name()) + (DefKind::Const(self.source_ast_id_map.ast_id(it)), it.name()) } ast::ModuleItemKind::StaticDef(it) => { - (DefKind::Static(self.source_file_items.ast_id(it)), it.name()) + (DefKind::Static(self.source_ast_id_map.ast_id(it)), it.name()) } }; if let Some(name) = name { @@ -233,7 +233,7 @@ impl RawItemsCollector { Some(it) => it.as_name(), None => return, }; - let ast_id = self.source_file_items.ast_id(module); + let ast_id = self.source_ast_id_map.ast_id(module); if module.has_semi() { let item = self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id }); self.push_item(current_module, RawItem::Module(item)); @@ -297,7 +297,7 @@ impl RawItemsCollector { }; let name = m.name().map(|it| it.as_name()); - let ast_id = self.source_file_items.ast_id(m); + let ast_id = self.source_ast_id_map.ast_id(m); let export = m.has_atom_attr("macro_export"); let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export }); self.push_item(current_module, RawItem::Macro(m)); diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index f9d2d0247a..9dae4c3d1d 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -54,8 +54,8 @@ fn module_from_inline( ) -> Option { assert!(!module.has_semi()); let file_id = file_id.into(); - let file_items = db.file_items(file_id); - let item_id = file_items.ast_id(module).with_file_id(file_id); + let ast_id_map = db.ast_id_map(file_id); + let item_id = ast_id_map.ast_id(module).with_file_id(file_id); module_from_source(db, file_id, Some(item_id)) } diff --git a/crates/ra_hir/src/source_id.rs b/crates/ra_hir/src/source_id.rs index fb71417aff..0a8fb6d328 100644 --- a/crates/ra_hir/src/source_id.rs +++ b/crates/ra_hir/src/source_id.rs @@ -5,7 +5,9 @@ use ra_syntax::{SyntaxNodePtr, TreeArc, SyntaxNode, SourceFile, AstNode, ast}; use crate::{HirFileId, DefDatabase}; -/// `AstId` points to an AST node in any file +/// `AstId` points to an AST node in any file. +/// +/// It is stable across reparses, and can be used as salsa key/value. #[derive(Debug)] pub(crate) struct AstId { file_id: HirFileId, @@ -37,9 +39,7 @@ impl AstId { } pub(crate) fn to_node(&self, db: &impl DefDatabase) -> TreeArc { - let source_item_id = - SourceItemId { file_id: self.file_id(), item_id: self.file_ast_id.raw }; - let syntax_node = db.file_item(source_item_id); + let syntax_node = db.ast_id_to_node(self.file_id, self.file_ast_id.raw); N::cast(&syntax_node).unwrap().to_owned() } } @@ -47,7 +47,7 @@ impl AstId { /// `AstId` points to an AST node in a specific file. #[derive(Debug)] pub(crate) struct FileAstId { - raw: SourceFileItemId, + raw: ErasedFileAstId, _ty: PhantomData, } @@ -76,41 +76,29 @@ impl FileAstId { } } -/// Identifier of item within a specific file. This is stable over reparses, so -/// it's OK to use it as a salsa key/value. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -struct SourceFileItemId(RawId); -impl_arena_id!(SourceFileItemId); +pub struct ErasedFileAstId(RawId); +impl_arena_id!(ErasedFileAstId); -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct SourceItemId { - file_id: HirFileId, - item_id: SourceFileItemId, -} - -/// Maps items' `SyntaxNode`s to `SourceFileItemId`s and back. +/// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back. #[derive(Debug, PartialEq, Eq)] -pub struct SourceFileItems { - arena: Arena, +pub struct AstIdMap { + arena: Arena, } -impl SourceFileItems { - pub(crate) fn file_items_query( - db: &impl DefDatabase, - file_id: HirFileId, - ) -> Arc { +impl AstIdMap { + pub(crate) fn ast_id_map_query(db: &impl DefDatabase, file_id: HirFileId) -> Arc { let source_file = db.hir_parse(file_id); - Arc::new(SourceFileItems::from_source_file(&source_file)) + Arc::new(AstIdMap::from_source_file(&source_file)) } pub(crate) fn file_item_query( db: &impl DefDatabase, - source_item_id: SourceItemId, + file_id: HirFileId, + ast_id: ErasedFileAstId, ) -> TreeArc { - let source_file = db.hir_parse(source_item_id.file_id); - db.file_items(source_item_id.file_id).arena[source_item_id.item_id] - .to_node(&source_file) - .to_owned() + let source_file = db.hir_parse(file_id); + db.ast_id_map(file_id).arena[ast_id].to_node(&source_file).to_owned() } pub(crate) fn ast_id(&self, item: &N) -> FileAstId { @@ -118,7 +106,7 @@ impl SourceFileItems { let raw = match self.arena.iter().find(|(_id, i)| **i == ptr) { Some((it, _)) => it, None => panic!( - "Can't find {:?} in SourceFileItems:\n{:?}", + "Can't find {:?} in AstIdMap:\n{:?}", item.syntax(), self.arena.iter().map(|(_id, i)| i).collect::>(), ), @@ -127,8 +115,8 @@ impl SourceFileItems { FileAstId { raw, _ty: PhantomData } } - fn from_source_file(source_file: &SourceFile) -> SourceFileItems { - let mut res = SourceFileItems { arena: Arena::default() }; + fn from_source_file(source_file: &SourceFile) -> AstIdMap { + let mut res = AstIdMap { arena: Arena::default() }; // By walking the tree in bread-first order we make sure that parents // get lower ids then children. That is, adding a new child does not // change parent's id. This means that, say, adding a new function to a @@ -143,7 +131,7 @@ impl SourceFileItems { res } - fn alloc(&mut self, item: &SyntaxNode) -> SourceFileItemId { + fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId { self.arena.alloc(SyntaxNodePtr::new(item)) } } diff --git a/crates/ra_ide_api/src/change.rs b/crates/ra_ide_api/src/change.rs index 26fde91bcf..a4a086931b 100644 --- a/crates/ra_ide_api/src/change.rs +++ b/crates/ra_ide_api/src/change.rs @@ -220,8 +220,8 @@ impl RootDatabase { self.query(ra_db::ParseQuery).sweep(sweep); self.query(hir::db::HirParseQuery).sweep(sweep); - self.query(hir::db::FileItemsQuery).sweep(sweep); - self.query(hir::db::FileItemQuery).sweep(sweep); + self.query(hir::db::AstIdMapQuery).sweep(sweep); + self.query(hir::db::AstIdToNodeQuery).sweep(sweep); self.query(hir::db::RawItemsWithSourceMapQuery).sweep(sweep); self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep); From 1325a31e34a3b4bf5104a743bcb8217ef5c4f3cd Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 26 Mar 2019 19:15:39 +0300 Subject: [PATCH 37/45] reduce visibility --- crates/ra_hir/src/code_model_api.rs | 4 ++-- crates/ra_hir/src/ids.rs | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 88c13566c4..624c25c4d6 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -13,7 +13,7 @@ use crate::{ adt::{EnumVariantId, StructFieldId, VariantDef}, generics::GenericParams, docs::{Documentation, Docs, docs_from_ast}, - ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId}, + ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeAliasId}, impl_block::ImplBlock, resolve::Resolver, diagnostics::DiagnosticSink, @@ -672,7 +672,7 @@ impl Docs for Trait { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct TypeAlias { - pub(crate) id: TypeId, + pub(crate) id: TypeAliasId, } impl TypeAlias { diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index d8a25e2466..eb9939df79 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -21,7 +21,7 @@ pub struct HirInterner { consts: LocationInterner, ConstId>, statics: LocationInterner, StaticId>, traits: LocationInterner, TraitId>, - types: LocationInterner, TypeId>, + types: LocationInterner, TypeAliasId>, } impl HirInterner { @@ -229,7 +229,7 @@ pub(crate) trait AstItemDef: ArenaId + Clone { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct FunctionId(RawId); +pub(crate) struct FunctionId(RawId); impl_arena_id!(FunctionId); impl AstItemDef for FunctionId { fn interner(interner: &HirInterner) -> &LocationInterner, Self> { @@ -238,7 +238,7 @@ impl AstItemDef for FunctionId { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct StructId(RawId); +pub(crate) struct StructId(RawId); impl_arena_id!(StructId); impl AstItemDef for StructId { fn interner(interner: &HirInterner) -> &LocationInterner, Self> { @@ -247,7 +247,7 @@ impl AstItemDef for StructId { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct EnumId(RawId); +pub(crate) struct EnumId(RawId); impl_arena_id!(EnumId); impl AstItemDef for EnumId { fn interner(interner: &HirInterner) -> &LocationInterner, Self> { @@ -256,7 +256,7 @@ impl AstItemDef for EnumId { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct ConstId(RawId); +pub(crate) struct ConstId(RawId); impl_arena_id!(ConstId); impl AstItemDef for ConstId { fn interner(interner: &HirInterner) -> &LocationInterner, Self> { @@ -265,7 +265,7 @@ impl AstItemDef for ConstId { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct StaticId(RawId); +pub(crate) struct StaticId(RawId); impl_arena_id!(StaticId); impl AstItemDef for StaticId { fn interner(interner: &HirInterner) -> &LocationInterner, Self> { @@ -274,7 +274,7 @@ impl AstItemDef for StaticId { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct TraitId(RawId); +pub(crate) struct TraitId(RawId); impl_arena_id!(TraitId); impl AstItemDef for TraitId { fn interner(interner: &HirInterner) -> &LocationInterner, Self> { @@ -283,9 +283,9 @@ impl AstItemDef for TraitId { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct TypeId(RawId); -impl_arena_id!(TypeId); -impl AstItemDef for TypeId { +pub(crate) struct TypeAliasId(RawId); +impl_arena_id!(TypeAliasId); +impl AstItemDef for TypeAliasId { fn interner(interner: &HirInterner) -> &LocationInterner, Self> { &interner.types } From a3fee2bda04be27a63d9298f6e97555c904df4da Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 26 Mar 2019 19:54:52 +0300 Subject: [PATCH 38/45] more realistic test for incrementality --- .../ra_hir/src/nameres/tests/incremental.rs | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/crates/ra_hir/src/nameres/tests/incremental.rs b/crates/ra_hir/src/nameres/tests/incremental.rs index a059634e20..001f76ac30 100644 --- a/crates/ra_hir/src/nameres/tests/incremental.rs +++ b/crates/ra_hir/src/nameres/tests/incremental.rs @@ -92,12 +92,14 @@ fn adding_inner_items_should_not_invalidate_def_map() { #[test] fn typing_inside_a_macro_should_not_invalidate_def_map() { - check_def_map_is_not_recomputed( + let (mut db, pos) = MockDatabase::with_position( " //- /lib.rs macro_rules! m { ($ident:ident) => { - struct Foo; + fn f() { + $ident + $ident; + }; } } mod foo; @@ -109,8 +111,23 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() { <|> m!(X); ", - " - m!(Y); - ", ); + { + let events = db.log_executed(|| { + let module = crate::source_binder::module_from_file_id(&db, pos.file_id).unwrap(); + let decls = module.declarations(&db); + assert_eq!(decls.len(), 1); + }); + assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) + } + db.set_file_text(pos.file_id, Arc::new("m!(Y);".to_string())); + + { + let events = db.log_executed(|| { + let module = crate::source_binder::module_from_file_id(&db, pos.file_id).unwrap(); + let decls = module.declarations(&db); + assert_eq!(decls.len(), 1); + }); + assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) + } } From 7b81c088f79a19289008f635d2e2e4105a750b96 Mon Sep 17 00:00:00 2001 From: Marco Groppo Date: Tue, 26 Mar 2019 23:12:46 +0100 Subject: [PATCH 39/45] Flip any binary expression except assignments. --- crates/ra_assists/src/flip_binexpr.rs | 124 ++++++++++++-------------- 1 file changed, 58 insertions(+), 66 deletions(-) diff --git a/crates/ra_assists/src/flip_binexpr.rs b/crates/ra_assists/src/flip_binexpr.rs index 8a0737b556..ec377642e0 100644 --- a/crates/ra_assists/src/flip_binexpr.rs +++ b/crates/ra_assists/src/flip_binexpr.rs @@ -3,38 +3,26 @@ use ra_syntax::ast::{AstNode, BinExpr, BinOp}; use crate::{AssistCtx, Assist, AssistId}; -/// Flip binary comparison expressions (==, !=, >, >=, <, <=). +/// Flip binary expression assist. pub(crate) fn flip_binexpr(mut ctx: AssistCtx) -> Option { let expr = ctx.node_at_offset::()?; let lhs = expr.lhs()?.syntax(); let rhs = expr.rhs()?.syntax(); let op_range = expr.op()?.range(); - // The assist should be available only if the cursor is on the operator + // The assist should be applied only if the cursor is on the operator let cursor_in_range = ctx.frange.range.is_subrange(&op_range); - // The assist should be available only for these binary operators - // (it should not change the meaning of the expression) - let allowed_ops = [ - BinOp::EqualityTest, - BinOp::NegatedEqualityTest, - BinOp::GreaterTest, - BinOp::GreaterEqualTest, - BinOp::LesserTest, - BinOp::LesserEqualTest, - ]; - let op_kind = expr.op_kind()?; - if !cursor_in_range || !allowed_ops.iter().any(|o| *o == op_kind) { + if !cursor_in_range { return None; } - let new_op = match op_kind { - BinOp::GreaterTest => Some("<"), - BinOp::GreaterEqualTest => Some("<="), - BinOp::LesserTest => Some(">"), - BinOp::LesserEqualTest => Some(">="), - _ => None, - }; + let action: FlipAction = expr.op_kind()?.into(); + // The assist should not be applied for certain operators + if let FlipAction::DontFlip = action { + return None; + } + ctx.add_action(AssistId("flip_binexpr"), "flip binary expression", |edit| { edit.target(op_range); - if let Some(new_op) = new_op { + if let FlipAction::FlipAndReplaceOp(new_op) = action { edit.replace(op_range, new_op); } edit.replace(lhs.range(), rhs.text()); @@ -44,14 +32,56 @@ pub(crate) fn flip_binexpr(mut ctx: AssistCtx) -> Option for FlipAction { + fn from(op_kind: BinOp) -> Self { + match op_kind { + BinOp::Assignment => FlipAction::DontFlip, + BinOp::AddAssign => FlipAction::DontFlip, + BinOp::DivAssign => FlipAction::DontFlip, + BinOp::MulAssign => FlipAction::DontFlip, + BinOp::RemAssign => FlipAction::DontFlip, + BinOp::ShrAssign => FlipAction::DontFlip, + BinOp::ShlAssign => FlipAction::DontFlip, + BinOp::SubAssign => FlipAction::DontFlip, + BinOp::BitOrAssign => FlipAction::DontFlip, + BinOp::BitAndAssign => FlipAction::DontFlip, + BinOp::BitXorAssign => FlipAction::DontFlip, + BinOp::GreaterTest => FlipAction::FlipAndReplaceOp("<"), + BinOp::GreaterEqualTest => FlipAction::FlipAndReplaceOp("<="), + BinOp::LesserTest => FlipAction::FlipAndReplaceOp(">"), + BinOp::LesserEqualTest => FlipAction::FlipAndReplaceOp(">="), + _ => FlipAction::Flip, + } + } +} + #[cfg(test)] mod tests { use super::*; - use crate::helpers::{check_assist, check_assist_target}; + use crate::helpers::{ check_assist, check_assist_target, check_assist_not_applicable }; #[test] - fn flip_eq_operands_for_simple_stmt() { + fn flip_binexpr_target_is_the_op() { + check_assist_target(flip_binexpr, "fn f() { let res = 1 ==<|> 2; }", "==") + } + + #[test] + fn flip_binexpr_not_applicable_for_assignment() { + check_assist_not_applicable(flip_binexpr, "fn f() { let mut _x = 1; _x +=<|> 2 }") + } + + #[test] + fn flip_binexpr_works_for_eq() { check_assist( flip_binexpr, "fn f() { let res = 1 ==<|> 2; }", @@ -60,16 +90,7 @@ mod tests { } #[test] - fn flip_neq_operands_for_simple_stmt() { - check_assist( - flip_binexpr, - "fn f() { let res = 1 !=<|> 2; }", - "fn f() { let res = 2 !=<|> 1; }", - ) - } - - #[test] - fn flip_gt_operands_for_simple_stmt() { + fn flip_binexpr_works_for_gt() { check_assist( flip_binexpr, "fn f() { let res = 1 ><|> 2; }", @@ -78,25 +99,7 @@ mod tests { } #[test] - fn flip_gteq_operands_for_simple_stmt() { - check_assist( - flip_binexpr, - "fn f() { let res = 1 >=<|> 2; }", - "fn f() { let res = 2 <=<|> 1; }", - ) - } - - #[test] - fn flip_lt_operands_for_simple_stmt() { - check_assist( - flip_binexpr, - "fn f() { let res = 1 <<|> 2; }", - "fn f() { let res = 2 ><|> 1; }", - ) - } - - #[test] - fn flip_lteq_operands_for_simple_stmt() { + fn flip_binexpr_works_for_lteq() { check_assist( flip_binexpr, "fn f() { let res = 1 <=<|> 2; }", @@ -105,7 +108,7 @@ mod tests { } #[test] - fn flip_eq_operands_for_complex_stmt() { + fn flip_binexpr_works_for_complex_expr() { check_assist( flip_binexpr, "fn f() { let res = (1 + 1) ==<|> (2 + 2); }", @@ -114,7 +117,7 @@ mod tests { } #[test] - fn flip_eq_operands_in_match_expr() { + fn flip_binexpr_works_inside_match() { check_assist( flip_binexpr, r#" @@ -135,15 +138,4 @@ mod tests { "#, ) } - - #[test] - fn flip_eq_operands_target() { - check_assist_target(flip_binexpr, "fn f() { let res = 1 ==<|> 2; }", "==") - } - - #[test] - fn flip_gt_operands_target() { - check_assist_target(flip_binexpr, "fn f() { let res = 1 ><|> 2; }", ">") - } - } From 8890539e400d20f44b85f056b0b456967f5a1a53 Mon Sep 17 00:00:00 2001 From: Hrvoje Ban Date: Wed, 27 Mar 2019 07:52:59 +0100 Subject: [PATCH 40/45] Use EXE extension for pre-commit hook on Window --- crates/tools/src/lib.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/tools/src/lib.rs b/crates/tools/src/lib.rs index 1e29c63d46..11b52ccb7f 100644 --- a/crates/tools/src/lib.rs +++ b/crates/tools/src/lib.rs @@ -115,7 +115,11 @@ pub fn install_rustfmt() -> Result<()> { } pub fn install_format_hook() -> Result<()> { - let result_path = Path::new("./.git/hooks/pre-commit"); + let result_path = Path::new(if cfg!(windows) { + "./.git/hooks/pre-commit.exe" + } else { + "./.git/hooks/pre-commit" + }); if !result_path.exists() { run("cargo build --package tools --bin pre-commit", ".")?; if cfg!(windows) { From 3419fe297a8d6d718cebc0dbc2d75839886988d1 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 27 Mar 2019 13:38:33 +0300 Subject: [PATCH 41/45] :arrow_up: salsa --- Cargo.lock | 12 ++++++------ crates/ra_db/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 00f2338c9e..4e8af93b1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -944,7 +944,7 @@ dependencies = [ "ra_syntax 0.1.0", "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "salsa 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "salsa 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "test_utils 0.1.0", ] @@ -1335,7 +1335,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "salsa" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "derive-new 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1344,13 +1344,13 @@ dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "salsa-macros 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "salsa-macros 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "salsa-macros" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1969,8 +1969,8 @@ dependencies = [ "checksum rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7540fc8b0c49f096ee9c961cda096467dce8084bec6bdca2fc83895fd9b28cb8" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" -"checksum salsa 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cad0e2348e2f80725b2980914a08a00267136c3ecf720896d3f7f08eef51e08f" -"checksum salsa-macros 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c79744109df21b80aef1367669b0a9e4985bc966e76bf0e9321b222ec0b9fbb" +"checksum salsa 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "486b65955bed2bd0315c76090877e3102c5ba03e3c897c3da68b2b81797cb703" +"checksum salsa-macros 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2902d1ca1b4948a856b78ead01fd997962c4806f0e4ba491c1f385d10225d2d8" "checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml index c9b8f3c8e1..2b0380ddf9 100644 --- a/crates/ra_db/Cargo.toml +++ b/crates/ra_db/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" authors = ["rust-analyzer developers"] [dependencies] -salsa = "0.10.0" +salsa = "0.11.0" relative-path = "0.4.0" rustc-hash = "1.0" parking_lot = "0.7.0" From e8477f326059377a8a7b3489bd7c452d1ea98187 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 27 Mar 2019 16:31:13 +0300 Subject: [PATCH 42/45] update salsa some more --- Cargo.lock | 12 ++++++------ crates/ra_db/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4e8af93b1a..9a5630db6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -944,7 +944,7 @@ dependencies = [ "ra_syntax 0.1.0", "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "salsa 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "salsa 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", "test_utils 0.1.0", ] @@ -1335,7 +1335,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "salsa" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "derive-new 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1344,13 +1344,13 @@ dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "salsa-macros 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "salsa-macros 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "salsa-macros" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1969,8 +1969,8 @@ dependencies = [ "checksum rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7540fc8b0c49f096ee9c961cda096467dce8084bec6bdca2fc83895fd9b28cb8" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" -"checksum salsa 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "486b65955bed2bd0315c76090877e3102c5ba03e3c897c3da68b2b81797cb703" -"checksum salsa-macros 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2902d1ca1b4948a856b78ead01fd997962c4806f0e4ba491c1f385d10225d2d8" +"checksum salsa 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "94f14bbb013866db2fbeceb97ed82cf35f42020edd39d35268f0b11f89399c79" +"checksum salsa-macros 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1ad27cbae189e9739a96e469d37c6a6deafec36f1282d4fdf4681eae67c9dd39" "checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml index 2b0380ddf9..581cd32fdf 100644 --- a/crates/ra_db/Cargo.toml +++ b/crates/ra_db/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" authors = ["rust-analyzer developers"] [dependencies] -salsa = "0.11.0" +salsa = "0.11.1" relative-path = "0.4.0" rustc-hash = "1.0" parking_lot = "0.7.0" From f0fcd02013c7c22c040092d556323faa90409d2b Mon Sep 17 00:00:00 2001 From: memoryruins Date: Thu, 28 Mar 2019 12:15:18 -0400 Subject: [PATCH 43/45] Add extern_crate_self to ra_parser. --- crates/ra_parser/src/grammar.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/ra_parser/src/grammar.rs b/crates/ra_parser/src/grammar.rs index e428faffbf..b2ffeff8c7 100644 --- a/crates/ra_parser/src/grammar.rs +++ b/crates/ra_parser/src/grammar.rs @@ -184,6 +184,10 @@ fn name_ref(p: &mut Parser) { let m = p.start(); p.bump(); m.complete(p, NAME_REF); + } else if p.at(SELF_KW) { + let m = p.start(); + p.bump(); + m.complete(p, SELF_KW); } else { p.err_and_bump("expected identifier"); } From 7a06282d71d41508f30a9b7513eecfba998b9aa9 Mon Sep 17 00:00:00 2001 From: memoryruins Date: Thu, 28 Mar 2019 12:15:44 -0400 Subject: [PATCH 44/45] Add tests to ra_syntax for extern_crate_self --- .../tests/data/parser/ok/0007_extern_crate.rs | 1 + .../tests/data/parser/ok/0007_extern_crate.txt | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.rs b/crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.rs index 3ce3366765..ab81a608cb 100644 --- a/crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.rs +++ b/crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.rs @@ -1,2 +1,3 @@ extern crate foo; extern crate foo as bar; +extern crate self as baz; diff --git a/crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.txt b/crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.txt index 5558d952e5..0176260c14 100644 --- a/crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.txt +++ b/crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.txt @@ -1,4 +1,4 @@ -SOURCE_FILE@[0; 43) +SOURCE_FILE@[0; 69) EXTERN_CRATE_ITEM@[0; 17) EXTERN_KW@[0; 6) WHITESPACE@[6; 7) @@ -23,3 +23,18 @@ SOURCE_FILE@[0; 43) IDENT@[38; 41) "bar" SEMI@[41; 42) WHITESPACE@[42; 43) + EXTERN_CRATE_ITEM@[43; 68) + EXTERN_KW@[43; 49) + WHITESPACE@[49; 50) + CRATE_KW@[50; 55) + WHITESPACE@[55; 56) + SELF_KW@[56; 60) + SELF_KW@[56; 60) + WHITESPACE@[60; 61) + ALIAS@[61; 67) + AS_KW@[61; 63) + WHITESPACE@[63; 64) + NAME@[64; 67) + IDENT@[64; 67) "baz" + SEMI@[67; 68) + WHITESPACE@[68; 69) From 156b1ddf67b09ae2fabfa8ff49732fcb88072720 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Thu, 28 Mar 2019 21:32:17 +0100 Subject: [PATCH 45/45] Fix emacs-lsp runnables support with native json (In that case args is a vector, which string-join doesn't like.) --- editors/emacs/ra-emacs-lsp.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editors/emacs/ra-emacs-lsp.el b/editors/emacs/ra-emacs-lsp.el index 955703edba..84c018b66f 100644 --- a/editors/emacs/ra-emacs-lsp.el +++ b/editors/emacs/ra-emacs-lsp.el @@ -159,7 +159,7 @@ (interactive (list (rust-analyzer--select-runnable))) (-let (((&hash "env" "bin" "args" "label") runnable)) (compilation-start - (string-join (cons bin args) " ") + (string-join (append (list bin) args '()) " ") ;; cargo-process-mode is nice, but try to work without it... (if (functionp 'cargo-process-mode) 'cargo-process-mode nil) (lambda (_) (concat "*" label "*")))