diff --git a/analysis/resolution.ml b/analysis/resolution.ml index 97e1d2b89e9..8e6a581f045 100644 --- a/analysis/resolution.ml +++ b/analysis/resolution.ml @@ -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 @@ -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 @@ -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 @@ -295,6 +304,24 @@ 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 @@ -302,6 +329,15 @@ let rec resolve_literal resolution expression = | _ -> 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 @@ -309,10 +345,10 @@ let rec resolve_literal resolution expression = 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 diff --git a/analysis/test/environmentTest.ml b/analysis/test/environmentTest.ml index 507e44c06a8..5e2af85aaf7 100644 --- a/analysis/test/environmentTest.ml +++ b/analysis/test/environmentTest.ml @@ -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 = ... @@ -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); diff --git a/analysis/test/resolutionTest.ml b/analysis/test/resolutionTest.ml index b5e1270bd4d..aaeb14e77e3 100644 --- a/analysis/test/resolutionTest.ml +++ b/analysis/test/resolutionTest.ml @@ -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 _ =