Skip to content

Commit

Permalink
Register type aliases typed as Any
Browse files Browse the repository at this point in the history
Summary:
EDIT: first version was based on wrong assumptions - aliases and globals are separate concepts, differentiated by whether there is an explicit annotation
python/mypy#3494

-----

Some examples where we rely on globals typed as `Any` (they might have some other value on the RHS of the assign) to be valid types:
diffusion/IGSRVHG/browse/master/distillery/gatekeeper/restraints/checks/request/app_os.py$36,45#
https://fburl.com/biggrep/rmd6ufa4

Typeshed mock.pyi uses this for some of its classes:
https://github.com/python/typeshed/blob/0730fe5fcb269df3f1b72e2544671fec1498a6db/stdlib/3/unittest/mock.pyi

Reviewed By: sinancepel

Differential Revision: D13858398

fbshipit-source-id: 290c5d9644c88c52082c58c052ad9a8efe172d64
  • Loading branch information
shannonzhu authored and facebook-github-bot committed Jan 30, 2019
1 parent e2999b8 commit f89aee0
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 27 deletions.
76 changes: 50 additions & 26 deletions analysis/environment.ml
Original file line number Diff line number Diff line change
Expand Up @@ -434,48 +434,72 @@ let register_aliases (module Handler: Handler) sources =
let order = (module Handler.TypeOrderHandler : TypeOrder.Handler) in
let collect_aliases { Source.handle; statements; qualifier; _ } =
let rec visit_statement ~qualifier ?(in_class_body = false) aliases { Node.value; _ } =
let is_allowable_alias_annotation = function
| Some {
Node.value = Access
(SimpleAccess [
Access.Identifier "typing";
Access.Identifier "Type";
Access.Identifier "__getitem__";
Access.Call _;
]);
_;
} ->
true
| None ->
true
| _ ->
false
in
match value with
| Assign {
Assign.target = { Node.value = Access (SimpleAccess target); _ };
annotation;
value;
_;
}
when is_allowable_alias_annotation annotation ->
} ->
let target =
let access = Access.delocalize_qualified target in
if in_class_body then
access
else
qualifier @ access
in
let target_annotation =
Type.create
~aliases:Handler.aliases
(Node.create (Access (SimpleAccess target)) ~location:(Node.location value))
in
begin
match Node.value value with
| Access _ ->
match Node.value value, annotation with
| _, Some {
Node.value =
Access
(SimpleAccess [
Access.Identifier "typing";
Access.Identifier "Type";
Access.Identifier "__getitem__";
Access.Call {
Node.value = [{
Argument.value = ({
Node.value = Access (SimpleAccess [
Access.Identifier "mypy_extensions";
Access.Identifier "TypedDict";
Access.Identifier "__getitem__";
Access.Call _;
]);
_;
} as annotation);
_;
}];
_;
};
]);
_;
} ->
if not (Type.equal target_annotation Type.Top) then
(handle, target, annotation) :: aliases
else
aliases
| _, Some ({
Node.value =
Access
(SimpleAccess[
Access.Identifier "typing";
Access.Identifier "Any";
]);
_;
} as annotation) ->
if not (Type.equal target_annotation Type.Top) then
(handle, target, annotation) :: aliases
else
aliases
| Access _, None ->
let value = Expression.delocalize value in
let value_annotation = Type.create ~aliases:Handler.aliases value in
let target_annotation =
Type.create
~aliases:Handler.aliases
(Node.create (Access (SimpleAccess target)) ~location:(Node.location value))
in
if not (Type.equal target_annotation Type.Top ||
Type.equal value_annotation Type.Top ||
Type.equal value_annotation target_annotation) then
Expand Down
22 changes: 22 additions & 0 deletions analysis/test/environmentTest.ml
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,28 @@ let test_register_aliases _ =
|};
]
["x.C", "x.C"];
assert_resolved
[
parse
{|
A = int
B: typing.Type[int] = int
C: typing.Type[int]
D: typing.Any
E = ...
F = A
G = 1
|};
]
[
"A", "int";
"B", "B";
"C", "C";
"D", "typing.Any";
"E", "E";
"F", "int";
"G", "G";
];
()
Expand Down
15 changes: 14 additions & 1 deletion analysis/test/integration/annotationTest.ml
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,17 @@ let test_check_undefined_type _ =
let test_check_invalid_type _ =
assert_type_errors
{|
MyType: typing.Type[int] = int
MyType = int
x: MyType = 1
|}
[];
assert_type_errors
{|
# Type aliases cannot be annotated
MyType: typing.Type[int] = int
x: MyType = 1
|}
["Invalid type [31]: Expression `MyType` is not a valid type."];
assert_type_errors
{|
x: MyType = 1
Expand All @@ -200,6 +207,12 @@ let test_check_invalid_type _ =
MyType: typing.Any
x: MyType = 1
|}
[];
assert_type_errors
{|
MyType: typing.Any
x: typing.List[MyType] = [1]
|}
[]


Expand Down
4 changes: 4 additions & 0 deletions analysis/test/typeCheckTest.ml
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,10 @@ let test_check_annotation _ =
assert_check_annotation
"x: typing.Type[int] = int"
"x"
["Invalid type [31]: Expression `x` is not a valid type."];
assert_check_annotation
"x = int"
"x"
[];
assert_check_annotation
"x: typing.Any"
Expand Down

0 comments on commit f89aee0

Please sign in to comment.