Skip to content

Commit daaf192

Browse files
committed
Add better abstractions to place.rs
1 parent bfea391 commit daaf192

File tree

3 files changed

+54
-15
lines changed

3 files changed

+54
-15
lines changed

crates/ty_python_semantic/src/semantic_index/builder.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use crate::semantic_index::definition::{
3333
use crate::semantic_index::expression::{Expression, ExpressionKind};
3434
use crate::semantic_index::place::{
3535
FileScopeId, NodeWithScopeKey, NodeWithScopeKind, NodeWithScopeRef, PlaceExpr,
36-
PlaceExprSubSegment, PlaceTableBuilder, Scope, ScopeId, ScopeKind, ScopedPlaceId,
36+
PlaceTableBuilder, Scope, ScopeId, ScopeKind, ScopedPlaceId,
3737
};
3838
use crate::semantic_index::predicate::{
3939
PatternPredicate, PatternPredicateKind, Predicate, PredicateNode, ScopedPredicateId,
@@ -1965,7 +1965,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
19651965
| ast::Expr::Subscript(ast::ExprSubscript { ctx, .. }) => {
19661966
if let Ok(mut place_expr) = PlaceExpr::try_from(expr) {
19671967
if self.is_method_of_class().is_some()
1968-
&& matches!(place_expr.sub_segments(), &[PlaceExprSubSegment::Member(_)])
1968+
&& place_expr.is_instance_attribute_candidate()
19691969
{
19701970
// We specifically mark attribute assignments to the first parameter of a method,
19711971
// i.e. typically `self` or `cls`.

crates/ty_python_semantic/src/semantic_index/place.rs

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -191,16 +191,59 @@ impl PlaceExpr {
191191
&self.root_name
192192
}
193193

194+
/// If the place expression has the form `<NAME>.<MEMBER>`
195+
/// (meaning it *may* be an instance attribute),
196+
/// return `Some(<MEMBER>)`. Else, return `None`.
197+
///
198+
/// This method is internal to the semantic-index submodule.
199+
/// It *only* checks that the AST structure of the `Place` is
200+
/// correct. It does not check whether the `Place` actually occured in
201+
/// a method context, or whether the `<NAME>` actually refers to the first
202+
/// parameter of the method (i.e. `self`). To answer those questions,
203+
/// use [`Self::as_instance_attribute`].
204+
pub(super) fn as_instance_attribute_candidate(&self) -> Option<&Name> {
205+
if self.sub_segments.len() == 1 {
206+
self.sub_segments[0].as_member()
207+
} else {
208+
None
209+
}
210+
}
211+
212+
/// Return `true` if the place expression has the form `<NAME>.<MEMBER>`,
213+
/// indicating that it *may* be an instance attribute if we are in a method context.
214+
///
215+
/// This method is internal to the semantic-index submodule.
216+
/// It *only* checks that the AST structure of the `Place` is
217+
/// correct. It does not check whether the `Place` actually occured in
218+
/// a method context, or whether the `<NAME>` actually refers to the first
219+
/// parameter of the method (i.e. `self`). To answer those questions,
220+
/// use [`Self::is_instance_attribute`].
221+
pub(super) fn is_instance_attribute_candidate(&self) -> bool {
222+
self.as_instance_attribute_candidate().is_some()
223+
}
224+
194225
/// Does the place expression have the form `self.{name}` (`self` is the first parameter of the method)?
195226
pub(super) fn is_instance_attribute_named(&self, name: &str) -> bool {
196-
self.is_instance_attribute()
197-
&& self.sub_segments.len() == 1
198-
&& self.sub_segments[0].as_member().unwrap().as_str() == name
227+
self.as_instance_attribute().map(Name::as_str) == Some(name)
199228
}
200229

201230
/// Is the place an instance attribute?
202-
pub fn is_instance_attribute(&self) -> bool {
203-
self.flags.contains(PlaceFlags::IS_INSTANCE_ATTRIBUTE)
231+
pub(crate) fn is_instance_attribute(&self) -> bool {
232+
let is_instance_attribute = self.flags.contains(PlaceFlags::IS_INSTANCE_ATTRIBUTE);
233+
if is_instance_attribute {
234+
debug_assert!(self.is_instance_attribute_candidate());
235+
}
236+
is_instance_attribute
237+
}
238+
239+
/// Return `Some(<ATTRIBUTE>)` if the place expression is an instance attribute.
240+
pub(crate) fn as_instance_attribute(&self) -> Option<&Name> {
241+
if self.is_instance_attribute() {
242+
debug_assert!(self.as_instance_attribute_candidate().is_some());
243+
self.as_instance_attribute_candidate()
244+
} else {
245+
None
246+
}
204247
}
205248

206249
/// Is the place used in its containing scope?
@@ -570,9 +613,9 @@ impl PlaceTable {
570613
self.places().filter(|place_expr| place_expr.is_name())
571614
}
572615

573-
pub fn instance_attributes(&self) -> impl Iterator<Item = &PlaceExpr> {
616+
pub fn instance_attributes(&self) -> impl Iterator<Item = &Name> {
574617
self.places()
575-
.filter(|place_expr| place_expr.is_instance_attribute())
618+
.filter_map(|place_expr| place_expr.as_instance_attribute())
576619
}
577620

578621
/// Returns the place named `name`.

crates/ty_python_semantic/src/types/ide_support.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -185,12 +185,8 @@ impl AllMembers {
185185
let index = semantic_index(db, file);
186186
for function_scope_id in attribute_scopes(db, class_body_scope) {
187187
let place_table = index.place_table(function_scope_id);
188-
for instance_attribute in place_table.instance_attributes() {
189-
let name = instance_attribute.sub_segments()[0]
190-
.as_member()
191-
.expect("Non-members should never be registered as instance attributes");
192-
self.members.insert(name.clone());
193-
}
188+
self.members
189+
.extend(place_table.instance_attributes().cloned());
194190
}
195191
}
196192
}

0 commit comments

Comments
 (0)