aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-02-18 17:35:10 +0000
committerAleksey Kladov <[email protected]>2020-02-26 11:55:50 +0000
commitc3a4c4429de83450654795534e64e878a774a088 (patch)
tree12d89798f61b276f8bd640db07276a7d4e92b1c2 /crates/ra_assists
parent04deae3dba7c9b7054f7a1d64e4b93a05aecc132 (diff)
Refactor primary IDE API
This introduces the new type -- Semantics. Semantics maps SyntaxNodes to various semantic info, such as type, name resolution or macro expansions. To do so, Semantics maintains a HashMap which maps every node it saw to the file from which the node originated. This is enough to get all the necessary hir bits just from syntax.
Diffstat (limited to 'crates/ra_assists')
-rw-r--r--crates/ra_assists/src/assist_ctx.rs40
-rw-r--r--crates/ra_assists/src/ast_transform.rs65
-rw-r--r--crates/ra_assists/src/handlers/add_explicit_type.rs5
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs29
-rw-r--r--crates/ra_assists/src/handlers/add_new.rs11
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs42
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs23
-rw-r--r--crates/ra_assists/src/handlers/inline_local_variable.rs3
-rw-r--r--crates/ra_assists/src/lib.rs11
-rw-r--r--crates/ra_assists/src/utils.rs24
10 files changed, 100 insertions, 153 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs
index 5aab5fb8b..c25d2e323 100644
--- a/crates/ra_assists/src/assist_ctx.rs
+++ b/crates/ra_assists/src/assist_ctx.rs
@@ -1,6 +1,6 @@
1//! This module defines `AssistCtx` -- the API surface that is exposed to assists. 1//! This module defines `AssistCtx` -- the API surface that is exposed to assists.
2use hir::{InFile, SourceAnalyzer, SourceBinder}; 2use hir::Semantics;
3use ra_db::{FileRange, SourceDatabase}; 3use ra_db::FileRange;
4use ra_fmt::{leading_indent, reindent}; 4use ra_fmt::{leading_indent, reindent};
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
@@ -74,29 +74,23 @@ pub(crate) type AssistHandler = fn(AssistCtx) -> Option<Assist>;
74/// Note, however, that we don't actually use such two-phase logic at the 74/// Note, however, that we don't actually use such two-phase logic at the
75/// moment, because the LSP API is pretty awkward in this place, and it's much 75/// moment, because the LSP API is pretty awkward in this place, and it's much
76/// easier to just compute the edit eagerly :-) 76/// easier to just compute the edit eagerly :-)
77#[derive(Debug)] 77#[derive(Clone)]
78pub(crate) struct AssistCtx<'a> { 78pub(crate) struct AssistCtx<'a> {
79 pub(crate) sema: &'a Semantics<'a, RootDatabase>,
79 pub(crate) db: &'a RootDatabase, 80 pub(crate) db: &'a RootDatabase,
80 pub(crate) frange: FileRange, 81 pub(crate) frange: FileRange,
81 source_file: SourceFile, 82 source_file: SourceFile,
82 should_compute_edit: bool, 83 should_compute_edit: bool,
83} 84}
84 85
85impl Clone for AssistCtx<'_> {
86 fn clone(&self) -> Self {
87 AssistCtx {
88 db: self.db,
89 frange: self.frange,
90 source_file: self.source_file.clone(),
91 should_compute_edit: self.should_compute_edit,
92 }
93 }
94}
95
96impl<'a> AssistCtx<'a> { 86impl<'a> AssistCtx<'a> {
97 pub fn new(db: &RootDatabase, frange: FileRange, should_compute_edit: bool) -> AssistCtx { 87 pub fn new(
98 let parse = db.parse(frange.file_id); 88 sema: &'a Semantics<'a, RootDatabase>,
99 AssistCtx { db, frange, source_file: parse.tree(), should_compute_edit } 89 frange: FileRange,
90 should_compute_edit: bool,
91 ) -> AssistCtx<'a> {
92 let source_file = sema.parse(frange.file_id);
93 AssistCtx { sema, db: sema.db, frange, source_file, should_compute_edit }
100 } 94 }
101 95
102 pub(crate) fn add_assist( 96 pub(crate) fn add_assist(
@@ -138,18 +132,6 @@ impl<'a> AssistCtx<'a> {
138 pub(crate) fn covering_element(&self) -> SyntaxElement { 132 pub(crate) fn covering_element(&self) -> SyntaxElement {
139 find_covering_element(self.source_file.syntax(), self.frange.range) 133 find_covering_element(self.source_file.syntax(), self.frange.range)
140 } 134 }
141 pub(crate) fn source_binder(&self) -> SourceBinder<'a, RootDatabase> {
142 SourceBinder::new(self.db)
143 }
144 pub(crate) fn source_analyzer(
145 &self,
146 node: &SyntaxNode,
147 offset: Option<TextUnit>,
148 ) -> SourceAnalyzer {
149 let src = InFile::new(self.frange.file_id.into(), node);
150 self.source_binder().analyze(src, offset)
151 }
152
153 pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement { 135 pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement {
154 find_covering_element(self.source_file.syntax(), range) 136 find_covering_element(self.source_file.syntax(), range)
155 } 137 }
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs
index c6d15af5f..7846e9798 100644
--- a/crates/ra_assists/src/ast_transform.rs
+++ b/crates/ra_assists/src/ast_transform.rs
@@ -1,15 +1,12 @@
1//! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined. 1//! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined.
2use rustc_hash::FxHashMap; 2use rustc_hash::FxHashMap;
3 3
4use hir::{InFile, PathResolution}; 4use hir::{PathResolution, SemanticsScope};
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::ast::{self, AstNode}; 6use ra_syntax::ast::{self, AstNode};
7 7
8pub trait AstTransform<'a> { 8pub trait AstTransform<'a> {
9 fn get_substitution( 9 fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode>;
10 &self,
11 node: InFile<&ra_syntax::SyntaxNode>,
12 ) -> Option<ra_syntax::SyntaxNode>;
13 10
14 fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a>; 11 fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a>;
15 fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a> 12 fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a>
@@ -23,10 +20,7 @@ pub trait AstTransform<'a> {
23struct NullTransformer; 20struct NullTransformer;
24 21
25impl<'a> AstTransform<'a> for NullTransformer { 22impl<'a> AstTransform<'a> for NullTransformer {
26 fn get_substitution( 23 fn get_substitution(&self, _node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> {
27 &self,
28 _node: InFile<&ra_syntax::SyntaxNode>,
29 ) -> Option<ra_syntax::SyntaxNode> {
30 None 24 None
31 } 25 }
32 fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { 26 fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> {
@@ -35,14 +29,16 @@ impl<'a> AstTransform<'a> for NullTransformer {
35} 29}
36 30
37pub struct SubstituteTypeParams<'a> { 31pub struct SubstituteTypeParams<'a> {
38 db: &'a RootDatabase, 32 source_scope: &'a SemanticsScope<'a, RootDatabase>,
39 substs: FxHashMap<hir::TypeParam, ast::TypeRef>, 33 substs: FxHashMap<hir::TypeParam, ast::TypeRef>,
40 previous: Box<dyn AstTransform<'a> + 'a>, 34 previous: Box<dyn AstTransform<'a> + 'a>,
41} 35}
42 36
43impl<'a> SubstituteTypeParams<'a> { 37impl<'a> SubstituteTypeParams<'a> {
44 pub fn for_trait_impl( 38 pub fn for_trait_impl(
39 source_scope: &'a SemanticsScope<'a, RootDatabase>,
45 db: &'a RootDatabase, 40 db: &'a RootDatabase,
41 // FIXME: there's implicit invariant that `trait_` and `source_scope` match...
46 trait_: hir::Trait, 42 trait_: hir::Trait,
47 impl_block: ast::ImplBlock, 43 impl_block: ast::ImplBlock,
48 ) -> SubstituteTypeParams<'a> { 44 ) -> SubstituteTypeParams<'a> {
@@ -56,7 +52,7 @@ impl<'a> SubstituteTypeParams<'a> {
56 .zip(substs.into_iter()) 52 .zip(substs.into_iter())
57 .collect(); 53 .collect();
58 return SubstituteTypeParams { 54 return SubstituteTypeParams {
59 db, 55 source_scope,
60 substs: substs_by_param, 56 substs: substs_by_param,
61 previous: Box::new(NullTransformer), 57 previous: Box::new(NullTransformer),
62 }; 58 };
@@ -80,15 +76,15 @@ impl<'a> SubstituteTypeParams<'a> {
80 } 76 }
81 fn get_substitution_inner( 77 fn get_substitution_inner(
82 &self, 78 &self,
83 node: InFile<&ra_syntax::SyntaxNode>, 79 node: &ra_syntax::SyntaxNode,
84 ) -> Option<ra_syntax::SyntaxNode> { 80 ) -> Option<ra_syntax::SyntaxNode> {
85 let type_ref = ast::TypeRef::cast(node.value.clone())?; 81 let type_ref = ast::TypeRef::cast(node.clone())?;
86 let path = match &type_ref { 82 let path = match &type_ref {
87 ast::TypeRef::PathType(path_type) => path_type.path()?, 83 ast::TypeRef::PathType(path_type) => path_type.path()?,
88 _ => return None, 84 _ => return None,
89 }; 85 };
90 let analyzer = hir::SourceAnalyzer::new(self.db, node, None); 86 let path = hir::Path::from_ast(path)?;
91 let resolution = analyzer.resolve_path(self.db, &path)?; 87 let resolution = self.source_scope.resolve_hir_path(&path)?;
92 match resolution { 88 match resolution {
93 hir::PathResolution::TypeParam(tp) => Some(self.substs.get(&tp)?.syntax().clone()), 89 hir::PathResolution::TypeParam(tp) => Some(self.substs.get(&tp)?.syntax().clone()),
94 _ => None, 90 _ => None,
@@ -97,10 +93,7 @@ impl<'a> SubstituteTypeParams<'a> {
97} 93}
98 94
99impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> { 95impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> {
100 fn get_substitution( 96 fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> {
101 &self,
102 node: InFile<&ra_syntax::SyntaxNode>,
103 ) -> Option<ra_syntax::SyntaxNode> {
104 self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node)) 97 self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node))
105 } 98 }
106 fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { 99 fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> {
@@ -109,29 +102,34 @@ impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> {
109} 102}
110 103
111pub struct QualifyPaths<'a> { 104pub struct QualifyPaths<'a> {
105 target_scope: &'a SemanticsScope<'a, RootDatabase>,
106 source_scope: &'a SemanticsScope<'a, RootDatabase>,
112 db: &'a RootDatabase, 107 db: &'a RootDatabase,
113 from: Option<hir::Module>,
114 previous: Box<dyn AstTransform<'a> + 'a>, 108 previous: Box<dyn AstTransform<'a> + 'a>,
115} 109}
116 110
117impl<'a> QualifyPaths<'a> { 111impl<'a> QualifyPaths<'a> {
118 pub fn new(db: &'a RootDatabase, from: Option<hir::Module>) -> Self { 112 pub fn new(
119 Self { db, from, previous: Box::new(NullTransformer) } 113 target_scope: &'a SemanticsScope<'a, RootDatabase>,
114 source_scope: &'a SemanticsScope<'a, RootDatabase>,
115 db: &'a RootDatabase,
116 ) -> Self {
117 Self { target_scope, source_scope, db, previous: Box::new(NullTransformer) }
120 } 118 }
121 119
122 fn get_substitution_inner( 120 fn get_substitution_inner(
123 &self, 121 &self,
124 node: InFile<&ra_syntax::SyntaxNode>, 122 node: &ra_syntax::SyntaxNode,
125 ) -> Option<ra_syntax::SyntaxNode> { 123 ) -> Option<ra_syntax::SyntaxNode> {
126 // FIXME handle value ns? 124 // FIXME handle value ns?
127 let from = self.from?; 125 let from = self.target_scope.module()?;
128 let p = ast::Path::cast(node.value.clone())?; 126 let p = ast::Path::cast(node.clone())?;
129 if p.segment().and_then(|s| s.param_list()).is_some() { 127 if p.segment().and_then(|s| s.param_list()).is_some() {
130 // don't try to qualify `Fn(Foo) -> Bar` paths, they are in prelude anyway 128 // don't try to qualify `Fn(Foo) -> Bar` paths, they are in prelude anyway
131 return None; 129 return None;
132 } 130 }
133 let analyzer = hir::SourceAnalyzer::new(self.db, node, None); 131 let hir_path = hir::Path::from_ast(p.clone());
134 let resolution = analyzer.resolve_path(self.db, &p)?; 132 let resolution = self.source_scope.resolve_hir_path(&hir_path?)?;
135 match resolution { 133 match resolution {
136 PathResolution::Def(def) => { 134 PathResolution::Def(def) => {
137 let found_path = from.find_use_path(self.db, def)?; 135 let found_path = from.find_use_path(self.db, def)?;
@@ -140,7 +138,7 @@ impl<'a> QualifyPaths<'a> {
140 let type_args = p 138 let type_args = p
141 .segment() 139 .segment()
142 .and_then(|s| s.type_arg_list()) 140 .and_then(|s| s.type_arg_list())
143 .map(|arg_list| apply(self, node.with_value(arg_list))); 141 .map(|arg_list| apply(self, arg_list));
144 if let Some(type_args) = type_args { 142 if let Some(type_args) = type_args {
145 let last_segment = path.segment().unwrap(); 143 let last_segment = path.segment().unwrap();
146 path = path.with_segment(last_segment.with_type_args(type_args)) 144 path = path.with_segment(last_segment.with_type_args(type_args))
@@ -157,11 +155,11 @@ impl<'a> QualifyPaths<'a> {
157 } 155 }
158} 156}
159 157
160pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: InFile<N>) -> N { 158pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N {
161 let syntax = node.value.syntax(); 159 let syntax = node.syntax();
162 let result = ra_syntax::algo::replace_descendants(syntax, &|element| match element { 160 let result = ra_syntax::algo::replace_descendants(syntax, &|element| match element {
163 ra_syntax::SyntaxElement::Node(n) => { 161 ra_syntax::SyntaxElement::Node(n) => {
164 let replacement = transformer.get_substitution(node.with_value(&n))?; 162 let replacement = transformer.get_substitution(&n)?;
165 Some(replacement.into()) 163 Some(replacement.into())
166 } 164 }
167 _ => None, 165 _ => None,
@@ -170,10 +168,7 @@ pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: InFile<N>
170} 168}
171 169
172impl<'a> AstTransform<'a> for QualifyPaths<'a> { 170impl<'a> AstTransform<'a> for QualifyPaths<'a> {
173 fn get_substitution( 171 fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> {
174 &self,
175 node: InFile<&ra_syntax::SyntaxNode>,
176 ) -> Option<ra_syntax::SyntaxNode> {
177 self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node)) 172 self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node))
178 } 173 }
179 fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { 174 fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> {
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs
index 2cb9d2f48..a63ef48b1 100644
--- a/crates/ra_assists/src/handlers/add_explicit_type.rs
+++ b/crates/ra_assists/src/handlers/add_explicit_type.rs
@@ -51,14 +51,13 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> {
51 } 51 }
52 } 52 }
53 // Infer type 53 // Infer type
54 let db = ctx.db; 54 let ty = ctx.sema.type_of_expr(&expr)?;
55 let analyzer = ctx.source_analyzer(stmt.syntax(), None);
56 let ty = analyzer.type_of(db, &expr)?;
57 // Assist not applicable if the type is unknown 55 // Assist not applicable if the type is unknown
58 if ty.contains_unknown() { 56 if ty.contains_unknown() {
59 return None; 57 return None;
60 } 58 }
61 59
60 let db = ctx.db;
62 ctx.add_assist( 61 ctx.add_assist(
63 AssistId("add_explicit_type"), 62 AssistId("add_explicit_type"),
64 format!("Insert explicit type '{}'", ty.display(db)), 63 format!("Insert explicit type '{}'", ty.display(db)),
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
index ab21388c8..4005014bd 100644
--- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
@@ -1,4 +1,4 @@
1use hir::{HasSource, InFile}; 1use hir::HasSource;
2use ra_syntax::{ 2use ra_syntax::{
3 ast::{self, edit, make, AstNode, NameOwner}, 3 ast::{self, edit, make, AstNode, NameOwner},
4 SmolStr, 4 SmolStr,
@@ -104,9 +104,7 @@ fn add_missing_impl_members_inner(
104 let impl_node = ctx.find_node_at_offset::<ast::ImplBlock>()?; 104 let impl_node = ctx.find_node_at_offset::<ast::ImplBlock>()?;
105 let impl_item_list = impl_node.item_list()?; 105 let impl_item_list = impl_node.item_list()?;
106 106
107 let analyzer = ctx.source_analyzer(impl_node.syntax(), None); 107 let trait_ = resolve_target_trait(&ctx.sema, &impl_node)?;
108
109 let trait_ = resolve_target_trait(ctx.db, &analyzer, &impl_node)?;
110 108
111 let def_name = |item: &ast::ImplItem| -> Option<SmolStr> { 109 let def_name = |item: &ast::ImplItem| -> Option<SmolStr> {
112 match item { 110 match item {
@@ -117,7 +115,7 @@ fn add_missing_impl_members_inner(
117 .map(|it| it.text().clone()) 115 .map(|it| it.text().clone())
118 }; 116 };
119 117
120 let missing_items = get_missing_impl_items(ctx.db, &analyzer, &impl_node) 118 let missing_items = get_missing_impl_items(&ctx.sema, &impl_node)
121 .iter() 119 .iter()
122 .map(|i| match i { 120 .map(|i| match i {
123 hir::AssocItem::Function(i) => ast::ImplItem::FnDef(i.source(ctx.db).value), 121 hir::AssocItem::Function(i) => ast::ImplItem::FnDef(i.source(ctx.db).value),
@@ -138,23 +136,17 @@ fn add_missing_impl_members_inner(
138 return None; 136 return None;
139 } 137 }
140 138
141 let db = ctx.db; 139 let sema = ctx.sema;
142 let file_id = ctx.frange.file_id;
143 let trait_file_id = trait_.source(db).file_id;
144 140
145 ctx.add_assist(AssistId(assist_id), label, |edit| { 141 ctx.add_assist(AssistId(assist_id), label, |edit| {
146 let n_existing_items = impl_item_list.impl_items().count(); 142 let n_existing_items = impl_item_list.impl_items().count();
147 let module = hir::SourceAnalyzer::new( 143 let source_scope = sema.scope_for_def(trait_);
148 db, 144 let target_scope = sema.scope(impl_item_list.syntax());
149 hir::InFile::new(file_id.into(), impl_node.syntax()), 145 let ast_transform = QualifyPaths::new(&target_scope, &source_scope, sema.db)
150 None, 146 .or(SubstituteTypeParams::for_trait_impl(&source_scope, sema.db, trait_, impl_node));
151 )
152 .module();
153 let ast_transform = QualifyPaths::new(db, module)
154 .or(SubstituteTypeParams::for_trait_impl(db, trait_, impl_node));
155 let items = missing_items 147 let items = missing_items
156 .into_iter() 148 .into_iter()
157 .map(|it| ast_transform::apply(&*ast_transform, InFile::new(trait_file_id, it))) 149 .map(|it| ast_transform::apply(&*ast_transform, it))
158 .map(|it| match it { 150 .map(|it| match it {
159 ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)), 151 ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)),
160 _ => it, 152 _ => it,
@@ -181,9 +173,10 @@ fn add_body(fn_def: ast::FnDef) -> ast::FnDef {
181 173
182#[cfg(test)] 174#[cfg(test)]
183mod tests { 175mod tests {
184 use super::*;
185 use crate::helpers::{check_assist, check_assist_not_applicable}; 176 use crate::helpers::{check_assist, check_assist_not_applicable};
186 177
178 use super::*;
179
187 #[test] 180 #[test]
188 fn test_add_missing_impl_members() { 181 fn test_add_missing_impl_members() {
189 check_assist( 182 check_assist(
diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/add_new.rs
index dd070e8ec..166e907fb 100644
--- a/crates/ra_assists/src/handlers/add_new.rs
+++ b/crates/ra_assists/src/handlers/add_new.rs
@@ -1,5 +1,5 @@
1use format_buf::format; 1use format_buf::format;
2use hir::{Adt, InFile}; 2use hir::Adt;
3use join_to_string::join; 3use join_to_string::join;
4use ra_syntax::{ 4use ra_syntax::{
5 ast::{ 5 ast::{
@@ -133,16 +133,11 @@ fn find_struct_impl(ctx: &AssistCtx, strukt: &ast::StructDef) -> Option<Option<a
133 let module = strukt.syntax().ancestors().find(|node| { 133 let module = strukt.syntax().ancestors().find(|node| {
134 ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) 134 ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind())
135 })?; 135 })?;
136 let mut sb = ctx.source_binder();
137 136
138 let struct_def = { 137 let struct_def = ctx.sema.to_def(strukt)?;
139 let src = InFile { file_id: ctx.frange.file_id.into(), value: strukt.clone() };
140 sb.to_def(src)?
141 };
142 138
143 let block = module.descendants().filter_map(ast::ImplBlock::cast).find_map(|impl_blk| { 139 let block = module.descendants().filter_map(ast::ImplBlock::cast).find_map(|impl_blk| {
144 let src = InFile { file_id: ctx.frange.file_id.into(), value: impl_blk.clone() }; 140 let blk = ctx.sema.to_def(&impl_blk)?;
145 let blk = sb.to_def(src)?;
146 141
147 // FIXME: handle e.g. `struct S<T>; impl<U> S<U> {}` 142 // FIXME: handle e.g. `struct S<T>; impl<U> S<U> {}`
148 // (we currently use the wrong type parameter) 143 // (we currently use the wrong type parameter)
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index c4aea2a06..edf0cf6d0 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -3,8 +3,8 @@ use crate::{
3 insert_use_statement, AssistId, 3 insert_use_statement, AssistId,
4}; 4};
5use hir::{ 5use hir::{
6 db::HirDatabase, AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, 6 AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait,
7 SourceAnalyzer, Trait, Type, 7 Type,
8}; 8};
9use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; 9use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase};
10use ra_prof::profile; 10use ra_prof::profile;
@@ -78,14 +78,9 @@ impl AutoImportAssets {
78 78
79 fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistCtx) -> Option<Self> { 79 fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistCtx) -> Option<Self> {
80 let syntax_under_caret = method_call.syntax().to_owned(); 80 let syntax_under_caret = method_call.syntax().to_owned();
81 let source_analyzer = ctx.source_analyzer(&syntax_under_caret, None); 81 let module_with_name_to_import = ctx.sema.scope(&syntax_under_caret).module()?;
82 let module_with_name_to_import = source_analyzer.module()?;
83 Some(Self { 82 Some(Self {
84 import_candidate: ImportCandidate::for_method_call( 83 import_candidate: ImportCandidate::for_method_call(&ctx.sema, &method_call)?,
85 &method_call,
86 &source_analyzer,
87 ctx.db,
88 )?,
89 module_with_name_to_import, 84 module_with_name_to_import,
90 syntax_under_caret, 85 syntax_under_caret,
91 }) 86 })
@@ -97,14 +92,9 @@ impl AutoImportAssets {
97 return None; 92 return None;
98 } 93 }
99 94
100 let source_analyzer = ctx.source_analyzer(&syntax_under_caret, None); 95 let module_with_name_to_import = ctx.sema.scope(&syntax_under_caret).module()?;
101 let module_with_name_to_import = source_analyzer.module()?;
102 Some(Self { 96 Some(Self {
103 import_candidate: ImportCandidate::for_regular_path( 97 import_candidate: ImportCandidate::for_regular_path(&ctx.sema, &path_under_caret)?,
104 &path_under_caret,
105 &source_analyzer,
106 ctx.db,
107 )?,
108 module_with_name_to_import, 98 module_with_name_to_import,
109 syntax_under_caret, 99 syntax_under_caret,
110 }) 100 })
@@ -229,25 +219,23 @@ enum ImportCandidate {
229 219
230impl ImportCandidate { 220impl ImportCandidate {
231 fn for_method_call( 221 fn for_method_call(
222 sema: &Semantics<RootDatabase>,
232 method_call: &ast::MethodCallExpr, 223 method_call: &ast::MethodCallExpr,
233 source_analyzer: &SourceAnalyzer,
234 db: &impl HirDatabase,
235 ) -> Option<Self> { 224 ) -> Option<Self> {
236 if source_analyzer.resolve_method_call(method_call).is_some() { 225 if sema.resolve_method_call(method_call).is_some() {
237 return None; 226 return None;
238 } 227 }
239 Some(Self::TraitMethod( 228 Some(Self::TraitMethod(
240 source_analyzer.type_of(db, &method_call.expr()?)?, 229 sema.type_of_expr(&method_call.expr()?)?,
241 method_call.name_ref()?.syntax().to_string(), 230 method_call.name_ref()?.syntax().to_string(),
242 )) 231 ))
243 } 232 }
244 233
245 fn for_regular_path( 234 fn for_regular_path(
235 sema: &Semantics<RootDatabase>,
246 path_under_caret: &ast::Path, 236 path_under_caret: &ast::Path,
247 source_analyzer: &SourceAnalyzer,
248 db: &impl HirDatabase,
249 ) -> Option<Self> { 237 ) -> Option<Self> {
250 if source_analyzer.resolve_path(db, path_under_caret).is_some() { 238 if sema.resolve_path(path_under_caret).is_some() {
251 return None; 239 return None;
252 } 240 }
253 241
@@ -256,17 +244,15 @@ impl ImportCandidate {
256 let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; 244 let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?;
257 let qualifier_start_path = 245 let qualifier_start_path =
258 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; 246 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
259 if let Some(qualifier_start_resolution) = 247 if let Some(qualifier_start_resolution) = sema.resolve_path(&qualifier_start_path) {
260 source_analyzer.resolve_path(db, &qualifier_start_path)
261 {
262 let qualifier_resolution = if qualifier_start_path == qualifier { 248 let qualifier_resolution = if qualifier_start_path == qualifier {
263 qualifier_start_resolution 249 qualifier_start_resolution
264 } else { 250 } else {
265 source_analyzer.resolve_path(db, &qualifier)? 251 sema.resolve_path(&qualifier)?
266 }; 252 };
267 if let PathResolution::Def(ModuleDef::Adt(assoc_item_path)) = qualifier_resolution { 253 if let PathResolution::Def(ModuleDef::Adt(assoc_item_path)) = qualifier_resolution {
268 Some(ImportCandidate::TraitAssocItem( 254 Some(ImportCandidate::TraitAssocItem(
269 assoc_item_path.ty(db), 255 assoc_item_path.ty(sema.db),
270 segment.syntax().to_string(), 256 segment.syntax().to_string(),
271 )) 257 ))
272 } else { 258 } else {
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index ae2437ed3..e5d8c639d 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -2,10 +2,11 @@
2 2
3use std::iter; 3use std::iter;
4 4
5use hir::{db::HirDatabase, Adt, HasSource}; 5use hir::{db::HirDatabase, Adt, HasSource, Semantics};
6use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner}; 6use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner};
7 7
8use crate::{Assist, AssistCtx, AssistId}; 8use crate::{Assist, AssistCtx, AssistId};
9use ra_ide_db::RootDatabase;
9 10
10// Assist: fill_match_arms 11// Assist: fill_match_arms
11// 12//
@@ -46,10 +47,9 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
46 }; 47 };
47 48
48 let expr = match_expr.expr()?; 49 let expr = match_expr.expr()?;
49 let (enum_def, module) = { 50 let enum_def = resolve_enum_def(&ctx.sema, &expr)?;
50 let analyzer = ctx.source_analyzer(expr.syntax(), None); 51 let module = ctx.sema.scope(expr.syntax()).module()?;
51 (resolve_enum_def(ctx.db, &analyzer, &expr)?, analyzer.module()?) 52
52 };
53 let variants = enum_def.variants(ctx.db); 53 let variants = enum_def.variants(ctx.db);
54 if variants.is_empty() { 54 if variants.is_empty() {
55 return None; 55 return None;
@@ -81,18 +81,11 @@ fn is_trivial(arm: &ast::MatchArm) -> bool {
81 } 81 }
82} 82}
83 83
84fn resolve_enum_def( 84fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> {
85 db: &impl HirDatabase, 85 sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
86 analyzer: &hir::SourceAnalyzer,
87 expr: &ast::Expr,
88) -> Option<hir::Enum> {
89 let expr_ty = analyzer.type_of(db, &expr)?;
90
91 let result = expr_ty.autoderef(db).find_map(|ty| match ty.as_adt() {
92 Some(Adt::Enum(e)) => Some(e), 86 Some(Adt::Enum(e)) => Some(e),
93 _ => None, 87 _ => None,
94 }); 88 })
95 result
96} 89}
97 90
98fn build_pat( 91fn build_pat(
diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs
index 91b588243..53a72309b 100644
--- a/crates/ra_assists/src/handlers/inline_local_variable.rs
+++ b/crates/ra_assists/src/handlers/inline_local_variable.rs
@@ -44,8 +44,7 @@ pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option<Assist> {
44 } else { 44 } else {
45 let_stmt.syntax().text_range() 45 let_stmt.syntax().text_range()
46 }; 46 };
47 let analyzer = ctx.source_analyzer(bind_pat.syntax(), None); 47 let refs = ctx.sema.find_all_refs(&bind_pat);
48 let refs = analyzer.find_all_refs(&bind_pat);
49 if refs.is_empty() { 48 if refs.is_empty() {
50 return None; 49 return None;
51 }; 50 };
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 79fe43aa4..c28a9b92b 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -19,6 +19,7 @@ use ra_text_edit::TextEdit;
19 19
20pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler}; 20pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler};
21pub use crate::handlers::replace_qualified_name_with_use::insert_use_statement; 21pub use crate::handlers::replace_qualified_name_with_use::insert_use_statement;
22use hir::Semantics;
22 23
23/// Unique identifier of the assist, should not be shown to the user 24/// Unique identifier of the assist, should not be shown to the user
24/// directly. 25/// directly.
@@ -63,7 +64,8 @@ pub struct ResolvedAssist {
63/// Assists are returned in the "unresolved" state, that is only labels are 64/// Assists are returned in the "unresolved" state, that is only labels are
64/// returned, without actual edits. 65/// returned, without actual edits.
65pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> { 66pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> {
66 let ctx = AssistCtx::new(db, range, false); 67 let sema = Semantics::new(db);
68 let ctx = AssistCtx::new(&sema, range, false);
67 handlers::all() 69 handlers::all()
68 .iter() 70 .iter()
69 .filter_map(|f| f(ctx.clone())) 71 .filter_map(|f| f(ctx.clone()))
@@ -77,7 +79,8 @@ pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabe
77/// Assists are returned in the "resolved" state, that is with edit fully 79/// Assists are returned in the "resolved" state, that is with edit fully
78/// computed. 80/// computed.
79pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> { 81pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> {
80 let ctx = AssistCtx::new(db, range, true); 82 let sema = Semantics::new(db);
83 let ctx = AssistCtx::new(&sema, range, true);
81 let mut a = handlers::all() 84 let mut a = handlers::all()
82 .iter() 85 .iter()
83 .filter_map(|f| f(ctx.clone())) 86 .filter_map(|f| f(ctx.clone()))
@@ -165,6 +168,7 @@ mod helpers {
165 use test_utils::{add_cursor, assert_eq_text, extract_range_or_offset, RangeOrOffset}; 168 use test_utils::{add_cursor, assert_eq_text, extract_range_or_offset, RangeOrOffset};
166 169
167 use crate::{AssistCtx, AssistHandler}; 170 use crate::{AssistCtx, AssistHandler};
171 use hir::Semantics;
168 172
169 pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { 173 pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
170 let (mut db, file_id) = RootDatabase::with_single_file(text); 174 let (mut db, file_id) = RootDatabase::with_single_file(text);
@@ -202,7 +206,8 @@ mod helpers {
202 206
203 let (db, file_id) = with_single_file(&before); 207 let (db, file_id) = with_single_file(&before);
204 let frange = FileRange { file_id, range }; 208 let frange = FileRange { file_id, range };
205 let assist_ctx = AssistCtx::new(&db, frange, true); 209 let sema = Semantics::new(&db);
210 let assist_ctx = AssistCtx::new(&sema, frange, true);
206 211
207 match (assist(assist_ctx), expected) { 212 match (assist(assist_ctx), expected) {
208 (Some(assist), ExpectedResult::After(after)) => { 213 (Some(assist), ExpectedResult::After(after)) => {
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs
index 6ff44c95c..92d3ed471 100644
--- a/crates/ra_assists/src/utils.rs
+++ b/crates/ra_assists/src/utils.rs
@@ -1,16 +1,15 @@
1//! Assorted functions shared by several assists. 1//! Assorted functions shared by several assists.
2 2
3use hir::Semantics;
4use ra_ide_db::RootDatabase;
3use ra_syntax::{ 5use ra_syntax::{
4 ast::{self, make, NameOwner}, 6 ast::{self, make, NameOwner},
5 AstNode, T, 7 AstNode, T,
6}; 8};
7
8use hir::db::HirDatabase;
9use rustc_hash::FxHashSet; 9use rustc_hash::FxHashSet;
10 10
11pub fn get_missing_impl_items( 11pub fn get_missing_impl_items(
12 db: &impl HirDatabase, 12 sema: &Semantics<RootDatabase>,
13 analyzer: &hir::SourceAnalyzer,
14 impl_block: &ast::ImplBlock, 13 impl_block: &ast::ImplBlock,
15) -> Vec<hir::AssocItem> { 14) -> Vec<hir::AssocItem> {
16 // Names must be unique between constants and functions. However, type aliases 15 // Names must be unique between constants and functions. However, type aliases
@@ -42,15 +41,17 @@ pub fn get_missing_impl_items(
42 } 41 }
43 } 42 }
44 43
45 resolve_target_trait(db, analyzer, impl_block).map_or(vec![], |target_trait| { 44 resolve_target_trait(sema, impl_block).map_or(vec![], |target_trait| {
46 target_trait 45 target_trait
47 .items(db) 46 .items(sema.db)
48 .iter() 47 .iter()
49 .filter(|i| match i { 48 .filter(|i| match i {
50 hir::AssocItem::Function(f) => !impl_fns_consts.contains(&f.name(db).to_string()), 49 hir::AssocItem::Function(f) => {
51 hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(db).to_string()), 50 !impl_fns_consts.contains(&f.name(sema.db).to_string())
51 }
52 hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(sema.db).to_string()),
52 hir::AssocItem::Const(c) => c 53 hir::AssocItem::Const(c) => c
53 .name(db) 54 .name(sema.db)
54 .map(|n| !impl_fns_consts.contains(&n.to_string())) 55 .map(|n| !impl_fns_consts.contains(&n.to_string()))
55 .unwrap_or_default(), 56 .unwrap_or_default(),
56 }) 57 })
@@ -60,8 +61,7 @@ pub fn get_missing_impl_items(
60} 61}
61 62
62pub(crate) fn resolve_target_trait( 63pub(crate) fn resolve_target_trait(
63 db: &impl HirDatabase, 64 sema: &Semantics<RootDatabase>,
64 analyzer: &hir::SourceAnalyzer,
65 impl_block: &ast::ImplBlock, 65 impl_block: &ast::ImplBlock,
66) -> Option<hir::Trait> { 66) -> Option<hir::Trait> {
67 let ast_path = impl_block 67 let ast_path = impl_block
@@ -70,7 +70,7 @@ pub(crate) fn resolve_target_trait(
70 .and_then(ast::PathType::cast)? 70 .and_then(ast::PathType::cast)?
71 .path()?; 71 .path()?;
72 72
73 match analyzer.resolve_path(db, &ast_path) { 73 match sema.resolve_path(&ast_path) {
74 Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def), 74 Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def),
75 _ => None, 75 _ => None,
76 } 76 }