Skip to content

Commit ca119f4

Browse files
committed
Yet another attempt at nice API
1 parent 889851b commit ca119f4

File tree

8 files changed

+312
-77
lines changed

8 files changed

+312
-77
lines changed

crates/ra_hir/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ macro_rules! impl_froms {
2626
}
2727
}
2828

29+
mod semantics;
2930
pub mod db;
3031
pub mod source_analyzer;
3132
pub mod source_binder;
@@ -45,6 +46,7 @@ pub use crate::{
4546
StructField, Trait, Type, TypeAlias, TypeParam, Union, VariantDef,
4647
},
4748
has_source::HasSource,
49+
semantics::Semantics,
4850
source_analyzer::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer},
4951
source_binder::SourceBinder,
5052
};

crates/ra_hir/src/semantics.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
use std::{cell::RefCell, iter::successors};
2+
3+
use ra_db::FileId;
4+
use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken};
5+
use rustc_hash::FxHashMap;
6+
7+
use crate::{
8+
db::HirDatabase, source_binder::ToDef, Function, HirFileId, InFile, MacroDef, PathResolution,
9+
SourceAnalyzer, SourceBinder, StructField, Type,
10+
};
11+
12+
pub struct Semantics<'a, DB> {
13+
pub db: &'a DB,
14+
sb: RefCell<SourceBinder<'a, DB>>,
15+
cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>,
16+
}
17+
18+
impl<DB: HirDatabase> Semantics<'_, DB> {
19+
pub fn new(db: &DB) -> Semantics<DB> {
20+
let sb = RefCell::new(SourceBinder::new(db));
21+
Semantics { db, sb, cache: RefCell::default() }
22+
}
23+
24+
pub fn file_syntax(&self, file_id: FileId) -> ast::SourceFile {
25+
let tree = self.db.parse(file_id).tree();
26+
self.cache(tree.syntax().clone(), file_id.into());
27+
tree
28+
}
29+
30+
pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken {
31+
let parent = token.parent();
32+
let parent = self.find_file(parent);
33+
let sa = self.sb.borrow_mut().analyze(parent.as_ref(), None);
34+
35+
let token = successors(Some(parent.with_value(token)), |token| {
36+
let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?;
37+
let tt = macro_call.token_tree()?;
38+
if !token.value.text_range().is_subrange(&tt.syntax().text_range()) {
39+
return None;
40+
}
41+
let exp = sa.expand(self.db, token.with_value(&macro_call))?;
42+
let token = exp.map_token_down(self.db, token.as_ref())?;
43+
44+
self.cache(find_root(&token.value.parent()), token.file_id);
45+
46+
Some(token)
47+
})
48+
.last()
49+
.unwrap();
50+
51+
token.value
52+
}
53+
54+
pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
55+
let node = self.find_file(node);
56+
node.ancestors_with_macros(self.db).map(|it| it.value)
57+
}
58+
59+
pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> {
60+
self.analyze(expr.syntax().clone()).type_of(self.db, &expr)
61+
}
62+
63+
pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<Type> {
64+
self.analyze(pat.syntax().clone()).type_of_pat(self.db, &pat)
65+
}
66+
67+
pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
68+
self.analyze(call.syntax().clone()).resolve_method_call(call)
69+
}
70+
71+
pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<StructField> {
72+
self.analyze(field.syntax().clone()).resolve_field(field)
73+
}
74+
75+
pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<StructField> {
76+
self.analyze(field.syntax().clone()).resolve_record_field(field)
77+
}
78+
79+
pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> {
80+
let sa = self.analyze(macro_call.syntax().clone());
81+
let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call);
82+
sa.resolve_macro_call(self.db, macro_call)
83+
}
84+
85+
pub fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> {
86+
self.analyze(path.syntax().clone()).resolve_path(self.db, path)
87+
}
88+
89+
// FIXME: use this instead?
90+
// pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option<???>;
91+
92+
pub fn to_def<T: ToDef>(&self, src: T) -> Option<T::Def> {
93+
let src = self.find_file(src.syntax().clone()).with_value(src);
94+
let mut sb = self.sb.borrow_mut();
95+
T::to_def(&mut sb, src)
96+
}
97+
98+
fn analyze(&self, node: SyntaxNode) -> SourceAnalyzer {
99+
let src = self.find_file(node.clone());
100+
self.sb.borrow_mut().analyze(src.as_ref(), None)
101+
}
102+
103+
fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) {
104+
assert!(root_node.parent().is_none());
105+
let mut cache = self.cache.borrow_mut();
106+
let prev = cache.insert(root_node, file_id);
107+
assert!(prev.is_none());
108+
}
109+
fn lookup(&self, root_node: &SyntaxNode) -> HirFileId {
110+
let cache = self.cache.borrow();
111+
cache[root_node]
112+
}
113+
114+
fn find_file(&self, node: SyntaxNode) -> InFile<SyntaxNode> {
115+
let root_node = find_root(&node);
116+
let file_id = self.lookup(&root_node);
117+
InFile::new(file_id, node)
118+
}
119+
}
120+
121+
fn find_root(node: &SyntaxNode) -> SyntaxNode {
122+
node.ancestors().last().unwrap()
123+
}

crates/ra_ide/src/goto_type_definition.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
11
//! FIXME: write short doc here
22
3-
use hir::db::AstDatabase;
43
use ra_ide_db::RootDatabase;
5-
use ra_syntax::{ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset};
4+
use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset};
65

7-
use crate::{
8-
display::ToNav, expand::descend_into_macros, FilePosition, NavigationTarget, RangeInfo,
9-
};
6+
use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo};
107

118
pub(crate) fn goto_type_definition(
129
db: &RootDatabase,
1310
position: FilePosition,
1411
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
15-
let file = db.parse_or_expand(position.file_id.into())?;
16-
let token = pick_best(file.token_at_offset(position.offset))?;
17-
let token = descend_into_macros(db, position.file_id, token);
18-
19-
let node = token
20-
.value
21-
.ancestors()
22-
.find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
23-
24-
let analyzer = hir::SourceAnalyzer::new(db, token.with_value(&node), None);
12+
let sema = hir::Semantics::new(db);
13+
14+
let file: ast::SourceFile = sema.file_syntax(position.file_id);
15+
let token: SyntaxToken = pick_best(file.syntax().token_at_offset(position.offset))?;
16+
let token: SyntaxToken = sema.descend_into_macros(token);
17+
18+
let (ty, node) = sema.ancestors_with_macros(token.parent()).find_map(|node| {
19+
let ty = match_ast! {
20+
match node {
21+
ast::Expr(expr) => { sema.type_of_expr(&expr)? },
22+
ast::Pat(pat) => { sema.type_of_pat(&pat)? },
23+
_ => { return None },
24+
}
25+
};
2526

26-
let ty: hir::Type = ast::Expr::cast(node.clone())
27-
.and_then(|e| analyzer.type_of(db, &e))
28-
.or_else(|| ast::Pat::cast(node.clone()).and_then(|p| analyzer.type_of_pat(db, &p)))?;
27+
Some((ty, node))
28+
})?;
2929

3030
let adt_def = ty.autoderef(db).find_map(|ty| ty.as_adt())?;
3131

crates/ra_ide/src/references.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use ra_syntax::{
2828
use crate::{display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo};
2929

3030
pub(crate) use self::{
31-
classify::{classify_name, classify_name_ref},
31+
classify::{classify_name, classify_name_ref, classify_name_ref2},
3232
rename::rename,
3333
};
3434
pub(crate) use ra_ide_db::defs::NameDefinition;

crates/ra_ide/src/references/classify.rs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Functions that are used to classify an element from its definition or reference.
22
3-
use hir::{InFile, PathResolution, SourceBinder};
3+
use hir::{InFile, PathResolution, Semantics, SourceBinder};
44
use ra_prof::profile;
55
use ra_syntax::{ast, AstNode};
66
use test_utils::tested_by;
@@ -69,3 +69,60 @@ pub(crate) fn classify_name_ref(
6969
};
7070
Some(res)
7171
}
72+
73+
pub(crate) fn classify_name_ref2(
74+
sema: &Semantics<RootDatabase>,
75+
name_ref: &ast::NameRef,
76+
) -> Option<NameDefinition> {
77+
let _p = profile("classify_name_ref");
78+
79+
let parent = name_ref.syntax().parent()?;
80+
81+
if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
82+
tested_by!(goto_def_for_methods);
83+
if let Some(func) = sema.resolve_method_call(&method_call) {
84+
return Some(from_module_def(func.into()));
85+
}
86+
}
87+
88+
if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
89+
tested_by!(goto_def_for_fields);
90+
if let Some(field) = sema.resolve_field(&field_expr) {
91+
return Some(from_struct_field(field));
92+
}
93+
}
94+
95+
if let Some(record_field) = ast::RecordField::cast(parent.clone()) {
96+
tested_by!(goto_def_for_record_fields);
97+
tested_by!(goto_def_for_field_init_shorthand);
98+
if let Some(field_def) = sema.resolve_record_field(&record_field) {
99+
return Some(from_struct_field(field_def));
100+
}
101+
}
102+
103+
if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
104+
tested_by!(goto_def_for_macros);
105+
if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
106+
return Some(NameDefinition::Macro(macro_def));
107+
}
108+
}
109+
110+
let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
111+
let resolved = sema.resolve_path(&path)?;
112+
let res = match resolved {
113+
PathResolution::Def(def) => from_module_def(def),
114+
PathResolution::AssocItem(item) => {
115+
let def = match item {
116+
hir::AssocItem::Function(it) => it.into(),
117+
hir::AssocItem::Const(it) => it.into(),
118+
hir::AssocItem::TypeAlias(it) => it.into(),
119+
};
120+
from_module_def(def)
121+
}
122+
PathResolution::Local(local) => NameDefinition::Local(local),
123+
PathResolution::TypeParam(par) => NameDefinition::TypeParam(par),
124+
PathResolution::Macro(def) => NameDefinition::Macro(def),
125+
PathResolution::SelfType(impl_block) => NameDefinition::SelfType(impl_block),
126+
};
127+
Some(res)
128+
}

crates/ra_ide/src/snapshots/rainbow_highlighting.html

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@
2525
.keyword\.control { color: #F0DFAF; font-weight: bold; }
2626
</style>
2727
<pre><code><span class="keyword">fn</span> <span class="function">main</span>() {
28-
<span class="keyword">let</span> <span class="variable" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span> = <span class="string">"hello"</span>;
29-
<span class="keyword">let</span> <span class="variable" data-binding-hash="4303609361109701698" style="color: hsl(242,75%,88%);">x</span> = <span class="variable" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span>.to_string();
30-
<span class="keyword">let</span> <span class="variable" data-binding-hash="13865792086344377029" style="color: hsl(340,64%,86%);">y</span> = <span class="variable" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span>.to_string();
28+
<span class="keyword">let</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> = <span class="string">"hello"</span>;
29+
<span class="keyword">let</span> <span class="variable" data-binding-hash="2705725358298919760" style="color: hsl(17,51%,74%);">x</span> = <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span>.to_string();
30+
<span class="keyword">let</span> <span class="variable" data-binding-hash="3365759661443752373" style="color: hsl(127,76%,66%);">y</span> = <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span>.to_string();
3131

32-
<span class="keyword">let</span> <span class="variable" data-binding-hash="7011301204224269512" style="color: hsl(198,45%,40%);">x</span> = <span class="string">"other color please!"</span>;
33-
<span class="keyword">let</span> <span class="variable" data-binding-hash="12461245066629867975" style="color: hsl(132,91%,68%);">y</span> = <span class="variable" data-binding-hash="7011301204224269512" style="color: hsl(198,45%,40%);">x</span>.to_string();
32+
<span class="keyword">let</span> <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span> = <span class="string">"other color please!"</span>;
33+
<span class="keyword">let</span> <span class="variable" data-binding-hash="6717528807933952652" style="color: hsl(85,49%,84%);">y</span> = <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span>.to_string();
3434
}
3535

3636
<span class="keyword">fn</span> <span class="function">bar</span>() {
37-
<span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut" data-binding-hash="2217585909179791122" style="color: hsl(280,74%,48%);">hello</span> = <span class="string">"hello"</span>;
37+
<span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> = <span class="string">"hello"</span>;
3838
}</code></pre>

0 commit comments

Comments
 (0)