diff options
author | Aleksey Kladov <[email protected]> | 2020-02-18 17:35:10 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-02-26 11:55:50 +0000 |
commit | c3a4c4429de83450654795534e64e878a774a088 (patch) | |
tree | 12d89798f61b276f8bd640db07276a7d4e92b1c2 /crates/ra_assists | |
parent | 04deae3dba7c9b7054f7a1d64e4b93a05aecc132 (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.rs | 40 | ||||
-rw-r--r-- | crates/ra_assists/src/ast_transform.rs | 65 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/add_explicit_type.rs | 5 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/add_missing_impl_members.rs | 29 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/add_new.rs | 11 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/auto_import.rs | 42 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/fill_match_arms.rs | 23 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/inline_local_variable.rs | 3 | ||||
-rw-r--r-- | crates/ra_assists/src/lib.rs | 11 | ||||
-rw-r--r-- | crates/ra_assists/src/utils.rs | 24 |
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. |
2 | use hir::{InFile, SourceAnalyzer, SourceBinder}; | 2 | use hir::Semantics; |
3 | use ra_db::{FileRange, SourceDatabase}; | 3 | use ra_db::FileRange; |
4 | use ra_fmt::{leading_indent, reindent}; | 4 | use ra_fmt::{leading_indent, reindent}; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use 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)] |
78 | pub(crate) struct AssistCtx<'a> { | 78 | pub(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 | ||
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 | |||
96 | impl<'a> AssistCtx<'a> { | 86 | impl<'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. |
2 | use rustc_hash::FxHashMap; | 2 | use rustc_hash::FxHashMap; |
3 | 3 | ||
4 | use hir::{InFile, PathResolution}; | 4 | use hir::{PathResolution, SemanticsScope}; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::ast::{self, AstNode}; | 6 | use ra_syntax::ast::{self, AstNode}; |
7 | 7 | ||
8 | pub trait AstTransform<'a> { | 8 | pub 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> { | |||
23 | struct NullTransformer; | 20 | struct NullTransformer; |
24 | 21 | ||
25 | impl<'a> AstTransform<'a> for NullTransformer { | 22 | impl<'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 | ||
37 | pub struct SubstituteTypeParams<'a> { | 31 | pub 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 | ||
43 | impl<'a> SubstituteTypeParams<'a> { | 37 | impl<'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 | ||
99 | impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> { | 95 | impl<'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 | ||
111 | pub struct QualifyPaths<'a> { | 104 | pub 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 | ||
117 | impl<'a> QualifyPaths<'a> { | 111 | impl<'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 | ||
160 | pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: InFile<N>) -> N { | 158 | pub 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 | ||
172 | impl<'a> AstTransform<'a> for QualifyPaths<'a> { | 170 | impl<'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 @@ | |||
1 | use hir::{HasSource, InFile}; | 1 | use hir::HasSource; |
2 | use ra_syntax::{ | 2 | use 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)] |
183 | mod tests { | 175 | mod 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 @@ | |||
1 | use format_buf::format; | 1 | use format_buf::format; |
2 | use hir::{Adt, InFile}; | 2 | use hir::Adt; |
3 | use join_to_string::join; | 3 | use join_to_string::join; |
4 | use ra_syntax::{ | 4 | use 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 | }; |
5 | use hir::{ | 5 | use 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 | }; |
9 | use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; | 9 | use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; |
10 | use ra_prof::profile; | 10 | use 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 | ||
230 | impl ImportCandidate { | 220 | impl 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 | ||
3 | use std::iter; | 3 | use std::iter; |
4 | 4 | ||
5 | use hir::{db::HirDatabase, Adt, HasSource}; | 5 | use hir::{db::HirDatabase, Adt, HasSource, Semantics}; |
6 | use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner}; | 6 | use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner}; |
7 | 7 | ||
8 | use crate::{Assist, AssistCtx, AssistId}; | 8 | use crate::{Assist, AssistCtx, AssistId}; |
9 | use 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 | ||
84 | fn resolve_enum_def( | 84 | fn 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 | ||
98 | fn build_pat( | 91 | fn 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 | ||
20 | pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler}; | 20 | pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler}; |
21 | pub use crate::handlers::replace_qualified_name_with_use::insert_use_statement; | 21 | pub use crate::handlers::replace_qualified_name_with_use::insert_use_statement; |
22 | use 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. |
65 | pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> { | 66 | pub 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. |
79 | pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> { | 81 | pub 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 | ||
3 | use hir::Semantics; | ||
4 | use ra_ide_db::RootDatabase; | ||
3 | use ra_syntax::{ | 5 | use ra_syntax::{ |
4 | ast::{self, make, NameOwner}, | 6 | ast::{self, make, NameOwner}, |
5 | AstNode, T, | 7 | AstNode, T, |
6 | }; | 8 | }; |
7 | |||
8 | use hir::db::HirDatabase; | ||
9 | use rustc_hash::FxHashSet; | 9 | use rustc_hash::FxHashSet; |
10 | 10 | ||
11 | pub fn get_missing_impl_items( | 11 | pub 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 | ||
62 | pub(crate) fn resolve_target_trait( | 63 | pub(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 | } |