New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow subscripted aliases at function scope #4000
Changes from 6 commits
4c9b023
a464a53
e4c1ef4
38ecc68
178826c
358ca96
5cbe056
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1732,11 +1732,10 @@ def alias_fallback(self, tp: Type) -> Instance: | |
return Instance(fb_info, []) | ||
|
||
def analyze_alias(self, rvalue: Expression, | ||
allow_unnormalized: bool) -> Tuple[Optional[Type], List[str]]: | ||
warn_bound_tvar: bool = False) -> Tuple[Optional[Type], List[str]]: | ||
"""Check if 'rvalue' represents a valid type allowed for aliasing | ||
(e.g. not a type variable). If yes, return the corresponding type and a list of | ||
qualified type variable names for generic aliases. | ||
If 'allow_unnormalized' is True, allow types like builtins.list[T]. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this PR also affect whether There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, I just noticed that this parameter is unused in the function body when I added the new one. |
||
""" | ||
dynamic = bool(self.function_stack and self.function_stack[-1].is_dynamic()) | ||
global_scope = not self.type and not self.function_stack | ||
|
@@ -1751,7 +1750,8 @@ def analyze_alias(self, rvalue: Expression, | |
self.is_typeshed_stub_file, | ||
allow_unnormalized=True, | ||
in_dynamic_func=dynamic, | ||
global_scope=global_scope) | ||
global_scope=global_scope, | ||
warn_bound_tvar=warn_bound_tvar) | ||
if res: | ||
alias_tvars = [name for (name, _) in | ||
res.accept(TypeVariableQuery(self.lookup_qualified, self.tvar_scope))] | ||
|
@@ -1770,11 +1770,12 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> None: | |
lvalue = s.lvalues[0] | ||
if not isinstance(lvalue, NameExpr): | ||
return | ||
if (len(s.lvalues) == 1 and not self.is_func_scope() and | ||
not (self.type and isinstance(s.rvalue, NameExpr) and lvalue.is_def) | ||
if (len(s.lvalues) == 1 and | ||
not ((self.type or self.is_func_scope()) and | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This condition is hard to understand. Add a comment describing what it does at a high level and maybe also give some concrete examples. It's also worth trying to simplify the nested boolean expression. Ideas:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, fixed. I replaced this with three |
||
isinstance(s.rvalue, NameExpr) and lvalue.is_def) | ||
and not s.type): | ||
rvalue = s.rvalue | ||
res, alias_tvars = self.analyze_alias(rvalue, allow_unnormalized=True) | ||
res, alias_tvars = self.analyze_alias(rvalue, warn_bound_tvar=True) | ||
if not res: | ||
return | ||
node = self.lookup(lvalue.name, lvalue) | ||
|
@@ -3374,7 +3375,7 @@ def visit_index_expr(self, expr: IndexExpr) -> None: | |
elif isinstance(expr.base, RefExpr) and expr.base.kind == TYPE_ALIAS: | ||
# Special form -- subscripting a generic type alias. | ||
# Perform the type substitution and create a new alias. | ||
res, alias_tvars = self.analyze_alias(expr, allow_unnormalized=self.is_stub_file) | ||
res, alias_tvars = self.analyze_alias(expr) | ||
expr.analyzed = TypeAliasExpr(res, alias_tvars, fallback=self.alias_fallback(res), | ||
in_runtime=True) | ||
expr.analyzed.line = expr.line | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not directly related to this PR, but this was exposed by a test I updated. For example:
By the way, this use case is not mentioned in PEP 484, but there are tests for this, for example
testClassValuedAttributesGeneric
. There are of course subtleties related toType[...]
, but I think this change doesn't add unsafety, but removes some false positives.