Skip to content

Commit

Permalink
[autofix][insert-type] lift shadow properties to real properties
Browse files Browse the repository at this point in the history
Summary: This reuses another piece of annotate exports to make valid type annotations when a field is cast but never initialized.

Reviewed By: panagosg7

Differential Revision: D16114811

fbshipit-source-id: 8756e140bf826432c73c670e071bd34f927d2279
  • Loading branch information
Andre Kuhlenschmidt authored and facebook-github-bot committed Jul 10, 2019
1 parent 84e6b0f commit ff7f12b
Show file tree
Hide file tree
Showing 14 changed files with 174 additions and 12 deletions.
14 changes: 7 additions & 7 deletions src/services/type_info/insert_type.ml
Original file line number Diff line number Diff line change
Expand Up @@ -195,22 +195,22 @@ let fail_when_ty_size_exceeds size_limit ty =


(* Generate an equivalent Flow_ast.Type *)
let serialize ?(imports_react=false) ty =
(new Utils.patch_up_react_mapper ~imports_react ())#on_t () ty
let serialize ?(imports_react=false) loc ty =
(new Utils.patch_up_react_mapper ~imports_react ())#on_t loc ty
|> Ty_utils.simplify_type ~simplify_empty:true
|> Ty_serializer.type_
|> function
| Ok ast -> Utils.patch_up_type_ast ast
| Error msg -> raise (unexpected (FailedToSerialize {ty; error_message=msg}))

let remove_ambiguous_types ~ambiguity_strategy ty =
let remove_ambiguous_types ~ambiguity_strategy ty loc =
match ambiguity_strategy with
| Fail ->
begin try fail_on_ambiguity ty with
| FoundAmbiguousType ->
raise @@ expected @@ MulipleTypesPossibleAtPoint {
specialized=specialize_temporary_types ty |> serialize;
generalized=generalize_temporary_types ty |> serialize;
specialized=specialize_temporary_types ty |> serialize loc;
generalized=generalize_temporary_types ty |> serialize loc;
}
end
| Temporary -> allow_temporary_arr_and_obj_types ty
Expand Down Expand Up @@ -242,8 +242,8 @@ class mapper ?(size_limit=30) ~ambiguity_strategy
| Utils.Fatal error -> raise @@ expected @@
FailedToValidateType{error; error_message=Utils.serialize_validation_error error}
end;
let ty = remove_ambiguous_types ~ambiguity_strategy ty in
(location, serialize ~imports_react:true ty)
let ty = remove_ambiguous_types ~ambiguity_strategy ty location in
(location, serialize ~imports_react:true location ty)

method private synth_type_annotation_hint loc =
Flow_ast.Type.Available (this#synth_type loc)
Expand Down
25 changes: 20 additions & 5 deletions src/services/type_info/insert_type_utils.ml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ let serialize_validation_error = function
| Empty_SomeUnknownUpper u ->
Utils_js.spf "Empty_SomeUnknownUpper (use: %s)" u

let warn_shadow_prop ?(strip_root=None) name loc =
Hh_logger.warn "ShadowProp %s at %s" name (Reason.string_of_loc ?strip_root loc)

exception Fatal of validation_error
(* Raise an validation_error if there isn't a user facing type that is equivalent to the Ty *)
class type_validator_visitor = object(_)
Expand Down Expand Up @@ -121,9 +124,9 @@ let is_react_loc loc =
| _ -> false

(* Apply stylistic changes to react types *)
class ['A] patch_up_react_mapper ?(imports_react=false) () = object (this)
inherit ['A] Ty.endo_ty as super
method! on_t a t =
class patch_up_react_mapper ?(imports_react=false) () = object (this)
inherit [_] Ty.endo_ty as super
method! on_t loc t =
match t with

(* If 'react' is not imported, then we treat the symbol as Remote, so that
Expand All @@ -139,12 +142,24 @@ class ['A] patch_up_react_mapper ?(imports_react=false) () = object (this)
def_loc;
_} as symbol, kind, args_opt)
when is_react_loc def_loc ->
let args_opt = Flow_ast_mapper.map_opt (ListUtils.ident_map (this#on_t a)) args_opt in
let args_opt = Flow_ast_mapper.map_opt (ListUtils.ident_map (this#on_t loc)) args_opt in
let symbol = if imports_react
then { symbol with Ty.name = "React." ^ name }
else { symbol with Ty.provenance = Ty.Remote { Ty.imported_as = None } } in
Ty.Generic (symbol, kind, args_opt)
| _ -> super#on_t a t
| _ -> super#on_t loc t

method! on_prop loc prop =
let prop = match prop with
| Ty.NamedProp (name, named_prop)
when Reason.is_internal_name name ->
warn_shadow_prop name loc;
(* Shadow props appear as regular props *)
let name = String.sub name 1 (String.length name - 1) in
Ty.NamedProp (name, named_prop)
| prop -> prop
in
super#on_prop loc prop
end

(* Returns true if the location given a zero width location. *)
Expand Down
12 changes: 12 additions & 0 deletions tests/autofix-empty-object/.flowconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[ignore]

[include]

[libs]

[lints]

[options]
experimental.well_formed_exports=true

[strict]
1 change: 1 addition & 0 deletions tests/autofix-empty-object/.testconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
shell: test.sh
3 changes: 3 additions & 0 deletions tests/autofix-empty-object/a.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// @flow

module.exports = {};
65 changes: 65 additions & 0 deletions tests/autofix-empty-object/autofix-empty-object.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
> insert-type a.js 3 18 3 20
cat a.js
// @flow

module.exports = ({}: {});
> insert-type b.js 3 23 3 25
cat b.js
// @flow

module.exports = { f: ({}: {}) };
> insert-type c.js 3 18 3 20
cat c.js
// @flow

const obj = { f: ({}: {}) };

obj.f = { x: 1 };
obj.f = { x: "a" };

module.exports = obj;
> insert-type d.js 3 13 3 16
cat d.js
// @flow

const obj: {f: {x: number}, g: {x: string}} = { };

obj.f = { x: 1 };
obj["g"] = { x: "a" };

module.exports = obj;
> insert-type e.js 3 13 3 15
cat e.js
// @flow

const obj: {a: number} = {};
declare function foo(x: { a: number }): void;
foo(obj);
module.exports = obj;
> insert-type f.js 3 13 3 15
cat f.js
// @flow

const obj: {f: number} = {};
(obj.f: number);
module.exports = obj;
> insert-type g.js 5 13 5 15
cat g.js
// @flow

declare function foo<X: { a: 1 }>(x: X): void;

const obj: {a: 1} = {};
foo(obj);
module.exports = obj;
> insert-type h.js 4 5 4 8
cat h.js
// @flow


let obj: {x: number} = {};
obj.x = 5;

module.exports = obj;
> flow status
No errors!
3 changes: 3 additions & 0 deletions tests/autofix-empty-object/b.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// @flow

module.exports = { f: {} };
8 changes: 8 additions & 0 deletions tests/autofix-empty-object/c.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// @flow

const obj = { f: {} };

obj.f = { x: 1 };
obj.f = { x: "a" };

module.exports = obj;
8 changes: 8 additions & 0 deletions tests/autofix-empty-object/d.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// @flow

const obj = { };

obj.f = { x: 1 };
obj["g"] = { x: "a" };

module.exports = obj;
6 changes: 6 additions & 0 deletions tests/autofix-empty-object/e.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// @flow

const obj = {};
declare function foo(x: { a: number }): void;
foo(obj);
module.exports = obj;
5 changes: 5 additions & 0 deletions tests/autofix-empty-object/f.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// @flow

const obj = {};
(obj.f: number);
module.exports = obj;
7 changes: 7 additions & 0 deletions tests/autofix-empty-object/g.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// @flow

declare function foo<X: { a: 1 }>(x: X): void;

const obj = {};
foo(obj);
module.exports = obj;
7 changes: 7 additions & 0 deletions tests/autofix-empty-object/h.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// @flow


let obj = {};
obj.x = 5;

module.exports = obj;
22 changes: 22 additions & 0 deletions tests/autofix-empty-object/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash

update_in_place(){
local FILE=$1;
echo "> insert-type" "$@"
assert_ok "$FLOW" autofix insert-type --in-place "$@"
assert_ok "$FLOW" force-recheck "$FILE"
echo "cat $FILE"
cat "$FILE"
}

update_in_place a.js 3 18 3 20
update_in_place b.js 3 23 3 25
update_in_place c.js 3 18 3 20
update_in_place d.js 3 13 3 16
update_in_place e.js 3 13 3 15
update_in_place f.js 3 13 3 15
update_in_place g.js 5 13 5 15
update_in_place h.js 4 5 4 8

echo "> flow status"
assert_ok "$FLOW" status --strip-root

0 comments on commit ff7f12b

Please sign in to comment.