Skip to content

Commit 957b2f3

Browse files
committed
Yet another attempt at nice API
1 parent d3040c0 commit 957b2f3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1016
-952
lines changed

crates/ra_assists/src/assist_ctx.rs

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! This module defines `AssistCtx` -- the API surface that is exposed to assists.
2-
use hir::{InFile, SourceAnalyzer, SourceBinder};
3-
use ra_db::{FileRange, SourceDatabase};
2+
use hir::Semantics;
3+
use ra_db::FileRange;
44
use ra_fmt::{leading_indent, reindent};
55
use ra_ide_db::RootDatabase;
66
use ra_syntax::{
@@ -11,6 +11,7 @@ use ra_syntax::{
1111
use ra_text_edit::TextEditBuilder;
1212

1313
use crate::{AssistAction, AssistId, AssistLabel, GroupLabel, ResolvedAssist};
14+
use std::rc::Rc;
1415

1516
#[derive(Clone, Debug)]
1617
pub(crate) struct Assist(pub(crate) Vec<AssistInfo>);
@@ -74,29 +75,22 @@ pub(crate) type AssistHandler = fn(AssistCtx) -> Option<Assist>;
7475
/// Note, however, that we don't actually use such two-phase logic at the
7576
/// moment, because the LSP API is pretty awkward in this place, and it's much
7677
/// easier to just compute the edit eagerly :-)
77-
#[derive(Debug)]
78+
#[derive(Clone)]
7879
pub(crate) struct AssistCtx<'a> {
80+
// FIXME: Rc does not make much sense here
81+
pub(crate) sema: Rc<Semantics<'a, RootDatabase>>,
7982
pub(crate) db: &'a RootDatabase,
8083
pub(crate) frange: FileRange,
8184
source_file: SourceFile,
8285
should_compute_edit: bool,
8386
}
8487

85-
impl 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-
9688
impl<'a> AssistCtx<'a> {
9789
pub fn new(db: &RootDatabase, frange: FileRange, should_compute_edit: bool) -> AssistCtx {
98-
let parse = db.parse(frange.file_id);
99-
AssistCtx { db, frange, source_file: parse.tree(), should_compute_edit }
90+
let sema = Semantics::new(db);
91+
let sema = Rc::new(sema);
92+
let source_file = sema.parse(frange.file_id);
93+
AssistCtx { sema, db, frange, source_file, should_compute_edit }
10094
}
10195

10296
pub(crate) fn add_assist(
@@ -138,18 +132,6 @@ impl<'a> AssistCtx<'a> {
138132
pub(crate) fn covering_element(&self) -> SyntaxElement {
139133
find_covering_element(self.source_file.syntax(), self.frange.range)
140134
}
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-
153135
pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement {
154136
find_covering_element(self.source_file.syntax(), range)
155137
}

crates/ra_assists/src/ast_transform.rs

Lines changed: 30 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
//! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined.
22
use rustc_hash::FxHashMap;
33

4-
use hir::{InFile, PathResolution};
4+
use hir::{PathResolution, SemanticsScope};
55
use ra_ide_db::RootDatabase;
66
use ra_syntax::ast::{self, AstNode};
77

88
pub trait AstTransform<'a> {
9-
fn get_substitution(
10-
&self,
11-
node: InFile<&ra_syntax::SyntaxNode>,
12-
) -> Option<ra_syntax::SyntaxNode>;
9+
fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode>;
1310

1411
fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a>;
1512
fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a>
@@ -23,10 +20,7 @@ pub trait AstTransform<'a> {
2320
struct NullTransformer;
2421

2522
impl<'a> AstTransform<'a> for NullTransformer {
26-
fn get_substitution(
27-
&self,
28-
_node: InFile<&ra_syntax::SyntaxNode>,
29-
) -> Option<ra_syntax::SyntaxNode> {
23+
fn get_substitution(&self, _node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> {
3024
None
3125
}
3226
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 {
3529
}
3630

3731
pub struct SubstituteTypeParams<'a> {
38-
db: &'a RootDatabase,
32+
source_scope: &'a SemanticsScope<'a, RootDatabase>,
3933
substs: FxHashMap<hir::TypeParam, ast::TypeRef>,
4034
previous: Box<dyn AstTransform<'a> + 'a>,
4135
}
4236

4337
impl<'a> SubstituteTypeParams<'a> {
4438
pub fn for_trait_impl(
39+
source_scope: &'a SemanticsScope<'a, RootDatabase>,
4540
db: &'a RootDatabase,
41+
// FIXME: there's implicit invariant that `trait_` and `source_scope` match...
4642
trait_: hir::Trait,
4743
impl_block: ast::ImplBlock,
4844
) -> SubstituteTypeParams<'a> {
@@ -56,7 +52,7 @@ impl<'a> SubstituteTypeParams<'a> {
5652
.zip(substs.into_iter())
5753
.collect();
5854
return SubstituteTypeParams {
59-
db,
55+
source_scope,
6056
substs: substs_by_param,
6157
previous: Box::new(NullTransformer),
6258
};
@@ -80,15 +76,15 @@ impl<'a> SubstituteTypeParams<'a> {
8076
}
8177
fn get_substitution_inner(
8278
&self,
83-
node: InFile<&ra_syntax::SyntaxNode>,
79+
node: &ra_syntax::SyntaxNode,
8480
) -> Option<ra_syntax::SyntaxNode> {
85-
let type_ref = ast::TypeRef::cast(node.value.clone())?;
81+
let type_ref = ast::TypeRef::cast(node.clone())?;
8682
let path = match &type_ref {
8783
ast::TypeRef::PathType(path_type) => path_type.path()?,
8884
_ => return None,
8985
};
90-
let analyzer = hir::SourceAnalyzer::new(self.db, node, None);
91-
let resolution = analyzer.resolve_path(self.db, &path)?;
86+
let path = hir::Path::from_ast(path)?;
87+
let resolution = self.source_scope.resolve_hir_path(&path)?;
9288
match resolution {
9389
hir::PathResolution::TypeParam(tp) => Some(self.substs.get(&tp)?.syntax().clone()),
9490
_ => None,
@@ -97,10 +93,7 @@ impl<'a> SubstituteTypeParams<'a> {
9793
}
9894

9995
impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> {
100-
fn get_substitution(
101-
&self,
102-
node: InFile<&ra_syntax::SyntaxNode>,
103-
) -> Option<ra_syntax::SyntaxNode> {
96+
fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> {
10497
self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node))
10598
}
10699
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> {
109102
}
110103

111104
pub struct QualifyPaths<'a> {
105+
target_scope: &'a SemanticsScope<'a, RootDatabase>,
106+
source_scope: &'a SemanticsScope<'a, RootDatabase>,
112107
db: &'a RootDatabase,
113-
from: Option<hir::Module>,
114108
previous: Box<dyn AstTransform<'a> + 'a>,
115109
}
116110

117111
impl<'a> QualifyPaths<'a> {
118-
pub fn new(db: &'a RootDatabase, from: Option<hir::Module>) -> Self {
119-
Self { db, from, previous: Box::new(NullTransformer) }
112+
pub fn new(
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) }
120118
}
121119

122120
fn get_substitution_inner(
123121
&self,
124-
node: InFile<&ra_syntax::SyntaxNode>,
122+
node: &ra_syntax::SyntaxNode,
125123
) -> Option<ra_syntax::SyntaxNode> {
126124
// FIXME handle value ns?
127-
let from = self.from?;
128-
let p = ast::Path::cast(node.value.clone())?;
125+
let from = self.target_scope.module()?;
126+
let p = ast::Path::cast(node.clone())?;
129127
if p.segment().and_then(|s| s.param_list()).is_some() {
130128
// don't try to qualify `Fn(Foo) -> Bar` paths, they are in prelude anyway
131129
return None;
132130
}
133-
let analyzer = hir::SourceAnalyzer::new(self.db, node, None);
134-
let resolution = analyzer.resolve_path(self.db, &p)?;
131+
let hir_path = hir::Path::from_ast(p.clone());
132+
let resolution = self.source_scope.resolve_hir_path(&hir_path?)?;
135133
match resolution {
136134
PathResolution::Def(def) => {
137135
let found_path = from.find_use_path(self.db, def)?;
@@ -140,7 +138,7 @@ impl<'a> QualifyPaths<'a> {
140138
let type_args = p
141139
.segment()
142140
.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));
144142
if let Some(type_args) = type_args {
145143
let last_segment = path.segment().unwrap();
146144
path = path.with_segment(last_segment.with_type_args(type_args))
@@ -157,11 +155,11 @@ impl<'a> QualifyPaths<'a> {
157155
}
158156
}
159157

160-
pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: InFile<N>) -> N {
161-
let syntax = node.value.syntax();
158+
pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N {
159+
let syntax = node.syntax();
162160
let result = ra_syntax::algo::replace_descendants(syntax, &|element| match element {
163161
ra_syntax::SyntaxElement::Node(n) => {
164-
let replacement = transformer.get_substitution(node.with_value(&n))?;
162+
let replacement = transformer.get_substitution(&n)?;
165163
Some(replacement.into())
166164
}
167165
_ => None,
@@ -170,10 +168,7 @@ pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: InFile<N>
170168
}
171169

172170
impl<'a> AstTransform<'a> for QualifyPaths<'a> {
173-
fn get_substitution(
174-
&self,
175-
node: InFile<&ra_syntax::SyntaxNode>,
176-
) -> Option<ra_syntax::SyntaxNode> {
171+
fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode> {
177172
self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node))
178173
}
179174
fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> {

crates/ra_assists/src/handlers/add_explicit_type.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,13 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> {
5151
}
5252
}
5353
// Infer type
54-
let db = ctx.db;
55-
let analyzer = ctx.source_analyzer(stmt.syntax(), None);
56-
let ty = analyzer.type_of(db, &expr)?;
54+
let ty = ctx.sema.type_of_expr(&expr)?;
5755
// Assist not applicable if the type is unknown
5856
if ty.contains_unknown() {
5957
return None;
6058
}
6159

60+
let db = ctx.db;
6261
ctx.add_assist(
6362
AssistId("add_explicit_type"),
6463
format!("Insert explicit type '{}'", ty.display(db)),

crates/ra_assists/src/handlers/add_missing_impl_members.rs

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use hir::{HasSource, InFile};
1+
use hir::HasSource;
22
use ra_syntax::{
33
ast::{self, edit, make, AstNode, NameOwner},
44
SmolStr,
@@ -104,9 +104,7 @@ fn add_missing_impl_members_inner(
104104
let impl_node = ctx.find_node_at_offset::<ast::ImplBlock>()?;
105105
let impl_item_list = impl_node.item_list()?;
106106

107-
let analyzer = ctx.source_analyzer(impl_node.syntax(), None);
108-
109-
let trait_ = resolve_target_trait(ctx.db, &analyzer, &impl_node)?;
107+
let trait_ = resolve_target_trait(&ctx.sema, &impl_node)?;
110108

111109
let def_name = |item: &ast::ImplItem| -> Option<SmolStr> {
112110
match item {
@@ -117,7 +115,7 @@ fn add_missing_impl_members_inner(
117115
.map(|it| it.text().clone())
118116
};
119117

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)
121119
.iter()
122120
.map(|i| match i {
123121
hir::AssocItem::Function(i) => ast::ImplItem::FnDef(i.source(ctx.db).value),
@@ -138,23 +136,18 @@ fn add_missing_impl_members_inner(
138136
return None;
139137
}
140138

139+
let sema = ctx.sema.clone();
141140
let db = ctx.db;
142-
let file_id = ctx.frange.file_id;
143-
let trait_file_id = trait_.source(db).file_id;
144141

145142
ctx.add_assist(AssistId(assist_id), label, |edit| {
146143
let n_existing_items = impl_item_list.impl_items().count();
147-
let module = hir::SourceAnalyzer::new(
148-
db,
149-
hir::InFile::new(file_id.into(), impl_node.syntax()),
150-
None,
151-
)
152-
.module();
153-
let ast_transform = QualifyPaths::new(db, module)
154-
.or(SubstituteTypeParams::for_trait_impl(db, trait_, impl_node));
144+
let source_scope = sema.scope_for_def(trait_);
145+
let target_scope = sema.scope(impl_item_list.syntax());
146+
let ast_transform = QualifyPaths::new(&target_scope, &source_scope, db)
147+
.or(SubstituteTypeParams::for_trait_impl(&source_scope, db, trait_, impl_node));
155148
let items = missing_items
156149
.into_iter()
157-
.map(|it| ast_transform::apply(&*ast_transform, InFile::new(trait_file_id, it)))
150+
.map(|it| ast_transform::apply(&*ast_transform, it))
158151
.map(|it| match it {
159152
ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)),
160153
_ => it,
@@ -181,9 +174,10 @@ fn add_body(fn_def: ast::FnDef) -> ast::FnDef {
181174

182175
#[cfg(test)]
183176
mod tests {
184-
use super::*;
185177
use crate::helpers::{check_assist, check_assist_not_applicable};
186178

179+
use super::*;
180+
187181
#[test]
188182
fn test_add_missing_impl_members() {
189183
check_assist(

crates/ra_assists/src/handlers/add_new.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use format_buf::format;
2-
use hir::{Adt, InFile};
2+
use hir::Adt;
33
use join_to_string::join;
44
use ra_syntax::{
55
ast::{
@@ -133,16 +133,11 @@ fn find_struct_impl(ctx: &AssistCtx, strukt: &ast::StructDef) -> Option<Option<a
133133
let module = strukt.syntax().ancestors().find(|node| {
134134
ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind())
135135
})?;
136-
let mut sb = ctx.source_binder();
137136

138-
let struct_def = {
139-
let src = InFile { file_id: ctx.frange.file_id.into(), value: strukt.clone() };
140-
sb.to_def(src)?
141-
};
137+
let struct_def = ctx.sema.to_def(strukt)?;
142138

143139
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() };
145-
let blk = sb.to_def(src)?;
140+
let blk = ctx.sema.to_def(&impl_blk)?;
146141

147142
// FIXME: handle e.g. `struct S<T>; impl<U> S<U> {}`
148143
// (we currently use the wrong type parameter)

0 commit comments

Comments
 (0)