Skip to content

Commit

Permalink
Implement resolve literal for more expressions
Browse files Browse the repository at this point in the history
Summary: Make this a bit more robust, now that what we consider resolvable literals is a bit more significant (see rest of stack). We also have had confusion in the past when seeing something like `x = {1: 2}` annotated to be unknown since we didn't support resolving this as a literal. Avoid this by supporting the obvious (and safe) cases.

Reviewed By: sinancepel

Differential Revision: D13822554

fbshipit-source-id: 370e7c81931491b2a1d3ea82eaf5696e969a798b
  • Loading branch information
shannonzhu authored and facebook-github-bot committed Jan 30, 2019
1 parent 566a600 commit e2999b8
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 11 deletions.
46 changes: 41 additions & 5 deletions analysis/resolution.ml
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,8 @@ let is_invariance_mismatch { order; _ } ~left ~right =
false


(* In general, python expressions can be self-referential. This non-recursive resolution only checks
literals and annotations found in the resolution map, without any resolutions/joins. *)
(* In general, python expressions can be self-referential. This resolution only checks
literals and annotations found in the resolution map, without resolving expressions. *)
let rec resolve_literal resolution expression =
let open Ast.Expression in
match Node.value expression with
Expand Down Expand Up @@ -268,6 +268,15 @@ let rec resolve_literal resolution expression =
resolve_literal resolution expression
|> Type.awaitable_value

| BooleanOperator { BooleanOperator.left; right; _ } ->
let annotation =
join
resolution
(resolve_literal resolution left)
(resolve_literal resolution right)
in
if Type.is_concrete annotation then annotation else Type.Object

| Complex _ ->
Type.complex

Expand All @@ -284,7 +293,7 @@ let rec resolve_literal resolution expression =
if Type.is_concrete key_annotation && Type.is_concrete value_annotation then
Type.dictionary ~key:key_annotation ~value:value_annotation
else
Type.Top
Type.Object

| False ->
Type.bool
Expand All @@ -295,24 +304,51 @@ let rec resolve_literal resolution expression =
| Integer _ ->
Type.integer

| List elements ->
let parameter =
let join sofar element =
join resolution sofar (resolve_literal resolution element)
in
List.fold ~init:Type.Bottom ~f:join elements
in
if Type.is_concrete parameter then Type.list parameter else Type.Object

| Set elements ->
let parameter =
let join sofar element =
join resolution sofar (resolve_literal resolution element)
in
List.fold ~init:Type.Bottom ~f:join elements
in
if Type.is_concrete parameter then Type.set parameter else Type.Object

| String { StringLiteral.kind; _ } ->
begin
match kind with
| StringLiteral.Bytes -> Type.bytes
| _ -> Type.string
end

| Ternary { Ternary.target; alternative; _ } ->
let annotation =
join
resolution
(resolve_literal resolution target)
(resolve_literal resolution alternative)
in
if Type.is_concrete annotation then annotation else Type.Object

| True ->
Type.bool

| Tuple elements ->
Type.tuple (List.map elements ~f:(resolve_literal resolution))

| Expression.Yield _ ->
Type.yield Type.Top
Type.yield Type.Object

| _ ->
Type.Top
Type.Object

let resolve_mutable_literals resolution ~expression ~resolved ~expected =
match expression with
Expand Down
6 changes: 4 additions & 2 deletions analysis/test/environmentTest.ml
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,8 @@ let test_register_globals _ =
parse
~qualifier:(Access.create "qualifier")
{|
with_join = 1 or 'asdf' # don't join with an incomplete environment
with_join = 1 or 'asdf'
with_resolve = with_join
annotated: int = 1
unannotated = 'string'
stub: int = ...
Expand All @@ -568,7 +569,8 @@ let test_register_globals _ =
in
Environment.register_globals (module Handler) resolution source;
assert_global "qualifier.undefined" None;
assert_global "qualifier.with_join" (Some Type.Top);
assert_global "qualifier.with_join" (Some (Type.union [Type.integer; Type.string]));
assert_global "qualifier.with_resolve" (Some Type.Top);
assert_global "qualifier.annotated" (Some Type.integer);
assert_global "qualifier.unannotated" (Some Type.string);
assert_global "qualifier.stub" (Some Type.integer);
Expand Down
31 changes: 27 additions & 4 deletions analysis/test/resolutionTest.ml
Original file line number Diff line number Diff line change
Expand Up @@ -111,22 +111,45 @@ let test_resolve_literal _ =
| { Node.value = Statement.Expression expression; _ } -> expression
| _ -> failwith "No Assign to parse"
in
assert_equal ~printer:Type.show (Resolution.resolve_literal resolution expression) expected
assert_equal ~printer:Type.show expected (Resolution.resolve_literal resolution expression)
in
assert_resolve_literal "i" Type.Top;
assert_resolve_literal "await i" Type.Top;
assert_resolve_literal "await awaitable" Type.Top;
assert_resolve_literal "\"\"" Type.string;
assert_resolve_literal "1" Type.integer;
assert_resolve_literal "1+1" Type.Top;
assert_resolve_literal "1+1" Type.Object;
assert_resolve_literal "j" Type.Top;
assert_resolve_literal "foo()" Type.Top;
assert_resolve_literal "C()" (Type.Primitive "C");
assert_resolve_literal "C" (Type.meta (Type.Primitive "C"));
assert_resolve_literal "none" Type.none;

(* Dictionary *)
assert_resolve_literal "{'a': 1}" (Type.dictionary ~key:Type.string ~value:Type.integer);
assert_resolve_literal "{**foo}" Type.Top;
assert_resolve_literal "{'a': 1, **foo}" Type.Top
assert_resolve_literal "{'a': i}" (Type.Object);
assert_resolve_literal "{**foo}" Type.Object;
assert_resolve_literal "{'a': 1, **foo}" Type.Object;

(* Boolean Operator *)
assert_resolve_literal "1 or 2" (Type.integer);
assert_resolve_literal "True or 1" (Type.union [Type.bool; Type.integer]);
assert_resolve_literal "True or i" (Type.Object);

(* List *)
assert_resolve_literal "[1]" (Type.list Type.integer);
assert_resolve_literal "[1, 'string']" (Type.list (Type.Union [Type.integer; Type.string]));
assert_resolve_literal "[1, i]" (Type.Object);

(* Set *)
assert_resolve_literal "{1}" (Type.set Type.integer);
assert_resolve_literal "{1, 'string'}" (Type.set (Type.Union [Type.integer; Type.string]));
assert_resolve_literal "{1, i}" (Type.Object);

(* Ternary *)
assert_resolve_literal "1 if x else 2" (Type.integer);
assert_resolve_literal "'hi' if x else 1" (Type.union [Type.string; Type.integer]);
assert_resolve_literal "1 if i else i" (Type.Object)


let test_resolve_mutable_literals _ =
Expand Down

0 comments on commit e2999b8

Please sign in to comment.