From dc93bdc983b9caa43aaee7efdba792f5d304dbc1 Mon Sep 17 00:00:00 2001 From: Simon Lydell Date: Sat, 8 Apr 2017 16:52:38 +0200 Subject: [PATCH] Sync the Flow tests (#1163) * Extract custom tests from tests/flow/ Approach: 1. Remove all .js files in tests/flow except .snap.js files. 2. Copy over all .js files from tests/ in the flow repo. 3. Go through the diff looking for deletions. - It was easy to see which deletions were due to changes in the tests due to updates in the flow repo. - For the rest of the deletions, I used `git blame` to verify that they had been added by us since the flow tests were copied over. This makes tests/flow/ simply a copy of the tests from the flow repo, making it easier to sync with the upstream flow tests in the future. * Add a script for syncing the flow tests * Sync the flow tests --- README.md | 45 +- package.json | 2 + scripts/sync-flow-tests.js | 125 ++ .../abnormal/__snapshots__/jsfmt.spec.js.snap | 11 + tests/flow/abnormal/toplevel_throw.js | 3 + .../annot/__snapshots__/jsfmt.spec.js.snap | 64 - tests/flow/annot/annot.js | 32 - .../arrays/__snapshots__/jsfmt.spec.js.snap | 28 - .../__snapshots__/jsfmt.spec.js.snap | 101 ++ .../jsfmt.spec.js | 0 .../lib/__snapshots__/jsfmt.spec.js.snap | 80 + tests/flow/call_caching1/lib/core.js | 25 + tests/flow/call_caching1/lib/immutable.js | 8 + tests/flow/call_caching1/lib/jsfmt.spec.js | 1 + tests/flow/call_caching1/test.js | 10 + tests/flow/call_caching1/test2.js | 18 + tests/flow/call_caching1/test3.js | 16 + .../__snapshots__/jsfmt.spec.js.snap | 16 + tests/flow/call_caching2/jsfmt.spec.js | 1 + .../lib/__snapshots__/jsfmt.spec.js.snap | 24 + tests/flow/call_caching2/lib/immutable.js | 9 + tests/flow/call_caching2/lib/jsfmt.spec.js | 1 + tests/flow/call_caching2/test.js | 5 + .../__snapshots__/jsfmt.spec.js.snap | 573 +++++++ tests/flow/class_fields/base_class.js | 72 + tests/flow/class_fields/derived_class.js | 134 ++ tests/flow/class_fields/generic_class.js | 31 + tests/flow/class_fields/jsfmt.spec.js | 1 + tests/flow/class_fields/scoping.js | 40 + .../__snapshots__/jsfmt.spec.js.snap | 46 + .../class_method_default_parameters.js | 21 + .../jsfmt.spec.js | 1 + .../classes/__snapshots__/jsfmt.spec.js.snap | 15 + tests/flow/classes/extends_any.js | 5 + .../__snapshots__/jsfmt.spec.js.snap | 30 +- tests/flow/constructor/constructor.js | 15 +- .../foo/bar/__snapshots__/jsfmt.spec.js.snap | 4 + .../foo/bar/nested_test.js | 2 + .../A.js | 4 + .../__snapshots__/jsfmt.spec.js.snap | 35 + .../index.js | 7 + .../jsfmt.spec.js | 1 + ...pe_alias.js => DeclareModule_TypeAlias.js} | 3 - .../lib/__snapshots__/jsfmt.spec.js.snap | 8 +- .../__snapshots__/jsfmt.spec.js.snap | 58 +- .../flow/destructuring/destructuring_param.js | 13 - tests/flow/destructuring/poly.js | 1 - .../refinement_non_termination.js | 10 + tests/flow/dom/Document.js | 1 + .../flow/dom/__snapshots__/jsfmt.spec.js.snap | 20 +- tests/flow/dom/traversal.js | 8 +- .../__snapshots__/jsfmt.spec.js.snap | 22 + .../jsfmt.spec.js | 1 + .../test.js | 8 + .../__snapshots__/jsfmt.spec.js.snap | 20 + .../jsfmt.spec.js | 1 + .../test.js | 7 + .../__snapshots__/jsfmt.spec.js.snap | 20 + .../jsfmt.spec.js | 1 + .../test.js | 7 + .../__snapshots__/jsfmt.spec.js.snap | 20 + .../jsfmt.spec.js | 1 + .../test.js | 7 + .../__snapshots__/jsfmt.spec.js.snap | 11 + .../esproposal_export_star_as.enable/test.js | 3 + .../__snapshots__/jsfmt.spec.js.snap | 11 + .../esproposal_export_star_as.ignore/test.js | 3 + .../__snapshots__/jsfmt.spec.js.snap | 12 + .../jsfmt.spec.js | 1 + .../esproposal_export_star_as.warn/test.js | 3 + .../fetch/__snapshots__/jsfmt.spec.js.snap | 68 +- tests/flow/fetch/request.js | 34 +- .../__snapshots__/jsfmt.spec.js.snap | 34 + tests/flow/focus-check/a.js | 3 + tests/flow/focus-check/b.js | 3 + tests/flow/focus-check/jsfmt.spec.js | 1 + tests/flow/focus-check/test.js | 3 + .../function/__snapshots__/jsfmt.spec.js.snap | 253 ++- tests/flow/function/apply.js | 10 +- tests/flow/function/bind.js | 19 + tests/flow/function/call.js | 10 +- tests/flow/function/rest.js | 54 +- tests/flow/function/rest_type.js | 31 + .../__snapshots__/jsfmt.spec.js.snap | 67 + tests/flow/generators/refi.js | 31 + .../__snapshots__/jsfmt.spec.js.snap | 0 .../a.js | 0 .../b.js | 0 .../c.js | 0 tests/flow/get-imports/jsfmt.spec.js | 1 + .../__snapshots__/jsfmt.spec.js.snap | 56 +- .../getters_and_setters.js | 24 +- .../__snapshots__/jsfmt.spec.js.snap | 256 ++- .../flow/getters_and_setters_enabled/class.js | 3 + .../declare_class.js | 47 + .../getters_and_setters_enabled/jsfmt.spec.js | 6 +- .../getters_and_setters_enabled/object.js | 3 + .../object_type.js | 53 + .../flow/getters_and_setters_enabled/react.js | 13 +- .../haste_name_reducers_defaults/Module1.js | 7 + .../__snapshots__/jsfmt.spec.js.snap | 35 + .../haste_name_reducers_defaults/index.js | 5 + .../jsfmt.spec.js | 1 + .../__snapshots__/jsfmt.spec.js.snap | 18 + .../haste_use_name_reducers/dir1/Module1.js | 7 + .../dir1/__snapshots__/jsfmt.spec.js.snap | 20 + .../dir1/jsfmt.spec.js | 1 + .../dir2/Module2.ios.js | 3 + .../dir2/__snapshots__/jsfmt.spec.js.snap | 12 + .../dir2/jsfmt.spec.js | 1 + tests/flow/haste_use_name_reducers/index.js | 6 + .../haste_use_name_reducers/jsfmt.spec.js | 1 + .../__snapshots__/jsfmt.spec.js.snap | 72 + tests/flow/implements/jsfmt.spec.js | 1 + tests/flow/implements/test.js | 30 + .../__snapshots__/jsfmt.spec.js.snap | 17 - tests/flow/incremental_cycle_break/A.js | 6 + tests/flow/incremental_cycle_break/B.js | 5 + .../__snapshots__/jsfmt.spec.js.snap | 33 + .../incremental_cycle_break/jsfmt.spec.js | 1 + tests/flow/incremental_cycle_break/tmp1/A.js | 0 .../tmp1/__snapshots__/jsfmt.spec.js.snap | 6 + .../tmp1/jsfmt.spec.js | 1 + tests/flow/incremental_haste_blacklist/A.js | 1 + .../__snapshots__/jsfmt.spec.js.snap | 17 + .../flow/incremental_haste_blacklist/index.js | 2 + .../incremental_haste_blacklist/jsfmt.spec.js | 1 + .../A.js | 1 + .../__snapshots__/jsfmt.spec.js.snap | 17 + .../index.js | 2 + .../jsfmt.spec.js | 1 + .../iterable/__snapshots__/jsfmt.spec.js.snap | 36 +- tests/flow/iterable/array.js | 12 +- tests/flow/iterable/string.js | 6 +- .../__snapshots__/jsfmt.spec.js.snap | 65 + tests/flow/match_failure/disjoint_union.js | 14 + tests/flow/match_failure/enum.js | 12 + tests/flow/match_failure/jsfmt.spec.js | 1 + tests/flow/more_react/InitializedFields.js | 14 + .../__snapshots__/jsfmt.spec.js.snap | 104 +- tests/flow/more_react/checkPropTypes.js | 14 + tests/flow/more_react/propTypes.js | 5 +- .../__snapshots__/jsfmt.spec.js.snap | 297 ++++ tests/flow/multiflow/apply.js | 40 + tests/flow/multiflow/issue3443.js | 18 + tests/flow/multiflow/jsfmt.spec.js | 1 + tests/flow/multiflow/jsx.js | 47 + tests/flow/multiflow/spread.js | 28 + .../__snapshots__/jsfmt.spec.js.snap | 34 + .../flow/multiflow_with_flowlib/jsfmt.spec.js | 1 + tests/flow/multiflow_with_flowlib/spread.js | 14 + .../__snapshots__/jsfmt.spec.js.snap | 12 +- tests/flow/new_react/classes.js | 2 +- tests/flow/new_react/new_react.js | 2 +- tests/flow/new_react/state4.js | 2 +- .../__snapshots__/jsfmt.spec.js.snap | 39 +- tests/flow/new_spread/type.js | 13 +- tests/flow/new_spread/type_generic.js | 3 + .../stream/__snapshots__/jsfmt.spec.js.snap | 9 + tests/flow/node_tests/stream/stream.js | 4 + .../__snapshots__/jsfmt.spec.js.snap | 8 +- tests/flow/object_api/object_keys.js | 4 +- .../__snapshots__/jsfmt.spec.js.snap | 37 +- tests/flow/object_assign/apply.js | 1 - tests/flow/object_assign/spread.js | 15 + .../objects/__snapshots__/jsfmt.spec.js.snap | 63 + tests/flow/objects/compatibility.js | 29 + .../optional/__snapshots__/jsfmt.spec.js.snap | 27 +- tests/flow/optional/loop.js | 5 + tests/flow/optional/optional.js | 5 +- .../__snapshots__/jsfmt.spec.js.snap | 19 + .../flow/optional_props/test3_exact_annot.js | 7 + .../react/__snapshots__/jsfmt.spec.js.snap | 1495 ++++++++++++++++- tests/flow/react/create_class.js | 174 ++ .../create_class_initial_state_sealed.js | 86 + .../flow/react/create_class_statics_sealed.js | 54 + tests/flow/react/proptype_any.js | 19 + tests/flow/react/proptype_arrayOf.js | 33 +- tests/flow/react/proptype_custom_validator.js | 18 + tests/flow/react/proptype_incompatible.js | 10 + tests/flow/react/proptype_instanceOf.js | 41 + tests/flow/react/proptype_missing.js | 3 +- tests/flow/react/proptype_objectOf.js | 31 + tests/flow/react/proptype_oneOf.js | 82 +- tests/flow/react/proptype_oneOfType.js | 82 +- tests/flow/react/proptype_shape.js | 35 + tests/flow/react/proptypes_builtins.js | 27 + tests/flow/react/proptypes_sealed.js | 24 + .../__snapshots__/jsfmt.spec.js.snap | 92 + .../es6class-proptypes-callsite.js | 26 + .../es6class-proptypes-module.js | 15 + tests/flow/recheck-haste/B1.js | 6 + tests/flow/recheck-haste/B3.js | 6 + .../__snapshots__/jsfmt.spec.js.snap | 34 + tests/flow/recheck-haste/dir1B/B2.js | 6 + .../dir1B/__snapshots__/jsfmt.spec.js.snap | 18 + tests/flow/recheck-haste/dir1B/jsfmt.spec.js | 1 + .../recheck/__snapshots__/jsfmt.spec.js.snap | 60 + tests/flow/recheck/i1.js | 5 + tests/flow/recheck/i2.js | 5 + tests/flow/recheck/j1.js | 5 + tests/flow/recheck/j2.js | 5 + .../tmp1i/__snapshots__/jsfmt.spec.js.snap | 16 + tests/flow/recheck/tmp1i/i1.js | 5 + tests/flow/recheck/tmp1i/jsfmt.spec.js | 1 + .../tmp1j/__snapshots__/jsfmt.spec.js.snap | 16 + tests/flow/recheck/tmp1j/j1.js | 5 + tests/flow/recheck/tmp1j/jsfmt.spec.js | 1 + .../__snapshots__/jsfmt.spec.js.snap | 98 +- .../flow/refinements/missing-property-cond.js | 12 + tests/flow/refinements/tagged_union.js | 38 +- .../__snapshots__/jsfmt.spec.js.snap | 24 + tests/flow/return_new/test.js | 11 + .../shape/__snapshots__/jsfmt.spec.js.snap | 11 + tests/flow/shape/shadow.js | 3 + tests/flow/suppress/D.js | 8 + .../suppress/__snapshots__/jsfmt.spec.js.snap | 21 + .../__snapshots__/jsfmt.spec.js.snap | 32 +- .../flow/type-at-pos/function_expressions.js | 14 +- .../unicode/__snapshots__/jsfmt.spec.js.snap | 7 - tests/flow/unicode/keys.js | 1 - .../union/__snapshots__/jsfmt.spec.js.snap | 88 + tests/flow/union/blowup.js | 43 + .../__snapshots__/jsfmt.spec.js.snap | 4 +- tests/flow/union_new/test10.js | 2 +- .../__snapshots__/jsfmt.spec.js.snap | 22 + tests/flow_array_comments/jsfmt.spec.js | 1 + .../test.js} | 0 .../__snapshots__/jsfmt.spec.js.snap | 8 + tests/flow_array_union/jsfmt.spec.js | 1 + .../union.js => flow_array_union/test.js} | 0 .../__snapshots__/jsfmt.spec.js.snap | 68 + tests/flow_function_parentheses/jsfmt.spec.js | 1 + tests/flow_function_parentheses/test.js | 31 + .../__snapshots__/jsfmt.spec.js.snap | 18 + .../flow_import_type_specifier/jsfmt.spec.js | 1 + .../test.js} | 0 yarn.lock | 6 + 238 files changed, 7129 insertions(+), 413 deletions(-) create mode 100644 scripts/sync-flow-tests.js create mode 100644 tests/flow/abnormal/toplevel_throw.js create mode 100644 tests/flow/call_caching1/__snapshots__/jsfmt.spec.js.snap rename tests/flow/{get-imports-and-importers => call_caching1}/jsfmt.spec.js (100%) create mode 100644 tests/flow/call_caching1/lib/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/call_caching1/lib/core.js create mode 100644 tests/flow/call_caching1/lib/immutable.js create mode 100644 tests/flow/call_caching1/lib/jsfmt.spec.js create mode 100644 tests/flow/call_caching1/test.js create mode 100644 tests/flow/call_caching1/test2.js create mode 100644 tests/flow/call_caching1/test3.js create mode 100644 tests/flow/call_caching2/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/call_caching2/jsfmt.spec.js create mode 100644 tests/flow/call_caching2/lib/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/call_caching2/lib/immutable.js create mode 100644 tests/flow/call_caching2/lib/jsfmt.spec.js create mode 100644 tests/flow/call_caching2/test.js create mode 100644 tests/flow/class_fields/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/class_fields/base_class.js create mode 100644 tests/flow/class_fields/derived_class.js create mode 100644 tests/flow/class_fields/generic_class.js create mode 100644 tests/flow/class_fields/jsfmt.spec.js create mode 100644 tests/flow/class_fields/scoping.js create mode 100644 tests/flow/class_method_default_parameters/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/class_method_default_parameters/class_method_default_parameters.js create mode 100644 tests/flow/class_method_default_parameters/jsfmt.spec.js create mode 100644 tests/flow/classes/extends_any.js create mode 100644 tests/flow/declaration_files_incremental_haste_name_reducers/A.js create mode 100644 tests/flow/declaration_files_incremental_haste_name_reducers/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/declaration_files_incremental_haste_name_reducers/index.js create mode 100644 tests/flow/declaration_files_incremental_haste_name_reducers/jsfmt.spec.js rename tests/flow/declare_type/lib/{declare_module_type_alias.js => DeclareModule_TypeAlias.js} (65%) delete mode 100644 tests/flow/destructuring/destructuring_param.js create mode 100644 tests/flow/destructuring/refinement_non_termination.js create mode 100644 tests/flow/esproposal_class_instance_fields.ignore/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/esproposal_class_instance_fields.ignore/jsfmt.spec.js create mode 100644 tests/flow/esproposal_class_instance_fields.ignore/test.js create mode 100644 tests/flow/esproposal_class_instance_fields.warn/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/esproposal_class_instance_fields.warn/jsfmt.spec.js create mode 100644 tests/flow/esproposal_class_instance_fields.warn/test.js create mode 100644 tests/flow/esproposal_class_static_fields.ignore/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/esproposal_class_static_fields.ignore/jsfmt.spec.js create mode 100644 tests/flow/esproposal_class_static_fields.ignore/test.js create mode 100644 tests/flow/esproposal_class_static_fields.warn/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/esproposal_class_static_fields.warn/jsfmt.spec.js create mode 100644 tests/flow/esproposal_class_static_fields.warn/test.js create mode 100644 tests/flow/esproposal_export_star_as.enable/test.js create mode 100644 tests/flow/esproposal_export_star_as.ignore/test.js create mode 100644 tests/flow/esproposal_export_star_as.warn/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/esproposal_export_star_as.warn/jsfmt.spec.js create mode 100644 tests/flow/esproposal_export_star_as.warn/test.js create mode 100644 tests/flow/focus-check/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/focus-check/a.js create mode 100644 tests/flow/focus-check/b.js create mode 100644 tests/flow/focus-check/jsfmt.spec.js create mode 100644 tests/flow/focus-check/test.js create mode 100644 tests/flow/function/rest_type.js create mode 100644 tests/flow/generators/refi.js rename tests/flow/{get-imports-and-importers => get-imports}/__snapshots__/jsfmt.spec.js.snap (100%) rename tests/flow/{get-imports-and-importers => get-imports}/a.js (100%) rename tests/flow/{get-imports-and-importers => get-imports}/b.js (100%) rename tests/flow/{get-imports-and-importers => get-imports}/c.js (100%) create mode 100644 tests/flow/get-imports/jsfmt.spec.js create mode 100644 tests/flow/getters_and_setters_enabled/declare_class.js create mode 100644 tests/flow/getters_and_setters_enabled/object_type.js create mode 100644 tests/flow/haste_name_reducers_defaults/Module1.js create mode 100644 tests/flow/haste_name_reducers_defaults/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/haste_name_reducers_defaults/index.js create mode 100644 tests/flow/haste_name_reducers_defaults/jsfmt.spec.js create mode 100644 tests/flow/haste_use_name_reducers/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/haste_use_name_reducers/dir1/Module1.js create mode 100644 tests/flow/haste_use_name_reducers/dir1/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/haste_use_name_reducers/dir1/jsfmt.spec.js create mode 100644 tests/flow/haste_use_name_reducers/dir2/Module2.ios.js create mode 100644 tests/flow/haste_use_name_reducers/dir2/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/haste_use_name_reducers/dir2/jsfmt.spec.js create mode 100644 tests/flow/haste_use_name_reducers/index.js create mode 100644 tests/flow/haste_use_name_reducers/jsfmt.spec.js create mode 100644 tests/flow/implements/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/implements/jsfmt.spec.js create mode 100644 tests/flow/implements/test.js create mode 100644 tests/flow/incremental_cycle_break/A.js create mode 100644 tests/flow/incremental_cycle_break/B.js create mode 100644 tests/flow/incremental_cycle_break/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/incremental_cycle_break/jsfmt.spec.js create mode 100644 tests/flow/incremental_cycle_break/tmp1/A.js create mode 100644 tests/flow/incremental_cycle_break/tmp1/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/incremental_cycle_break/tmp1/jsfmt.spec.js create mode 100644 tests/flow/incremental_haste_blacklist/A.js create mode 100644 tests/flow/incremental_haste_blacklist/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/incremental_haste_blacklist/index.js create mode 100644 tests/flow/incremental_haste_blacklist/jsfmt.spec.js create mode 100644 tests/flow/incremental_haste_name_reducers_duplicate/A.js create mode 100644 tests/flow/incremental_haste_name_reducers_duplicate/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/incremental_haste_name_reducers_duplicate/index.js create mode 100644 tests/flow/incremental_haste_name_reducers_duplicate/jsfmt.spec.js create mode 100644 tests/flow/match_failure/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/match_failure/disjoint_union.js create mode 100644 tests/flow/match_failure/enum.js create mode 100644 tests/flow/match_failure/jsfmt.spec.js create mode 100644 tests/flow/more_react/InitializedFields.js create mode 100644 tests/flow/more_react/checkPropTypes.js create mode 100644 tests/flow/multiflow/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/multiflow/apply.js create mode 100644 tests/flow/multiflow/issue3443.js create mode 100644 tests/flow/multiflow/jsfmt.spec.js create mode 100644 tests/flow/multiflow/jsx.js create mode 100644 tests/flow/multiflow/spread.js create mode 100644 tests/flow/multiflow_with_flowlib/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/multiflow_with_flowlib/jsfmt.spec.js create mode 100644 tests/flow/multiflow_with_flowlib/spread.js create mode 100644 tests/flow/new_spread/type_generic.js create mode 100644 tests/flow/object_assign/spread.js create mode 100644 tests/flow/objects/compatibility.js create mode 100644 tests/flow/optional/loop.js create mode 100644 tests/flow/optional_props/test3_exact_annot.js create mode 100644 tests/flow/react/create_class.js create mode 100644 tests/flow/react/create_class_initial_state_sealed.js create mode 100644 tests/flow/react/create_class_statics_sealed.js create mode 100644 tests/flow/react/proptype_any.js create mode 100644 tests/flow/react/proptype_custom_validator.js create mode 100644 tests/flow/react/proptype_incompatible.js create mode 100644 tests/flow/react/proptype_instanceOf.js create mode 100644 tests/flow/react/proptype_shape.js create mode 100644 tests/flow/react/proptypes_builtins.js create mode 100644 tests/flow/react/proptypes_sealed.js create mode 100644 tests/flow/react_modules/es6class-proptypes-callsite.js create mode 100644 tests/flow/react_modules/es6class-proptypes-module.js create mode 100644 tests/flow/recheck-haste/B1.js create mode 100644 tests/flow/recheck-haste/B3.js create mode 100644 tests/flow/recheck-haste/dir1B/B2.js create mode 100644 tests/flow/recheck-haste/dir1B/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/recheck-haste/dir1B/jsfmt.spec.js create mode 100644 tests/flow/recheck/i1.js create mode 100644 tests/flow/recheck/i2.js create mode 100644 tests/flow/recheck/j1.js create mode 100644 tests/flow/recheck/j2.js create mode 100644 tests/flow/recheck/tmp1i/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/recheck/tmp1i/i1.js create mode 100644 tests/flow/recheck/tmp1i/jsfmt.spec.js create mode 100644 tests/flow/recheck/tmp1j/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow/recheck/tmp1j/j1.js create mode 100644 tests/flow/recheck/tmp1j/jsfmt.spec.js create mode 100644 tests/flow/shape/shadow.js create mode 100644 tests/flow/suppress/D.js delete mode 100644 tests/flow/unicode/keys.js create mode 100644 tests/flow/union/blowup.js create mode 100644 tests/flow_array_comments/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow_array_comments/jsfmt.spec.js rename tests/{flow/arrays/comments.js => flow_array_comments/test.js} (100%) create mode 100644 tests/flow_array_union/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow_array_union/jsfmt.spec.js rename tests/{flow/arrays/union.js => flow_array_union/test.js} (100%) create mode 100644 tests/flow_function_parentheses/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow_function_parentheses/jsfmt.spec.js create mode 100644 tests/flow_function_parentheses/test.js create mode 100644 tests/flow_import_type_specifier/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/flow_import_type_specifier/jsfmt.spec.js rename tests/{flow/import_type/import_type_specifier.js => flow_import_type_specifier/test.js} (100%) diff --git a/README.md b/README.md index 4cb8099a..8c193e0e 100644 --- a/README.md +++ b/README.md @@ -442,23 +442,30 @@ Show the world you're using *Prettier* → [![styled with prettier](https://img. ## Contributing -We will work on better docs over time, but in the mean time, here are -a few notes if you are interested in contributing: +To get up and running, install the dependencies and run the tests: -* You should be able to get up and running with just `yarn`. -* This uses [Jest](https://facebook.github.io/jest/) snapshots for tests. The entire Flow test suite is - included here. You can make changes and run `jest -u`, and then - `git diff` to see the styles that changed. Always update the - snapshots if opening a PR. -* If you can, look at [commands.md](commands.md) and check out - [Wadler's paper](http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf) - to understand how this works. I will try to write a better explanation soon. -* I haven't set up any automated tests yet, but for now as long as you - run `jest -u` to update the snapshots and I see them in the PR, that's fine. -* You can run `AST_COMPARE=1 jest` for a more robust test run. That - formats each file, re-parses it, and compares the new AST with the - original one and makes sure they are semantically equivalent. - * Each test folder has a `jsfmt.spec.js` that runs the tests. - Normally you can just put `run_spec(__dirname);` there but if you want to pass - specific options, you can add the options object as the 2nd parameter like: - `run_spec(__dirname, { parser: 'babylon' });` +``` +yarn +yarn test +``` + +Here's what you need to know about the tests: + +* The tests uses [Jest](https://facebook.github.io/jest/) snapshots. +* You can make changes and run `jest -u` to update the snapshots. Then run `git + diff` to take a look at what changed. Always update the snapshots when opening + a PR. +* You can run `AST_COMPARE=1 jest` for a more robust test run. That formats each + file, re-parses it, and compares the new AST with the original one and makes + sure they are semantically equivalent. +* Each test folder has a `jsfmt.spec.js` that runs the tests. Normally you can + just put `run_spec(__dirname);` there. You can also pass options and + additional parsers, like this: + `run_spec(__dirname, { trailingComma: "es5" }, ["babylon"]);` +* `tests/flow/` contains the Flow test suite, and is not supposed to be edited + by hand. To update it, clone the Flow repo next to the Prettier repo and run: + `node scripts/sync-flow-tests.js ../flow/tests/`. + +If you can, take look at [commands.md](commands.md) and check out [Wadler's +paper](http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf) to +understand how Prettier works. diff --git a/package.json b/package.json index 204b4849..22fd8dbb 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,8 @@ "devDependencies": { "diff": "3.2.0", "jest": "19.0.1", + "mkdirp": "^0.5.1", + "rimraf": "^2.6.1", "rollup": "0.41.1", "rollup-plugin-commonjs": "7.0.0", "rollup-plugin-json": "2.1.0", diff --git a/scripts/sync-flow-tests.js b/scripts/sync-flow-tests.js new file mode 100644 index 00000000..68a82f74 --- /dev/null +++ b/scripts/sync-flow-tests.js @@ -0,0 +1,125 @@ +const fs = require("fs"); +const flowParser = require("flow-parser"); +const glob = require("glob"); +const mkdirp = require("mkdirp"); +const path = require("path"); +const rimraf = require("rimraf"); + +const DEFAULT_SPEC_CONTENT = "run_spec(__dirname);\n"; +const SPEC_FILE_NAME = "jsfmt.spec.js"; +const FLOW_TESTS_DIR = path.join(__dirname, "..", "tests", "flow"); + +function tryParse(file, content) { + const ast = flowParser.parse(content, { + esproposal_class_instance_fields: true, + esproposal_class_static_fields: true, + esproposal_export_star_as: true + }); + + if (ast.errors.length > 0) { + const line = ast.errors[0].loc.start.line; + const column = ast.errors[0].loc.start.column; + const message = ast.errors[0].message; + return `${file}:${line}:${column}: ${message}`; + } + + return null; +} + +function syncTests(syncDir) { + const specFiles = glob.sync(path.join(FLOW_TESTS_DIR, "**", SPEC_FILE_NAME)); + const filesToCopy = glob.sync(path.join(syncDir, "**/*.js")); + + if (filesToCopy.length === 0) { + throw new Error( + [ + "Couldn't find any files to copy.", + `Please make sure that \`${syncDir}\` exists and contains the flow tests.` + ].join("\n") + ) + } + + const specContents = specFiles.reduce((obj, specFile) => { + obj[specFile] = fs.readFileSync(specFile, "utf8"); + return obj; + }, {}); + + const skipped = []; + + rimraf.sync(FLOW_TESTS_DIR); + + filesToCopy.forEach(file => { + const content = fs.readFileSync(file, "utf8"); + const parseError = tryParse(file, content); + + if (parseError) { + skipped.push(parseError); + return; + } + + const newFile = path.join(FLOW_TESTS_DIR, path.relative(syncDir, file)); + const dirname = path.dirname(newFile); + const specFile = path.join(dirname, SPEC_FILE_NAME); + const specContent = specContents[specFile] || DEFAULT_SPEC_CONTENT; + + mkdirp.sync(dirname); + fs.writeFileSync(newFile, content); + fs.writeFileSync(specFile, specContent); + }); + + return skipped; +} + +function run(argv) { + if (argv.length !== 1) { + console.error( + [ + "You must provide the path to a flow tests directory to sync from!", + "Example: node scripts/sync-flow-tests.js ../flow/tests/" + ].join("\n") + ); + return 1; + } + + const syncDir = argv[0]; + let skipped = []; + + try { + skipped = syncTests(syncDir); + } catch (error) { + console.error(`Failed to sync.\n${error}`); + return 1; + } + + if (skipped.length > 0) { + console.log( + [ + "Some files were skipped due to syntax errors.", + "This is expected since flow tests for handling invalid code,", + "but that's not interesting for Prettier's tests.", + "This is the skipped stuff:", + "" + ] + .concat(skipped, "") + .join("\n") + ); + } + + console.log( + [ + "Done syncing! Now you need to:", + "", + `1. Optional: Adjust some ${SPEC_FILE_NAME} files.`, + "2. Run `jest -u` to create snapshots.", + "3. Run `git diff` to check how tests and snapshots have changed", + "4. Take a look at new snapshots to see if they're OK." + ].join("\n") + ); + + return 0; +} + +if (require.main === module) { + const exitCode = run(process.argv.slice(2)); + process.exit(exitCode); +} diff --git a/tests/flow/abnormal/__snapshots__/jsfmt.spec.js.snap b/tests/flow/abnormal/__snapshots__/jsfmt.spec.js.snap index 3434a6bb..829dbc53 100644 --- a/tests/flow/abnormal/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/abnormal/__snapshots__/jsfmt.spec.js.snap @@ -39,3 +39,14 @@ function foo() { } " `; + +exports[`toplevel_throw.js 1`] = ` +"// @flow + +throw new Error('foo'); // no error +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +throw new Error(\\"foo\\"); // no error +" +`; diff --git a/tests/flow/abnormal/toplevel_throw.js b/tests/flow/abnormal/toplevel_throw.js new file mode 100644 index 00000000..91f0b353 --- /dev/null +++ b/tests/flow/abnormal/toplevel_throw.js @@ -0,0 +1,3 @@ +// @flow + +throw new Error('foo'); // no error diff --git a/tests/flow/annot/__snapshots__/jsfmt.spec.js.snap b/tests/flow/annot/__snapshots__/jsfmt.spec.js.snap index f22c3d33..d215387d 100644 --- a/tests/flow/annot/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/annot/__snapshots__/jsfmt.spec.js.snap @@ -61,38 +61,6 @@ var zer : null = null; function foobar(n : ?number) : number | null | void { return n; } function barfoo(n : number | null | void) : ?number { return n; } - -type Banana = { - eat: string => boolean, -}; - -type Hex = {n: 0x01}; - -type T = { method: (a) => void }; - -type T = { method(a): void }; - -declare class X { method(a): void } - -declare function f(a): void; - -var f: (a) => void; - -interface F { m(string): number } - -interface F { m: (string) => number } - -function f(o: { f: (string) => void }) {} - -function f(o: { f(string): void }) {} - -type f = (...arg) => void; - -type f = (/* comment */ arg) => void; - -type f = (arg /* comment */) => void; - -type f = (?arg) => void; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ function foo(str: string, i: number): string { return str; @@ -163,38 +131,6 @@ function foobar(n: ?number): number | null | void { function barfoo(n: number | null | void): ?number { return n; } - -type Banana = { - eat: string => boolean -}; - -type Hex = { n: 0x01 }; - -type T = { method: a => void }; - -type T = { method(a): void }; - -declare class X { method(a): void } - -declare function f(a): void; - -var f: a => void; - -interface F { m(string): number } - -interface F { m: string => number } - -function f(o: { f: string => void }) {} - -function f(o: { f(string): void }) {} - -type f = (...arg) => void; - -type f = /* comment */ arg => void; - -type f = arg /* comment */ => void; - -type f = ?arg => void; " `; diff --git a/tests/flow/annot/annot.js b/tests/flow/annot/annot.js index 9e5a72b0..4d1b25a5 100644 --- a/tests/flow/annot/annot.js +++ b/tests/flow/annot/annot.js @@ -58,35 +58,3 @@ var zer : null = null; function foobar(n : ?number) : number | null | void { return n; } function barfoo(n : number | null | void) : ?number { return n; } - -type Banana = { - eat: string => boolean, -}; - -type Hex = {n: 0x01}; - -type T = { method: (a) => void }; - -type T = { method(a): void }; - -declare class X { method(a): void } - -declare function f(a): void; - -var f: (a) => void; - -interface F { m(string): number } - -interface F { m: (string) => number } - -function f(o: { f: (string) => void }) {} - -function f(o: { f(string): void }) {} - -type f = (...arg) => void; - -type f = (/* comment */ arg) => void; - -type f = (arg /* comment */) => void; - -type f = (?arg) => void; diff --git a/tests/flow/arrays/__snapshots__/jsfmt.spec.js.snap b/tests/flow/arrays/__snapshots__/jsfmt.spec.js.snap index b98fa23d..28b3cd9c 100644 --- a/tests/flow/arrays/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/arrays/__snapshots__/jsfmt.spec.js.snap @@ -82,27 +82,6 @@ module.exports = \\"arrays\\"; " `; -exports[`comments.js 1`] = ` -"export type FileMetaData = [ - /* id */ string, - /* mtime */ number, - /* visited */ 0|1, - /* dependencies */ Array, -]; - -export type ModuleMetaData = [Path, /* type */ number]; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -export type FileMetaData = [ - /* id */ string, - /* mtime */ number, - /* visited */ 0 | 1, - /* dependencies */ Array -]; - -export type ModuleMetaData = [Path, /* type */ number]; -" -`; - exports[`numeric_elem.js 1`] = ` "var arr = []; var day = new Date; @@ -121,10 +100,3 @@ arr[day] = 0; (arr[day]: string); // error: number ~> string " `; - -exports[`union.js 1`] = ` -"let arr: (number|string)[] = []; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -let arr: (number | string)[] = []; -" -`; diff --git a/tests/flow/call_caching1/__snapshots__/jsfmt.spec.js.snap b/tests/flow/call_caching1/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..2cc3053d --- /dev/null +++ b/tests/flow/call_caching1/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,101 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`test.js 1`] = ` +"// @flow + +const Immutable = require('immutable'); + +const tasksPerStatusMap = new Map( + [].map(taskStatus => [taskStatus, new Map()]), +); +for (let [taskStatus, tasksMap] of tasksPerStatusMap) { + tasksPerStatusMap.set(taskStatus, Immutable.Map(tasksMap)); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +const Immutable = require(\\"immutable\\"); + +const tasksPerStatusMap = new Map( + [].map(taskStatus => [taskStatus, new Map()]) +); +for (let [taskStatus, tasksMap] of tasksPerStatusMap) { + tasksPerStatusMap.set(taskStatus, Immutable.Map(tasksMap)); +} +" +`; + +exports[`test2.js 1`] = ` +"/* @flow */ + +declare class Bar { + update(updater: (value: this) => Bar): Bar; +} + +declare function foo( + initialValue: U, + callbackfn: (previousValue: U) => U +): U; + +declare var items: Bar; +declare var updater: (value: Bar) => Bar; + +foo( + items, + (acc) => acc.update(updater) +); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ + +declare class Bar { + update(updater: (value: this) => Bar): Bar +} + +declare function foo( + initialValue: U, + callbackfn: (previousValue: U) => U +): U; + +declare var items: Bar; +declare var updater: (value: Bar) => Bar; + +foo(items, acc => acc.update(updater)); +" +`; + +exports[`test3.js 1`] = ` +"// @flow + +declare class ImmBox { + static (x: any): ImmBox; + static (x: any): any; +} + +declare class Box { + constructor(x: T): void; + set(value: T): void; + get(): T; +} + +const outer = new Box(); +const inner = outer.get(); +outer.set(ImmBox(inner)); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +declare class ImmBox { + static (x: any): ImmBox, + static (x: any): any +} + +declare class Box { + constructor(x: T): void, + set(value: T): void, + get(): T +} + +const outer = new Box(); +const inner = outer.get(); +outer.set(ImmBox(inner)); +" +`; diff --git a/tests/flow/get-imports-and-importers/jsfmt.spec.js b/tests/flow/call_caching1/jsfmt.spec.js similarity index 100% rename from tests/flow/get-imports-and-importers/jsfmt.spec.js rename to tests/flow/call_caching1/jsfmt.spec.js diff --git a/tests/flow/call_caching1/lib/__snapshots__/jsfmt.spec.js.snap b/tests/flow/call_caching1/lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..64ae4467 --- /dev/null +++ b/tests/flow/call_caching1/lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,80 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`core.js 1`] = ` +"declare class Array { + @@iterator(): Iterator; + map(callbackfn: (value: T, index: number, array: Array) => U, thisArg?: any): Array; +} + +type IteratorResult = + | { done: true, value?: Return } + | { done: false, value: Yield }; + +interface $Iterator<+Yield,+Return,-Next> { + @@iterator(): $Iterator; + next(value?: Next): IteratorResult; +} +type Iterator<+T> = $Iterator; + +interface $Iterable<+Yield,+Return,-Next> { + @@iterator(): $Iterator; +} +type Iterable<+T> = $Iterable; + +declare class Map { + @@iterator(): Iterator<[K, V]>; + constructor(iterable: ?Iterable<[K, V]>): void; + set(key: K, value: V): Map; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +declare class Array { + @@iterator(): Iterator, + map( + callbackfn: (value: T, index: number, array: Array) => U, + thisArg?: any + ): Array +} + +type IteratorResult = + | { done: true, value?: Return } + | { done: false, value: Yield }; + +interface $Iterator<+Yield, +Return, -Next> { + @@iterator(): $Iterator, + next(value?: Next): IteratorResult +} +type Iterator<+T> = $Iterator; + +interface $Iterable<+Yield, +Return, -Next> { + @@iterator(): $Iterator +} +type Iterable<+T> = $Iterable; + +declare class Map { + @@iterator(): Iterator<[K, V]>, + constructor(iterable: ?Iterable<[K, V]>): void, + set(key: K, value: V): Map +} +" +`; + +exports[`immutable.js 1`] = ` +"declare module \\"immutable\\" { + declare class Map { + static (iter: Iterator<[K,V]>): Map; + static (object: {+[k:K]:V}): Map; + + set(key: K, value: V): this; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +declare module \\"immutable\\" { + declare class Map { + static (iter: Iterator<[K, V]>): Map, + static (object: { +[k: K]: V }): Map, + + set(key: K, value: V): this + } +} +" +`; diff --git a/tests/flow/call_caching1/lib/core.js b/tests/flow/call_caching1/lib/core.js new file mode 100644 index 00000000..8e660a0b --- /dev/null +++ b/tests/flow/call_caching1/lib/core.js @@ -0,0 +1,25 @@ +declare class Array { + @@iterator(): Iterator; + map(callbackfn: (value: T, index: number, array: Array) => U, thisArg?: any): Array; +} + +type IteratorResult = + | { done: true, value?: Return } + | { done: false, value: Yield }; + +interface $Iterator<+Yield,+Return,-Next> { + @@iterator(): $Iterator; + next(value?: Next): IteratorResult; +} +type Iterator<+T> = $Iterator; + +interface $Iterable<+Yield,+Return,-Next> { + @@iterator(): $Iterator; +} +type Iterable<+T> = $Iterable; + +declare class Map { + @@iterator(): Iterator<[K, V]>; + constructor(iterable: ?Iterable<[K, V]>): void; + set(key: K, value: V): Map; +} diff --git a/tests/flow/call_caching1/lib/immutable.js b/tests/flow/call_caching1/lib/immutable.js new file mode 100644 index 00000000..f3eb645a --- /dev/null +++ b/tests/flow/call_caching1/lib/immutable.js @@ -0,0 +1,8 @@ +declare module "immutable" { + declare class Map { + static (iter: Iterator<[K,V]>): Map; + static (object: {+[k:K]:V}): Map; + + set(key: K, value: V): this; + } +} diff --git a/tests/flow/call_caching1/lib/jsfmt.spec.js b/tests/flow/call_caching1/lib/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/call_caching1/lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/call_caching1/test.js b/tests/flow/call_caching1/test.js new file mode 100644 index 00000000..961ab581 --- /dev/null +++ b/tests/flow/call_caching1/test.js @@ -0,0 +1,10 @@ +// @flow + +const Immutable = require('immutable'); + +const tasksPerStatusMap = new Map( + [].map(taskStatus => [taskStatus, new Map()]), +); +for (let [taskStatus, tasksMap] of tasksPerStatusMap) { + tasksPerStatusMap.set(taskStatus, Immutable.Map(tasksMap)); +} diff --git a/tests/flow/call_caching1/test2.js b/tests/flow/call_caching1/test2.js new file mode 100644 index 00000000..3a6df900 --- /dev/null +++ b/tests/flow/call_caching1/test2.js @@ -0,0 +1,18 @@ +/* @flow */ + +declare class Bar { + update(updater: (value: this) => Bar): Bar; +} + +declare function foo( + initialValue: U, + callbackfn: (previousValue: U) => U +): U; + +declare var items: Bar; +declare var updater: (value: Bar) => Bar; + +foo( + items, + (acc) => acc.update(updater) +); diff --git a/tests/flow/call_caching1/test3.js b/tests/flow/call_caching1/test3.js new file mode 100644 index 00000000..4392f59b --- /dev/null +++ b/tests/flow/call_caching1/test3.js @@ -0,0 +1,16 @@ +// @flow + +declare class ImmBox { + static (x: any): ImmBox; + static (x: any): any; +} + +declare class Box { + constructor(x: T): void; + set(value: T): void; + get(): T; +} + +const outer = new Box(); +const inner = outer.get(); +outer.set(ImmBox(inner)); diff --git a/tests/flow/call_caching2/__snapshots__/jsfmt.spec.js.snap b/tests/flow/call_caching2/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..cb82180a --- /dev/null +++ b/tests/flow/call_caching2/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,16 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`test.js 1`] = ` +"// @flow + +function Foo(items: ?Iterable) { + Iterable(items || []).size; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +function Foo(items: ?Iterable) { + Iterable(items || []).size; +} +" +`; diff --git a/tests/flow/call_caching2/jsfmt.spec.js b/tests/flow/call_caching2/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/call_caching2/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/call_caching2/lib/__snapshots__/jsfmt.spec.js.snap b/tests/flow/call_caching2/lib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..7c2fa1e6 --- /dev/null +++ b/tests/flow/call_caching2/lib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`immutable.js 1`] = ` +"// Copyright 2004-present Facebook. All Rights Reserved. + +declare class Array { } + +declare class Iterable { + static >(iter: Iter): Iter; + static (iter: Array): Iterable; + size: number; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Copyright 2004-present Facebook. All Rights Reserved. + +declare class Array {} + +declare class Iterable { + static >(iter: Iter): Iter, + static (iter: Array): Iterable, + size: number +} +" +`; diff --git a/tests/flow/call_caching2/lib/immutable.js b/tests/flow/call_caching2/lib/immutable.js new file mode 100644 index 00000000..8331ab63 --- /dev/null +++ b/tests/flow/call_caching2/lib/immutable.js @@ -0,0 +1,9 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +declare class Array { } + +declare class Iterable { + static >(iter: Iter): Iter; + static (iter: Array): Iterable; + size: number; +} diff --git a/tests/flow/call_caching2/lib/jsfmt.spec.js b/tests/flow/call_caching2/lib/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/call_caching2/lib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/call_caching2/test.js b/tests/flow/call_caching2/test.js new file mode 100644 index 00000000..9ff68478 --- /dev/null +++ b/tests/flow/call_caching2/test.js @@ -0,0 +1,5 @@ +// @flow + +function Foo(items: ?Iterable) { + Iterable(items || []).size; +} diff --git a/tests/flow/class_fields/__snapshots__/jsfmt.spec.js.snap b/tests/flow/class_fields/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..3fd5bd83 --- /dev/null +++ b/tests/flow/class_fields/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,573 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`base_class.js 1`] = ` +"// @flow + +class Base { + unannotatedField; + annotatedField: number; + initializedField = 42; + initializedFieldWithThis = this.initializedField; + annotatedInitializedFieldValid: ?number = 42; + annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number + + static unannotatedField; + static annotatedField: number; + static initializedField = 'asdf'; + static initializedFieldWithThis = this.initializedField; + static annotatedInitializedFieldValid: ?number = 42; + static annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number +} + +var o = new Base(); + +/** + * Unannotated fields are open. + */ +(o.unannotatedField: string); +(o.unannotatedField: number); +(Base.unannotatedField: string); +(Base.unannotatedField: number); + +/** + * Annotated (but uninitialized) fields still have a type. + */ +(o.annotatedField: number); +(o.annotatedField: string); // Error: number ~> string +(Base.annotatedField: number); +(Base.annotatedField: string); // Error: number ~> string + +/** + * Initialized (but unannotated) fields assume the type of their initializer. + */ +(o.initializedField: number); +(o.initializedField: string); // Error: number ~> string +(Base.initializedField: string); +(Base.initializedField: number); // Error: string ~> number + +/** + * Initialized fields can reference \`this\`. + */ +(o.initializedFieldWithThis: number); +(o.initializedFieldWithThis: string); // Error: number ~> string +(Base.initializedFieldWithThis: string); +(Base.initializedFieldWithThis: number); // Error: string ~> number + +/** + * Initialized + annotated fields take the type of the annotation. + * (Note that this matters when the annotation is more general than the type of + * the initializer) + */ +(o.annotatedInitializedFieldValid: ?number); +(o.annotatedInitializedFieldValid: number); // Error: ?number ~> number +(Base.annotatedInitializedFieldValid: ?number); +(Base.annotatedInitializedFieldValid: number); // Error: ?number ~> number + +/** + * Initialized + annotated fields where the init/annot combo is a mismatch + * should assume the type of the annotation. + * + * (This happens in addition to erroring at the site of initialization) + */ +(o.annotatedInitializedFieldInvalid: number); +(o.annotatedInitializedFieldInvalid: string); // Error: number ~> string +(Base.annotatedInitializedFieldInvalid: number); +(Base.annotatedInitializedFieldInvalid: string); // Error: number ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +class Base { + unannotatedField; + annotatedField: number; + initializedField = 42; + initializedFieldWithThis = this.initializedField; + annotatedInitializedFieldValid: ?number = 42; + annotatedInitializedFieldInvalid: number = \\"asdf\\"; // Error: string ~> number + + static unannotatedField; + static annotatedField: number; + static initializedField = \\"asdf\\"; + static initializedFieldWithThis = this.initializedField; + static annotatedInitializedFieldValid: ?number = 42; + static annotatedInitializedFieldInvalid: number = \\"asdf\\"; // Error: string ~> number +} + +var o = new Base(); + +/** + * Unannotated fields are open. + */ +(o.unannotatedField: string); +(o.unannotatedField: number); +(Base.unannotatedField: string); +(Base.unannotatedField: number); + +/** + * Annotated (but uninitialized) fields still have a type. + */ +(o.annotatedField: number); +(o.annotatedField: string); // Error: number ~> string +(Base.annotatedField: number); +(Base.annotatedField: string); // Error: number ~> string + +/** + * Initialized (but unannotated) fields assume the type of their initializer. + */ +(o.initializedField: number); +(o.initializedField: string); // Error: number ~> string +(Base.initializedField: string); +(Base.initializedField: number); // Error: string ~> number + +/** + * Initialized fields can reference \`this\`. + */ +(o.initializedFieldWithThis: number); +(o.initializedFieldWithThis: string); // Error: number ~> string +(Base.initializedFieldWithThis: string); +(Base.initializedFieldWithThis: number); // Error: string ~> number + +/** + * Initialized + annotated fields take the type of the annotation. + * (Note that this matters when the annotation is more general than the type of + * the initializer) + */ +(o.annotatedInitializedFieldValid: ?number); +(o.annotatedInitializedFieldValid: number); // Error: ?number ~> number +(Base.annotatedInitializedFieldValid: ?number); +(Base.annotatedInitializedFieldValid: number); // Error: ?number ~> number + +/** + * Initialized + annotated fields where the init/annot combo is a mismatch + * should assume the type of the annotation. + * + * (This happens in addition to erroring at the site of initialization) + */ +(o.annotatedInitializedFieldInvalid: number); +(o.annotatedInitializedFieldInvalid: string); // Error: number ~> string +(Base.annotatedInitializedFieldInvalid: number); +(Base.annotatedInitializedFieldInvalid: string); // Error: number ~> string +" +`; + +exports[`derived_class.js 1`] = ` +"// @flow + +class Base { + base_unannotatedField; + base_annotatedField: number; + base_initializedField = 42; + base_initializedFieldWithThis = this.base_initializedField; + base_annotatedInitializedFieldValid: ?number = 42; + base_annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number + + static base_unannotatedField; + static base_annotatedField: number; + static base_initializedField = 'asdf'; + static base_initializedFieldWithThis = this.base_initializedField; + static base_annotatedInitializedFieldValid: ?number = 42; + static base_annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number + + inherited_initializer = 42; + static inherited_initializer = 42; +} + +class Child extends Base { + child_unannotatedField; + child_annotatedField: number; + child_initializedField = 42; + child_initializedFieldWithThis = this.child_initializedField; + child_annotatedInitializedFieldValid: ?number = 42; + child_annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number + + static child_unannotatedField; + static child_annotatedField: number; + static child_initializedField = 'asdf'; + static child_initializedFieldWithThis = this.child_initializedField; + static child_annotatedInitializedFieldValid: ?number = 42; + static child_annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number + + inherited_initializer; + static inherited_initializer; +} + +var o = new Child(); + +/** + * Unannotated fields are open. + */ +(o.base_unannotatedField: string); +(o.base_unannotatedField: number); +(Child.base_unannotatedField: string); +(Child.base_unannotatedField: number); + +(o.child_unannotatedField: string); +(o.child_unannotatedField: number); +(Child.child_unannotatedField: string); +(Child.child_unannotatedField: number); + + +/** + * Annotated (but uninitialized) fields still have a type. + */ +(o.base_annotatedField: number); +(o.base_annotatedField: string); // Error: number ~> string +(Child.base_annotatedField: number); +(Child.base_annotatedField: string); // Error: number ~> string + +(o.child_annotatedField: number); +(o.child_annotatedField: string); // Error: number ~> string +(Child.child_annotatedField: number); +(Child.child_annotatedField: string); // Error: number ~> string + +/** + * Initialized (but unannotated) fields assume the type of their initializer. + */ +(o.base_initializedField: number); +(o.base_initializedField: string); // Error: number ~> string +(Child.base_initializedField: string); +(Child.base_initializedField: number); // Error: string ~> number + +(o.child_initializedField: number); +(o.child_initializedField: string); // Error: number ~> string +(Child.child_initializedField: string); +(Child.child_initializedField: number); // Error: string ~> number + +/** + * Initialized fields can reference \`this\`. + */ +(o.base_initializedFieldWithThis: number); +(o.base_initializedFieldWithThis: string); // Error: number ~> string +(Child.base_initializedFieldWithThis: string); +(Child.base_initializedFieldWithThis: number); // Error: string ~> number + +(o.child_initializedFieldWithThis: number); +(o.child_initializedFieldWithThis: string); // Error: number ~> string +(Child.child_initializedFieldWithThis: string); +(Child.child_initializedFieldWithThis: number); // Error: string ~> number + +/** + * Initialized + annotated fields take the type of the annotation. + * (Note that this matters when the annotation is more general than the type of + * the initializer) + */ +(o.base_annotatedInitializedFieldValid: ?number); +(o.base_annotatedInitializedFieldValid: number); // Error: ?number ~> number +(Child.base_annotatedInitializedFieldValid: ?number); +(Child.base_annotatedInitializedFieldValid: number); // Error: ?number ~> number + +(o.child_annotatedInitializedFieldValid: ?number); +(o.child_annotatedInitializedFieldValid: number); // Error: ?number ~> number +(Child.child_annotatedInitializedFieldValid: ?number); +(Child.child_annotatedInitializedFieldValid: number); // Error: ?number ~> number + +/** + * Initialized + annotated fields where the init/annot combo is a mismatch + * should assume the type of the annotation. + * + * (This happens in addition to erroring at the site of initialization) + */ +(o.base_annotatedInitializedFieldInvalid: number); +(o.base_annotatedInitializedFieldInvalid: string); // Error: number ~> string +(Child.base_annotatedInitializedFieldInvalid: number); +(Child.base_annotatedInitializedFieldInvalid: string); // Error: number ~> string + +(o.child_annotatedInitializedFieldInvalid: number); +(o.child_annotatedInitializedFieldInvalid: string); // Error: number ~> string +(Child.child_annotatedInitializedFieldInvalid: number); +(Child.child_annotatedInitializedFieldInvalid: string); // Error: number ~> string + +/** + * Derived fields without an initializer that shadow base fields *with* an + * initializer should have the type of the base field. + */ +(o.inherited_initializer: number); +(o.inherited_initializer: string); // Error: number ~> string +(Child.inherited_initializer: number); +(Child.inherited_initializer: string); // Error: number ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +class Base { + base_unannotatedField; + base_annotatedField: number; + base_initializedField = 42; + base_initializedFieldWithThis = this.base_initializedField; + base_annotatedInitializedFieldValid: ?number = 42; + base_annotatedInitializedFieldInvalid: number = \\"asdf\\"; // Error: string ~> number + + static base_unannotatedField; + static base_annotatedField: number; + static base_initializedField = \\"asdf\\"; + static base_initializedFieldWithThis = this.base_initializedField; + static base_annotatedInitializedFieldValid: ?number = 42; + static base_annotatedInitializedFieldInvalid: number = \\"asdf\\"; // Error: string ~> number + + inherited_initializer = 42; + static inherited_initializer = 42; +} + +class Child extends Base { + child_unannotatedField; + child_annotatedField: number; + child_initializedField = 42; + child_initializedFieldWithThis = this.child_initializedField; + child_annotatedInitializedFieldValid: ?number = 42; + child_annotatedInitializedFieldInvalid: number = \\"asdf\\"; // Error: string ~> number + + static child_unannotatedField; + static child_annotatedField: number; + static child_initializedField = \\"asdf\\"; + static child_initializedFieldWithThis = this.child_initializedField; + static child_annotatedInitializedFieldValid: ?number = 42; + static child_annotatedInitializedFieldInvalid: number = \\"asdf\\"; // Error: string ~> number + + inherited_initializer; + static inherited_initializer; +} + +var o = new Child(); + +/** + * Unannotated fields are open. + */ +(o.base_unannotatedField: string); +(o.base_unannotatedField: number); +(Child.base_unannotatedField: string); +(Child.base_unannotatedField: number); + +(o.child_unannotatedField: string); +(o.child_unannotatedField: number); +(Child.child_unannotatedField: string); +(Child.child_unannotatedField: number); + +/** + * Annotated (but uninitialized) fields still have a type. + */ +(o.base_annotatedField: number); +(o.base_annotatedField: string); // Error: number ~> string +(Child.base_annotatedField: number); +(Child.base_annotatedField: string); // Error: number ~> string + +(o.child_annotatedField: number); +(o.child_annotatedField: string); // Error: number ~> string +(Child.child_annotatedField: number); +(Child.child_annotatedField: string); // Error: number ~> string + +/** + * Initialized (but unannotated) fields assume the type of their initializer. + */ +(o.base_initializedField: number); +(o.base_initializedField: string); // Error: number ~> string +(Child.base_initializedField: string); +(Child.base_initializedField: number); // Error: string ~> number + +(o.child_initializedField: number); +(o.child_initializedField: string); // Error: number ~> string +(Child.child_initializedField: string); +(Child.child_initializedField: number); // Error: string ~> number + +/** + * Initialized fields can reference \`this\`. + */ +(o.base_initializedFieldWithThis: number); +(o.base_initializedFieldWithThis: string); // Error: number ~> string +(Child.base_initializedFieldWithThis: string); +(Child.base_initializedFieldWithThis: number); // Error: string ~> number + +(o.child_initializedFieldWithThis: number); +(o.child_initializedFieldWithThis: string); // Error: number ~> string +(Child.child_initializedFieldWithThis: string); +(Child.child_initializedFieldWithThis: number); // Error: string ~> number + +/** + * Initialized + annotated fields take the type of the annotation. + * (Note that this matters when the annotation is more general than the type of + * the initializer) + */ +(o.base_annotatedInitializedFieldValid: ?number); +(o.base_annotatedInitializedFieldValid: number); // Error: ?number ~> number +(Child.base_annotatedInitializedFieldValid: ?number); +(Child.base_annotatedInitializedFieldValid: number); // Error: ?number ~> number + +(o.child_annotatedInitializedFieldValid: ?number); +(o.child_annotatedInitializedFieldValid: number); // Error: ?number ~> number +(Child.child_annotatedInitializedFieldValid: ?number); +(Child.child_annotatedInitializedFieldValid: number); // Error: ?number ~> number + +/** + * Initialized + annotated fields where the init/annot combo is a mismatch + * should assume the type of the annotation. + * + * (This happens in addition to erroring at the site of initialization) + */ +(o.base_annotatedInitializedFieldInvalid: number); +(o.base_annotatedInitializedFieldInvalid: string); // Error: number ~> string +(Child.base_annotatedInitializedFieldInvalid: number); +(Child.base_annotatedInitializedFieldInvalid: string); // Error: number ~> string + +(o.child_annotatedInitializedFieldInvalid: number); +(o.child_annotatedInitializedFieldInvalid: string); // Error: number ~> string +(Child.child_annotatedInitializedFieldInvalid: number); +(Child.child_annotatedInitializedFieldInvalid: string); // Error: number ~> string + +/** + * Derived fields without an initializer that shadow base fields *with* an + * initializer should have the type of the base field. + */ +(o.inherited_initializer: number); +(o.inherited_initializer: string); // Error: number ~> string +(Child.inherited_initializer: number); +(Child.inherited_initializer: string); // Error: number ~> string +" +`; + +exports[`generic_class.js 1`] = ` +"// @flow + +/** + * Fields annotated with a generic should assume a type once the type param + * is instantiated. + */ +class ClassAnnotated { + p: T; + static p: T; +} + +var o1 = new ClassAnnotated(); +o1.p = 42; +(o1.p: number); +(o1.p: string); // Error: number ~> string +ClassAnnotated.p = 42; +(ClassAnnotated.p: number); +(ClassAnnotated.p: string); // Error: number ~> string + + +/** + * It's always an error to initialized a generically-typed field with an + * expression of any type other than the generic itself. + */ +class ClassGenericInitialized { + invalid: T = 42; // Error: number ~> Generic + valid: T = ((42:any):T); + + static invalid: T = 42; // Error: number ~> Generic + static valid: T = ((42:any):T); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +/** + * Fields annotated with a generic should assume a type once the type param + * is instantiated. + */ +class ClassAnnotated { + p: T; + static p: T; +} + +var o1 = new ClassAnnotated(); +o1.p = 42; +(o1.p: number); +(o1.p: string); // Error: number ~> string +ClassAnnotated.p = 42; +(ClassAnnotated.p: number); +(ClassAnnotated.p: string); // Error: number ~> string + +/** + * It's always an error to initialized a generically-typed field with an + * expression of any type other than the generic itself. + */ +class ClassGenericInitialized { + invalid: T = 42; // Error: number ~> Generic + valid: T = ((42: any): T); + + static invalid: T = 42; // Error: number ~> Generic + static valid: T = ((42: any): T); +} +" +`; + +exports[`scoping.js 1`] = ` +"// @flow + +var someVar = 42; + +class Foo { + outer = someVar; + selfTyped: Foo; + selfTypedInit = new Foo(); + + static outer = someVar; + static selfTyped: Foo; + static selfTypedInit = new Foo(); + + constructor() { + var someVar = 'asdf'; + } +} + +/** + * Field initializers execute in a scope immediately under the scope outside the + * class definition. + */ +(new Foo().outer: number); +(new Foo().outer: string); // Error: number ~> string +(Foo.outer: number); +(Foo.outer: string); // Error: number ~> string + +/** + * Field initializers should be able to refer to the class type in their type + * annotations. + */ +(new Foo().selfTyped: Foo); +(new Foo().selfTyped: number); // Error: Foo ~> number +(Foo.selfTyped: Foo); +(Foo.selfTyped: number); // Error: Foo ~> number + +(new Foo().selfTypedInit: Foo); +(new Foo().selfTypedInit: number); // Error: Foo ~> number +(Foo.selfTypedInit: Foo); +(Foo.selfTypedInit: number); // Error: Foo ~> number +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +var someVar = 42; + +class Foo { + outer = someVar; + selfTyped: Foo; + selfTypedInit = new Foo(); + + static outer = someVar; + static selfTyped: Foo; + static selfTypedInit = new Foo(); + + constructor() { + var someVar = \\"asdf\\"; + } +} + +/** + * Field initializers execute in a scope immediately under the scope outside the + * class definition. + */ +(new Foo().outer: number); +(new Foo().outer: string); // Error: number ~> string +(Foo.outer: number); +(Foo.outer: string); // Error: number ~> string + +/** + * Field initializers should be able to refer to the class type in their type + * annotations. + */ +(new Foo().selfTyped: Foo); +(new Foo().selfTyped: number); // Error: Foo ~> number +(Foo.selfTyped: Foo); +(Foo.selfTyped: number); // Error: Foo ~> number + +(new Foo().selfTypedInit: Foo); +(new Foo().selfTypedInit: number); // Error: Foo ~> number +(Foo.selfTypedInit: Foo); +(Foo.selfTypedInit: number); // Error: Foo ~> number +" +`; diff --git a/tests/flow/class_fields/base_class.js b/tests/flow/class_fields/base_class.js new file mode 100644 index 00000000..b5ad46bc --- /dev/null +++ b/tests/flow/class_fields/base_class.js @@ -0,0 +1,72 @@ +// @flow + +class Base { + unannotatedField; + annotatedField: number; + initializedField = 42; + initializedFieldWithThis = this.initializedField; + annotatedInitializedFieldValid: ?number = 42; + annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number + + static unannotatedField; + static annotatedField: number; + static initializedField = 'asdf'; + static initializedFieldWithThis = this.initializedField; + static annotatedInitializedFieldValid: ?number = 42; + static annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number +} + +var o = new Base(); + +/** + * Unannotated fields are open. + */ +(o.unannotatedField: string); +(o.unannotatedField: number); +(Base.unannotatedField: string); +(Base.unannotatedField: number); + +/** + * Annotated (but uninitialized) fields still have a type. + */ +(o.annotatedField: number); +(o.annotatedField: string); // Error: number ~> string +(Base.annotatedField: number); +(Base.annotatedField: string); // Error: number ~> string + +/** + * Initialized (but unannotated) fields assume the type of their initializer. + */ +(o.initializedField: number); +(o.initializedField: string); // Error: number ~> string +(Base.initializedField: string); +(Base.initializedField: number); // Error: string ~> number + +/** + * Initialized fields can reference `this`. + */ +(o.initializedFieldWithThis: number); +(o.initializedFieldWithThis: string); // Error: number ~> string +(Base.initializedFieldWithThis: string); +(Base.initializedFieldWithThis: number); // Error: string ~> number + +/** + * Initialized + annotated fields take the type of the annotation. + * (Note that this matters when the annotation is more general than the type of + * the initializer) + */ +(o.annotatedInitializedFieldValid: ?number); +(o.annotatedInitializedFieldValid: number); // Error: ?number ~> number +(Base.annotatedInitializedFieldValid: ?number); +(Base.annotatedInitializedFieldValid: number); // Error: ?number ~> number + +/** + * Initialized + annotated fields where the init/annot combo is a mismatch + * should assume the type of the annotation. + * + * (This happens in addition to erroring at the site of initialization) + */ +(o.annotatedInitializedFieldInvalid: number); +(o.annotatedInitializedFieldInvalid: string); // Error: number ~> string +(Base.annotatedInitializedFieldInvalid: number); +(Base.annotatedInitializedFieldInvalid: string); // Error: number ~> string diff --git a/tests/flow/class_fields/derived_class.js b/tests/flow/class_fields/derived_class.js new file mode 100644 index 00000000..89b40c16 --- /dev/null +++ b/tests/flow/class_fields/derived_class.js @@ -0,0 +1,134 @@ +// @flow + +class Base { + base_unannotatedField; + base_annotatedField: number; + base_initializedField = 42; + base_initializedFieldWithThis = this.base_initializedField; + base_annotatedInitializedFieldValid: ?number = 42; + base_annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number + + static base_unannotatedField; + static base_annotatedField: number; + static base_initializedField = 'asdf'; + static base_initializedFieldWithThis = this.base_initializedField; + static base_annotatedInitializedFieldValid: ?number = 42; + static base_annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number + + inherited_initializer = 42; + static inherited_initializer = 42; +} + +class Child extends Base { + child_unannotatedField; + child_annotatedField: number; + child_initializedField = 42; + child_initializedFieldWithThis = this.child_initializedField; + child_annotatedInitializedFieldValid: ?number = 42; + child_annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number + + static child_unannotatedField; + static child_annotatedField: number; + static child_initializedField = 'asdf'; + static child_initializedFieldWithThis = this.child_initializedField; + static child_annotatedInitializedFieldValid: ?number = 42; + static child_annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number + + inherited_initializer; + static inherited_initializer; +} + +var o = new Child(); + +/** + * Unannotated fields are open. + */ +(o.base_unannotatedField: string); +(o.base_unannotatedField: number); +(Child.base_unannotatedField: string); +(Child.base_unannotatedField: number); + +(o.child_unannotatedField: string); +(o.child_unannotatedField: number); +(Child.child_unannotatedField: string); +(Child.child_unannotatedField: number); + + +/** + * Annotated (but uninitialized) fields still have a type. + */ +(o.base_annotatedField: number); +(o.base_annotatedField: string); // Error: number ~> string +(Child.base_annotatedField: number); +(Child.base_annotatedField: string); // Error: number ~> string + +(o.child_annotatedField: number); +(o.child_annotatedField: string); // Error: number ~> string +(Child.child_annotatedField: number); +(Child.child_annotatedField: string); // Error: number ~> string + +/** + * Initialized (but unannotated) fields assume the type of their initializer. + */ +(o.base_initializedField: number); +(o.base_initializedField: string); // Error: number ~> string +(Child.base_initializedField: string); +(Child.base_initializedField: number); // Error: string ~> number + +(o.child_initializedField: number); +(o.child_initializedField: string); // Error: number ~> string +(Child.child_initializedField: string); +(Child.child_initializedField: number); // Error: string ~> number + +/** + * Initialized fields can reference `this`. + */ +(o.base_initializedFieldWithThis: number); +(o.base_initializedFieldWithThis: string); // Error: number ~> string +(Child.base_initializedFieldWithThis: string); +(Child.base_initializedFieldWithThis: number); // Error: string ~> number + +(o.child_initializedFieldWithThis: number); +(o.child_initializedFieldWithThis: string); // Error: number ~> string +(Child.child_initializedFieldWithThis: string); +(Child.child_initializedFieldWithThis: number); // Error: string ~> number + +/** + * Initialized + annotated fields take the type of the annotation. + * (Note that this matters when the annotation is more general than the type of + * the initializer) + */ +(o.base_annotatedInitializedFieldValid: ?number); +(o.base_annotatedInitializedFieldValid: number); // Error: ?number ~> number +(Child.base_annotatedInitializedFieldValid: ?number); +(Child.base_annotatedInitializedFieldValid: number); // Error: ?number ~> number + +(o.child_annotatedInitializedFieldValid: ?number); +(o.child_annotatedInitializedFieldValid: number); // Error: ?number ~> number +(Child.child_annotatedInitializedFieldValid: ?number); +(Child.child_annotatedInitializedFieldValid: number); // Error: ?number ~> number + +/** + * Initialized + annotated fields where the init/annot combo is a mismatch + * should assume the type of the annotation. + * + * (This happens in addition to erroring at the site of initialization) + */ +(o.base_annotatedInitializedFieldInvalid: number); +(o.base_annotatedInitializedFieldInvalid: string); // Error: number ~> string +(Child.base_annotatedInitializedFieldInvalid: number); +(Child.base_annotatedInitializedFieldInvalid: string); // Error: number ~> string + +(o.child_annotatedInitializedFieldInvalid: number); +(o.child_annotatedInitializedFieldInvalid: string); // Error: number ~> string +(Child.child_annotatedInitializedFieldInvalid: number); +(Child.child_annotatedInitializedFieldInvalid: string); // Error: number ~> string + +/** + * Derived fields without an initializer that shadow base fields *with* an + * initializer should have the type of the base field. + */ +(o.inherited_initializer: number); +(o.inherited_initializer: string); // Error: number ~> string +(Child.inherited_initializer: number); +(Child.inherited_initializer: string); // Error: number ~> string diff --git a/tests/flow/class_fields/generic_class.js b/tests/flow/class_fields/generic_class.js new file mode 100644 index 00000000..d214ff6c --- /dev/null +++ b/tests/flow/class_fields/generic_class.js @@ -0,0 +1,31 @@ +// @flow + +/** + * Fields annotated with a generic should assume a type once the type param + * is instantiated. + */ +class ClassAnnotated { + p: T; + static p: T; +} + +var o1 = new ClassAnnotated(); +o1.p = 42; +(o1.p: number); +(o1.p: string); // Error: number ~> string +ClassAnnotated.p = 42; +(ClassAnnotated.p: number); +(ClassAnnotated.p: string); // Error: number ~> string + + +/** + * It's always an error to initialized a generically-typed field with an + * expression of any type other than the generic itself. + */ +class ClassGenericInitialized { + invalid: T = 42; // Error: number ~> Generic + valid: T = ((42:any):T); + + static invalid: T = 42; // Error: number ~> Generic + static valid: T = ((42:any):T); +} diff --git a/tests/flow/class_fields/jsfmt.spec.js b/tests/flow/class_fields/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/class_fields/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/class_fields/scoping.js b/tests/flow/class_fields/scoping.js new file mode 100644 index 00000000..462b5129 --- /dev/null +++ b/tests/flow/class_fields/scoping.js @@ -0,0 +1,40 @@ +// @flow + +var someVar = 42; + +class Foo { + outer = someVar; + selfTyped: Foo; + selfTypedInit = new Foo(); + + static outer = someVar; + static selfTyped: Foo; + static selfTypedInit = new Foo(); + + constructor() { + var someVar = 'asdf'; + } +} + +/** + * Field initializers execute in a scope immediately under the scope outside the + * class definition. + */ +(new Foo().outer: number); +(new Foo().outer: string); // Error: number ~> string +(Foo.outer: number); +(Foo.outer: string); // Error: number ~> string + +/** + * Field initializers should be able to refer to the class type in their type + * annotations. + */ +(new Foo().selfTyped: Foo); +(new Foo().selfTyped: number); // Error: Foo ~> number +(Foo.selfTyped: Foo); +(Foo.selfTyped: number); // Error: Foo ~> number + +(new Foo().selfTypedInit: Foo); +(new Foo().selfTypedInit: number); // Error: Foo ~> number +(Foo.selfTypedInit: Foo); +(Foo.selfTypedInit: number); // Error: Foo ~> number diff --git a/tests/flow/class_method_default_parameters/__snapshots__/jsfmt.spec.js.snap b/tests/flow/class_method_default_parameters/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..0a1d2236 --- /dev/null +++ b/tests/flow/class_method_default_parameters/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,46 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`class_method_default_parameters.js 1`] = ` +"class A { + + b: string; + + c(d = this.b) { // ok - can use \`this\` in function default parameter values + + } + + e() { + return this.b; + } + + f(g = this.e()) { // ok - can use \`this\` in function default parameter values + + } + + h(i: number = this.b) { // error + + } + +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class A { + b: string; + + c(d = this.b) { + // ok - can use \`this\` in function default parameter values + } + + e() { + return this.b; + } + + f(g = this.e()) { + // ok - can use \`this\` in function default parameter values + } + + h(i: number = this.b) { + // error + } +} +" +`; diff --git a/tests/flow/class_method_default_parameters/class_method_default_parameters.js b/tests/flow/class_method_default_parameters/class_method_default_parameters.js new file mode 100644 index 00000000..ea9a73eb --- /dev/null +++ b/tests/flow/class_method_default_parameters/class_method_default_parameters.js @@ -0,0 +1,21 @@ +class A { + + b: string; + + c(d = this.b) { // ok - can use `this` in function default parameter values + + } + + e() { + return this.b; + } + + f(g = this.e()) { // ok - can use `this` in function default parameter values + + } + + h(i: number = this.b) { // error + + } + +} diff --git a/tests/flow/class_method_default_parameters/jsfmt.spec.js b/tests/flow/class_method_default_parameters/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/class_method_default_parameters/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/classes/__snapshots__/jsfmt.spec.js.snap b/tests/flow/classes/__snapshots__/jsfmt.spec.js.snap index 20657717..b128e87b 100644 --- a/tests/flow/classes/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/classes/__snapshots__/jsfmt.spec.js.snap @@ -213,6 +213,21 @@ var alias2: Alias = _Alias.factory(); // error: bad pun " `; +exports[`extends_any.js 1`] = ` +"const Base: any = class {} +class Derived1 extends Base {} +class Derived2 extends Derived1 { + m() {} +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +const Base: any = class {}; +class Derived1 extends Base {} +class Derived2 extends Derived1 { + m() {} +} +" +`; + exports[`loc.js 1`] = ` "/* @flow */ diff --git a/tests/flow/classes/extends_any.js b/tests/flow/classes/extends_any.js new file mode 100644 index 00000000..78716313 --- /dev/null +++ b/tests/flow/classes/extends_any.js @@ -0,0 +1,5 @@ +const Base: any = class {} +class Derived1 extends Base {} +class Derived2 extends Derived1 { + m() {} +} diff --git a/tests/flow/constructor/__snapshots__/jsfmt.spec.js.snap b/tests/flow/constructor/__snapshots__/jsfmt.spec.js.snap index f5c85157..70b1d175 100644 --- a/tests/flow/constructor/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/constructor/__snapshots__/jsfmt.spec.js.snap @@ -9,7 +9,20 @@ class D { constructor():number { } } -module.exports = C; +// the return type of a constructor overrides the type of the class +declare class Bar {} +declare class Foo { + constructor(iterable: U): Bar; +} +(new Foo('x'): Bar); // ok +(new Foo(123): Bar); // error, number !~> string + +// also overrides when it returns a different specialization of the same class +declare class Baz { + constructor(iterable: U): Baz; +} +(new Baz('x'): Baz); // ok +(new Baz(123): Baz); // error, number !~> string ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ class C { constructor() {} @@ -19,6 +32,19 @@ class D { constructor(): number {} } -module.exports = C; +// the return type of a constructor overrides the type of the class +declare class Bar {} +declare class Foo { + constructor(iterable: U): Bar +} +(new Foo(\\"x\\"): Bar); // ok +(new Foo(123): Bar); // error, number !~> string + +// also overrides when it returns a different specialization of the same class +declare class Baz { + constructor(iterable: U): Baz +} +(new Baz(\\"x\\"): Baz); // ok +(new Baz(123): Baz); // error, number !~> string " `; diff --git a/tests/flow/constructor/constructor.js b/tests/flow/constructor/constructor.js index 92038b6e..0deb0c73 100644 --- a/tests/flow/constructor/constructor.js +++ b/tests/flow/constructor/constructor.js @@ -6,4 +6,17 @@ class D { constructor():number { } } -module.exports = C; +// the return type of a constructor overrides the type of the class +declare class Bar {} +declare class Foo { + constructor(iterable: U): Bar; +} +(new Foo('x'): Bar); // ok +(new Foo(123): Bar); // error, number !~> string + +// also overrides when it returns a different specialization of the same class +declare class Baz { + constructor(iterable: U): Baz; +} +(new Baz('x'): Baz); // ok +(new Baz(123): Baz); // error, number !~> string diff --git a/tests/flow/declaration_files_haste/foo/bar/__snapshots__/jsfmt.spec.js.snap b/tests/flow/declaration_files_haste/foo/bar/__snapshots__/jsfmt.spec.js.snap index e98d04b0..592fed3e 100644 --- a/tests/flow/declaration_files_haste/foo/bar/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/declaration_files_haste/foo/bar/__snapshots__/jsfmt.spec.js.snap @@ -6,6 +6,7 @@ exports[`nested_test.js 1`] = ` var docblock = require('qux/docblock'); var min = require('d3/min.js'); var corge = require('qux/corge'); +var SomeOtherModule = require('SomeOtherModule'); // make sure we don't pick up non-header @providesModule // annotations - see node_modules/qux/docblock.js @@ -14,12 +15,14 @@ var unreachable = require('annotation'); (docblock.fun(): string); (min.fun(): string); (corge.fun(): string); +(SomeOtherModule.fun(): string); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /* @flow */ var docblock = require(\\"qux/docblock\\"); var min = require(\\"d3/min.js\\"); var corge = require(\\"qux/corge\\"); +var SomeOtherModule = require(\\"SomeOtherModule\\"); // make sure we don't pick up non-header @providesModule // annotations - see node_modules/qux/docblock.js @@ -28,5 +31,6 @@ var unreachable = require(\\"annotation\\"); (docblock.fun(): string); (min.fun(): string); (corge.fun(): string); +(SomeOtherModule.fun(): string); " `; diff --git a/tests/flow/declaration_files_haste/foo/bar/nested_test.js b/tests/flow/declaration_files_haste/foo/bar/nested_test.js index c9522bc6..7a33a5b1 100644 --- a/tests/flow/declaration_files_haste/foo/bar/nested_test.js +++ b/tests/flow/declaration_files_haste/foo/bar/nested_test.js @@ -3,6 +3,7 @@ var docblock = require('qux/docblock'); var min = require('d3/min.js'); var corge = require('qux/corge'); +var SomeOtherModule = require('SomeOtherModule'); // make sure we don't pick up non-header @providesModule // annotations - see node_modules/qux/docblock.js @@ -11,3 +12,4 @@ var unreachable = require('annotation'); (docblock.fun(): string); (min.fun(): string); (corge.fun(): string); +(SomeOtherModule.fun(): string); diff --git a/tests/flow/declaration_files_incremental_haste_name_reducers/A.js b/tests/flow/declaration_files_incremental_haste_name_reducers/A.js new file mode 100644 index 00000000..df97d7da --- /dev/null +++ b/tests/flow/declaration_files_incremental_haste_name_reducers/A.js @@ -0,0 +1,4 @@ +/* @flow */ + +class AImplementation {} +export function foo(): AImplementation { return new AImplementation(); } diff --git a/tests/flow/declaration_files_incremental_haste_name_reducers/__snapshots__/jsfmt.spec.js.snap b/tests/flow/declaration_files_incremental_haste_name_reducers/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..00ed0966 --- /dev/null +++ b/tests/flow/declaration_files_incremental_haste_name_reducers/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,35 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`A.js 1`] = ` +"/* @flow */ + +class AImplementation {} +export function foo(): AImplementation { return new AImplementation(); } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ + +class AImplementation {} +export function foo(): AImplementation { + return new AImplementation(); +} +" +`; + +exports[`index.js 1`] = ` +"/* @flow */ + +var A = require('A'); +(A.foo(): boolean); // Error: Either AImplementation ~> boolean or ADefinition ~> boolean + +var test = require('test'); +(test.foo(): boolean); // Error: Either TestImplementation ~> boolean or TestDefinition ~> boolean +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ + +var A = require(\\"A\\"); +(A.foo(): boolean); // Error: Either AImplementation ~> boolean or ADefinition ~> boolean + +var test = require(\\"test\\"); +(test.foo(): boolean); // Error: Either TestImplementation ~> boolean or TestDefinition ~> boolean +" +`; diff --git a/tests/flow/declaration_files_incremental_haste_name_reducers/index.js b/tests/flow/declaration_files_incremental_haste_name_reducers/index.js new file mode 100644 index 00000000..c35cd1d0 --- /dev/null +++ b/tests/flow/declaration_files_incremental_haste_name_reducers/index.js @@ -0,0 +1,7 @@ +/* @flow */ + +var A = require('A'); +(A.foo(): boolean); // Error: Either AImplementation ~> boolean or ADefinition ~> boolean + +var test = require('test'); +(test.foo(): boolean); // Error: Either TestImplementation ~> boolean or TestDefinition ~> boolean diff --git a/tests/flow/declaration_files_incremental_haste_name_reducers/jsfmt.spec.js b/tests/flow/declaration_files_incremental_haste_name_reducers/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/declaration_files_incremental_haste_name_reducers/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/declare_type/lib/declare_module_type_alias.js b/tests/flow/declare_type/lib/DeclareModule_TypeAlias.js similarity index 65% rename from tests/flow/declare_type/lib/declare_module_type_alias.js rename to tests/flow/declare_type/lib/DeclareModule_TypeAlias.js index 5194cc67..325d344d 100644 --- a/tests/flow/declare_type/lib/declare_module_type_alias.js +++ b/tests/flow/declare_type/lib/DeclareModule_TypeAlias.js @@ -2,11 +2,8 @@ * @flow */ -// TODO: adding an empty line should not be dropped. - declare module ModuleAliasFoo { declare type baz = number; declare type toz = string; declare function foo(bar : baz) : toz; } -declare type Foo = string; diff --git a/tests/flow/declare_type/lib/__snapshots__/jsfmt.spec.js.snap b/tests/flow/declare_type/lib/__snapshots__/jsfmt.spec.js.snap index c9893e33..fff1c2bd 100644 --- a/tests/flow/declare_type/lib/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/declare_type/lib/__snapshots__/jsfmt.spec.js.snap @@ -1,31 +1,25 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`declare_module_type_alias.js 1`] = ` +exports[`DeclareModule_TypeAlias.js 1`] = ` "/** * @flow */ -// TODO: adding an empty line should not be dropped. - declare module ModuleAliasFoo { declare type baz = number; declare type toz = string; declare function foo(bar : baz) : toz; } -declare type Foo = string; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * @flow */ -// TODO: adding an empty line should not be dropped. - declare module ModuleAliasFoo { declare type baz = number; declare type toz = string; declare function foo(bar: baz): toz; } -declare type Foo = string; " `; diff --git a/tests/flow/destructuring/__snapshots__/jsfmt.spec.js.snap b/tests/flow/destructuring/__snapshots__/jsfmt.spec.js.snap index 243d6408..6bdd1e56 100644 --- a/tests/flow/destructuring/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/destructuring/__snapshots__/jsfmt.spec.js.snap @@ -372,37 +372,6 @@ var cp2_err: string = others.childprop2; // Error: number ~> string " `; -exports[`destructuring_param.js 1`] = ` -"function f(a, { b }) { - return a + b; -} - -// Doesn't parse right now - -// function g(a, { a }) { -// return a; -// } - -// function h({ a, { b } }, { c }, { { d } }) { -// return a + b + c + d; -// } -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -function f(a, { b }) { - return a + b; -} - -// Doesn't parse right now - -// function g(a, { a }) { -// return a; -// } - -// function h({ a, { b } }, { c }, { { d } }) { -// return a + b + c + d; -// } -" -`; - exports[`eager.js 1`] = ` "var x; ({x} = null); // error, property \`x\` can not be accessed on \`null\` @@ -427,7 +396,6 @@ function tup_pattern([ proj ] : [X]) {} // proj: X type Proj = [X]; function tup_pattern2([ proj ] : Proj) {} // proj: X -function rest_antipattern(...t: T) {} // nonsense function rest_pattern(...r: X[]) {} // r: X[] function obj_rest_pattern({ _, ...o } : { _: any, x: X }) { // o: { x: X } @@ -460,7 +428,6 @@ function tup_pattern([proj]: [X]) {} // proj: X type Proj = [X]; function tup_pattern2([proj]: Proj) {} // proj: X -function rest_antipattern(...t: T) {} // nonsense function rest_pattern(...r: X[]) {} // r: X[] function obj_rest_pattern({ _, ...o }: { _: any, x: X }) { @@ -530,6 +497,31 @@ var { x: o } = o; " `; +exports[`refinement_non_termination.js 1`] = ` +"// @flow + +function _([argArray]: Array) { + if (argArray instanceof NullValue || argArray instanceof UndefinedValue) { + } +}; + +class Value { } +class NullValue extends Value { } +class UndefinedValue extends Value { } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +function _([argArray]: Array) { + if (argArray instanceof NullValue || argArray instanceof UndefinedValue) { + } +} + +class Value {} +class NullValue extends Value {} +class UndefinedValue extends Value {} +" +`; + exports[`string_lit.js 1`] = ` "var { \\"key\\": val } = { key: \\"val\\" }; (val: void); // error: string ~> void diff --git a/tests/flow/destructuring/destructuring_param.js b/tests/flow/destructuring/destructuring_param.js deleted file mode 100644 index b9fc9d00..00000000 --- a/tests/flow/destructuring/destructuring_param.js +++ /dev/null @@ -1,13 +0,0 @@ -function f(a, { b }) { - return a + b; -} - -// Doesn't parse right now - -// function g(a, { a }) { -// return a; -// } - -// function h({ a, { b } }, { c }, { { d } }) { -// return a + b + c + d; -// } diff --git a/tests/flow/destructuring/poly.js b/tests/flow/destructuring/poly.js index bbfb5ccf..57c521b8 100644 --- a/tests/flow/destructuring/poly.js +++ b/tests/flow/destructuring/poly.js @@ -12,7 +12,6 @@ function tup_pattern([ proj ] : [X]) {} // proj: X type Proj = [X]; function tup_pattern2([ proj ] : Proj) {} // proj: X -function rest_antipattern(...t: T) {} // nonsense function rest_pattern(...r: X[]) {} // r: X[] function obj_rest_pattern({ _, ...o } : { _: any, x: X }) { // o: { x: X } diff --git a/tests/flow/destructuring/refinement_non_termination.js b/tests/flow/destructuring/refinement_non_termination.js new file mode 100644 index 00000000..4eda7a96 --- /dev/null +++ b/tests/flow/destructuring/refinement_non_termination.js @@ -0,0 +1,10 @@ +// @flow + +function _([argArray]: Array) { + if (argArray instanceof NullValue || argArray instanceof UndefinedValue) { + } +}; + +class Value { } +class NullValue extends Value { } +class UndefinedValue extends Value { } diff --git a/tests/flow/dom/Document.js b/tests/flow/dom/Document.js index 7bf23460..46d156e8 100644 --- a/tests/flow/dom/Document.js +++ b/tests/flow/dom/Document.js @@ -7,5 +7,6 @@ let tests = [ (document.createElement('link'): HTMLLinkElement); (document.createElement('option'): HTMLOptionElement); (document.createElement('select'): HTMLSelectElement); + (document.querySelector('select'): HTMLSelectElement | null); } ]; diff --git a/tests/flow/dom/__snapshots__/jsfmt.spec.js.snap b/tests/flow/dom/__snapshots__/jsfmt.spec.js.snap index 8376fc12..9ee068ba 100644 --- a/tests/flow/dom/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/dom/__snapshots__/jsfmt.spec.js.snap @@ -64,6 +64,7 @@ let tests = [ (document.createElement('link'): HTMLLinkElement); (document.createElement('option'): HTMLOptionElement); (document.createElement('select'): HTMLSelectElement); + (document.querySelector('select'): HTMLSelectElement | null); } ]; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -76,6 +77,7 @@ let tests = [ (document.createElement(\\"link\\"): HTMLLinkElement); (document.createElement(\\"option\\"): HTMLOptionElement); (document.createElement(\\"select\\"): HTMLSelectElement); + (document.querySelector(\\"select\\"): HTMLSelectElement | null); } ]; " @@ -644,15 +646,15 @@ let tests = [ function() { document.createNodeIterator(document.body, -1, node => NodeFilter.FILTER_ACCEPT); // valid document.createNodeIterator(document.body, -1, node => 'accept'); // invalid - document.createNodeIterator(document.body, -1, { accept: node => NodeFilter.FILTER_ACCEPT }); // valid - document.createNodeIterator(document.body, -1, { accept: node => 'accept' }); // invalid + document.createNodeIterator(document.body, -1, { acceptNode: node => NodeFilter.FILTER_ACCEPT }); // valid + document.createNodeIterator(document.body, -1, { acceptNode: node => 'accept' }); // invalid document.createNodeIterator(document.body, -1, {}); // invalid }, function() { document.createTreeWalker(document.body, -1, node => NodeFilter.FILTER_ACCEPT); // valid document.createTreeWalker(document.body, -1, node => 'accept'); // invalid - document.createTreeWalker(document.body, -1, { accept: node => NodeFilter.FILTER_ACCEPT }); // valid - document.createTreeWalker(document.body, -1, { accept: node => 'accept' }); // invalid + document.createTreeWalker(document.body, -1, { acceptNode: node => NodeFilter.FILTER_ACCEPT }); // valid + document.createTreeWalker(document.body, -1, { acceptNode: node => 'accept' }); // invalid document.createTreeWalker(document.body, -1, {}); // invalid }, ]; @@ -849,10 +851,10 @@ let tests = [ ); // valid document.createNodeIterator(document.body, -1, node => \\"accept\\"); // invalid document.createNodeIterator(document.body, -1, { - accept: node => NodeFilter.FILTER_ACCEPT + acceptNode: node => NodeFilter.FILTER_ACCEPT }); // valid document.createNodeIterator(document.body, -1, { - accept: node => \\"accept\\" + acceptNode: node => \\"accept\\" }); // invalid document.createNodeIterator(document.body, -1, {}); // invalid }, @@ -864,9 +866,11 @@ let tests = [ ); // valid document.createTreeWalker(document.body, -1, node => \\"accept\\"); // invalid document.createTreeWalker(document.body, -1, { - accept: node => NodeFilter.FILTER_ACCEPT + acceptNode: node => NodeFilter.FILTER_ACCEPT }); // valid - document.createTreeWalker(document.body, -1, { accept: node => \\"accept\\" }); // invalid + document.createTreeWalker(document.body, -1, { + acceptNode: node => \\"accept\\" + }); // invalid document.createTreeWalker(document.body, -1, {}); // invalid } ]; diff --git a/tests/flow/dom/traversal.js b/tests/flow/dom/traversal.js index a78ae274..69b743d0 100644 --- a/tests/flow/dom/traversal.js +++ b/tests/flow/dom/traversal.js @@ -181,15 +181,15 @@ let tests = [ function() { document.createNodeIterator(document.body, -1, node => NodeFilter.FILTER_ACCEPT); // valid document.createNodeIterator(document.body, -1, node => 'accept'); // invalid - document.createNodeIterator(document.body, -1, { accept: node => NodeFilter.FILTER_ACCEPT }); // valid - document.createNodeIterator(document.body, -1, { accept: node => 'accept' }); // invalid + document.createNodeIterator(document.body, -1, { acceptNode: node => NodeFilter.FILTER_ACCEPT }); // valid + document.createNodeIterator(document.body, -1, { acceptNode: node => 'accept' }); // invalid document.createNodeIterator(document.body, -1, {}); // invalid }, function() { document.createTreeWalker(document.body, -1, node => NodeFilter.FILTER_ACCEPT); // valid document.createTreeWalker(document.body, -1, node => 'accept'); // invalid - document.createTreeWalker(document.body, -1, { accept: node => NodeFilter.FILTER_ACCEPT }); // valid - document.createTreeWalker(document.body, -1, { accept: node => 'accept' }); // invalid + document.createTreeWalker(document.body, -1, { acceptNode: node => NodeFilter.FILTER_ACCEPT }); // valid + document.createTreeWalker(document.body, -1, { acceptNode: node => 'accept' }); // invalid document.createTreeWalker(document.body, -1, {}); // invalid }, ]; diff --git a/tests/flow/esproposal_class_instance_fields.ignore/__snapshots__/jsfmt.spec.js.snap b/tests/flow/esproposal_class_instance_fields.ignore/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..4256eb8d --- /dev/null +++ b/tests/flow/esproposal_class_instance_fields.ignore/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`test.js 1`] = ` +"/* @flow */ + +class Foo { + annotationOnly: string; + initOnly = 'asdf' + initWithAnnotation: string = 'asdf'; + [computed]: string; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ + +class Foo { + annotationOnly: string; + initOnly = \\"asdf\\"; + initWithAnnotation: string = \\"asdf\\"; + [computed]: string; +} +" +`; diff --git a/tests/flow/esproposal_class_instance_fields.ignore/jsfmt.spec.js b/tests/flow/esproposal_class_instance_fields.ignore/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/esproposal_class_instance_fields.ignore/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/esproposal_class_instance_fields.ignore/test.js b/tests/flow/esproposal_class_instance_fields.ignore/test.js new file mode 100644 index 00000000..7525cf35 --- /dev/null +++ b/tests/flow/esproposal_class_instance_fields.ignore/test.js @@ -0,0 +1,8 @@ +/* @flow */ + +class Foo { + annotationOnly: string; + initOnly = 'asdf' + initWithAnnotation: string = 'asdf'; + [computed]: string; +} diff --git a/tests/flow/esproposal_class_instance_fields.warn/__snapshots__/jsfmt.spec.js.snap b/tests/flow/esproposal_class_instance_fields.warn/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..ec5e8237 --- /dev/null +++ b/tests/flow/esproposal_class_instance_fields.warn/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`test.js 1`] = ` +"/* @flow */ + +class Foo { + annotationOnly: string; + initOnly = 'asdf'; + initWithAnnotation: string = 'asdf'; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ + +class Foo { + annotationOnly: string; + initOnly = \\"asdf\\"; + initWithAnnotation: string = \\"asdf\\"; +} +" +`; diff --git a/tests/flow/esproposal_class_instance_fields.warn/jsfmt.spec.js b/tests/flow/esproposal_class_instance_fields.warn/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/esproposal_class_instance_fields.warn/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/esproposal_class_instance_fields.warn/test.js b/tests/flow/esproposal_class_instance_fields.warn/test.js new file mode 100644 index 00000000..ac74ba3e --- /dev/null +++ b/tests/flow/esproposal_class_instance_fields.warn/test.js @@ -0,0 +1,7 @@ +/* @flow */ + +class Foo { + annotationOnly: string; + initOnly = 'asdf'; + initWithAnnotation: string = 'asdf'; +} diff --git a/tests/flow/esproposal_class_static_fields.ignore/__snapshots__/jsfmt.spec.js.snap b/tests/flow/esproposal_class_static_fields.ignore/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..2278d1bf --- /dev/null +++ b/tests/flow/esproposal_class_static_fields.ignore/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`test.js 1`] = ` +"/* @flow */ + +class Foo { + static annotationOnly: string; + static initOnly = 'asdf'; + static initWithAnnotation: string = 'asdf'; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ + +class Foo { + static annotationOnly: string; + static initOnly = \\"asdf\\"; + static initWithAnnotation: string = \\"asdf\\"; +} +" +`; diff --git a/tests/flow/esproposal_class_static_fields.ignore/jsfmt.spec.js b/tests/flow/esproposal_class_static_fields.ignore/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/esproposal_class_static_fields.ignore/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/esproposal_class_static_fields.ignore/test.js b/tests/flow/esproposal_class_static_fields.ignore/test.js new file mode 100644 index 00000000..f47f1b64 --- /dev/null +++ b/tests/flow/esproposal_class_static_fields.ignore/test.js @@ -0,0 +1,7 @@ +/* @flow */ + +class Foo { + static annotationOnly: string; + static initOnly = 'asdf'; + static initWithAnnotation: string = 'asdf'; +} diff --git a/tests/flow/esproposal_class_static_fields.warn/__snapshots__/jsfmt.spec.js.snap b/tests/flow/esproposal_class_static_fields.warn/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..2278d1bf --- /dev/null +++ b/tests/flow/esproposal_class_static_fields.warn/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`test.js 1`] = ` +"/* @flow */ + +class Foo { + static annotationOnly: string; + static initOnly = 'asdf'; + static initWithAnnotation: string = 'asdf'; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ + +class Foo { + static annotationOnly: string; + static initOnly = \\"asdf\\"; + static initWithAnnotation: string = \\"asdf\\"; +} +" +`; diff --git a/tests/flow/esproposal_class_static_fields.warn/jsfmt.spec.js b/tests/flow/esproposal_class_static_fields.warn/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/esproposal_class_static_fields.warn/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/esproposal_class_static_fields.warn/test.js b/tests/flow/esproposal_class_static_fields.warn/test.js new file mode 100644 index 00000000..f47f1b64 --- /dev/null +++ b/tests/flow/esproposal_class_static_fields.warn/test.js @@ -0,0 +1,7 @@ +/* @flow */ + +class Foo { + static annotationOnly: string; + static initOnly = 'asdf'; + static initWithAnnotation: string = 'asdf'; +} diff --git a/tests/flow/esproposal_export_star_as.enable/__snapshots__/jsfmt.spec.js.snap b/tests/flow/esproposal_export_star_as.enable/__snapshots__/jsfmt.spec.js.snap index e600eff1..ebeec82c 100644 --- a/tests/flow/esproposal_export_star_as.enable/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/esproposal_export_star_as.enable/__snapshots__/jsfmt.spec.js.snap @@ -35,3 +35,14 @@ export var str = \\"asdf\\"; export var num = 42; " `; + +exports[`test.js 1`] = ` +"// @flow + +export * as source from \\"./source\\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +export * as source from \\"./source\\"; +" +`; diff --git a/tests/flow/esproposal_export_star_as.enable/test.js b/tests/flow/esproposal_export_star_as.enable/test.js new file mode 100644 index 00000000..5fdc1272 --- /dev/null +++ b/tests/flow/esproposal_export_star_as.enable/test.js @@ -0,0 +1,3 @@ +// @flow + +export * as source from "./source"; diff --git a/tests/flow/esproposal_export_star_as.ignore/__snapshots__/jsfmt.spec.js.snap b/tests/flow/esproposal_export_star_as.ignore/__snapshots__/jsfmt.spec.js.snap index 4c408ba9..1aa7c086 100644 --- a/tests/flow/esproposal_export_star_as.ignore/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/esproposal_export_star_as.ignore/__snapshots__/jsfmt.spec.js.snap @@ -35,3 +35,14 @@ export var str = \\"asdf\\"; export var num = 42; " `; + +exports[`test.js 1`] = ` +"// @flow + +export * as source from \\"./source\\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +export * as source from \\"./source\\"; +" +`; diff --git a/tests/flow/esproposal_export_star_as.ignore/test.js b/tests/flow/esproposal_export_star_as.ignore/test.js new file mode 100644 index 00000000..5fdc1272 --- /dev/null +++ b/tests/flow/esproposal_export_star_as.ignore/test.js @@ -0,0 +1,3 @@ +// @flow + +export * as source from "./source"; diff --git a/tests/flow/esproposal_export_star_as.warn/__snapshots__/jsfmt.spec.js.snap b/tests/flow/esproposal_export_star_as.warn/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..510cda6b --- /dev/null +++ b/tests/flow/esproposal_export_star_as.warn/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,12 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`test.js 1`] = ` +"/* @flow */ + +export * as foo from \\"./test\\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ + +export * as foo from \\"./test\\"; +" +`; diff --git a/tests/flow/esproposal_export_star_as.warn/jsfmt.spec.js b/tests/flow/esproposal_export_star_as.warn/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/esproposal_export_star_as.warn/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/esproposal_export_star_as.warn/test.js b/tests/flow/esproposal_export_star_as.warn/test.js new file mode 100644 index 00000000..ee71a9b8 --- /dev/null +++ b/tests/flow/esproposal_export_star_as.warn/test.js @@ -0,0 +1,3 @@ +/* @flow */ + +export * as foo from "./test"; diff --git a/tests/flow/fetch/__snapshots__/jsfmt.spec.js.snap b/tests/flow/fetch/__snapshots__/jsfmt.spec.js.snap index 5b90de60..6f38991e 100644 --- a/tests/flow/fetch/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/fetch/__snapshots__/jsfmt.spec.js.snap @@ -134,7 +134,30 @@ const h: Request = new Request('http://example.org', { cache: 'default' }) // correct +var bodyUsed: boolean = h.bodyUsed; + +h.text().then((t: string) => t); // correct +h.text().then((t: Buffer) => t); // incorrect +h.arrayBuffer().then((ab: ArrayBuffer) => ab); // correct +h.arrayBuffer().then((ab: Buffer) => ab); // incorrect + const i: Request = new Request('http://example.org', { + method: 'POST', + headers: { + 'Content-Type': 'application/octet-stream' + }, + body: new ArrayBuffer(10), +}); // correct + +const j: Request = new Request('http://example.org', { + method: 'POST', + headers: { + 'Content-Type': 'application/octet-stream' + }, + body: new Uint8Array(10), +}); // correct + +const k: Request = new Request('http://example.org', { method: 'POST', headers: { 'Content-Type': 'image/jpeg' @@ -144,14 +167,14 @@ const i: Request = new Request('http://example.org', { cache: 'default' }) // correct -const j: Request = new Request('http://example.org', { +const l: Request = new Request('http://example.org', { method: 'GET', headers: 'Content-Type: image/jpeg', mode: 'cors', cache: 'default' }) // incorrect - headers is string -const k: Request = new Request('http://example.org', { +const m: Request = new Request('http://example.org', { method: 'CONNECT', headers: { 'Content-Type': 'image/jpeg' @@ -159,13 +182,6 @@ const k: Request = new Request('http://example.org', { mode: 'cors', cache: 'default' }) // incorrect - CONNECT is forbidden - -var l: boolean = h.bodyUsed; - -h.text().then((t: string) => t); // correct -h.text().then((t: Buffer) => t); // incorrect -h.arrayBuffer().then((ab: ArrayBuffer) => ab); // correct -h.arrayBuffer().then((ab: Buffer) => ab); // incorrect ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /* @flow */ const a: Request = new Request(); // incorrect @@ -186,7 +202,30 @@ const h: Request = new Request(\\"http://example.org\\", { cache: \\"default\\" }); // correct +var bodyUsed: boolean = h.bodyUsed; + +h.text().then((t: string) => t); // correct +h.text().then((t: Buffer) => t); // incorrect +h.arrayBuffer().then((ab: ArrayBuffer) => ab); // correct +h.arrayBuffer().then((ab: Buffer) => ab); // incorrect + const i: Request = new Request(\\"http://example.org\\", { + method: \\"POST\\", + headers: { + \\"Content-Type\\": \\"application/octet-stream\\" + }, + body: new ArrayBuffer(10) +}); // correct + +const j: Request = new Request(\\"http://example.org\\", { + method: \\"POST\\", + headers: { + \\"Content-Type\\": \\"application/octet-stream\\" + }, + body: new Uint8Array(10) +}); // correct + +const k: Request = new Request(\\"http://example.org\\", { method: \\"POST\\", headers: { \\"Content-Type\\": \\"image/jpeg\\" @@ -196,14 +235,14 @@ const i: Request = new Request(\\"http://example.org\\", { cache: \\"default\\" }); // correct -const j: Request = new Request(\\"http://example.org\\", { +const l: Request = new Request(\\"http://example.org\\", { method: \\"GET\\", headers: \\"Content-Type: image/jpeg\\", mode: \\"cors\\", cache: \\"default\\" }); // incorrect - headers is string -const k: Request = new Request(\\"http://example.org\\", { +const m: Request = new Request(\\"http://example.org\\", { method: \\"CONNECT\\", headers: { \\"Content-Type\\": \\"image/jpeg\\" @@ -211,13 +250,6 @@ const k: Request = new Request(\\"http://example.org\\", { mode: \\"cors\\", cache: \\"default\\" }); // incorrect - CONNECT is forbidden - -var l: boolean = h.bodyUsed; - -h.text().then((t: string) => t); // correct -h.text().then((t: Buffer) => t); // incorrect -h.arrayBuffer().then((ab: ArrayBuffer) => ab); // correct -h.arrayBuffer().then((ab: Buffer) => ab); // incorrect " `; diff --git a/tests/flow/fetch/request.js b/tests/flow/fetch/request.js index 1f8981e9..e2a9380c 100644 --- a/tests/flow/fetch/request.js +++ b/tests/flow/fetch/request.js @@ -17,7 +17,30 @@ const h: Request = new Request('http://example.org', { cache: 'default' }) // correct +var bodyUsed: boolean = h.bodyUsed; + +h.text().then((t: string) => t); // correct +h.text().then((t: Buffer) => t); // incorrect +h.arrayBuffer().then((ab: ArrayBuffer) => ab); // correct +h.arrayBuffer().then((ab: Buffer) => ab); // incorrect + const i: Request = new Request('http://example.org', { + method: 'POST', + headers: { + 'Content-Type': 'application/octet-stream' + }, + body: new ArrayBuffer(10), +}); // correct + +const j: Request = new Request('http://example.org', { + method: 'POST', + headers: { + 'Content-Type': 'application/octet-stream' + }, + body: new Uint8Array(10), +}); // correct + +const k: Request = new Request('http://example.org', { method: 'POST', headers: { 'Content-Type': 'image/jpeg' @@ -27,14 +50,14 @@ const i: Request = new Request('http://example.org', { cache: 'default' }) // correct -const j: Request = new Request('http://example.org', { +const l: Request = new Request('http://example.org', { method: 'GET', headers: 'Content-Type: image/jpeg', mode: 'cors', cache: 'default' }) // incorrect - headers is string -const k: Request = new Request('http://example.org', { +const m: Request = new Request('http://example.org', { method: 'CONNECT', headers: { 'Content-Type': 'image/jpeg' @@ -42,10 +65,3 @@ const k: Request = new Request('http://example.org', { mode: 'cors', cache: 'default' }) // incorrect - CONNECT is forbidden - -var l: boolean = h.bodyUsed; - -h.text().then((t: string) => t); // correct -h.text().then((t: Buffer) => t); // incorrect -h.arrayBuffer().then((ab: ArrayBuffer) => ab); // correct -h.arrayBuffer().then((ab: Buffer) => ab); // incorrect diff --git a/tests/flow/focus-check/__snapshots__/jsfmt.spec.js.snap b/tests/flow/focus-check/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..d08751f7 --- /dev/null +++ b/tests/flow/focus-check/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,34 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`a.js 1`] = ` +"// @flow + +(require('./b'): number); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +(require(\\"./b\\"): number); +" +`; + +exports[`b.js 1`] = ` +"// @flow + +module.exports = \\"\\"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +module.exports = \\"\\"; +" +`; + +exports[`test.js 1`] = ` +"// @flow + +(\\"\\": number); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +(\\"\\": number); +" +`; diff --git a/tests/flow/focus-check/a.js b/tests/flow/focus-check/a.js new file mode 100644 index 00000000..09d81409 --- /dev/null +++ b/tests/flow/focus-check/a.js @@ -0,0 +1,3 @@ +// @flow + +(require('./b'): number); diff --git a/tests/flow/focus-check/b.js b/tests/flow/focus-check/b.js new file mode 100644 index 00000000..5bd72ac0 --- /dev/null +++ b/tests/flow/focus-check/b.js @@ -0,0 +1,3 @@ +// @flow + +module.exports = ""; diff --git a/tests/flow/focus-check/jsfmt.spec.js b/tests/flow/focus-check/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/focus-check/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/focus-check/test.js b/tests/flow/focus-check/test.js new file mode 100644 index 00000000..77de3239 --- /dev/null +++ b/tests/flow/focus-check/test.js @@ -0,0 +1,3 @@ +// @flow + +("": number); diff --git a/tests/flow/function/__snapshots__/jsfmt.spec.js.snap b/tests/flow/function/__snapshots__/jsfmt.spec.js.snap index 89d3175a..b27f9446 100644 --- a/tests/flow/function/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/function/__snapshots__/jsfmt.spec.js.snap @@ -11,8 +11,8 @@ test.apply(\\"\\", [\\"\\", 0]); // wrong this is an error test.apply(0, [\\"\\", 0]); // error: lookup \`length\` on Number -// not enough arguments is an error (via incompatible RestT) -test.apply(\\"\\", [\\"\\"]); // error: string ~> number +// not enough arguments is an error +test.apply(\\"\\", [\\"\\"]); // error: void ~> number // mistyped arguments is an error test.apply(\\"\\", [\\"\\", \\"\\"]); // error: string ~> number (2nd arg) @@ -43,6 +43,12 @@ test.apply(\\"\\", \\"not array\\"); // error: expect array of args function test2(): number { return 0; } (test2.apply(): number); (test2.apply(\\"\\"): number); + +// callable objects +function test3(x: { (a: string, b: string): void }) { + x.apply(x, ['foo', 'bar']); // ok + x.apply(x, ['foo', 123]); // error, number !~> string +} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ function test(a: string, b: number): number { return this.length; // expect []/\\"\\" this @@ -54,8 +60,8 @@ test.apply(\\"\\", [\\"\\", 0]); // wrong this is an error test.apply(0, [\\"\\", 0]); // error: lookup \`length\` on Number -// not enough arguments is an error (via incompatible RestT) -test.apply(\\"\\", [\\"\\"]); // error: string ~> number +// not enough arguments is an error +test.apply(\\"\\", [\\"\\"]); // error: void ~> number // mistyped arguments is an error test.apply(\\"\\", [\\"\\", \\"\\"]); // error: string ~> number (2nd arg) @@ -90,6 +96,12 @@ function test2(): number { } (test2.apply(): number); (test2.apply(\\"\\"): number); + +// callable objects +function test3(x: { (a: string, b: string): void }) { + x.apply(x, [\\"foo\\", \\"bar\\"]); // ok + x.apply(x, [\\"foo\\", 123]); // error, number !~> string +} " `; @@ -102,6 +114,25 @@ let tests = [ y('bar'); // ok y(123); // error, number !~> string }, + + // callable objects + function(x: { (a: string, b: string): void }) { + let y = x.bind(x, 'foo'); + y('bar'); // ok + y(123); // error, number !~> string + }, + + // non-callable objects + function(x: { a: string }) { + x.bind(x, 'foo'); // error + }, + + // callable objects with overridden \`bind\` method + function(x: {(a: string, b: string): void, bind(a: string): void}) { + (x.bind('foo'): void); // ok + (x.bind(123): void); // error, number !~> string + }, + ]; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // @flow @@ -111,6 +142,24 @@ let tests = [ let y = x.bind(x, \\"foo\\"); y(\\"bar\\"); // ok y(123); // error, number !~> string + }, + + // callable objects + function(x: { (a: string, b: string): void }) { + let y = x.bind(x, \\"foo\\"); + y(\\"bar\\"); // ok + y(123); // error, number !~> string + }, + + // non-callable objects + function(x: { a: string }) { + x.bind(x, \\"foo\\"); // error + }, + + // callable objects with overridden \`bind\` method + function(x: { (a: string, b: string): void, bind(a: string): void }) { + (x.bind(\\"foo\\"): void); // ok + (x.bind(123): void); // error, number !~> string } ]; " @@ -129,8 +178,8 @@ test.call(\\"\\", \\"\\", 0); // wrong this is an error test.call(0, \\"\\", 0); // error: lookup \`length\` on Number -// not enough arguments is an error (via incompatible RestT) -test.call(\\"\\", \\"\\"); // error: string ~> number +// not enough arguments is an error +test.call(\\"\\", \\"\\"); // error: void ~> number // mistyped arguments is an error test.call(\\"\\", \\"\\", \\"\\"); // error: string ~> number (2nd arg) @@ -152,6 +201,12 @@ f([0, 0]); // error: number ~> string (1st arg) function test2(): number { return 0; } (test2.call(): number); (test2.call(\\"\\"): number); + +// callable objects +function test3(x: { (a: string, b: string): void }) { + x.call(x, 'foo', 'bar'); // ok + x.call(x, 'foo', 123); // error, number !~> string +} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // @flow @@ -165,8 +220,8 @@ test.call(\\"\\", \\"\\", 0); // wrong this is an error test.call(0, \\"\\", 0); // error: lookup \`length\` on Number -// not enough arguments is an error (via incompatible RestT) -test.call(\\"\\", \\"\\"); // error: string ~> number +// not enough arguments is an error +test.call(\\"\\", \\"\\"); // error: void ~> number // mistyped arguments is an error test.call(\\"\\", \\"\\", \\"\\"); // error: string ~> number (2nd arg) @@ -192,6 +247,12 @@ function test2(): number { } (test2.call(): number); (test2.call(\\"\\"): number); + +// callable objects +function test3(x: { (a: string, b: string): void }) { + x.call(x, \\"foo\\", \\"bar\\"); // ok + x.call(x, \\"foo\\", 123); // error, number !~> string +} " `; @@ -337,42 +398,202 @@ var e = (d.bind(1): Function)(); exports[`rest.js 1`] = ` "/* regression tests */ -function rest_array(...xs: Array): T { +function rest_array(...xs: Array): T { // Ok, arrays can be rest params return xs[0]; } -// Warn, singleton tuple types don't represent rest params -function rest_tuple(...xs: [T]): T { +function rest_tuple(...xs: [T]): T { // Ok, tuples can be rest params return xs[0]; } -function rest_any(...xs: any): any { +function rest_ro_array(...xs: $ReadOnlyArray): T { // Ok return xs[0]; } -// Warn, arbitrary subtypes of an array type don't represent rest params -function rest_t>(...xs: T): U { +function rest_any(...xs: any): any { // Ok, any can be a rest param return xs[0]; } + +function rest_t>(...xs: T): U { // Ok, bounded targ can be rest + return xs[0]; +} + +// These are ok bounds for the rest param +function unbound_rest_t(...xs: T): void {} +function mixed_rest_t(...xs: T): void {} +function array_rest_t>(...xs: T): void {} +function roarray_rest_t>(...xs: T): void {} +function iterable_rest_t>(...xs: T): void {} +function empty_rest_t(...xs: T): void {} +function bounds_on_bounds() { + return function(...xs: T): void {} +} + +// These are bad bounds for the rest param +function bad_unbound_rest_t(...xs: T): T { + return xs.pop(); // Error - no bound on T +} +function string_rest_t(...xs: T): void {} // Error - rest param can't be a string +function empty_rest_t(...xs: T): void {} // Error - rest param can't be empty + +type Rest = Array; +function rest_alias(...xs: Rest): void {} // Ok + +function rest_union(...xs: [1,2] | Array): number { // OK + return xs[0]; +} + +function rest_intersection(...xs: { x: number } & [1,2]): number { // OK + return xs[0] + xs.x; +} + +function empty_rest>(...xs: T): T { return xs; } +(empty_rest(): empty); // Error Array ~> empty + +function return_rest_param>( + f: (...args: Args) => void, +): (...args: Args) => number { + return function(...args) { + return args; // Error: Array ~> number + } +} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /* regression tests */ function rest_array(...xs: Array): T { + // Ok, arrays can be rest params return xs[0]; } -// Warn, singleton tuple types don't represent rest params function rest_tuple(...xs: [T]): T { + // Ok, tuples can be rest params + return xs[0]; +} + +function rest_ro_array(...xs: $ReadOnlyArray): T { + // Ok return xs[0]; } function rest_any(...xs: any): any { + // Ok, any can be a rest param return xs[0]; } -// Warn, arbitrary subtypes of an array type don't represent rest params function rest_t>(...xs: T): U { + // Ok, bounded targ can be rest return xs[0]; } + +// These are ok bounds for the rest param +function unbound_rest_t(...xs: T): void {} +function mixed_rest_t(...xs: T): void {} +function array_rest_t>(...xs: T): void {} +function roarray_rest_t>(...xs: T): void {} +function iterable_rest_t>(...xs: T): void {} +function empty_rest_t(...xs: T): void {} +function bounds_on_bounds() { + return function(...xs: T): void {}; +} + +// These are bad bounds for the rest param +function bad_unbound_rest_t(...xs: T): T { + return xs.pop(); // Error - no bound on T +} +function string_rest_t(...xs: T): void {} // Error - rest param can't be a string +function empty_rest_t(...xs: T): void {} // Error - rest param can't be empty + +type Rest = Array; +function rest_alias(...xs: Rest): void {} // Ok + +function rest_union(...xs: [1, 2] | Array): number { + // OK + return xs[0]; +} + +function rest_intersection(...xs: { x: number } & [1, 2]): number { + // OK + return xs[0] + xs.x; +} + +function empty_rest>(...xs: T): T { + return xs; +} +(empty_rest(): empty); // Error Array ~> empty + +function return_rest_param>( + f: (...args: Args) => void +): (...args: Args) => number { + return function(...args) { + return args; // Error: Array ~> number + }; +} +" +`; + +exports[`rest_type.js 1`] = ` +"/* regression tests */ + +type rest_array = (...xs: Array) => T; // Ok, arrays can be rest params + +type rest_tuple = (...xs: [T]) => T; // Ok, tuples can be rest params + +type rest_ro_array = (...xs: $ReadOnlyArray) => T; // Ok + +type rest_any = (...xs: any) => any; // Ok, any can be a rest param + +type rest_t = >(...xs: T) => U; // Ok, bounded targ can be rest + +type unbound_rest_t = (...xs: T) => void; // Should be error but no way to check yet :( +function test_unbound_rest(f: (x: T, ...xs: T) => void) { + f(123); // Error - number ~> array - luckily this errors +} + +type string_rest_t = (...xs: string) => void; // Should be error but no way to check yet :( +function test_string_rest(f: string_rest_t) { + f('hello'); // Error - string ~> array - luckily this errors +} + +type Rest = Array; +type rest_alias = (...xs: Rest) => void; // Ok + +type rest_union = (...xs: [1,2] | Array) => number; // OK + +type rest_intersection = (...xs: { x: number } & [1,2]) => number; // OK + +type empty_rest = >(...xs: T) => T; // OK +((f: empty_rest) => (f(): empty)); // Error Array ~> empty +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* regression tests */ + +type rest_array = (...xs: Array) => T; // Ok, arrays can be rest params + +type rest_tuple = (...xs: [T]) => T; // Ok, tuples can be rest params + +type rest_ro_array = (...xs: $ReadOnlyArray) => T; // Ok + +type rest_any = (...xs: any) => any; // Ok, any can be a rest param + +type rest_t = >(...xs: T) => U; // Ok, bounded targ can be rest + +type unbound_rest_t = (...xs: T) => void; // Should be error but no way to check yet :( +function test_unbound_rest(f: (x: T, ...xs: T) => void) { + f(123); // Error - number ~> array - luckily this errors +} + +type string_rest_t = (...xs: string) => void; // Should be error but no way to check yet :( +function test_string_rest(f: string_rest_t) { + f(\\"hello\\"); // Error - string ~> array - luckily this errors +} + +type Rest = Array; +type rest_alias = (...xs: Rest) => void; // Ok + +type rest_union = (...xs: [1, 2] | Array) => number; // OK + +type rest_intersection = (...xs: { x: number } & [1, 2]) => number; // OK + +type empty_rest = >(...xs: T) => T; // OK +(f: empty_rest) => (f(): empty); // Error Array ~> empty " `; diff --git a/tests/flow/function/apply.js b/tests/flow/function/apply.js index 527839d3..3cf2bfb0 100644 --- a/tests/flow/function/apply.js +++ b/tests/flow/function/apply.js @@ -8,8 +8,8 @@ test.apply("", ["", 0]); // wrong this is an error test.apply(0, ["", 0]); // error: lookup `length` on Number -// not enough arguments is an error (via incompatible RestT) -test.apply("", [""]); // error: string ~> number +// not enough arguments is an error +test.apply("", [""]); // error: void ~> number // mistyped arguments is an error test.apply("", ["", ""]); // error: string ~> number (2nd arg) @@ -40,3 +40,9 @@ test.apply("", "not array"); // error: expect array of args function test2(): number { return 0; } (test2.apply(): number); (test2.apply(""): number); + +// callable objects +function test3(x: { (a: string, b: string): void }) { + x.apply(x, ['foo', 'bar']); // ok + x.apply(x, ['foo', 123]); // error, number !~> string +} diff --git a/tests/flow/function/bind.js b/tests/flow/function/bind.js index 1e29e8ee..e43c9534 100644 --- a/tests/flow/function/bind.js +++ b/tests/flow/function/bind.js @@ -6,4 +6,23 @@ let tests = [ y('bar'); // ok y(123); // error, number !~> string }, + + // callable objects + function(x: { (a: string, b: string): void }) { + let y = x.bind(x, 'foo'); + y('bar'); // ok + y(123); // error, number !~> string + }, + + // non-callable objects + function(x: { a: string }) { + x.bind(x, 'foo'); // error + }, + + // callable objects with overridden `bind` method + function(x: {(a: string, b: string): void, bind(a: string): void}) { + (x.bind('foo'): void); // ok + (x.bind(123): void); // error, number !~> string + }, + ]; diff --git a/tests/flow/function/call.js b/tests/flow/function/call.js index ebb6acd2..01423c20 100644 --- a/tests/flow/function/call.js +++ b/tests/flow/function/call.js @@ -10,8 +10,8 @@ test.call("", "", 0); // wrong this is an error test.call(0, "", 0); // error: lookup `length` on Number -// not enough arguments is an error (via incompatible RestT) -test.call("", ""); // error: string ~> number +// not enough arguments is an error +test.call("", ""); // error: void ~> number // mistyped arguments is an error test.call("", "", ""); // error: string ~> number (2nd arg) @@ -33,3 +33,9 @@ f([0, 0]); // error: number ~> string (1st arg) function test2(): number { return 0; } (test2.call(): number); (test2.call(""): number); + +// callable objects +function test3(x: { (a: string, b: string): void }) { + x.call(x, 'foo', 'bar'); // ok + x.call(x, 'foo', 123); // error, number !~> string +} diff --git a/tests/flow/function/rest.js b/tests/flow/function/rest.js index 78f52926..4d98e693 100644 --- a/tests/flow/function/rest.js +++ b/tests/flow/function/rest.js @@ -1,19 +1,61 @@ /* regression tests */ -function rest_array(...xs: Array): T { +function rest_array(...xs: Array): T { // Ok, arrays can be rest params return xs[0]; } -// Warn, singleton tuple types don't represent rest params -function rest_tuple(...xs: [T]): T { +function rest_tuple(...xs: [T]): T { // Ok, tuples can be rest params return xs[0]; } -function rest_any(...xs: any): any { +function rest_ro_array(...xs: $ReadOnlyArray): T { // Ok return xs[0]; } -// Warn, arbitrary subtypes of an array type don't represent rest params -function rest_t>(...xs: T): U { +function rest_any(...xs: any): any { // Ok, any can be a rest param return xs[0]; } + +function rest_t>(...xs: T): U { // Ok, bounded targ can be rest + return xs[0]; +} + +// These are ok bounds for the rest param +function unbound_rest_t(...xs: T): void {} +function mixed_rest_t(...xs: T): void {} +function array_rest_t>(...xs: T): void {} +function roarray_rest_t>(...xs: T): void {} +function iterable_rest_t>(...xs: T): void {} +function empty_rest_t(...xs: T): void {} +function bounds_on_bounds() { + return function(...xs: T): void {} +} + +// These are bad bounds for the rest param +function bad_unbound_rest_t(...xs: T): T { + return xs.pop(); // Error - no bound on T +} +function string_rest_t(...xs: T): void {} // Error - rest param can't be a string +function empty_rest_t(...xs: T): void {} // Error - rest param can't be empty + +type Rest = Array; +function rest_alias(...xs: Rest): void {} // Ok + +function rest_union(...xs: [1,2] | Array): number { // OK + return xs[0]; +} + +function rest_intersection(...xs: { x: number } & [1,2]): number { // OK + return xs[0] + xs.x; +} + +function empty_rest>(...xs: T): T { return xs; } +(empty_rest(): empty); // Error Array ~> empty + +function return_rest_param>( + f: (...args: Args) => void, +): (...args: Args) => number { + return function(...args) { + return args; // Error: Array ~> number + } +} diff --git a/tests/flow/function/rest_type.js b/tests/flow/function/rest_type.js new file mode 100644 index 00000000..77b0f08a --- /dev/null +++ b/tests/flow/function/rest_type.js @@ -0,0 +1,31 @@ +/* regression tests */ + +type rest_array = (...xs: Array) => T; // Ok, arrays can be rest params + +type rest_tuple = (...xs: [T]) => T; // Ok, tuples can be rest params + +type rest_ro_array = (...xs: $ReadOnlyArray) => T; // Ok + +type rest_any = (...xs: any) => any; // Ok, any can be a rest param + +type rest_t = >(...xs: T) => U; // Ok, bounded targ can be rest + +type unbound_rest_t = (...xs: T) => void; // Should be error but no way to check yet :( +function test_unbound_rest(f: (x: T, ...xs: T) => void) { + f(123); // Error - number ~> array - luckily this errors +} + +type string_rest_t = (...xs: string) => void; // Should be error but no way to check yet :( +function test_string_rest(f: string_rest_t) { + f('hello'); // Error - string ~> array - luckily this errors +} + +type Rest = Array; +type rest_alias = (...xs: Rest) => void; // Ok + +type rest_union = (...xs: [1,2] | Array) => number; // OK + +type rest_intersection = (...xs: { x: number } & [1,2]) => number; // OK + +type empty_rest = >(...xs: T) => T; // OK +((f: empty_rest) => (f(): empty)); // Error Array ~> empty diff --git a/tests/flow/generators/__snapshots__/jsfmt.spec.js.snap b/tests/flow/generators/__snapshots__/jsfmt.spec.js.snap index 9c432202..dbac50ad 100644 --- a/tests/flow/generators/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/generators/__snapshots__/jsfmt.spec.js.snap @@ -577,6 +577,73 @@ if (multiple_return_result.done) { " `; +exports[`refi.js 1`] = ` +"function *a(x: {a: void | string}): Generator { + if (!x.a) return; + (x.a: string); // ok + yield; + (x.a: string); // error +} + +function *b(x: void | string): Generator { + if (!x) return; + (x: string); // ok + yield; + (x: string); // ok +} + +declare function fn(): Generator; + +function *c(x: {a: void | string}): Generator { + const gen = fn(); + if (!x.a) return; + (x.a: string); // ok + yield * gen; + (x.a: string); // error +} + +function *d(x: void | string): Generator { + const gen = fn(); + if (!x) return; + (x: string); // ok + yield * gen; + (x: string); // ok +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function* a(x: { a: void | string }): Generator { + if (!x.a) return; + (x.a: string); // ok + yield; + (x.a: string); // error +} + +function* b(x: void | string): Generator { + if (!x) return; + (x: string); // ok + yield; + (x: string); // ok +} + +declare function fn(): Generator; + +function* c(x: { a: void | string }): Generator { + const gen = fn(); + if (!x.a) return; + (x.a: string); // ok + yield* gen; + (x.a: string); // error +} + +function* d(x: void | string): Generator { + const gen = fn(); + if (!x) return; + (x: string); // ok + yield* gen; + (x: string); // ok +} +" +`; + exports[`return.js 1`] = ` "function test1(gen: Generator) { // You can pass whatever you like to return, it doesn't need to be related to diff --git a/tests/flow/generators/refi.js b/tests/flow/generators/refi.js new file mode 100644 index 00000000..cd5d7e9e --- /dev/null +++ b/tests/flow/generators/refi.js @@ -0,0 +1,31 @@ +function *a(x: {a: void | string}): Generator { + if (!x.a) return; + (x.a: string); // ok + yield; + (x.a: string); // error +} + +function *b(x: void | string): Generator { + if (!x) return; + (x: string); // ok + yield; + (x: string); // ok +} + +declare function fn(): Generator; + +function *c(x: {a: void | string}): Generator { + const gen = fn(); + if (!x.a) return; + (x.a: string); // ok + yield * gen; + (x.a: string); // error +} + +function *d(x: void | string): Generator { + const gen = fn(); + if (!x) return; + (x: string); // ok + yield * gen; + (x: string); // ok +} diff --git a/tests/flow/get-imports-and-importers/__snapshots__/jsfmt.spec.js.snap b/tests/flow/get-imports/__snapshots__/jsfmt.spec.js.snap similarity index 100% rename from tests/flow/get-imports-and-importers/__snapshots__/jsfmt.spec.js.snap rename to tests/flow/get-imports/__snapshots__/jsfmt.spec.js.snap diff --git a/tests/flow/get-imports-and-importers/a.js b/tests/flow/get-imports/a.js similarity index 100% rename from tests/flow/get-imports-and-importers/a.js rename to tests/flow/get-imports/a.js diff --git a/tests/flow/get-imports-and-importers/b.js b/tests/flow/get-imports/b.js similarity index 100% rename from tests/flow/get-imports-and-importers/b.js rename to tests/flow/get-imports/b.js diff --git a/tests/flow/get-imports-and-importers/c.js b/tests/flow/get-imports/c.js similarity index 100% rename from tests/flow/get-imports-and-importers/c.js rename to tests/flow/get-imports/c.js diff --git a/tests/flow/get-imports/jsfmt.spec.js b/tests/flow/get-imports/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/get-imports/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/getters_and_setters_disabled/__snapshots__/jsfmt.spec.js.snap b/tests/flow/getters_and_setters_disabled/__snapshots__/jsfmt.spec.js.snap index c9a2dafe..818009cb 100644 --- a/tests/flow/getters_and_setters_disabled/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/getters_and_setters_disabled/__snapshots__/jsfmt.spec.js.snap @@ -7,9 +7,31 @@ exports[`getters_and_setters.js 1`] = ` var f = { get a() { return 4; }, - set b(x: number) { this.c = b; }, + set b(x: number) { this.c = x; }, c: 10, + get ['d']() { return 'foo'; }, + set ['d'](x: number) {}, }; + +type T = { + get a(): number, + set b(x: number): void, + c: 10, +} + +declare class Foo { + get a(): number; + set b(x: number): void; + c: 10; +} + +class Bar { + get a() { return 4; } + set b(x: number) { this.c = x; } + c: number; + get ['d']() { return 'foo'; } + set ['d'](x: number) {} +} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * @flow @@ -20,9 +42,39 @@ var f = { return 4; }, set b(x: number) { - this.c = b; + this.c = x; }, + c: 10, + get [\\"d\\"]() { + return \\"foo\\"; + }, + set [\\"d\\"](x: number) {} +}; + +type T = { + a: () => number, + b: (x: number) => void, c: 10 }; + +declare class Foo { + a: () => number, + b: (x: number) => void, + c: 10 +} + +class Bar { + get a() { + return 4; + } + set b(x: number) { + this.c = x; + } + c: number; + get [\\"d\\"]() { + return \\"foo\\"; + } + set [\\"d\\"](x: number) {} +} " `; diff --git a/tests/flow/getters_and_setters_disabled/getters_and_setters.js b/tests/flow/getters_and_setters_disabled/getters_and_setters.js index f20bb486..a297327a 100644 --- a/tests/flow/getters_and_setters_disabled/getters_and_setters.js +++ b/tests/flow/getters_and_setters_disabled/getters_and_setters.js @@ -4,6 +4,28 @@ var f = { get a() { return 4; }, - set b(x: number) { this.c = b; }, + set b(x: number) { this.c = x; }, c: 10, + get ['d']() { return 'foo'; }, + set ['d'](x: number) {}, }; + +type T = { + get a(): number, + set b(x: number): void, + c: 10, +} + +declare class Foo { + get a(): number; + set b(x: number): void; + c: 10; +} + +class Bar { + get a() { return 4; } + set b(x: number) { this.c = x; } + c: number; + get ['d']() { return 'foo'; } + set ['d'](x: number) {} +} diff --git a/tests/flow/getters_and_setters_enabled/__snapshots__/jsfmt.spec.js.snap b/tests/flow/getters_and_setters_enabled/__snapshots__/jsfmt.spec.js.snap index 4e80b1e7..d33d5bbf 100644 --- a/tests/flow/getters_and_setters_enabled/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/getters_and_setters_enabled/__snapshots__/jsfmt.spec.js.snap @@ -33,6 +33,9 @@ class Foo { propOverriddenWithSetter: number; set propOverriddenWithSetter(x: string) { } + + set [z](x: string) {} + get [z](): string { return string; } }; var foo = new Foo(); @@ -107,6 +110,11 @@ class Foo { propOverriddenWithSetter: number; set propOverriddenWithSetter(x: string) {} + + set [z](x: string) {} + get [z](): string { + return string; + } } var foo = new Foo(); @@ -133,6 +141,105 @@ foo.propOverriddenWithSetter = 123; // Error number ~> string " `; +exports[`declare_class.js 1`] = ` +"/** + * @flow + */ + +var z: number = 123; + +declare class Foo { + get goodGetterWithAnnotation(): number; + set goodSetterWithAnnotation(x: number): void; + + get propWithMatchingGetterAndSetter(): number; + set propWithMatchingGetterAndSetter(x: number): void; + + // The getter and setter need not have the same type - no error + get propWithSubtypingGetterAndSetter(): ?number; + set propWithSubtypingGetterAndSetter(x: number): void; + + // The getter and setter need not have the same type - no error + set propWithSubtypingGetterAndSetterReordered(x: number): void; + get propWithSubtypingGetterAndSetterReordered(): ?number; + + get propWithMismatchingGetterAndSetter(): number; + set propWithMismatchingGetterAndSetter(x: string): void; // doesn't match getter (OK) + + propOverriddenWithGetter: number; + get propOverriddenWithGetter(): string; + + propOverriddenWithSetter: number; + set propOverriddenWithSetter(x: string): void; +}; + +var foo = new Foo(); + +// Test getting properties with getters +var testGetterNoError2: number = foo.goodGetterWithAnnotation; + +var testGetterWithError2: string = foo.goodGetterWithAnnotation; // Error number ~> string + +// Test setting properties with getters +foo.goodSetterWithAnnotation = 123; + +foo.goodSetterWithAnnotation = \\"hello\\"; // Error string ~> number + +var testSubtypingGetterAndSetter: number = foo.propWithSubtypingGetterAndSetter; // Error ?number ~> number + +var testPropOverridenWithGetter: number = foo.propOverriddenWithGetter; // Error string ~> number +foo.propOverriddenWithSetter = 123; // Error number ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ + +var z: number = 123; + +declare class Foo { + goodGetterWithAnnotation: () => number, + goodSetterWithAnnotation: (x: number) => void, + + propWithMatchingGetterAndSetter: () => number, + propWithMatchingGetterAndSetter: (x: number) => void, + + // The getter and setter need not have the same type - no error + propWithSubtypingGetterAndSetter: () => ?number, + propWithSubtypingGetterAndSetter: (x: number) => void, + + // The getter and setter need not have the same type - no error + propWithSubtypingGetterAndSetterReordered: (x: number) => void, + propWithSubtypingGetterAndSetterReordered: () => ?number, + + propWithMismatchingGetterAndSetter: () => number, + propWithMismatchingGetterAndSetter: (x: string) => void, // doesn't match getter (OK) + + propOverriddenWithGetter: number, + propOverriddenWithGetter: () => string, + + propOverriddenWithSetter: number, + propOverriddenWithSetter: (x: string) => void +} + +var foo = new Foo(); + +// Test getting properties with getters +var testGetterNoError2: number = foo.goodGetterWithAnnotation; + +var testGetterWithError2: string = foo.goodGetterWithAnnotation; // Error number ~> string + +// Test setting properties with getters +foo.goodSetterWithAnnotation = 123; + +foo.goodSetterWithAnnotation = \\"hello\\"; // Error string ~> number + +var testSubtypingGetterAndSetter: number = foo.propWithSubtypingGetterAndSetter; // Error ?number ~> number + +var testPropOverridenWithGetter: number = foo.propOverriddenWithGetter; // Error string ~> number +foo.propOverriddenWithSetter = 123; // Error number ~> string +" +`; + exports[`object.js 1`] = ` "/** * @flow @@ -166,6 +273,9 @@ var obj = { set exampleOfOrderOfGetterAndSetterReordered(x: B) {}, get exampleOfOrderOfGetterAndSetterReordered(): A { return new A(); }, + + set [z](x: string) {}, + get [z](): string { return string; }, }; @@ -244,6 +354,11 @@ var obj = { set exampleOfOrderOfGetterAndSetterReordered(x: B) {}, get exampleOfOrderOfGetterAndSetterReordered(): A { return new A(); + }, + + set [z](x: string) {}, + get [z](): string { + return string; } }; @@ -273,34 +388,153 @@ var testExampleOrOrderOfGetterAndSetterReordered: number = obj.exampleOfOrderOfG " `; -exports[`react.js 1`] = ` +exports[`object_type.js 1`] = ` "/** * @flow */ -React.createClass({ - propTypes: { - get a() { return 4; }, - set b(x: number) { this.c = x; }, - c: 10, - } -}); +var z: number = 123; + +class A {} +class B extends A {} +class C extends A {} + +type T = { + get goodGetterWithAnnotation(): number, + set goodSetterWithAnnotation(x: number): void, + + get propWithMatchingGetterAndSetter(): number, + set propWithMatchingGetterAndSetter(x: number): void, + + // The getter and setter need not have the same type + get propWithSubtypingGetterAndSetter(): ?number, // OK + set propWithSubtypingGetterAndSetter(x: number): void, + + set propWithSubtypingGetterAndSetterReordered(x: number): void, // OK + get propWithSubtypingGetterAndSetterReordered(): ?number, + + get exampleOfOrderOfGetterAndSetter(): A, + set exampleOfOrderOfGetterAndSetter(x: B): void, + + set exampleOfOrderOfGetterAndSetterReordered(x: B): void, + get exampleOfOrderOfGetterAndSetterReordered(): A, +}; + +function test(obj: T) { + // Test getting properties with getters + var testGetterNoError2: number = obj.goodGetterWithAnnotation; + + var testGetterWithError2: string = obj.goodGetterWithAnnotation; // Error number ~> string + + // Test setting properties with getters + obj.goodSetterWithAnnotation = 123; + + obj.goodSetterWithAnnotation = \\"hello\\"; // Error string ~> number + + var testSubtypingGetterAndSetter: number = obj.propWithSubtypingGetterAndSetter; // Error ?number ~> number + + // When building this feature, it was tempting to flow the setter into the + // getter and then use either the getter or setter as the type of the + // property. This example shows the danger of using the getter's type + obj.exampleOfOrderOfGetterAndSetter = new C(); // Error C ~> B + + // And this example shows the danger of using the setter's type. + var testExampleOrOrderOfGetterAndSetterReordered: number = + obj.exampleOfOrderOfGetterAndSetterReordered; // Error A ~> B +} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * @flow */ -React.createClass({ +var z: number = 123; + +class A {} +class B extends A {} +class C extends A {} + +type T = { + goodGetterWithAnnotation: () => number, + goodSetterWithAnnotation: (x: number) => void, + propWithMatchingGetterAndSetter: () => number, + propWithMatchingGetterAndSetter: (x: number) => void, + // The getter and setter need not have the same type + propWithSubtypingGetterAndSetter: () => ?number, // OK + propWithSubtypingGetterAndSetter: (x: number) => void, + propWithSubtypingGetterAndSetterReordered: (x: number) => void, // OK + propWithSubtypingGetterAndSetterReordered: () => ?number, + exampleOfOrderOfGetterAndSetter: () => A, + exampleOfOrderOfGetterAndSetter: (x: B) => void, + exampleOfOrderOfGetterAndSetterReordered: (x: B) => void, + exampleOfOrderOfGetterAndSetterReordered: () => A +}; + +function test(obj: T) { + // Test getting properties with getters + var testGetterNoError2: number = obj.goodGetterWithAnnotation; + + var testGetterWithError2: string = obj.goodGetterWithAnnotation; // Error number ~> string + + // Test setting properties with getters + obj.goodSetterWithAnnotation = 123; + + obj.goodSetterWithAnnotation = \\"hello\\"; // Error string ~> number + + var testSubtypingGetterAndSetter: number = obj.propWithSubtypingGetterAndSetter; // Error ?number ~> number + + // When building this feature, it was tempting to flow the setter into the + // getter and then use either the getter or setter as the type of the + // property. This example shows the danger of using the getter's type + obj.exampleOfOrderOfGetterAndSetter = new C(); // Error C ~> B + + // And this example shows the danger of using the setter's type. + var testExampleOrOrderOfGetterAndSetterReordered: number = obj.exampleOfOrderOfGetterAndSetterReordered; // Error A ~> B +} +" +`; + +exports[`react.js 1`] = ` +"/** + * @flow + */ + +import React from \\"react\\"; + +const Example = React.createClass({ + propTypes: { + get a() { return React.PropTypes.number.isRequired; }, + set b(x: number) { this.c = x; }, + c: React.PropTypes.string, + } +}); + +(); // error: property \`a\` not found +(); // ok +(); // error: number ~> string +(); // error: number ~> string +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ + +import React from \\"react\\"; + +const Example = React.createClass({ propTypes: { get a() { - return 4; + return React.PropTypes.number.isRequired; }, set b(x: number) { this.c = x; }, - c: 10 + c: React.PropTypes.string } }); + +; // error: property \`a\` not found +; // ok +; // error: number ~> string +; // error: number ~> string " `; diff --git a/tests/flow/getters_and_setters_enabled/class.js b/tests/flow/getters_and_setters_enabled/class.js index c9a3707e..5f3c9038 100644 --- a/tests/flow/getters_and_setters_enabled/class.js +++ b/tests/flow/getters_and_setters_enabled/class.js @@ -30,6 +30,9 @@ class Foo { propOverriddenWithSetter: number; set propOverriddenWithSetter(x: string) { } + + set [z](x: string) {} + get [z](): string { return string; } }; var foo = new Foo(); diff --git a/tests/flow/getters_and_setters_enabled/declare_class.js b/tests/flow/getters_and_setters_enabled/declare_class.js new file mode 100644 index 00000000..a851cf7e --- /dev/null +++ b/tests/flow/getters_and_setters_enabled/declare_class.js @@ -0,0 +1,47 @@ +/** + * @flow + */ + +var z: number = 123; + +declare class Foo { + get goodGetterWithAnnotation(): number; + set goodSetterWithAnnotation(x: number): void; + + get propWithMatchingGetterAndSetter(): number; + set propWithMatchingGetterAndSetter(x: number): void; + + // The getter and setter need not have the same type - no error + get propWithSubtypingGetterAndSetter(): ?number; + set propWithSubtypingGetterAndSetter(x: number): void; + + // The getter and setter need not have the same type - no error + set propWithSubtypingGetterAndSetterReordered(x: number): void; + get propWithSubtypingGetterAndSetterReordered(): ?number; + + get propWithMismatchingGetterAndSetter(): number; + set propWithMismatchingGetterAndSetter(x: string): void; // doesn't match getter (OK) + + propOverriddenWithGetter: number; + get propOverriddenWithGetter(): string; + + propOverriddenWithSetter: number; + set propOverriddenWithSetter(x: string): void; +}; + +var foo = new Foo(); + +// Test getting properties with getters +var testGetterNoError2: number = foo.goodGetterWithAnnotation; + +var testGetterWithError2: string = foo.goodGetterWithAnnotation; // Error number ~> string + +// Test setting properties with getters +foo.goodSetterWithAnnotation = 123; + +foo.goodSetterWithAnnotation = "hello"; // Error string ~> number + +var testSubtypingGetterAndSetter: number = foo.propWithSubtypingGetterAndSetter; // Error ?number ~> number + +var testPropOverridenWithGetter: number = foo.propOverriddenWithGetter; // Error string ~> number +foo.propOverriddenWithSetter = 123; // Error number ~> string diff --git a/tests/flow/getters_and_setters_enabled/jsfmt.spec.js b/tests/flow/getters_and_setters_enabled/jsfmt.spec.js index 1840e9d3..21dba9fe 100644 --- a/tests/flow/getters_and_setters_enabled/jsfmt.spec.js +++ b/tests/flow/getters_and_setters_enabled/jsfmt.spec.js @@ -1 +1,5 @@ -run_spec(__dirname, null, ["babylon"]); +run_spec(__dirname); + +// TODO: These tests used to be run in babylon as well, but currently babylon +// fails to parse them. +// run_spec(__dirname, null, ["babylon"]); diff --git a/tests/flow/getters_and_setters_enabled/object.js b/tests/flow/getters_and_setters_enabled/object.js index 5e54f25f..2a1dbd3f 100644 --- a/tests/flow/getters_and_setters_enabled/object.js +++ b/tests/flow/getters_and_setters_enabled/object.js @@ -30,6 +30,9 @@ var obj = { set exampleOfOrderOfGetterAndSetterReordered(x: B) {}, get exampleOfOrderOfGetterAndSetterReordered(): A { return new A(); }, + + set [z](x: string) {}, + get [z](): string { return string; }, }; diff --git a/tests/flow/getters_and_setters_enabled/object_type.js b/tests/flow/getters_and_setters_enabled/object_type.js new file mode 100644 index 00000000..932f5994 --- /dev/null +++ b/tests/flow/getters_and_setters_enabled/object_type.js @@ -0,0 +1,53 @@ +/** + * @flow + */ + +var z: number = 123; + +class A {} +class B extends A {} +class C extends A {} + +type T = { + get goodGetterWithAnnotation(): number, + set goodSetterWithAnnotation(x: number): void, + + get propWithMatchingGetterAndSetter(): number, + set propWithMatchingGetterAndSetter(x: number): void, + + // The getter and setter need not have the same type + get propWithSubtypingGetterAndSetter(): ?number, // OK + set propWithSubtypingGetterAndSetter(x: number): void, + + set propWithSubtypingGetterAndSetterReordered(x: number): void, // OK + get propWithSubtypingGetterAndSetterReordered(): ?number, + + get exampleOfOrderOfGetterAndSetter(): A, + set exampleOfOrderOfGetterAndSetter(x: B): void, + + set exampleOfOrderOfGetterAndSetterReordered(x: B): void, + get exampleOfOrderOfGetterAndSetterReordered(): A, +}; + +function test(obj: T) { + // Test getting properties with getters + var testGetterNoError2: number = obj.goodGetterWithAnnotation; + + var testGetterWithError2: string = obj.goodGetterWithAnnotation; // Error number ~> string + + // Test setting properties with getters + obj.goodSetterWithAnnotation = 123; + + obj.goodSetterWithAnnotation = "hello"; // Error string ~> number + + var testSubtypingGetterAndSetter: number = obj.propWithSubtypingGetterAndSetter; // Error ?number ~> number + + // When building this feature, it was tempting to flow the setter into the + // getter and then use either the getter or setter as the type of the + // property. This example shows the danger of using the getter's type + obj.exampleOfOrderOfGetterAndSetter = new C(); // Error C ~> B + + // And this example shows the danger of using the setter's type. + var testExampleOrOrderOfGetterAndSetterReordered: number = + obj.exampleOfOrderOfGetterAndSetterReordered; // Error A ~> B +} diff --git a/tests/flow/getters_and_setters_enabled/react.js b/tests/flow/getters_and_setters_enabled/react.js index 811acd9e..f9af67f9 100644 --- a/tests/flow/getters_and_setters_enabled/react.js +++ b/tests/flow/getters_and_setters_enabled/react.js @@ -2,10 +2,17 @@ * @flow */ -React.createClass({ +import React from "react"; + +const Example = React.createClass({ propTypes: { - get a() { return 4; }, + get a() { return React.PropTypes.number.isRequired; }, set b(x: number) { this.c = x; }, - c: 10, + c: React.PropTypes.string, } }); + +(); // error: property `a` not found +(); // ok +(); // error: number ~> string +(); // error: number ~> string diff --git a/tests/flow/haste_name_reducers_defaults/Module1.js b/tests/flow/haste_name_reducers_defaults/Module1.js new file mode 100644 index 00000000..cd65a747 --- /dev/null +++ b/tests/flow/haste_name_reducers_defaults/Module1.js @@ -0,0 +1,7 @@ +/* @flow */ + +var test = require('test'); + +(test: boolean); + +module.exports = test; diff --git a/tests/flow/haste_name_reducers_defaults/__snapshots__/jsfmt.spec.js.snap b/tests/flow/haste_name_reducers_defaults/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..1d225042 --- /dev/null +++ b/tests/flow/haste_name_reducers_defaults/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,35 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Module1.js 1`] = ` +"/* @flow */ + +var test = require('test'); + +(test: boolean); + +module.exports = test; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ + +var test = require(\\"test\\"); + +(test: boolean); + +module.exports = test; +" +`; + +exports[`index.js 1`] = ` +"/* @flow */ + +(require('Module1'): boolean); +(require('Module2').foo(): boolean); +require('Module3'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ + +(require(\\"Module1\\"): boolean); +(require(\\"Module2\\").foo(): boolean); +require(\\"Module3\\"); +" +`; diff --git a/tests/flow/haste_name_reducers_defaults/index.js b/tests/flow/haste_name_reducers_defaults/index.js new file mode 100644 index 00000000..c04bb0d9 --- /dev/null +++ b/tests/flow/haste_name_reducers_defaults/index.js @@ -0,0 +1,5 @@ +/* @flow */ + +(require('Module1'): boolean); +(require('Module2').foo(): boolean); +require('Module3'); diff --git a/tests/flow/haste_name_reducers_defaults/jsfmt.spec.js b/tests/flow/haste_name_reducers_defaults/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/haste_name_reducers_defaults/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/haste_use_name_reducers/__snapshots__/jsfmt.spec.js.snap b/tests/flow/haste_use_name_reducers/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..67c6f6ad --- /dev/null +++ b/tests/flow/haste_use_name_reducers/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`index.js 1`] = ` +"/* @flow */ + +(require('Module1'): boolean); +require('Module2'); +require('Module3'); +require('Module4'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ + +(require(\\"Module1\\"): boolean); +require(\\"Module2\\"); +require(\\"Module3\\"); +require(\\"Module4\\"); +" +`; diff --git a/tests/flow/haste_use_name_reducers/dir1/Module1.js b/tests/flow/haste_use_name_reducers/dir1/Module1.js new file mode 100644 index 00000000..01bb486e --- /dev/null +++ b/tests/flow/haste_use_name_reducers/dir1/Module1.js @@ -0,0 +1,7 @@ +/* @flow */ + +require('test'); + +class Impl {} + +module.exports = new Impl(); diff --git a/tests/flow/haste_use_name_reducers/dir1/__snapshots__/jsfmt.spec.js.snap b/tests/flow/haste_use_name_reducers/dir1/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..b97604cc --- /dev/null +++ b/tests/flow/haste_use_name_reducers/dir1/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Module1.js 1`] = ` +"/* @flow */ + +require('test'); + +class Impl {} + +module.exports = new Impl(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ + +require(\\"test\\"); + +class Impl {} + +module.exports = new Impl(); +" +`; diff --git a/tests/flow/haste_use_name_reducers/dir1/jsfmt.spec.js b/tests/flow/haste_use_name_reducers/dir1/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/haste_use_name_reducers/dir1/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/haste_use_name_reducers/dir2/Module2.ios.js b/tests/flow/haste_use_name_reducers/dir2/Module2.ios.js new file mode 100644 index 00000000..c3301330 --- /dev/null +++ b/tests/flow/haste_use_name_reducers/dir2/Module2.ios.js @@ -0,0 +1,3 @@ +/* @flow */ + +require('test2'); diff --git a/tests/flow/haste_use_name_reducers/dir2/__snapshots__/jsfmt.spec.js.snap b/tests/flow/haste_use_name_reducers/dir2/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..0641de1a --- /dev/null +++ b/tests/flow/haste_use_name_reducers/dir2/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,12 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Module2.ios.js 1`] = ` +"/* @flow */ + +require('test2'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ + +require(\\"test2\\"); +" +`; diff --git a/tests/flow/haste_use_name_reducers/dir2/jsfmt.spec.js b/tests/flow/haste_use_name_reducers/dir2/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/haste_use_name_reducers/dir2/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/haste_use_name_reducers/index.js b/tests/flow/haste_use_name_reducers/index.js new file mode 100644 index 00000000..11468dbc --- /dev/null +++ b/tests/flow/haste_use_name_reducers/index.js @@ -0,0 +1,6 @@ +/* @flow */ + +(require('Module1'): boolean); +require('Module2'); +require('Module3'); +require('Module4'); diff --git a/tests/flow/haste_use_name_reducers/jsfmt.spec.js b/tests/flow/haste_use_name_reducers/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/haste_use_name_reducers/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/implements/__snapshots__/jsfmt.spec.js.snap b/tests/flow/implements/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..ebd37df5 --- /dev/null +++ b/tests/flow/implements/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,72 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`test.js 1`] = ` +"/* @noflow */ + +interface IFoo { foo: string } + +class C1 implements IFoo {} // error: property \`foo\` not found +class C2 implements IFoo { foo: number } // error: number <~> string +class C3 implements IFoo { foo: string } // ok + +(new C1: IFoo); // ok, we already errored at def site + +interface IBar { bar: number } + +class C4 implements IFoo, IBar {} // error: properties \`foo\`, \`bar\` not found +(new C4: IBar); // ok, we already errored at def site + +interface IFooBar extends IFoo { bar: number } + +class C5 implements IFooBar {} // error: properties \`foo\`, \`bar\` not found +(new C5: IFooBar); // ok, already errored at def site +(new C5: IFoo); // ok, already errored at def site +(new C5: IBar); // error: property \`bar\` not found (despite IBar < IFooBar) + +class C6 extends C1 {} +(new C6: IFoo); // ok, C1 implements IFoo + +class C7 implements C1 {} // error: C1 is a class, expected an interface + +// ensure BoundT substituted appropriately +interface IPoly { x: T } +class C8 implements IPoly { x: T } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @noflow */ + +interface IFoo { foo: string } + +class C1 implements IFoo {} // error: property \`foo\` not found +class C2 implements IFoo { + foo: number; +} // error: number <~> string +class C3 implements IFoo { + foo: string; +} // ok + +(new C1(): IFoo); // ok, we already errored at def site + +interface IBar { bar: number } + +class C4 implements IFoo, IBar {} // error: properties \`foo\`, \`bar\` not found +(new C4(): IBar); // ok, we already errored at def site + +interface IFooBar extends IFoo { bar: number } + +class C5 implements IFooBar {} // error: properties \`foo\`, \`bar\` not found +(new C5(): IFooBar); // ok, already errored at def site +(new C5(): IFoo); // ok, already errored at def site +(new C5(): IBar); // error: property \`bar\` not found (despite IBar < IFooBar) + +class C6 extends C1 {} +(new C6(): IFoo); // ok, C1 implements IFoo + +class C7 implements C1 {} // error: C1 is a class, expected an interface + +// ensure BoundT substituted appropriately +interface IPoly { x: T } +class C8 implements IPoly { + x: T; +} +" +`; diff --git a/tests/flow/implements/jsfmt.spec.js b/tests/flow/implements/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/implements/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/implements/test.js b/tests/flow/implements/test.js new file mode 100644 index 00000000..68b28e1c --- /dev/null +++ b/tests/flow/implements/test.js @@ -0,0 +1,30 @@ +/* @noflow */ + +interface IFoo { foo: string } + +class C1 implements IFoo {} // error: property `foo` not found +class C2 implements IFoo { foo: number } // error: number <~> string +class C3 implements IFoo { foo: string } // ok + +(new C1: IFoo); // ok, we already errored at def site + +interface IBar { bar: number } + +class C4 implements IFoo, IBar {} // error: properties `foo`, `bar` not found +(new C4: IBar); // ok, we already errored at def site + +interface IFooBar extends IFoo { bar: number } + +class C5 implements IFooBar {} // error: properties `foo`, `bar` not found +(new C5: IFooBar); // ok, already errored at def site +(new C5: IFoo); // ok, already errored at def site +(new C5: IBar); // error: property `bar` not found (despite IBar < IFooBar) + +class C6 extends C1 {} +(new C6: IFoo); // ok, C1 implements IFoo + +class C7 implements C1 {} // error: C1 is a class, expected an interface + +// ensure BoundT substituted appropriately +interface IPoly { x: T } +class C8 implements IPoly { x: T } diff --git a/tests/flow/import_type/__snapshots__/jsfmt.spec.js.snap b/tests/flow/import_type/__snapshots__/jsfmt.spec.js.snap index 657b4935..a7e0adbe 100644 --- a/tests/flow/import_type/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/import_type/__snapshots__/jsfmt.spec.js.snap @@ -347,23 +347,6 @@ function foo() { " `; -exports[`import_type_specifier.js 1`] = ` -"/** - * @flow - */ - -import { Foo, type Baz } from \\"../module\\"; -import type {} from 'foo'; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -/** - * @flow - */ - -import { Foo, type Baz } from \\"../module\\"; -import type {} from \\"foo\\"; -" -`; - exports[`issue-359.js 1`] = ` "/* @flow */ diff --git a/tests/flow/incremental_cycle_break/A.js b/tests/flow/incremental_cycle_break/A.js new file mode 100644 index 00000000..9b88b202 --- /dev/null +++ b/tests/flow/incremental_cycle_break/A.js @@ -0,0 +1,6 @@ +/** + * @providesModule A + * @flow + */ + +require('B'); diff --git a/tests/flow/incremental_cycle_break/B.js b/tests/flow/incremental_cycle_break/B.js new file mode 100644 index 00000000..1cabf2a3 --- /dev/null +++ b/tests/flow/incremental_cycle_break/B.js @@ -0,0 +1,5 @@ +/** + * @providesModule B + * @flow + */ +require('A'); diff --git a/tests/flow/incremental_cycle_break/__snapshots__/jsfmt.spec.js.snap b/tests/flow/incremental_cycle_break/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..91f744e3 --- /dev/null +++ b/tests/flow/incremental_cycle_break/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`A.js 1`] = ` +"/** + * @providesModule A + * @flow + */ + +require('B'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule A + * @flow + */ + +require(\\"B\\"); +" +`; + +exports[`B.js 1`] = ` +"/** + * @providesModule B + * @flow + */ +require('A'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule B + * @flow + */ +require(\\"A\\"); +" +`; diff --git a/tests/flow/incremental_cycle_break/jsfmt.spec.js b/tests/flow/incremental_cycle_break/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/incremental_cycle_break/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/incremental_cycle_break/tmp1/A.js b/tests/flow/incremental_cycle_break/tmp1/A.js new file mode 100644 index 00000000..e69de29b diff --git a/tests/flow/incremental_cycle_break/tmp1/__snapshots__/jsfmt.spec.js.snap b/tests/flow/incremental_cycle_break/tmp1/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..0d57f9ff --- /dev/null +++ b/tests/flow/incremental_cycle_break/tmp1/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,6 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`A.js 1`] = ` +"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +" +`; diff --git a/tests/flow/incremental_cycle_break/tmp1/jsfmt.spec.js b/tests/flow/incremental_cycle_break/tmp1/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/incremental_cycle_break/tmp1/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/incremental_haste_blacklist/A.js b/tests/flow/incremental_haste_blacklist/A.js new file mode 100644 index 00000000..e667c471 --- /dev/null +++ b/tests/flow/incremental_haste_blacklist/A.js @@ -0,0 +1 @@ +/* @flow */ diff --git a/tests/flow/incremental_haste_blacklist/__snapshots__/jsfmt.spec.js.snap b/tests/flow/incremental_haste_blacklist/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..9ff1237b --- /dev/null +++ b/tests/flow/incremental_haste_blacklist/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`A.js 1`] = ` +"/* @flow */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +" +`; + +exports[`index.js 1`] = ` +"/* @flow */ +require('A'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +require(\\"A\\"); +" +`; diff --git a/tests/flow/incremental_haste_blacklist/index.js b/tests/flow/incremental_haste_blacklist/index.js new file mode 100644 index 00000000..2ad887c5 --- /dev/null +++ b/tests/flow/incremental_haste_blacklist/index.js @@ -0,0 +1,2 @@ +/* @flow */ +require('A'); diff --git a/tests/flow/incremental_haste_blacklist/jsfmt.spec.js b/tests/flow/incremental_haste_blacklist/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/incremental_haste_blacklist/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/incremental_haste_name_reducers_duplicate/A.js b/tests/flow/incremental_haste_name_reducers_duplicate/A.js new file mode 100644 index 00000000..e667c471 --- /dev/null +++ b/tests/flow/incremental_haste_name_reducers_duplicate/A.js @@ -0,0 +1 @@ +/* @flow */ diff --git a/tests/flow/incremental_haste_name_reducers_duplicate/__snapshots__/jsfmt.spec.js.snap b/tests/flow/incremental_haste_name_reducers_duplicate/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..9ff1237b --- /dev/null +++ b/tests/flow/incremental_haste_name_reducers_duplicate/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`A.js 1`] = ` +"/* @flow */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +" +`; + +exports[`index.js 1`] = ` +"/* @flow */ +require('A'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +require(\\"A\\"); +" +`; diff --git a/tests/flow/incremental_haste_name_reducers_duplicate/index.js b/tests/flow/incremental_haste_name_reducers_duplicate/index.js new file mode 100644 index 00000000..2ad887c5 --- /dev/null +++ b/tests/flow/incremental_haste_name_reducers_duplicate/index.js @@ -0,0 +1,2 @@ +/* @flow */ +require('A'); diff --git a/tests/flow/incremental_haste_name_reducers_duplicate/jsfmt.spec.js b/tests/flow/incremental_haste_name_reducers_duplicate/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/incremental_haste_name_reducers_duplicate/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/iterable/__snapshots__/jsfmt.spec.js.snap b/tests/flow/iterable/__snapshots__/jsfmt.spec.js.snap index 6a6b92ed..72056d24 100644 --- a/tests/flow/iterable/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/iterable/__snapshots__/jsfmt.spec.js.snap @@ -3,25 +3,21 @@ exports[`array.js 1`] = ` "/* @flow */ -var arrayTest1: Iterable = ([1, 2]: Array); -var arrayTest2: Iterable = [1,2,\\"hi\\"]; -var arrayTest3: Iterable<*> = [1,2,3]; +(([1, 2]: Array): Iterable); +([1,2,\\"hi\\"]: Iterable); +([1,2,3]: Iterable<*>); -// Error string ~> number -var arrayTest4: Iterable = [\\"hi\\"]; -// Error string ~> number -var arrayTest5: Iterable = [\\"hi\\", 1]; +([\\"hi\\"]: Iterable); // Error string ~> number +([\\"hi\\", 1]: Iterable); // Error number ~> string ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /* @flow */ -var arrayTest1: Iterable = ([1, 2]: Array); -var arrayTest2: Iterable = [1, 2, \\"hi\\"]; -var arrayTest3: Iterable<*> = [1, 2, 3]; +(([1, 2]: Array): Iterable); +([1, 2, \\"hi\\"]: Iterable); +([1, 2, 3]: Iterable<*>); -// Error string ~> number -var arrayTest4: Iterable = [\\"hi\\"]; -// Error string ~> number -var arrayTest5: Iterable = [\\"hi\\", 1]; +([\\"hi\\"]: Iterable); // Error string ~> number +([\\"hi\\", 1]: Iterable); // Error number ~> string " `; @@ -244,15 +240,15 @@ function setTest4(set: Set): Iterable { exports[`string.js 1`] = ` "/* @flow */ -var stringTest1: Iterable = \\"hi\\"; -var stringTest3: Iterable<*> = \\"hi\\"; -var stringTest3: Iterable = \\"hi\\"; // Error - string is a Iterable +(\\"hi\\": Iterable); +(\\"hi\\": Iterable<*>); +(\\"hi\\": Iterable); // Error - string is a Iterable ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /* @flow */ -var stringTest1: Iterable = \\"hi\\"; -var stringTest3: Iterable<*> = \\"hi\\"; -var stringTest3: Iterable = \\"hi\\"; // Error - string is a Iterable +(\\"hi\\": Iterable); +(\\"hi\\": Iterable<*>); +(\\"hi\\": Iterable); // Error - string is a Iterable " `; diff --git a/tests/flow/iterable/array.js b/tests/flow/iterable/array.js index c50c20f9..87655229 100644 --- a/tests/flow/iterable/array.js +++ b/tests/flow/iterable/array.js @@ -1,10 +1,8 @@ /* @flow */ -var arrayTest1: Iterable = ([1, 2]: Array); -var arrayTest2: Iterable = [1,2,"hi"]; -var arrayTest3: Iterable<*> = [1,2,3]; +(([1, 2]: Array): Iterable); +([1,2,"hi"]: Iterable); +([1,2,3]: Iterable<*>); -// Error string ~> number -var arrayTest4: Iterable = ["hi"]; -// Error string ~> number -var arrayTest5: Iterable = ["hi", 1]; +(["hi"]: Iterable); // Error string ~> number +(["hi", 1]: Iterable); // Error number ~> string diff --git a/tests/flow/iterable/string.js b/tests/flow/iterable/string.js index bae6cec8..34922d09 100644 --- a/tests/flow/iterable/string.js +++ b/tests/flow/iterable/string.js @@ -1,5 +1,5 @@ /* @flow */ -var stringTest1: Iterable = "hi"; -var stringTest3: Iterable<*> = "hi"; -var stringTest3: Iterable = "hi"; // Error - string is a Iterable +("hi": Iterable); +("hi": Iterable<*>); +("hi": Iterable); // Error - string is a Iterable diff --git a/tests/flow/match_failure/__snapshots__/jsfmt.spec.js.snap b/tests/flow/match_failure/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..aaa2dfa0 --- /dev/null +++ b/tests/flow/match_failure/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,65 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`disjoint_union.js 1`] = ` +"/* @flow */ + +type Shape = + {type: 'rectangle', width: number, height: number} | + {type: 'circle', radius: number}; + +function area(shape: Shape): number { + if (shape.type === 'square') { // TODO: this should be an error + return shape.width * shape.height; + } else if (shape.type === 'circle') { + return Math.PI * Math.pow(shape.radius, 2); + } + throw \\"unreachable\\"; // TODO: this shouldn't be needed +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ + +type Shape = + | { type: \\"rectangle\\", width: number, height: number } + | { type: \\"circle\\", radius: number }; + +function area(shape: Shape): number { + if (shape.type === \\"square\\") { + // TODO: this should be an error + return shape.width * shape.height; + } else if (shape.type === \\"circle\\") { + return Math.PI * Math.pow(shape.radius, 2); + } + throw \\"unreachable\\"; // TODO: this shouldn't be needed +} +" +`; + +exports[`enum.js 1`] = ` +"// @flow + +type Binary = 0 | 1; + +function stringifyBinary(binary: Binary): string { + if (binary === 0) { + return 'zero'; + } else if (binary === 2) { // oops + return 'one'; + } + throw \\"unreachable\\"; // TODO: this shouldn't be needed +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +type Binary = 0 | 1; + +function stringifyBinary(binary: Binary): string { + if (binary === 0) { + return \\"zero\\"; + } else if (binary === 2) { + // oops + return \\"one\\"; + } + throw \\"unreachable\\"; // TODO: this shouldn't be needed +} +" +`; diff --git a/tests/flow/match_failure/disjoint_union.js b/tests/flow/match_failure/disjoint_union.js new file mode 100644 index 00000000..2cb49b9e --- /dev/null +++ b/tests/flow/match_failure/disjoint_union.js @@ -0,0 +1,14 @@ +/* @flow */ + +type Shape = + {type: 'rectangle', width: number, height: number} | + {type: 'circle', radius: number}; + +function area(shape: Shape): number { + if (shape.type === 'square') { // TODO: this should be an error + return shape.width * shape.height; + } else if (shape.type === 'circle') { + return Math.PI * Math.pow(shape.radius, 2); + } + throw "unreachable"; // TODO: this shouldn't be needed +} diff --git a/tests/flow/match_failure/enum.js b/tests/flow/match_failure/enum.js new file mode 100644 index 00000000..e9ac12b4 --- /dev/null +++ b/tests/flow/match_failure/enum.js @@ -0,0 +1,12 @@ +// @flow + +type Binary = 0 | 1; + +function stringifyBinary(binary: Binary): string { + if (binary === 0) { + return 'zero'; + } else if (binary === 2) { // oops + return 'one'; + } + throw "unreachable"; // TODO: this shouldn't be needed +} diff --git a/tests/flow/match_failure/jsfmt.spec.js b/tests/flow/match_failure/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/match_failure/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/more_react/InitializedFields.js b/tests/flow/more_react/InitializedFields.js new file mode 100644 index 00000000..babddab0 --- /dev/null +++ b/tests/flow/more_react/InitializedFields.js @@ -0,0 +1,14 @@ +/** + * @providesModule InitializedFields.react + */ + +var React = require('react'); + +/** This is a regression test for a bug where we forgot to mark the fields of + * react classes as initialized, when the class was created with createClass(). + * This would manifest as complaining that metric requires an annotation */ +var App = React.createClass({ + metrics: [1,2,3], +}); + +module.exports = App; diff --git a/tests/flow/more_react/__snapshots__/jsfmt.spec.js.snap b/tests/flow/more_react/__snapshots__/jsfmt.spec.js.snap index a31a0a25..2dbd729f 100644 --- a/tests/flow/more_react/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/more_react/__snapshots__/jsfmt.spec.js.snap @@ -110,6 +110,39 @@ module.exports = App; " `; +exports[`InitializedFields.js 1`] = ` +"/** + * @providesModule InitializedFields.react + */ + +var React = require('react'); + +/** This is a regression test for a bug where we forgot to mark the fields of + * react classes as initialized, when the class was created with createClass(). + * This would manifest as complaining that metric requires an annotation */ +var App = React.createClass({ + metrics: [1,2,3], +}); + +module.exports = App; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule InitializedFields.react + */ + +var React = require(\\"react\\"); + +/** This is a regression test for a bug where we forgot to mark the fields of + * react classes as initialized, when the class was created with createClass(). + * This would manifest as complaining that metric requires an annotation */ +var App = React.createClass({ + metrics: [1, 2, 3] +}); + +module.exports = App; +" +`; + exports[`JSX.js 1`] = ` " /* @providesModule JSX */ @@ -140,6 +173,67 @@ module.exports = app; " `; +exports[`checkPropTypes.js 1`] = ` +"/* @flow */ + +import { PropTypes, checkPropTypes } from \\"react\\"; + +checkPropTypes({ foo: PropTypes.string }, { foo: 'foo' }, 'value', 'TestComponent'); // OK + +checkPropTypes({ foo: PropTypes.string }, { foo: 'foo' }); // error: missing arguments +checkPropTypes({ foo: PropTypes.string }, { foo: 'foo' }, 'value'); // error: missing argument + +checkPropTypes({ bar: PropTypes.string }, { foo: 'foo' }, 'value', 'TestComponent'); // error: property not found + +checkPropTypes({ foo: PropTypes.string }, { foo: 'foo' }, 'value', 'TestComponent', () => 123); // error: number ~> string +checkPropTypes({ foo: PropTypes.string }, { foo: 'foo' }, 'value', 'TestComponent', () => null); // OK +checkPropTypes({ foo: PropTypes.string }, { foo: 'foo' }, 'value', 'TestComponent', () => undefined); // OK +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ + +import { PropTypes, checkPropTypes } from \\"react\\"; + +checkPropTypes( + { foo: PropTypes.string }, + { foo: \\"foo\\" }, + \\"value\\", + \\"TestComponent\\" +); // OK + +checkPropTypes({ foo: PropTypes.string }, { foo: \\"foo\\" }); // error: missing arguments +checkPropTypes({ foo: PropTypes.string }, { foo: \\"foo\\" }, \\"value\\"); // error: missing argument + +checkPropTypes( + { bar: PropTypes.string }, + { foo: \\"foo\\" }, + \\"value\\", + \\"TestComponent\\" +); // error: property not found + +checkPropTypes( + { foo: PropTypes.string }, + { foo: \\"foo\\" }, + \\"value\\", + \\"TestComponent\\", + () => 123 +); // error: number ~> string +checkPropTypes( + { foo: PropTypes.string }, + { foo: \\"foo\\" }, + \\"value\\", + \\"TestComponent\\", + () => null +); // OK +checkPropTypes( + { foo: PropTypes.string }, + { foo: \\"foo\\" }, + \\"value\\", + \\"TestComponent\\", + () => undefined +); // OK +" +`; + exports[`propTypes.js 1`] = ` "var React = require('React'); @@ -155,10 +249,7 @@ var D = React.createClass({ } }); -; +; // errors: properties \`name\` and \`title\` not found ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ var React = require(\\"React\\"); @@ -174,9 +265,6 @@ var D = React.createClass({ } }); -; +; // errors: properties \`name\` and \`title\` not found " `; diff --git a/tests/flow/more_react/checkPropTypes.js b/tests/flow/more_react/checkPropTypes.js new file mode 100644 index 00000000..29fd2fbe --- /dev/null +++ b/tests/flow/more_react/checkPropTypes.js @@ -0,0 +1,14 @@ +/* @flow */ + +import { PropTypes, checkPropTypes } from "react"; + +checkPropTypes({ foo: PropTypes.string }, { foo: 'foo' }, 'value', 'TestComponent'); // OK + +checkPropTypes({ foo: PropTypes.string }, { foo: 'foo' }); // error: missing arguments +checkPropTypes({ foo: PropTypes.string }, { foo: 'foo' }, 'value'); // error: missing argument + +checkPropTypes({ bar: PropTypes.string }, { foo: 'foo' }, 'value', 'TestComponent'); // error: property not found + +checkPropTypes({ foo: PropTypes.string }, { foo: 'foo' }, 'value', 'TestComponent', () => 123); // error: number ~> string +checkPropTypes({ foo: PropTypes.string }, { foo: 'foo' }, 'value', 'TestComponent', () => null); // OK +checkPropTypes({ foo: PropTypes.string }, { foo: 'foo' }, 'value', 'TestComponent', () => undefined); // OK diff --git a/tests/flow/more_react/propTypes.js b/tests/flow/more_react/propTypes.js index 3bb8a2a0..e85723a1 100644 --- a/tests/flow/more_react/propTypes.js +++ b/tests/flow/more_react/propTypes.js @@ -12,7 +12,4 @@ var D = React.createClass({ } }); -; +; // errors: properties `name` and `title` not found diff --git a/tests/flow/multiflow/__snapshots__/jsfmt.spec.js.snap b/tests/flow/multiflow/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..11ed0a49 --- /dev/null +++ b/tests/flow/multiflow/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,297 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`apply.js 1`] = ` +"// @flow + +function apply, Ret>( + fn: (...Args) => Ret, + args: Args, +): Ret { + return fn(...args); +} + +function noRest(x: 'hi', y: 123): true { return true; } +apply(noRest, ['hi', 123]); // No error +apply(noRest, ['hi', 456]); // Error - 456 ~> 123 +apply(noRest, ['hi']); // Error - too few args +apply(noRest, ['hi', 123, false]); // No error - too many args is fine + +// withRest behaves the same as noRest except you can't pass too many args in +function withRest(...rest: ['hi', 123]): true { return true; } +apply(withRest, ['hi', 123]); // No error +apply(withRest, ['hi', 456]); // Error - 456 ~> 123 +apply(withRest, ['hi']); // Error - too few args +apply(withRest, ['hi', 123, false]); // Error - too many args + +// Same thing, but with types instead of functions +declare var applyType: , Ret>( + fn: (...Args) => Ret, + args: Args, +) => Ret; + +function noRest(x: 'hi', y: 123): true { return true; } +applyType(noRest, ['hi', 123]); // No error +applyType(noRest, ['hi', 456]); // Error - 456 ~> 123 +applyType(noRest, ['hi']); // Error - too few args +applyType(noRest, ['hi', 123, false]); // No error - too many args is fine + +// withRest behaves the same as noRest except you can't pass too many args in +function withRest(...rest: ['hi', 123]): true { return true; } +applyType(withRest, ['hi', 123]); // No error +applyType(withRest, ['hi', 456]); // Error - 456 ~> 123 +applyType(withRest, ['hi']); // Error - too few args +applyType(withRest, ['hi', 123, false]); // Error - too many args +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +function apply, Ret>( + fn: (...Args) => Ret, + args: Args +): Ret { + return fn(...args); +} + +function noRest(x: \\"hi\\", y: 123): true { + return true; +} +apply(noRest, [\\"hi\\", 123]); // No error +apply(noRest, [\\"hi\\", 456]); // Error - 456 ~> 123 +apply(noRest, [\\"hi\\"]); // Error - too few args +apply(noRest, [\\"hi\\", 123, false]); // No error - too many args is fine + +// withRest behaves the same as noRest except you can't pass too many args in +function withRest(...rest: [\\"hi\\", 123]): true { + return true; +} +apply(withRest, [\\"hi\\", 123]); // No error +apply(withRest, [\\"hi\\", 456]); // Error - 456 ~> 123 +apply(withRest, [\\"hi\\"]); // Error - too few args +apply(withRest, [\\"hi\\", 123, false]); // Error - too many args + +// Same thing, but with types instead of functions +declare var applyType: , Ret>( + fn: (...Args) => Ret, + args: Args +) => Ret; + +function noRest(x: \\"hi\\", y: 123): true { + return true; +} +applyType(noRest, [\\"hi\\", 123]); // No error +applyType(noRest, [\\"hi\\", 456]); // Error - 456 ~> 123 +applyType(noRest, [\\"hi\\"]); // Error - too few args +applyType(noRest, [\\"hi\\", 123, false]); // No error - too many args is fine + +// withRest behaves the same as noRest except you can't pass too many args in +function withRest(...rest: [\\"hi\\", 123]): true { + return true; +} +applyType(withRest, [\\"hi\\", 123]); // No error +applyType(withRest, [\\"hi\\", 456]); // Error - 456 ~> 123 +applyType(withRest, [\\"hi\\"]); // Error - too few args +applyType(withRest, [\\"hi\\", 123, false]); // Error - too many args +" +`; + +exports[`issue3443.js 1`] = ` +"// @flow + +// Adapted from https://github.com/facebook/flow/issues/3443 + +class A { + f(...args: any[]) {} +} + +class B extends A { + f(...args) { + this.f(...args); + } +} + +function foo(...args) { + foo(1, ...args); +} +foo(123); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +// Adapted from https://github.com/facebook/flow/issues/3443 + +class A { + f(...args: any[]) {} +} + +class B extends A { + f(...args) { + this.f(...args); + } +} + +function foo(...args) { + foo(1, ...args); +} +foo(123); +" +`; + +exports[`jsx.js 1`] = ` +"/** + * @jsx JSX + * @flow + */ + +// This one for when there are no JSX attributes +declare function JSX< + Children: $ReadOnlyArray, + Elem, + C: (props: {}, children: Children) => Elem +>( + component: C, + props: null, + ...children: Children +): Elem; + +// This one for when there are JSX attributes. +declare function JSX< + Children: $ReadOnlyArray, + Elem, + Props: Object, + C: (props: Props, children: Children) => Elem +>( + component: C, + props: Props, + ...children: Children +): Elem; + +declare function AcceptsWhatever(props: {} | null, children: any): string; +(: number); // Error string ~> number +(Text: number); // Error string ~> number + +declare function ExpectsProps(props: { name: string }, children: any): string; +(); // Error - missing prop +(Text: number); // Error string ~> number + +declare function ExpectsChildrenTuple(props: any, children: [string]): string; +(); // Error - mising child +(Hi); // No error +({123}); // Error: number ~> string +(Hi {\\"there\\"}); // Error: too many children + +declare function ExpectsChildrenArray(props: any, children: Array): string; +(); // No error - 0 children is fine +(Hi); // No error - 1 child is fine +({123}); // Error: number ~> string +(Hi {\\"there\\"}); // No error - 2 children is fine +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @jsx JSX + * @flow + */ + +// This one for when there are no JSX attributes +declare function JSX< + Children: $ReadOnlyArray, + Elem, + C: (props: {}, children: Children) => Elem +>( + component: C, + props: null, + ...children: Children +): Elem; + +// This one for when there are JSX attributes. +declare function JSX< + Children: $ReadOnlyArray, + Elem, + Props: Object, + C: (props: Props, children: Children) => Elem +>( + component: C, + props: Props, + ...children: Children +): Elem; + +declare function AcceptsWhatever(props: {} | null, children: any): string; +(: number); // Error string ~> number +(Text: number); // Error string ~> number + +declare function ExpectsProps(props: { name: string }, children: any): string; +; // Error - missing prop +(Text: number); // Error string ~> number + +declare function ExpectsChildrenTuple(props: any, children: [string]): string; +; // Error - mising child +Hi; // No error +{123}; // Error: number ~> string +Hi {\\"there\\"}; // Error: too many children + +declare function ExpectsChildrenArray( + props: any, + children: Array +): string; +; // No error - 0 children is fine +Hi; // No error - 1 child is fine +{123}; // Error: number ~> string +Hi {\\"there\\"}; // No error - 2 children is fine +" +`; + +exports[`spread.js 1`] = ` +"// @flow + +function fun(x: 'hi', y: 123) {} +fun(...['hi', 123]); // No error +fun(...['hi'], ...[123]); // No error +fun(...['hi'], ...[], ...[123]); // No error +fun(...['hi'], ...[], ...[123], ...[true]); // No error +fun(...['hi'], ...[true], ...[123]); // Error: true ~> 123 + +declare var arrOf123: Array<123>; +fun('hi', ...arrOf123); // No error - ignore the fact arrOf123 could be empty + + +function funWithRestArray(x: 'hi', y: 123, ...rest: Array) {} +funWithRestArray(...['hi', 123]); // No error +funWithRestArray(...['hi'], ...[123]); // No error +funWithRestArray(...['hi'], ...[], ...[123]); // No error +funWithRestArray(...['hi'], ...[], ...[123], ...[456, 789]); // No error +funWithRestArray(...['hi'], ...[true], ...[123]); // Error: true ~> 123 + +funWithRestArray('hi', 123, ...arrOf123); // Ok +funWithRestArray('hi', ...arrOf123); // No error - ignore the fact arrOf123 could be empty +funWithRestArray('hi', ...arrOf123, ...arrOf123); // No error - ignore the fact arrOf123 could be empty + +// 2 errors +// 1. 'bye' ~> 123 in case the first spread is empty +// 2. 'bye' ~> number in case the first spread is not empty +funWithRestArray('hi', ...arrOf123, 'bye', ...arrOf123); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +function fun(x: \\"hi\\", y: 123) {} +fun(...[\\"hi\\", 123]); // No error +fun(...[\\"hi\\"], ...[123]); // No error +fun(...[\\"hi\\"], ...[], ...[123]); // No error +fun(...[\\"hi\\"], ...[], ...[123], ...[true]); // No error +fun(...[\\"hi\\"], ...[true], ...[123]); // Error: true ~> 123 + +declare var arrOf123: Array<123>; +fun(\\"hi\\", ...arrOf123); // No error - ignore the fact arrOf123 could be empty + +function funWithRestArray(x: \\"hi\\", y: 123, ...rest: Array) {} +funWithRestArray(...[\\"hi\\", 123]); // No error +funWithRestArray(...[\\"hi\\"], ...[123]); // No error +funWithRestArray(...[\\"hi\\"], ...[], ...[123]); // No error +funWithRestArray(...[\\"hi\\"], ...[], ...[123], ...[456, 789]); // No error +funWithRestArray(...[\\"hi\\"], ...[true], ...[123]); // Error: true ~> 123 + +funWithRestArray(\\"hi\\", 123, ...arrOf123); // Ok +funWithRestArray(\\"hi\\", ...arrOf123); // No error - ignore the fact arrOf123 could be empty +funWithRestArray(\\"hi\\", ...arrOf123, ...arrOf123); // No error - ignore the fact arrOf123 could be empty + +// 2 errors +// 1. 'bye' ~> 123 in case the first spread is empty +// 2. 'bye' ~> number in case the first spread is not empty +funWithRestArray(\\"hi\\", ...arrOf123, \\"bye\\", ...arrOf123); +" +`; diff --git a/tests/flow/multiflow/apply.js b/tests/flow/multiflow/apply.js new file mode 100644 index 00000000..56afd0d4 --- /dev/null +++ b/tests/flow/multiflow/apply.js @@ -0,0 +1,40 @@ +// @flow + +function apply, Ret>( + fn: (...Args) => Ret, + args: Args, +): Ret { + return fn(...args); +} + +function noRest(x: 'hi', y: 123): true { return true; } +apply(noRest, ['hi', 123]); // No error +apply(noRest, ['hi', 456]); // Error - 456 ~> 123 +apply(noRest, ['hi']); // Error - too few args +apply(noRest, ['hi', 123, false]); // No error - too many args is fine + +// withRest behaves the same as noRest except you can't pass too many args in +function withRest(...rest: ['hi', 123]): true { return true; } +apply(withRest, ['hi', 123]); // No error +apply(withRest, ['hi', 456]); // Error - 456 ~> 123 +apply(withRest, ['hi']); // Error - too few args +apply(withRest, ['hi', 123, false]); // Error - too many args + +// Same thing, but with types instead of functions +declare var applyType: , Ret>( + fn: (...Args) => Ret, + args: Args, +) => Ret; + +function noRest(x: 'hi', y: 123): true { return true; } +applyType(noRest, ['hi', 123]); // No error +applyType(noRest, ['hi', 456]); // Error - 456 ~> 123 +applyType(noRest, ['hi']); // Error - too few args +applyType(noRest, ['hi', 123, false]); // No error - too many args is fine + +// withRest behaves the same as noRest except you can't pass too many args in +function withRest(...rest: ['hi', 123]): true { return true; } +applyType(withRest, ['hi', 123]); // No error +applyType(withRest, ['hi', 456]); // Error - 456 ~> 123 +applyType(withRest, ['hi']); // Error - too few args +applyType(withRest, ['hi', 123, false]); // Error - too many args diff --git a/tests/flow/multiflow/issue3443.js b/tests/flow/multiflow/issue3443.js new file mode 100644 index 00000000..2cb7a65b --- /dev/null +++ b/tests/flow/multiflow/issue3443.js @@ -0,0 +1,18 @@ +// @flow + +// Adapted from https://github.com/facebook/flow/issues/3443 + +class A { + f(...args: any[]) {} +} + +class B extends A { + f(...args) { + this.f(...args); + } +} + +function foo(...args) { + foo(1, ...args); +} +foo(123); diff --git a/tests/flow/multiflow/jsfmt.spec.js b/tests/flow/multiflow/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/multiflow/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/multiflow/jsx.js b/tests/flow/multiflow/jsx.js new file mode 100644 index 00000000..d0df0ea1 --- /dev/null +++ b/tests/flow/multiflow/jsx.js @@ -0,0 +1,47 @@ +/** + * @jsx JSX + * @flow + */ + +// This one for when there are no JSX attributes +declare function JSX< + Children: $ReadOnlyArray, + Elem, + C: (props: {}, children: Children) => Elem +>( + component: C, + props: null, + ...children: Children +): Elem; + +// This one for when there are JSX attributes. +declare function JSX< + Children: $ReadOnlyArray, + Elem, + Props: Object, + C: (props: Props, children: Children) => Elem +>( + component: C, + props: Props, + ...children: Children +): Elem; + +declare function AcceptsWhatever(props: {} | null, children: any): string; +(: number); // Error string ~> number +(Text: number); // Error string ~> number + +declare function ExpectsProps(props: { name: string }, children: any): string; +(); // Error - missing prop +(Text: number); // Error string ~> number + +declare function ExpectsChildrenTuple(props: any, children: [string]): string; +(); // Error - mising child +(Hi); // No error +({123}); // Error: number ~> string +(Hi {"there"}); // Error: too many children + +declare function ExpectsChildrenArray(props: any, children: Array): string; +(); // No error - 0 children is fine +(Hi); // No error - 1 child is fine +({123}); // Error: number ~> string +(Hi {"there"}); // No error - 2 children is fine diff --git a/tests/flow/multiflow/spread.js b/tests/flow/multiflow/spread.js new file mode 100644 index 00000000..cea927f8 --- /dev/null +++ b/tests/flow/multiflow/spread.js @@ -0,0 +1,28 @@ +// @flow + +function fun(x: 'hi', y: 123) {} +fun(...['hi', 123]); // No error +fun(...['hi'], ...[123]); // No error +fun(...['hi'], ...[], ...[123]); // No error +fun(...['hi'], ...[], ...[123], ...[true]); // No error +fun(...['hi'], ...[true], ...[123]); // Error: true ~> 123 + +declare var arrOf123: Array<123>; +fun('hi', ...arrOf123); // No error - ignore the fact arrOf123 could be empty + + +function funWithRestArray(x: 'hi', y: 123, ...rest: Array) {} +funWithRestArray(...['hi', 123]); // No error +funWithRestArray(...['hi'], ...[123]); // No error +funWithRestArray(...['hi'], ...[], ...[123]); // No error +funWithRestArray(...['hi'], ...[], ...[123], ...[456, 789]); // No error +funWithRestArray(...['hi'], ...[true], ...[123]); // Error: true ~> 123 + +funWithRestArray('hi', 123, ...arrOf123); // Ok +funWithRestArray('hi', ...arrOf123); // No error - ignore the fact arrOf123 could be empty +funWithRestArray('hi', ...arrOf123, ...arrOf123); // No error - ignore the fact arrOf123 could be empty + +// 2 errors +// 1. 'bye' ~> 123 in case the first spread is empty +// 2. 'bye' ~> number in case the first spread is not empty +funWithRestArray('hi', ...arrOf123, 'bye', ...arrOf123); diff --git a/tests/flow/multiflow_with_flowlib/__snapshots__/jsfmt.spec.js.snap b/tests/flow/multiflow_with_flowlib/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..368b2379 --- /dev/null +++ b/tests/flow/multiflow_with_flowlib/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,34 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`spread.js 1`] = ` +"declare var iterableOf123: Iterable<123>; +function fun(x: 'hi', y: 123) {} +fun('hi', ...iterableOf123); // No error - ignore the fact iterableOf123 could be empty + +function funWithRestArray(x: 'hi', y: 123, ...rest: Array) {} + +funWithRestArray('hi', 123, ...iterableOf123); // Ok +funWithRestArray('hi', ...iterableOf123); // No error - ignore the fact iterableOf123 could be empty +funWithRestArray('hi', ...iterableOf123, ...iterableOf123); // No error - ignore the fact iterableOf123 could be empty + +// 2 errors +// 1. 'bye' ~> 123 in case the first spread is empty +// 2. 'bye' ~> number in case the first spread is not empty +funWithRestArray('hi', ...iterableOf123, 'bye', ...iterableOf123); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +declare var iterableOf123: Iterable<123>; +function fun(x: \\"hi\\", y: 123) {} +fun(\\"hi\\", ...iterableOf123); // No error - ignore the fact iterableOf123 could be empty + +function funWithRestArray(x: \\"hi\\", y: 123, ...rest: Array) {} + +funWithRestArray(\\"hi\\", 123, ...iterableOf123); // Ok +funWithRestArray(\\"hi\\", ...iterableOf123); // No error - ignore the fact iterableOf123 could be empty +funWithRestArray(\\"hi\\", ...iterableOf123, ...iterableOf123); // No error - ignore the fact iterableOf123 could be empty + +// 2 errors +// 1. 'bye' ~> 123 in case the first spread is empty +// 2. 'bye' ~> number in case the first spread is not empty +funWithRestArray(\\"hi\\", ...iterableOf123, \\"bye\\", ...iterableOf123); +" +`; diff --git a/tests/flow/multiflow_with_flowlib/jsfmt.spec.js b/tests/flow/multiflow_with_flowlib/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/multiflow_with_flowlib/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/multiflow_with_flowlib/spread.js b/tests/flow/multiflow_with_flowlib/spread.js new file mode 100644 index 00000000..4bf34c10 --- /dev/null +++ b/tests/flow/multiflow_with_flowlib/spread.js @@ -0,0 +1,14 @@ +declare var iterableOf123: Iterable<123>; +function fun(x: 'hi', y: 123) {} +fun('hi', ...iterableOf123); // No error - ignore the fact iterableOf123 could be empty + +function funWithRestArray(x: 'hi', y: 123, ...rest: Array) {} + +funWithRestArray('hi', 123, ...iterableOf123); // Ok +funWithRestArray('hi', ...iterableOf123); // No error - ignore the fact iterableOf123 could be empty +funWithRestArray('hi', ...iterableOf123, ...iterableOf123); // No error - ignore the fact iterableOf123 could be empty + +// 2 errors +// 1. 'bye' ~> 123 in case the first spread is empty +// 2. 'bye' ~> number in case the first spread is not empty +funWithRestArray('hi', ...iterableOf123, 'bye', ...iterableOf123); diff --git a/tests/flow/new_react/__snapshots__/jsfmt.spec.js.snap b/tests/flow/new_react/__snapshots__/jsfmt.spec.js.snap index 590423e2..2d460b7b 100644 --- a/tests/flow/new_react/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/new_react/__snapshots__/jsfmt.spec.js.snap @@ -294,7 +294,7 @@ var FooLegacy = React.createClass({ }, }); -FooLegacy.defaultProps = 0; // TODO: should be error +FooLegacy.defaultProps = 0; var foo_legacy: $jsx = ; FooLegacy.bar(); @@ -372,7 +372,7 @@ var FooLegacy = React.createClass({ } }); -FooLegacy.defaultProps = 0; // TODO: should be error +FooLegacy.defaultProps = 0; var foo_legacy: $jsx = ; FooLegacy.bar(); @@ -437,7 +437,7 @@ var C = React.createClass({ getDefaultProps(): { z: number } { return { z: 0 }; }, - getInitialState() { return 4; }, + getInitialState() { return null; }, render() { var foo: string = this.state; var bar: string = this.props; @@ -471,7 +471,7 @@ var C = React.createClass({ return { z: 0 }; }, getInitialState() { - return 4; + return null; }, render() { var foo: string = this.state; @@ -956,7 +956,7 @@ exports[`state4.js 1`] = ` "var React = require('React'); var C = React.createClass({ - getInitialState: function() { + getInitialState: function(): { x: number } { return { x: 0 }; }, @@ -970,7 +970,7 @@ var C = React.createClass({ var React = require(\\"React\\"); var C = React.createClass({ - getInitialState: function() { + getInitialState: function(): { x: number } { return { x: 0 }; }, diff --git a/tests/flow/new_react/classes.js b/tests/flow/new_react/classes.js index 7574a00a..2eceb28b 100644 --- a/tests/flow/new_react/classes.js +++ b/tests/flow/new_react/classes.js @@ -77,7 +77,7 @@ var FooLegacy = React.createClass({ }, }); -FooLegacy.defaultProps = 0; // TODO: should be error +FooLegacy.defaultProps = 0; var foo_legacy: $jsx = ; FooLegacy.bar(); diff --git a/tests/flow/new_react/new_react.js b/tests/flow/new_react/new_react.js index 70444d1c..4c086d81 100644 --- a/tests/flow/new_react/new_react.js +++ b/tests/flow/new_react/new_react.js @@ -12,7 +12,7 @@ var C = React.createClass({ getDefaultProps(): { z: number } { return { z: 0 }; }, - getInitialState() { return 4; }, + getInitialState() { return null; }, render() { var foo: string = this.state; var bar: string = this.props; diff --git a/tests/flow/new_react/state4.js b/tests/flow/new_react/state4.js index 9b1ea825..604445f9 100644 --- a/tests/flow/new_react/state4.js +++ b/tests/flow/new_react/state4.js @@ -1,7 +1,7 @@ var React = require('React'); var C = React.createClass({ - getInitialState: function() { + getInitialState: function(): { x: number } { return { x: 0 }; }, diff --git a/tests/flow/new_spread/__snapshots__/jsfmt.spec.js.snap b/tests/flow/new_spread/__snapshots__/jsfmt.spec.js.snap index 27be1b20..b59d4348 100644 --- a/tests/flow/new_spread/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/new_spread/__snapshots__/jsfmt.spec.js.snap @@ -29,8 +29,8 @@ declare var o2: O2; ({p:y}: O2); // error: y ~> T ({p:x,q:y}: O2); // ok -// can't make exact from inexact -type O3 = {|...{p:T}|}; // error: spread result is not exact +// can't make exact from inexact (TODO: force EvalT eagerly) +type O3 = {|...{p:T}|}; ({p:x}: O3); // error: spread result is not exact // exact type O4 = {...{|p:T|}}; @@ -61,8 +61,8 @@ declare var o6: O6; ({p:y}: O6); // ok ({p:y,q:x}: O6); // ok -// inexact p + exact p ~> exact -type O7 = {|...{p:T},...{|p:U|}|}; // error: spread result is not exact +// inexact p + exact p ~> exact (TODO: force EvalT eagerly) +type O7 = {|...{p:T},...{|p:U|}|}; ({p:y}: O7);// error: spread result is not exact // exact p + inexact p type O8 = {...{|p:T|},...{p:U}}; @@ -92,6 +92,11 @@ declare var o11: O11; type O12 = {...{|p:T|},...{|q:U|}}; declare var o12: O12; (o12: {p:T,q:U}); // ok + +// inline properties are exact +type O13 = {...{p:T},p:U}; +declare var o13: O13; +(o13: {p:U}); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ declare class T {} declare var x: T; @@ -121,8 +126,9 @@ declare var o2: O2; ({ p: y }: O2); // error: y ~> T ({ p: x, q: y }: O2); // ok -// can't make exact from inexact -type O3 = {| ...{ p: T } |}; // error: spread result is not exact +// can't make exact from inexact (TODO: force EvalT eagerly) +type O3 = {| ...{ p: T } |}; +({ p: x }: O3); // error: spread result is not exact // exact type O4 = { ...{| p: T |} }; @@ -153,8 +159,9 @@ declare var o6: O6; ({ p: y }: O6); // ok ({ p: y, q: x }: O6); // ok -// inexact p + exact p ~> exact -type O7 = {| ...{ p: T }, ...{| p: U |} |}; // error: spread result is not exact +// inexact p + exact p ~> exact (TODO: force EvalT eagerly) +type O7 = {| ...{ p: T }, ...{| p: U |} |}; +({ p: y }: O7); // error: spread result is not exact // exact p + inexact p type O8 = { ...{| p: T |}, ...{ p: U } }; @@ -184,6 +191,11 @@ declare var o11: O11; type O12 = { ...{| p: T |}, ...{| q: U |} }; declare var o12: O12; (o12: { p: T, q: U }); // ok + +// inline properties are exact +type O13 = { ...{ p: T }, p: U }; +declare var o13: O13; +(o13: { p: U }); " `; @@ -330,6 +342,17 @@ declare var o12: { ...{| [string]: T |}, ...{| [string]: U |} }; " `; +exports[`type_generic.js 1`] = ` +"declare function spread(a: A, b: B): {...A, ...B}; +(spread({p:0},{q:0}): {|+p:number,+q:number|}); // ok +(spread({p:0},{q:0}): {|+p:empty,+q:empty|}); // number ~> empty (x2) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +declare function spread(a: A, b: B): { ...A, ...B }; +(spread({ p: 0 }, { q: 0 }): {| +p: number, +q: number |}); // ok +(spread({ p: 0 }, { q: 0 }): {| +p: empty, +q: empty |}); // number ~> empty (x2) +" +`; + exports[`type_instance.js 1`] = ` "class A {+p: string|number} class B extends A {p: number} diff --git a/tests/flow/new_spread/type.js b/tests/flow/new_spread/type.js index 2aeee01b..dd359c51 100644 --- a/tests/flow/new_spread/type.js +++ b/tests/flow/new_spread/type.js @@ -26,8 +26,8 @@ declare var o2: O2; ({p:y}: O2); // error: y ~> T ({p:x,q:y}: O2); // ok -// can't make exact from inexact -type O3 = {|...{p:T}|}; // error: spread result is not exact +// can't make exact from inexact (TODO: force EvalT eagerly) +type O3 = {|...{p:T}|}; ({p:x}: O3); // error: spread result is not exact // exact type O4 = {...{|p:T|}}; @@ -58,8 +58,8 @@ declare var o6: O6; ({p:y}: O6); // ok ({p:y,q:x}: O6); // ok -// inexact p + exact p ~> exact -type O7 = {|...{p:T},...{|p:U|}|}; // error: spread result is not exact +// inexact p + exact p ~> exact (TODO: force EvalT eagerly) +type O7 = {|...{p:T},...{|p:U|}|}; ({p:y}: O7);// error: spread result is not exact // exact p + inexact p type O8 = {...{|p:T|},...{p:U}}; @@ -89,3 +89,8 @@ declare var o11: O11; type O12 = {...{|p:T|},...{|q:U|}}; declare var o12: O12; (o12: {p:T,q:U}); // ok + +// inline properties are exact +type O13 = {...{p:T},p:U}; +declare var o13: O13; +(o13: {p:U}); diff --git a/tests/flow/new_spread/type_generic.js b/tests/flow/new_spread/type_generic.js new file mode 100644 index 00000000..2e734d07 --- /dev/null +++ b/tests/flow/new_spread/type_generic.js @@ -0,0 +1,3 @@ +declare function spread(a: A, b: B): {...A, ...B}; +(spread({p:0},{q:0}): {|+p:number,+q:number|}); // ok +(spread({p:0},{q:0}): {|+p:empty,+q:empty|}); // number ~> empty (x2) diff --git a/tests/flow/node_tests/stream/__snapshots__/jsfmt.spec.js.snap b/tests/flow/node_tests/stream/__snapshots__/jsfmt.spec.js.snap index 9fb02e3e..d4e044de 100644 --- a/tests/flow/node_tests/stream/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/node_tests/stream/__snapshots__/jsfmt.spec.js.snap @@ -34,6 +34,10 @@ new MyReadStream() .pipe(new MyTransform()) .pipe(new MyWriteStream()); +new MyReadStream() + .on('error', () => {}) + .pipe(new MyDuplex()) + .once('close', () => {}); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /* @flow */ @@ -67,5 +71,10 @@ new MyReadStream() .pipe(new MyDuplex()) .pipe(new MyTransform()) .pipe(new MyWriteStream()); + +new MyReadStream() + .on(\\"error\\", () => {}) + .pipe(new MyDuplex()) + .once(\\"close\\", () => {}); " `; diff --git a/tests/flow/node_tests/stream/stream.js b/tests/flow/node_tests/stream/stream.js index 61cd149e..044a1da4 100644 --- a/tests/flow/node_tests/stream/stream.js +++ b/tests/flow/node_tests/stream/stream.js @@ -31,3 +31,7 @@ new MyReadStream() .pipe(new MyTransform()) .pipe(new MyWriteStream()); +new MyReadStream() + .on('error', () => {}) + .pipe(new MyDuplex()) + .once('close', () => {}); diff --git a/tests/flow/object_api/__snapshots__/jsfmt.spec.js.snap b/tests/flow/object_api/__snapshots__/jsfmt.spec.js.snap index cf569e87..3ae8fd69 100644 --- a/tests/flow/object_api/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/object_api/__snapshots__/jsfmt.spec.js.snap @@ -152,12 +152,12 @@ var sealed = {one: 'one', two: 'two'}; var unsealed = {}; Object.keys(unsealed).forEach(k => { - (k : number) // error: number ~> string + (k : number) // error: string ~> number }); var dict: { [k: number]: string } = {}; Object.keys(dict).forEach(k => { - (k : number) // error: number ~> string + (k : number) // error: string ~> number }); var any: Object = {}; @@ -185,12 +185,12 @@ var sealed = { one: \\"one\\", two: \\"two\\" }; var unsealed = {}; Object.keys(unsealed).forEach(k => { - (k: number); // error: number ~> string + (k: number); // error: string ~> number }); var dict: { [k: number]: string } = {}; Object.keys(dict).forEach(k => { - (k: number); // error: number ~> string + (k: number); // error: string ~> number }); var any: Object = {}; diff --git a/tests/flow/object_api/object_keys.js b/tests/flow/object_api/object_keys.js index 2af5c7fc..2689f9de 100644 --- a/tests/flow/object_api/object_keys.js +++ b/tests/flow/object_api/object_keys.js @@ -6,12 +6,12 @@ var sealed = {one: 'one', two: 'two'}; var unsealed = {}; Object.keys(unsealed).forEach(k => { - (k : number) // error: number ~> string + (k : number) // error: string ~> number }); var dict: { [k: number]: string } = {}; Object.keys(dict).forEach(k => { - (k : number) // error: number ~> string + (k : number) // error: string ~> number }); var any: Object = {}; diff --git a/tests/flow/object_assign/__snapshots__/jsfmt.spec.js.snap b/tests/flow/object_assign/__snapshots__/jsfmt.spec.js.snap index d011c3c8..9856cd60 100644 --- a/tests/flow/object_assign/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/object_assign/__snapshots__/jsfmt.spec.js.snap @@ -104,7 +104,6 @@ exports[`apply.js 1`] = ` "// @flow (Object.assign.apply(null, [{}, {a: 1}, {b: 'foo'}]): {a: number, b: string}); -(Object.assign({}, ...[{a: 1}, {b: 'foo'}]): {a: number, b: string}); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // @flow @@ -112,7 +111,6 @@ exports[`apply.js 1`] = ` a: number, b: string }); -(Object.assign({}, ...[{ a: 1 }, { b: \\"foo\\" }]): { a: number, b: string }); " `; @@ -131,6 +129,41 @@ Object.assign({ a: \\"foo\\" }, 123); " `; +exports[`spread.js 1`] = ` +"// @flow + +declare var arrOfObjs: Array<{ foo: string }>; +declare var roArrOfObjs: $ReadOnlyArray<{foo: string}>; +declare var tup: [{foo: string}, {bar: number}]; + +(Object.assign({}, ...arrOfObjs): { foo: number}); // Error: string ~> number +(Object.assign({}, ...roArrOfObjs): { foo: number}); // Error: string ~> number +(Object.assign({}, ...tup): { foo: string, bar: boolean}); // Error: number ~> boolean + +(Object.assign( + {}, + ...[{a: 1}, {b: 'foo'}], + ...[{c: true}], +): {a: number, b: true, c: boolean}); // Error: 'foo' => true +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +declare var arrOfObjs: Array<{ foo: string }>; +declare var roArrOfObjs: $ReadOnlyArray<{ foo: string }>; +declare var tup: [{ foo: string }, { bar: number }]; + +(Object.assign({}, ...arrOfObjs): { foo: number }); // Error: string ~> number +(Object.assign({}, ...roArrOfObjs): { foo: number }); // Error: string ~> number +(Object.assign({}, ...tup): { foo: string, bar: boolean }); // Error: number ~> boolean + +(Object.assign({}, ...[{ a: 1 }, { b: \\"foo\\" }], ...[{ c: true }]): { + a: number, + b: true, + c: boolean +}); // Error: 'foo' => true +" +`; + exports[`undefined.js 1`] = ` "/* @flow */ diff --git a/tests/flow/object_assign/apply.js b/tests/flow/object_assign/apply.js index 6bb4c551..6583510c 100644 --- a/tests/flow/object_assign/apply.js +++ b/tests/flow/object_assign/apply.js @@ -1,4 +1,3 @@ // @flow (Object.assign.apply(null, [{}, {a: 1}, {b: 'foo'}]): {a: number, b: string}); -(Object.assign({}, ...[{a: 1}, {b: 'foo'}]): {a: number, b: string}); diff --git a/tests/flow/object_assign/spread.js b/tests/flow/object_assign/spread.js new file mode 100644 index 00000000..603bc46e --- /dev/null +++ b/tests/flow/object_assign/spread.js @@ -0,0 +1,15 @@ +// @flow + +declare var arrOfObjs: Array<{ foo: string }>; +declare var roArrOfObjs: $ReadOnlyArray<{foo: string}>; +declare var tup: [{foo: string}, {bar: number}]; + +(Object.assign({}, ...arrOfObjs): { foo: number}); // Error: string ~> number +(Object.assign({}, ...roArrOfObjs): { foo: number}); // Error: string ~> number +(Object.assign({}, ...tup): { foo: string, bar: boolean}); // Error: number ~> boolean + +(Object.assign( + {}, + ...[{a: 1}, {b: 'foo'}], + ...[{c: true}], +): {a: number, b: true, c: boolean}); // Error: 'foo' => true diff --git a/tests/flow/objects/__snapshots__/jsfmt.spec.js.snap b/tests/flow/objects/__snapshots__/jsfmt.spec.js.snap index dfdbf1d9..043f2a8b 100644 --- a/tests/flow/objects/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/objects/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,68 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`compatibility.js 1`] = ` +"let tests = [ + function(x: { x: { foo: string } }, y: { x: { bar: number } }) { + x = y; // 2 errors: \`foo\` not found in y.x; \`bar\` not found in x.x + }, + + function(x: { foo: string }, y: { foo: number }) { + x = y; // 2 errors: string !~> number; number !~> string + }, + + function(x: { x: { foo: string } }, y: { x: { foo: number } }) { + x = y; // 2 errors: string !~> number; number !~> string + }, + + function(x: { +foo: string }, y: { +foo: number }) { + x = y; // 1 error: number !~> string + }, + + function(x: { x: { +foo: string } }, y: { x: { +foo: number } }) { + x = y; // 2 errors: string !~> number; number !~> string + }, + + function(x: { -foo: string }, y: { -foo: number }) { + x = y; // 1 error: string !~> number + }, + + function(x: { x: { -foo: string } }, y: { x: { -foo: number } }) { + x = y; // 2 errors: string !~> number; number !~> string + }, +]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +let tests = [ + function(x: { x: { foo: string } }, y: { x: { bar: number } }) { + x = y; // 2 errors: \`foo\` not found in y.x; \`bar\` not found in x.x + }, + + function(x: { foo: string }, y: { foo: number }) { + x = y; // 2 errors: string !~> number; number !~> string + }, + + function(x: { x: { foo: string } }, y: { x: { foo: number } }) { + x = y; // 2 errors: string !~> number; number !~> string + }, + + function(x: { +foo: string }, y: { +foo: number }) { + x = y; // 1 error: number !~> string + }, + + function(x: { x: { +foo: string } }, y: { x: { +foo: number } }) { + x = y; // 2 errors: string !~> number; number !~> string + }, + + function(x: { -foo: string }, y: { -foo: number }) { + x = y; // 1 error: string !~> number + }, + + function(x: { x: { -foo: string } }, y: { x: { -foo: number } }) { + x = y; // 2 errors: string !~> number; number !~> string + } +]; +" +`; + exports[`conversion.js 1`] = ` "/* @flow */ diff --git a/tests/flow/objects/compatibility.js b/tests/flow/objects/compatibility.js new file mode 100644 index 00000000..e684c454 --- /dev/null +++ b/tests/flow/objects/compatibility.js @@ -0,0 +1,29 @@ +let tests = [ + function(x: { x: { foo: string } }, y: { x: { bar: number } }) { + x = y; // 2 errors: `foo` not found in y.x; `bar` not found in x.x + }, + + function(x: { foo: string }, y: { foo: number }) { + x = y; // 2 errors: string !~> number; number !~> string + }, + + function(x: { x: { foo: string } }, y: { x: { foo: number } }) { + x = y; // 2 errors: string !~> number; number !~> string + }, + + function(x: { +foo: string }, y: { +foo: number }) { + x = y; // 1 error: number !~> string + }, + + function(x: { x: { +foo: string } }, y: { x: { +foo: number } }) { + x = y; // 2 errors: string !~> number; number !~> string + }, + + function(x: { -foo: string }, y: { -foo: number }) { + x = y; // 1 error: string !~> number + }, + + function(x: { x: { -foo: string } }, y: { x: { -foo: number } }) { + x = y; // 2 errors: string !~> number; number !~> string + }, +]; diff --git a/tests/flow/optional/__snapshots__/jsfmt.spec.js.snap b/tests/flow/optional/__snapshots__/jsfmt.spec.js.snap index 7c1df7b8..fb869555 100644 --- a/tests/flow/optional/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/optional/__snapshots__/jsfmt.spec.js.snap @@ -37,6 +37,23 @@ class C { " `; +exports[`loop.js 1`] = ` +"/* Regression test. Improper handling of OptionalT repositioning can cause + * reasons to grow \\"optional x\\" -> \\"optional optional x\\" -> \\"optional optional + * optional x\\", which thwarts reason-based caches that prevent nontermination. + */ +function f(x:T,y?:T) { x = y } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* Regression test. Improper handling of OptionalT repositioning can cause + * reasons to grow \\"optional x\\" -> \\"optional optional x\\" -> \\"optional optional + * optional x\\", which thwarts reason-based caches that prevent nontermination. + */ +function f(x: T, y?: T) { + x = y; +} +" +`; + exports[`maybe.js 1`] = ` "function foo(x?: string): ?string { return x; @@ -160,8 +177,9 @@ foo(); function qux(x=\\"hello\\",...y):string { foo(x); return y[0]; } -qux(0,0); -qux(0,...[\\"\\",42]); +qux(0,0); // Error, number ~> string +qux(0,...[42, \\"\\"]); // Error, number ~> string +qux(0,...[\\"\\",42]); // No error module.exports = qux; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -178,8 +196,9 @@ function qux(x = \\"hello\\", ...y): string { return y[0]; } -qux(0, 0); -qux(0, ...[\\"\\", 42]); +qux(0, 0); // Error, number ~> string +qux(0, ...[42, \\"\\"]); // Error, number ~> string +qux(0, ...[\\"\\", 42]); // No error module.exports = qux; " diff --git a/tests/flow/optional/loop.js b/tests/flow/optional/loop.js new file mode 100644 index 00000000..5334899a --- /dev/null +++ b/tests/flow/optional/loop.js @@ -0,0 +1,5 @@ +/* Regression test. Improper handling of OptionalT repositioning can cause + * reasons to grow "optional x" -> "optional optional x" -> "optional optional + * optional x", which thwarts reason-based caches that prevent nontermination. + */ +function f(x:T,y?:T) { x = y } diff --git a/tests/flow/optional/optional.js b/tests/flow/optional/optional.js index 79fa1d53..579b9355 100644 --- a/tests/flow/optional/optional.js +++ b/tests/flow/optional/optional.js @@ -6,7 +6,8 @@ foo(); function qux(x="hello",...y):string { foo(x); return y[0]; } -qux(0,0); -qux(0,...["",42]); +qux(0,0); // Error, number ~> string +qux(0,...[42, ""]); // Error, number ~> string +qux(0,...["",42]); // No error module.exports = qux; diff --git a/tests/flow/optional_props/__snapshots__/jsfmt.spec.js.snap b/tests/flow/optional_props/__snapshots__/jsfmt.spec.js.snap index 7b95834a..bc57d645 100644 --- a/tests/flow/optional_props/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/optional_props/__snapshots__/jsfmt.spec.js.snap @@ -177,6 +177,25 @@ class A { " `; +exports[`test3_exact_annot.js 1`] = ` +"/* The logic that allows ({}: {p?:T}), described in test3.js, should _not_ also + fire for exact annotations. */ + +const a: {| a: number |} = { a: 1 }; +const b: { a: number, b?: number } = a; // error: property \`b\` not found +b.b = 0; // because subsequent writes would widen the exact object +(a.b: number); // error: property \`b\` not found +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* The logic that allows ({}: {p?:T}), described in test3.js, should _not_ also + fire for exact annotations. */ + +const a: {| a: number |} = { a: 1 }; +const b: { a: number, b?: number } = a; // error: property \`b\` not found +b.b = 0; // because subsequent writes would widen the exact object +(a.b: number); // error: property \`b\` not found +" +`; + exports[`test3_failure.js 1`] = ` "// generalization of failure in test3.js diff --git a/tests/flow/optional_props/test3_exact_annot.js b/tests/flow/optional_props/test3_exact_annot.js new file mode 100644 index 00000000..64620a10 --- /dev/null +++ b/tests/flow/optional_props/test3_exact_annot.js @@ -0,0 +1,7 @@ +/* The logic that allows ({}: {p?:T}), described in test3.js, should _not_ also + fire for exact annotations. */ + +const a: {| a: number |} = { a: 1 }; +const b: { a: number, b?: number } = a; // error: property `b` not found +b.b = 0; // because subsequent writes would widen the exact object +(a.b: number); // error: property `b` not found diff --git a/tests/flow/react/__snapshots__/jsfmt.spec.js.snap b/tests/flow/react/__snapshots__/jsfmt.spec.js.snap index 5cbf44da..8c85795d 100644 --- a/tests/flow/react/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/react/__snapshots__/jsfmt.spec.js.snap @@ -27,6 +27,661 @@ module.exports = AudienceInsightsContainer; " `; +exports[`create_class.js 1`] = ` +"import React from \\"react\\"; + +const A = React.createClass({ + mixins: [{ propTypes: { foo: React.PropTypes.string.isRequired } }], + propTypes: { bar: React.PropTypes.number.isRequired }, + m() { + (this.props.foo: empty); // error: string ~> empty + (this.props.bar: empty); // error: number ~> empty + } +}); + +const B = React.createClass({ + p: \\"\\", + m() { + this.p = 0; // error: number ~> string + }, + mm() { + this.m.apply(null); // OK: this.m is autobound, so \`this.p\` will always be found + } +}); + +const C = React.createClass({ + getInitialState(): Object { + return { foo: 0 }; + }, + m() { + this.state.foo; // OK: state is unknown + } +}); + +const D = React.createClass({ + mixins: [{ + getInitialState(): Object { + return { foo: 0 }; + }, + }], + getInitialState() { + return { bar: 0 }; + }, + m() { + this.state.foo; // OK: state is unknown (due to unknown mixin) + } +}); + +const E = React.createClass({ + foo: 0, + m() { + (this.foo: string); // error: number ~> string + }, + mm() { + var props: { m(): void } = { m: this.m }; + props.m(); // OK: this.m is autobound, so \`this.foo\` will always be found + } +}); + +const F = React.createClass({ + getInitialState(): { [string]: mixed } { + return { foo: 0 }; + }, + m() { + this.state.foo; + this.state.bar; + }, +}); + +const G = React.createClass({ + mixins: [], + autobind: true, + statics: {}, + m() { + (this.mixins: mixed); // error: property \`mixins\` not found + (this.autobind: mixed); // error: property \`autobind\` not found + (this.statics: mixed); // error: property \`statics\` not found + }, +}); + +const H = React.createClass({ + statics: { q: 0 }, + getDefaultProps() { + (this.q: empty); // error: number ~> empty + return {}; + }, +}); + +const I = React.createClass({ + propTypes: ({}: {[string]: any}), + m() { + (this.props.foo: empty); // OK + } +}); + +const J = React.createClass({ + mixins: [{ + getInitialState() { + return this.constructor.calculateState(); + }, + }], + statics: { + calculateState() { + return { foo: 0 }; + }, + }, + m() { + (this.state.foo: empty); // number ~> empty + }, +}); + +const K = React.createClass({ + propTypes: { + foo: React.PropTypes.string.isRequired, + }, + getInitialState() { + this.mm(); // cause error in mm below + return this.props; + }, + m() { + (this.props.foo: empty); // string ~> empty + (this.state.foo: empty); // string ~> empty + }, + mm() { + this.state.foo; // error: property fo not found (called by getInitialState) + } +}); + +const L = React.createClass({ + propTypes: { + foo: React.PropTypes.string.isRequired, + }, + getInitialState() { + return { bar: 0 }; + }, + componentWillMount() { + (this.props.foo: empty); // string ~> empty + return 0; // number ~> void + }, + componentDidMount() { + (this.props.foo: empty); // string ~> empty + return 0; // number ~> void + }, + componentWillReceiveProps(nextProps) { + (this.props.foo: empty); // string ~> empty + (nextProps.foo: empty); // string ~> empty + return 0; // number ~> void + }, + shouldComponentUpdate(nextProps, nextState) { + (this.props.foo: empty); // string ~> empty + (this.state.bar: empty); // number ~> empty + (nextProps.foo: empty); // string ~> empty + (nextState.bar: empty); // number ~> empty + return 0; // number ~> bool + }, + componentWillUpdate(nextProps, nextState) { + (this.props.foo: empty); // string ~> empty + (this.state.bar: empty); // number ~> empty + (nextProps.foo: empty); // string ~> empty + (nextState.bar: empty); // number ~> empty + return 0; // number ~> void + }, + componentDidUpdate(nextProps, nextState) { + (this.props.foo: empty); // string ~> empty + (this.state.bar: empty); // number ~> empty + (nextProps.foo: empty); // string ~> empty + (nextState.bar: empty); // number ~> empty + return 0; // number ~> void + }, + componentWillUnmount() { + (this.props.foo: empty); // string ~> empty + (this.state.bar: empty); // number ~> empty + return 0; // number ~> void + }, +}); + +React.createClass({}); // error: spec must be [x] exact and [ ] sealed +React.createClass(({}: {})); // error: spec must be [ ] exact and [x] sealed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import React from \\"react\\"; + +const A = React.createClass({ + mixins: [{ propTypes: { foo: React.PropTypes.string.isRequired } }], + propTypes: { bar: React.PropTypes.number.isRequired }, + m() { + (this.props.foo: empty); // error: string ~> empty + (this.props.bar: empty); // error: number ~> empty + } +}); + +const B = React.createClass({ + p: \\"\\", + m() { + this.p = 0; // error: number ~> string + }, + mm() { + this.m.apply(null); // OK: this.m is autobound, so \`this.p\` will always be found + } +}); + +const C = React.createClass({ + getInitialState(): Object { + return { foo: 0 }; + }, + m() { + this.state.foo; // OK: state is unknown + } +}); + +const D = React.createClass({ + mixins: [ + { + getInitialState(): Object { + return { foo: 0 }; + } + } + ], + getInitialState() { + return { bar: 0 }; + }, + m() { + this.state.foo; // OK: state is unknown (due to unknown mixin) + } +}); + +const E = React.createClass({ + foo: 0, + m() { + (this.foo: string); // error: number ~> string + }, + mm() { + var props: { m(): void } = { m: this.m }; + props.m(); // OK: this.m is autobound, so \`this.foo\` will always be found + } +}); + +const F = React.createClass({ + getInitialState(): { [string]: mixed } { + return { foo: 0 }; + }, + m() { + this.state.foo; + this.state.bar; + } +}); + +const G = React.createClass({ + mixins: [], + autobind: true, + statics: {}, + m() { + (this.mixins: mixed); // error: property \`mixins\` not found + (this.autobind: mixed); // error: property \`autobind\` not found + (this.statics: mixed); // error: property \`statics\` not found + } +}); + +const H = React.createClass({ + statics: { q: 0 }, + getDefaultProps() { + (this.q: empty); // error: number ~> empty + return {}; + } +}); + +const I = React.createClass({ + propTypes: ({}: { [string]: any }), + m() { + (this.props.foo: empty); // OK + } +}); + +const J = React.createClass({ + mixins: [ + { + getInitialState() { + return this.constructor.calculateState(); + } + } + ], + statics: { + calculateState() { + return { foo: 0 }; + } + }, + m() { + (this.state.foo: empty); // number ~> empty + } +}); + +const K = React.createClass({ + propTypes: { + foo: React.PropTypes.string.isRequired + }, + getInitialState() { + this.mm(); // cause error in mm below + return this.props; + }, + m() { + (this.props.foo: empty); // string ~> empty + (this.state.foo: empty); // string ~> empty + }, + mm() { + this.state.foo; // error: property fo not found (called by getInitialState) + } +}); + +const L = React.createClass({ + propTypes: { + foo: React.PropTypes.string.isRequired + }, + getInitialState() { + return { bar: 0 }; + }, + componentWillMount() { + (this.props.foo: empty); // string ~> empty + return 0; // number ~> void + }, + componentDidMount() { + (this.props.foo: empty); // string ~> empty + return 0; // number ~> void + }, + componentWillReceiveProps(nextProps) { + (this.props.foo: empty); // string ~> empty + (nextProps.foo: empty); // string ~> empty + return 0; // number ~> void + }, + shouldComponentUpdate(nextProps, nextState) { + (this.props.foo: empty); // string ~> empty + (this.state.bar: empty); // number ~> empty + (nextProps.foo: empty); // string ~> empty + (nextState.bar: empty); // number ~> empty + return 0; // number ~> bool + }, + componentWillUpdate(nextProps, nextState) { + (this.props.foo: empty); // string ~> empty + (this.state.bar: empty); // number ~> empty + (nextProps.foo: empty); // string ~> empty + (nextState.bar: empty); // number ~> empty + return 0; // number ~> void + }, + componentDidUpdate(nextProps, nextState) { + (this.props.foo: empty); // string ~> empty + (this.state.bar: empty); // number ~> empty + (nextProps.foo: empty); // string ~> empty + (nextState.bar: empty); // number ~> empty + return 0; // number ~> void + }, + componentWillUnmount() { + (this.props.foo: empty); // string ~> empty + (this.state.bar: empty); // number ~> empty + return 0; // number ~> void + } +}); + +React.createClass({}); // error: spec must be [x] exact and [ ] sealed +React.createClass(({}: {})); // error: spec must be [ ] exact and [x] sealed +" +`; + +exports[`create_class_initial_state_sealed.js 1`] = ` +"import React from \\"react\\"; + +// initial state = None +React.createClass({ + f() { + this.setState({ q: 0 }); + }, + g() { + (this.state.q: empty); // number ~> empty + } +}); + +// initial state = Some (exact & sealed) [lit] +React.createClass({ + getInitialState() { + return { p: 0 }; + }, + f() { + this.setState({ q: 0 }); + }, + g() { + (this.state.q: empty); // number ~> empty + } +}); + +// initial state = Some (exact & sealed) [annot] +React.createClass({ + getInitialState(): {| p: number |} { + return { p: 0 }; + }, + f() { + this.setState({ q: 0 }); + }, + g() { + (this.state.q: empty); // number ~> empty + } +}); + +// initial state = Some (inexact & sealed) [annot] +React.createClass({ + getInitialState(): { p: number } { + return { p: 0 }; + }, + f() { + this.setState({ q: 0 }); // property \`q\` not found + }, + g() { + (this.state.q: empty); // property \`q\` not found + } +}); + +// mixins = (exact & sealed) + (exact & sealed) +React.createClass({ + mixins: [{ + getInitialState() { + return { foo: 0 }; + }, + }], + getInitialState() { + return { bar: 0 }; + }, + f() { + this.setState({ baz: 0 }); + }, + g() { + (this.state.baz: empty); // number ~> empty + } +}); + +// mixins = (exact & sealed) + (inexact & sealed) +React.createClass({ + mixins: [{ + getInitialState(): { foo: number } { + return { foo: 0 }; + }, + }], + getInitialState() { + return { bar: 0 }; + }, + f() { + this.setState({ baz: 0 }); // property \`baz\` not found + }, + g() { + (this.state.baz: empty); // property \`baz\` not found + } +}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import React from \\"react\\"; + +// initial state = None +React.createClass({ + f() { + this.setState({ q: 0 }); + }, + g() { + (this.state.q: empty); // number ~> empty + } +}); + +// initial state = Some (exact & sealed) [lit] +React.createClass({ + getInitialState() { + return { p: 0 }; + }, + f() { + this.setState({ q: 0 }); + }, + g() { + (this.state.q: empty); // number ~> empty + } +}); + +// initial state = Some (exact & sealed) [annot] +React.createClass({ + getInitialState(): {| p: number |} { + return { p: 0 }; + }, + f() { + this.setState({ q: 0 }); + }, + g() { + (this.state.q: empty); // number ~> empty + } +}); + +// initial state = Some (inexact & sealed) [annot] +React.createClass({ + getInitialState(): { p: number } { + return { p: 0 }; + }, + f() { + this.setState({ q: 0 }); // property \`q\` not found + }, + g() { + (this.state.q: empty); // property \`q\` not found + } +}); + +// mixins = (exact & sealed) + (exact & sealed) +React.createClass({ + mixins: [ + { + getInitialState() { + return { foo: 0 }; + } + } + ], + getInitialState() { + return { bar: 0 }; + }, + f() { + this.setState({ baz: 0 }); + }, + g() { + (this.state.baz: empty); // number ~> empty + } +}); + +// mixins = (exact & sealed) + (inexact & sealed) +React.createClass({ + mixins: [ + { + getInitialState(): { foo: number } { + return { foo: 0 }; + } + } + ], + getInitialState() { + return { bar: 0 }; + }, + f() { + this.setState({ baz: 0 }); // property \`baz\` not found + }, + g() { + (this.state.baz: empty); // property \`baz\` not found + } +}); +" +`; + +exports[`create_class_statics_sealed.js 1`] = ` +"import React from \\"react\\"; + +// statics = None +const A = React.createClass({ p: 0 }); +(A.bar: empty); // number ~> empty (inflow below) +A.bar = 0; + +// statics = Some (exact & sealed) [lit] +const B = React.createClass({ + statics: { foo: 0 }, +}); +(B.foo: empty); // number ~> empty +(B.bar: empty); // number ~> empty (inflow below) +B.bar = 0; + +// statics = Some (exact & sealed) [annot] +const C = React.createClass({ + statics: ({ foo: 0 }: {| foo: number |}), +}); +(C.foo: empty); // number ~> empty +(C.bar: empty); // number ~> empty (inflow below) +C.bar = 0; + +// statics = Some (inexact & sealed) [annot] +const D = React.createClass({ + statics: ({ foo: 0 }: { foo: number }), +}); +(D.foo: empty); // number ~> empty +(D.bar: empty); // property \`bar\` not found +D.bar = 0; // property \`bar\` not found + +// mixins: (exact & sealed) + (exact & sealed) +const E = React.createClass({ + mixins: [{ + statics: { foo: 0 }, + }], + statics: { bar: 0 }, +}); +(E.foo: empty); // number ~> empty +(E.bar: empty); // number ~> empty +(E.baz: empty); // number ~> empty (inflow below) +E.baz = 0; + +// mixins: (exact & sealed) + (inexact & sealed) +const F = React.createClass({ + mixins: [{ + statics: ({ foo: 0 }: { foo: number }), + }], + statics: { bar: 0 }, +}); +(F.foo: empty); // number ~> empty +(F.bar: empty); // number ~> empty +(F.baz: empty); // number ~> empty (inflow below) +F.baz = 0; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import React from \\"react\\"; + +// statics = None +const A = React.createClass({ p: 0 }); +(A.bar: empty); // number ~> empty (inflow below) +A.bar = 0; + +// statics = Some (exact & sealed) [lit] +const B = React.createClass({ + statics: { foo: 0 } +}); +(B.foo: empty); // number ~> empty +(B.bar: empty); // number ~> empty (inflow below) +B.bar = 0; + +// statics = Some (exact & sealed) [annot] +const C = React.createClass({ + statics: ({ foo: 0 }: {| foo: number |}) +}); +(C.foo: empty); // number ~> empty +(C.bar: empty); // number ~> empty (inflow below) +C.bar = 0; + +// statics = Some (inexact & sealed) [annot] +const D = React.createClass({ + statics: ({ foo: 0 }: { foo: number }) +}); +(D.foo: empty); // number ~> empty +(D.bar: empty); // property \`bar\` not found +D.bar = 0; // property \`bar\` not found + +// mixins: (exact & sealed) + (exact & sealed) +const E = React.createClass({ + mixins: [ + { + statics: { foo: 0 } + } + ], + statics: { bar: 0 } +}); +(E.foo: empty); // number ~> empty +(E.bar: empty); // number ~> empty +(E.baz: empty); // number ~> empty (inflow below) +E.baz = 0; + +// mixins: (exact & sealed) + (inexact & sealed) +const F = React.createClass({ + mixins: [ + { + statics: ({ foo: 0 }: { foo: number }) + } + ], + statics: { bar: 0 } +}); +(F.foo: empty); // number ~> empty +(F.bar: empty); // number ~> empty +(F.baz: empty); // number ~> empty (inflow below) +F.baz = 0; +" +`; + exports[`createElement_string.js 1`] = ` "// @flow import React from 'react'; @@ -149,21 +804,95 @@ var blah = ; // error bar, number given string expected " `; +exports[`proptype_any.js 1`] = ` +"const React = require(\\"react\\"); + +var AnyExample = React.createClass({ + propTypes: { + foo: (0: any), // OK + }, +}); + +(); // OK +(); // OK + +var AnyFunExample = React.createClass({ + propTypes: { + foo: (() => {}: Function), // OK + }, +}); + +(); // OK +(); // OK +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +const React = require(\\"react\\"); + +var AnyExample = React.createClass({ + propTypes: { + foo: (0: any) // OK + } +}); + +; // OK +; // OK + +var AnyFunExample = React.createClass({ + propTypes: { + foo: (() => {}: Function) // OK + } +}); + +; // OK +; // OK +" +`; + exports[`proptype_arrayOf.js 1`] = ` "/* @flow */ var React = require('react'); var Example = React.createClass({ propTypes: { - arr: React.PropTypes.arrayOf(React.PropTypes.number).isRequired + arr: React.PropTypes.arrayOf(React.PropTypes.number).isRequired, }, }); var ok_empty = var ok_numbers = +var fail_missing = var fail_not_array = var fail_mistyped_elems = + +/* Since the \`number\` proptype argument is not required, React will actually + allow \`null\` and \`undefined\` elements in the \`arr\` prop, but Flow has + currently ignores the innter prop type's required flag. */ +var todo_required = + +var OptionalExample = React.createClass({ + propTypes: { + arr: React.PropTypes.arrayOf(React.PropTypes.number), + } +}); + +(); // OK +(); // OK +(); // error: string ~> number + +var AnyExample = React.createClass({ + propTypes: { + arr: React.PropTypes.arrayOf((0:any)), // OK + }, +}); + +(); // error: still needs to be an array +(); // OK + +var InvalidExample = React.createClass({ + propTypes: { + arr: React.PropTypes.arrayOf(0), // error: number not a prop type + }, +}); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /* @flow */ @@ -177,8 +906,80 @@ var Example = React.createClass({ var ok_empty = ; var ok_numbers = ; +var fail_missing = ; var fail_not_array = ; var fail_mistyped_elems = ; + +/* Since the \`number\` proptype argument is not required, React will actually + allow \`null\` and \`undefined\` elements in the \`arr\` prop, but Flow has + currently ignores the innter prop type's required flag. */ +var todo_required = ; + +var OptionalExample = React.createClass({ + propTypes: { + arr: React.PropTypes.arrayOf(React.PropTypes.number) + } +}); + +; // OK +; // OK +; // error: string ~> number + +var AnyExample = React.createClass({ + propTypes: { + arr: React.PropTypes.arrayOf((0: any)) // OK + } +}); + +; // error: still needs to be an array +; // OK + +var InvalidExample = React.createClass({ + propTypes: { + arr: React.PropTypes.arrayOf(0) // error: number not a prop type + } +}); +" +`; + +exports[`proptype_custom_validator.js 1`] = ` +"const React = require(\\"react\\"); + +// Custom validator must match \`ReactPropsCheckType\` +var Example = React.createClass({ + propTypes: { + foo(props, propName, componentName, href) { + (props: empty); // ok: props is \`any\` + (propName: empty); // error: propName is a string + (componentName: empty); // error: componentName is a string + (href: empty); // error: href is an optional string + return (0: mixed); // error: should return ?Error + }, + } +}); + +// Inferred prop type is optional \`any\` +(); +(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +const React = require(\\"react\\"); + +// Custom validator must match \`ReactPropsCheckType\` +var Example = React.createClass({ + propTypes: { + foo(props, propName, componentName, href) { + (props: empty); // ok: props is \`any\` + (propName: empty); // error: propName is a string + (componentName: empty); // error: componentName is a string + (href: empty); // error: href is an optional string + return (0: mixed); // error: should return ?Error + } + } +}); + +// Inferred prop type is optional \`any\` +; +; " `; @@ -215,6 +1016,122 @@ var fail_mistyped = ; " `; +exports[`proptype_incompatible.js 1`] = ` +"const React = require(\\"react\\"); + +var Example = React.createClass({ + propTypes: { + foo: 0, // error: \`0\` is not a prop type + }, +}); + +(); // OK: don't cascade errors +(); // OK: don't cascade errors +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +const React = require(\\"react\\"); + +var Example = React.createClass({ + propTypes: { + foo: 0 // error: \`0\` is not a prop type + } +}); + +; // OK: don't cascade errors +; // OK: don't cascade errors +" +`; + +exports[`proptype_instanceOf.js 1`] = ` +"/* @flow */ + +class A {} +class B extends A {} +class C extends B {} + +var React = require('react'); +var Example = React.createClass({ + propTypes: { + x: React.PropTypes.instanceOf(B), + } +}); + +(); // error: A ~> B +(); // OK +(); // OK (C ~> B) +(); // error: string ~> B + +class Poly {x:T} +var PolyExample = React.createClass({ + propTypes: { + x: React.PropTypes.instanceOf(Poly).isRequired, + }, + m() { + (this.props.x.x: empty); // OK, T instantiated to \`any\` + } +}); + +// Different instantiations don't interact +()} />); // OK +()} />); // OK + +class PolyDefault {x:T} +var PolyDefaultExample = React.createClass({ + propTypes: { + x: React.PropTypes.instanceOf(PolyDefault).isRequired, + }, + m() { + (this.props.x.x: empty); // OK, T instantiated to \`any\` + } +}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ + +class A {} +class B extends A {} +class C extends B {} + +var React = require(\\"react\\"); +var Example = React.createClass({ + propTypes: { + x: React.PropTypes.instanceOf(B) + } +}); + +; // error: A ~> B +; // OK +; // OK (C ~> B) +; // error: string ~> B + +class Poly { + x: T; +} +var PolyExample = React.createClass({ + propTypes: { + x: React.PropTypes.instanceOf(Poly).isRequired + }, + m() { + (this.props.x.x: empty); // OK, T instantiated to \`any\` + } +}); + +// Different instantiations don't interact +)} />; // OK +)} />; // OK + +class PolyDefault { + x: T; +} +var PolyDefaultExample = React.createClass({ + propTypes: { + x: React.PropTypes.instanceOf(PolyDefault).isRequired + }, + m() { + (this.props.x.x: empty); // OK, T instantiated to \`any\` + } +}); +" +`; + exports[`proptype_missing.js 1`] = ` "/* @flow */ @@ -231,7 +1148,8 @@ exports[`proptype_missing.js 1`] = ` * We may change this back to the empty object at some point and fix the * situations where it didn't used to error */ -var React = React.createClass({ +var React = require('react'); +var Foo = React.createClass({ getID(): string { // So this would have been an error in 0.21.0 if we didn't make this.props // Object @@ -262,7 +1180,8 @@ var React = React.createClass({ * We may change this back to the empty object at some point and fix the * situations where it didn't used to error */ -var React = React.createClass({ +var React = require(\\"react\\"); +var Foo = React.createClass({ getID(): string { // So this would have been an error in 0.21.0 if we didn't make this.props // Object @@ -326,8 +1245,39 @@ var Example = React.createClass({ var ok_empty = var ok_numbers = +var fail_missing = var fail_not_object = var fail_mistyped_props = + +/* Since the \`number\` proptype argument is not required, React will actually + allow \`null\` and \`undefined\` elements in the \`obj\` prop, but Flow has + currently ignores the innter prop type's required flag. */ +var todo_required = + +var OptionalExample = React.createClass({ + propTypes: { + obj: React.PropTypes.objectOf(React.PropTypes.number), + } +}); + +(); // OK +(); // OK +(); // error: string ~> number + +var AnyExample = React.createClass({ + propTypes: { + obj: React.PropTypes.objectOf((0:any)), // OK + }, +}); + +(); // error: still needs to be an object +(); // OK + +var InvalidExample = React.createClass({ + propTypes: { + obj: React.PropTypes.objectOf(0), // error: number not a prop type + }, +}); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /* @flow */ @@ -341,8 +1291,39 @@ var Example = React.createClass({ var ok_empty = ; var ok_numbers = ; +var fail_missing = ; var fail_not_object = ; var fail_mistyped_props = ; + +/* Since the \`number\` proptype argument is not required, React will actually + allow \`null\` and \`undefined\` elements in the \`obj\` prop, but Flow has + currently ignores the innter prop type's required flag. */ +var todo_required = ; + +var OptionalExample = React.createClass({ + propTypes: { + obj: React.PropTypes.objectOf(React.PropTypes.number) + } +}); + +; // OK +; // OK +; // error: string ~> number + +var AnyExample = React.createClass({ + propTypes: { + obj: React.PropTypes.objectOf((0: any)) // OK + } +}); + +; // error: still needs to be an object +; // OK + +var InvalidExample = React.createClass({ + propTypes: { + obj: React.PropTypes.objectOf(0) // error: number not a prop type + } +}); " `; @@ -352,24 +1333,176 @@ exports[`proptype_oneOf.js 1`] = ` var React = require('react'); var Example = React.createClass({ propTypes: { - literal: React.PropTypes.oneOf([\\"foo\\"]).isRequired + str: React.PropTypes.oneOf([\\"foo\\", \\"bar\\"]), + num: React.PropTypes.oneOf([0, 1, 2]), + bool: React.PropTypes.oneOf([true]), + mixed: React.PropTypes.oneOf([\\"foo\\", 0, true]), }, }); -var ex1 = ; -var ex2 = ; +(); // OK +(); // error: 'baz' not in enum + +(); // OK +(); // error: 3 not in enum + +(); // OK +(); // error: false ~> true + +(); // OK +(); // OK +(); // error: 'baz' not in enum + +var RequiredExample = React.createClass({ + propTypes: { + p: React.PropTypes.oneOf([]).isRequired, + }, +}); + +(); // error: \`p\` not found + +var EmptyExample = React.createClass({ + propTypes: { + nil: React.PropTypes.oneOf([]), // i.e., \`empty\` + }, +}); + +(); // number ~> empty + +var AnyArrayExample = React.createClass({ + propTypes: { + any: React.PropTypes.oneOf((0:any)), + }, +}); + +(); // OK + +var AnyElemExample = React.createClass({ + propTypes: { + any: React.PropTypes.oneOf([\\"foo\\", (0:any)]), + }, +}); + +(); // OK + +var DynamicArrayExample = React.createClass({ + propTypes: { + dyn: React.PropTypes.oneOf(([]: Array)), + }, +}); + +(); // OK + +var DynamicElemExample = React.createClass({ + propTypes: { + dyn: React.PropTypes.oneOf([\\"foo\\", (\\"\\": string)]), + }, +}); + +(); // OK + +var InvalidArrayExample = React.createClass({ + propTypes: { + p: React.PropTypes.oneOf(0), // error: expected array, got 0 + }, +}); + +(); // OK, don't cascade errors + +var NonLiteralElemExample = React.createClass({ + propTypes: { + p: React.PropTypes.oneOf([{}]), // OK: allow non-literals + }, +}); +(); // OK, result is unknown/any ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /* @flow */ var React = require(\\"react\\"); var Example = React.createClass({ propTypes: { - literal: React.PropTypes.oneOf([\\"foo\\"]).isRequired + str: React.PropTypes.oneOf([\\"foo\\", \\"bar\\"]), + num: React.PropTypes.oneOf([0, 1, 2]), + bool: React.PropTypes.oneOf([true]), + mixed: React.PropTypes.oneOf([\\"foo\\", 0, true]) } }); -var ex1 = ; -var ex2 = ; +; // OK +; // error: 'baz' not in enum + +; // OK +; // error: 3 not in enum + +; // OK +; // error: false ~> true + +; // OK +; // OK +; // error: 'baz' not in enum + +var RequiredExample = React.createClass({ + propTypes: { + p: React.PropTypes.oneOf([]).isRequired + } +}); + +; // error: \`p\` not found + +var EmptyExample = React.createClass({ + propTypes: { + nil: React.PropTypes.oneOf([]) // i.e., \`empty\` + } +}); + +; // number ~> empty + +var AnyArrayExample = React.createClass({ + propTypes: { + any: React.PropTypes.oneOf((0: any)) + } +}); + +; // OK + +var AnyElemExample = React.createClass({ + propTypes: { + any: React.PropTypes.oneOf([\\"foo\\", (0: any)]) + } +}); + +; // OK + +var DynamicArrayExample = React.createClass({ + propTypes: { + dyn: React.PropTypes.oneOf(([]: Array)) + } +}); + +; // OK + +var DynamicElemExample = React.createClass({ + propTypes: { + dyn: React.PropTypes.oneOf([\\"foo\\", (\\"\\": string)]) + } +}); + +; // OK + +var InvalidArrayExample = React.createClass({ + propTypes: { + p: React.PropTypes.oneOf(0) // error: expected array, got 0 + } +}); + +; // OK, don't cascade errors + +var NonLiteralElemExample = React.createClass({ + propTypes: { + p: React.PropTypes.oneOf([{}]) // OK: allow non-literals + } +}); +; // OK, result is unknown/any " `; @@ -396,7 +1529,87 @@ var Example = React.createClass({ var ok_number = ; var ok_string = ; -var fail_bool = +var fail_missing = ; +var fail_bool = ; + +/* Since the proptype arguments are not required, React will actually allow + \`null\` and \`undefined\` elements in the \`prop\` prop, but Flow has currently + ignores the innter prop types' required flags. */ +var todo_required = ; + +var OptionalExample = React.createClass({ + propTypes: { + p: React.PropTypes.oneOfType([ + React.PropTypes.string, + ]), + }, +}); + +(); // OK +(); // OK +(); // error: number ~> string + +var EmptyExample = React.createClass({ + propTypes: { + nil: React.PropTypes.oneOfType([]), // i.e., \`empty\` + }, +}); + +(); // number ~> empty + +var AnyArrayExample = React.createClass({ + propTypes: { + any: React.PropTypes.oneOfType((0:any)), + }, +}); + +(); // OK + +var AnyElemExample = React.createClass({ + propTypes: { + any: React.PropTypes.oneOfType([ + React.PropTypes.string, + (0:any), + ]), + }, +}); + +(); // OK + +var DynamicArrayExample = React.createClass({ + propTypes: { + dyn: React.PropTypes.oneOfType(([]: Array)), + }, +}); + +(); // OK + +var DynamicElemExample = React.createClass({ + propTypes: { + dyn: React.PropTypes.oneOfType([ + React.PropTypes.string, + (() => {}: Function), + ]), + }, +}); + +(); // OK + +var InvalidArrayExample = React.createClass({ + propTypes: { + p: React.PropTypes.oneOfType(0), // error: expected array, got 0 + }, +}); + +(); // OK, don't cascade errors + +var InvalidElemExample = React.createClass({ + propTypes: { + p: React.PropTypes.oneOfType([{}]), // error: expected prop type, got {} + }, +}); + +(); // OK, don't cascade errors ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /* @flow */ @@ -420,6 +1633,268 @@ var Example = React.createClass({ var ok_number = ; var ok_string = ; +var fail_missing = ; var fail_bool = ; + +/* Since the proptype arguments are not required, React will actually allow + \`null\` and \`undefined\` elements in the \`prop\` prop, but Flow has currently + ignores the innter prop types' required flags. */ +var todo_required = ; + +var OptionalExample = React.createClass({ + propTypes: { + p: React.PropTypes.oneOfType([React.PropTypes.string]) + } +}); + +; // OK +; // OK +; // error: number ~> string + +var EmptyExample = React.createClass({ + propTypes: { + nil: React.PropTypes.oneOfType([]) // i.e., \`empty\` + } +}); + +; // number ~> empty + +var AnyArrayExample = React.createClass({ + propTypes: { + any: React.PropTypes.oneOfType((0: any)) + } +}); + +; // OK + +var AnyElemExample = React.createClass({ + propTypes: { + any: React.PropTypes.oneOfType([React.PropTypes.string, (0: any)]) + } +}); + +; // OK + +var DynamicArrayExample = React.createClass({ + propTypes: { + dyn: React.PropTypes.oneOfType(([]: Array)) + } +}); + +; // OK + +var DynamicElemExample = React.createClass({ + propTypes: { + dyn: React.PropTypes.oneOfType([ + React.PropTypes.string, + (() => {}: Function) + ]) + } +}); + +; // OK + +var InvalidArrayExample = React.createClass({ + propTypes: { + p: React.PropTypes.oneOfType(0) // error: expected array, got 0 + } +}); + +; // OK, don't cascade errors + +var InvalidElemExample = React.createClass({ + propTypes: { + p: React.PropTypes.oneOfType([{}]) // error: expected prop type, got {} + } +}); + +; // OK, don't cascade errors +" +`; + +exports[`proptype_shape.js 1`] = ` +"/* Shape should be a sealed, inexact object just like a type annotation. The + * below component's \`foo\` property should be equivalent to \`{ bar: string }\`, + * which would forbid reads/writes on an unknown \`baz\` property. + * + * If you see a single \\"number incompatible with string\\" error instead of two + * separate \\"property \`baz\` not found\\" errors, this is broken and we are + * treating the shape like an unsealed object and performing shadow read/writes. + */ + +import React from \\"react\\"; + +React.createClass({ + propTypes: { + foo: React.PropTypes.shape({ + bar: React.PropTypes.string.isRequired, + }).isRequired, + }, + + f() { + (this.props.foo.baz: string); + }, + + g() { + this.props.foo.baz = 0; + } +}); + +React.createClass({ + propTypes: { + foo: React.PropTypes.shape(({}: {[string]: any})).isRequired, + }, + f() { + (this.props.foo.bar: empty); // OK + }, +}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* Shape should be a sealed, inexact object just like a type annotation. The + * below component's \`foo\` property should be equivalent to \`{ bar: string }\`, + * which would forbid reads/writes on an unknown \`baz\` property. + * + * If you see a single \\"number incompatible with string\\" error instead of two + * separate \\"property \`baz\` not found\\" errors, this is broken and we are + * treating the shape like an unsealed object and performing shadow read/writes. + */ + +import React from \\"react\\"; + +React.createClass({ + propTypes: { + foo: React.PropTypes.shape({ + bar: React.PropTypes.string.isRequired + }).isRequired + }, + + f() { + (this.props.foo.baz: string); + }, + + g() { + this.props.foo.baz = 0; + } +}); + +React.createClass({ + propTypes: { + foo: React.PropTypes.shape(({}: { [string]: any })).isRequired + }, + f() { + (this.props.foo.bar: empty); // OK + } +}); +" +`; + +exports[`proptypes_builtins.js 1`] = ` +"import React from \\"react\\"; + +type NoFun = mixed => empty; + +// error: mixed ~> ReactPropsCheckType +// error: ReactPropsChainableTypeChecker ~> empty +(React.PropTypes.arrayOf : NoFun); + +// OK: mixed ~> any +// error: ReactPropsChainableTypeChecker ~> empty +(React.PropTypes.instanceOf : NoFun); + +// error: mixed ~> ReactPropsCheckType +// error: ReactPropsChainableTypeChecker ~> empty +(React.PropTypes.objectOf : NoFun); + +// error: mixed ~> Array +// error: ReactPropsChainableTypeChecker ~> empty +(React.PropTypes.oneOf : NoFun); + +// error: mixed ~> Array +// error: ReactPropsChainableTypeChecker ~> empty +(React.PropTypes.oneOfType : NoFun); + +// error: mixed ~> object type +// error: ReactPropsChainableTypeChecker ~> empty +(React.PropTypes.shape : NoFun); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import React from \\"react\\"; + +type NoFun = mixed => empty; + +// error: mixed ~> ReactPropsCheckType +// error: ReactPropsChainableTypeChecker ~> empty +(React.PropTypes.arrayOf: NoFun); + +// OK: mixed ~> any +// error: ReactPropsChainableTypeChecker ~> empty +(React.PropTypes.instanceOf: NoFun); + +// error: mixed ~> ReactPropsCheckType +// error: ReactPropsChainableTypeChecker ~> empty +(React.PropTypes.objectOf: NoFun); + +// error: mixed ~> Array +// error: ReactPropsChainableTypeChecker ~> empty +(React.PropTypes.oneOf: NoFun); + +// error: mixed ~> Array +// error: ReactPropsChainableTypeChecker ~> empty +(React.PropTypes.oneOfType: NoFun); + +// error: mixed ~> object type +// error: ReactPropsChainableTypeChecker ~> empty +(React.PropTypes.shape: NoFun); +" +`; + +exports[`proptypes_sealed.js 1`] = ` +"/* propTypes should be a sealed, inexact object just like a type annotation. The + * below component's propTypes should be equivalent to \`{ bar: string }\`, which + * would forbid reads/writes on an unknown \`baz\` property. + * + * If you see a single \\"number incompatible with string\\" error instead of two + * separate \\"property \`baz\` not found\\" errors, this is broken and we are + * treating propTypes like an unsealed object and performing shadow read/writes. + */ + +import React from \\"react\\"; + +React.createClass({ + propTypes: { + foo: React.PropTypes.string.isRequired, + }, + + f() { + (this.props.baz: string); + }, + + g() { + this.props.baz = 0; + } +}); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* propTypes should be a sealed, inexact object just like a type annotation. The + * below component's propTypes should be equivalent to \`{ bar: string }\`, which + * would forbid reads/writes on an unknown \`baz\` property. + * + * If you see a single \\"number incompatible with string\\" error instead of two + * separate \\"property \`baz\` not found\\" errors, this is broken and we are + * treating propTypes like an unsealed object and performing shadow read/writes. + */ + +import React from \\"react\\"; + +React.createClass({ + propTypes: { + foo: React.PropTypes.string.isRequired + }, + + f() { + (this.props.baz: string); + }, + + g() { + this.props.baz = 0; + } +}); " `; diff --git a/tests/flow/react/create_class.js b/tests/flow/react/create_class.js new file mode 100644 index 00000000..b5f36004 --- /dev/null +++ b/tests/flow/react/create_class.js @@ -0,0 +1,174 @@ +import React from "react"; + +const A = React.createClass({ + mixins: [{ propTypes: { foo: React.PropTypes.string.isRequired } }], + propTypes: { bar: React.PropTypes.number.isRequired }, + m() { + (this.props.foo: empty); // error: string ~> empty + (this.props.bar: empty); // error: number ~> empty + } +}); + +const B = React.createClass({ + p: "", + m() { + this.p = 0; // error: number ~> string + }, + mm() { + this.m.apply(null); // OK: this.m is autobound, so `this.p` will always be found + } +}); + +const C = React.createClass({ + getInitialState(): Object { + return { foo: 0 }; + }, + m() { + this.state.foo; // OK: state is unknown + } +}); + +const D = React.createClass({ + mixins: [{ + getInitialState(): Object { + return { foo: 0 }; + }, + }], + getInitialState() { + return { bar: 0 }; + }, + m() { + this.state.foo; // OK: state is unknown (due to unknown mixin) + } +}); + +const E = React.createClass({ + foo: 0, + m() { + (this.foo: string); // error: number ~> string + }, + mm() { + var props: { m(): void } = { m: this.m }; + props.m(); // OK: this.m is autobound, so `this.foo` will always be found + } +}); + +const F = React.createClass({ + getInitialState(): { [string]: mixed } { + return { foo: 0 }; + }, + m() { + this.state.foo; + this.state.bar; + }, +}); + +const G = React.createClass({ + mixins: [], + autobind: true, + statics: {}, + m() { + (this.mixins: mixed); // error: property `mixins` not found + (this.autobind: mixed); // error: property `autobind` not found + (this.statics: mixed); // error: property `statics` not found + }, +}); + +const H = React.createClass({ + statics: { q: 0 }, + getDefaultProps() { + (this.q: empty); // error: number ~> empty + return {}; + }, +}); + +const I = React.createClass({ + propTypes: ({}: {[string]: any}), + m() { + (this.props.foo: empty); // OK + } +}); + +const J = React.createClass({ + mixins: [{ + getInitialState() { + return this.constructor.calculateState(); + }, + }], + statics: { + calculateState() { + return { foo: 0 }; + }, + }, + m() { + (this.state.foo: empty); // number ~> empty + }, +}); + +const K = React.createClass({ + propTypes: { + foo: React.PropTypes.string.isRequired, + }, + getInitialState() { + this.mm(); // cause error in mm below + return this.props; + }, + m() { + (this.props.foo: empty); // string ~> empty + (this.state.foo: empty); // string ~> empty + }, + mm() { + this.state.foo; // error: property fo not found (called by getInitialState) + } +}); + +const L = React.createClass({ + propTypes: { + foo: React.PropTypes.string.isRequired, + }, + getInitialState() { + return { bar: 0 }; + }, + componentWillMount() { + (this.props.foo: empty); // string ~> empty + return 0; // number ~> void + }, + componentDidMount() { + (this.props.foo: empty); // string ~> empty + return 0; // number ~> void + }, + componentWillReceiveProps(nextProps) { + (this.props.foo: empty); // string ~> empty + (nextProps.foo: empty); // string ~> empty + return 0; // number ~> void + }, + shouldComponentUpdate(nextProps, nextState) { + (this.props.foo: empty); // string ~> empty + (this.state.bar: empty); // number ~> empty + (nextProps.foo: empty); // string ~> empty + (nextState.bar: empty); // number ~> empty + return 0; // number ~> bool + }, + componentWillUpdate(nextProps, nextState) { + (this.props.foo: empty); // string ~> empty + (this.state.bar: empty); // number ~> empty + (nextProps.foo: empty); // string ~> empty + (nextState.bar: empty); // number ~> empty + return 0; // number ~> void + }, + componentDidUpdate(nextProps, nextState) { + (this.props.foo: empty); // string ~> empty + (this.state.bar: empty); // number ~> empty + (nextProps.foo: empty); // string ~> empty + (nextState.bar: empty); // number ~> empty + return 0; // number ~> void + }, + componentWillUnmount() { + (this.props.foo: empty); // string ~> empty + (this.state.bar: empty); // number ~> empty + return 0; // number ~> void + }, +}); + +React.createClass({}); // error: spec must be [x] exact and [ ] sealed +React.createClass(({}: {})); // error: spec must be [ ] exact and [x] sealed diff --git a/tests/flow/react/create_class_initial_state_sealed.js b/tests/flow/react/create_class_initial_state_sealed.js new file mode 100644 index 00000000..946cbfa8 --- /dev/null +++ b/tests/flow/react/create_class_initial_state_sealed.js @@ -0,0 +1,86 @@ +import React from "react"; + +// initial state = None +React.createClass({ + f() { + this.setState({ q: 0 }); + }, + g() { + (this.state.q: empty); // number ~> empty + } +}); + +// initial state = Some (exact & sealed) [lit] +React.createClass({ + getInitialState() { + return { p: 0 }; + }, + f() { + this.setState({ q: 0 }); + }, + g() { + (this.state.q: empty); // number ~> empty + } +}); + +// initial state = Some (exact & sealed) [annot] +React.createClass({ + getInitialState(): {| p: number |} { + return { p: 0 }; + }, + f() { + this.setState({ q: 0 }); + }, + g() { + (this.state.q: empty); // number ~> empty + } +}); + +// initial state = Some (inexact & sealed) [annot] +React.createClass({ + getInitialState(): { p: number } { + return { p: 0 }; + }, + f() { + this.setState({ q: 0 }); // property `q` not found + }, + g() { + (this.state.q: empty); // property `q` not found + } +}); + +// mixins = (exact & sealed) + (exact & sealed) +React.createClass({ + mixins: [{ + getInitialState() { + return { foo: 0 }; + }, + }], + getInitialState() { + return { bar: 0 }; + }, + f() { + this.setState({ baz: 0 }); + }, + g() { + (this.state.baz: empty); // number ~> empty + } +}); + +// mixins = (exact & sealed) + (inexact & sealed) +React.createClass({ + mixins: [{ + getInitialState(): { foo: number } { + return { foo: 0 }; + }, + }], + getInitialState() { + return { bar: 0 }; + }, + f() { + this.setState({ baz: 0 }); // property `baz` not found + }, + g() { + (this.state.baz: empty); // property `baz` not found + } +}); diff --git a/tests/flow/react/create_class_statics_sealed.js b/tests/flow/react/create_class_statics_sealed.js new file mode 100644 index 00000000..5b6f2f40 --- /dev/null +++ b/tests/flow/react/create_class_statics_sealed.js @@ -0,0 +1,54 @@ +import React from "react"; + +// statics = None +const A = React.createClass({ p: 0 }); +(A.bar: empty); // number ~> empty (inflow below) +A.bar = 0; + +// statics = Some (exact & sealed) [lit] +const B = React.createClass({ + statics: { foo: 0 }, +}); +(B.foo: empty); // number ~> empty +(B.bar: empty); // number ~> empty (inflow below) +B.bar = 0; + +// statics = Some (exact & sealed) [annot] +const C = React.createClass({ + statics: ({ foo: 0 }: {| foo: number |}), +}); +(C.foo: empty); // number ~> empty +(C.bar: empty); // number ~> empty (inflow below) +C.bar = 0; + +// statics = Some (inexact & sealed) [annot] +const D = React.createClass({ + statics: ({ foo: 0 }: { foo: number }), +}); +(D.foo: empty); // number ~> empty +(D.bar: empty); // property `bar` not found +D.bar = 0; // property `bar` not found + +// mixins: (exact & sealed) + (exact & sealed) +const E = React.createClass({ + mixins: [{ + statics: { foo: 0 }, + }], + statics: { bar: 0 }, +}); +(E.foo: empty); // number ~> empty +(E.bar: empty); // number ~> empty +(E.baz: empty); // number ~> empty (inflow below) +E.baz = 0; + +// mixins: (exact & sealed) + (inexact & sealed) +const F = React.createClass({ + mixins: [{ + statics: ({ foo: 0 }: { foo: number }), + }], + statics: { bar: 0 }, +}); +(F.foo: empty); // number ~> empty +(F.bar: empty); // number ~> empty +(F.baz: empty); // number ~> empty (inflow below) +F.baz = 0; diff --git a/tests/flow/react/proptype_any.js b/tests/flow/react/proptype_any.js new file mode 100644 index 00000000..5e8df60a --- /dev/null +++ b/tests/flow/react/proptype_any.js @@ -0,0 +1,19 @@ +const React = require("react"); + +var AnyExample = React.createClass({ + propTypes: { + foo: (0: any), // OK + }, +}); + +(); // OK +(); // OK + +var AnyFunExample = React.createClass({ + propTypes: { + foo: (() => {}: Function), // OK + }, +}); + +(); // OK +(); // OK diff --git a/tests/flow/react/proptype_arrayOf.js b/tests/flow/react/proptype_arrayOf.js index f6cd3822..05de9015 100644 --- a/tests/flow/react/proptype_arrayOf.js +++ b/tests/flow/react/proptype_arrayOf.js @@ -3,12 +3,43 @@ var React = require('react'); var Example = React.createClass({ propTypes: { - arr: React.PropTypes.arrayOf(React.PropTypes.number).isRequired + arr: React.PropTypes.arrayOf(React.PropTypes.number).isRequired, }, }); var ok_empty = var ok_numbers = +var fail_missing = var fail_not_array = var fail_mistyped_elems = + +/* Since the `number` proptype argument is not required, React will actually + allow `null` and `undefined` elements in the `arr` prop, but Flow has + currently ignores the innter prop type's required flag. */ +var todo_required = + +var OptionalExample = React.createClass({ + propTypes: { + arr: React.PropTypes.arrayOf(React.PropTypes.number), + } +}); + +(); // OK +(); // OK +(); // error: string ~> number + +var AnyExample = React.createClass({ + propTypes: { + arr: React.PropTypes.arrayOf((0:any)), // OK + }, +}); + +(); // error: still needs to be an array +(); // OK + +var InvalidExample = React.createClass({ + propTypes: { + arr: React.PropTypes.arrayOf(0), // error: number not a prop type + }, +}); diff --git a/tests/flow/react/proptype_custom_validator.js b/tests/flow/react/proptype_custom_validator.js new file mode 100644 index 00000000..82e54696 --- /dev/null +++ b/tests/flow/react/proptype_custom_validator.js @@ -0,0 +1,18 @@ +const React = require("react"); + +// Custom validator must match `ReactPropsCheckType` +var Example = React.createClass({ + propTypes: { + foo(props, propName, componentName, href) { + (props: empty); // ok: props is `any` + (propName: empty); // error: propName is a string + (componentName: empty); // error: componentName is a string + (href: empty); // error: href is an optional string + return (0: mixed); // error: should return ?Error + }, + } +}); + +// Inferred prop type is optional `any` +(); +(); diff --git a/tests/flow/react/proptype_incompatible.js b/tests/flow/react/proptype_incompatible.js new file mode 100644 index 00000000..86a5d175 --- /dev/null +++ b/tests/flow/react/proptype_incompatible.js @@ -0,0 +1,10 @@ +const React = require("react"); + +var Example = React.createClass({ + propTypes: { + foo: 0, // error: `0` is not a prop type + }, +}); + +(); // OK: don't cascade errors +(); // OK: don't cascade errors diff --git a/tests/flow/react/proptype_instanceOf.js b/tests/flow/react/proptype_instanceOf.js new file mode 100644 index 00000000..880fa078 --- /dev/null +++ b/tests/flow/react/proptype_instanceOf.js @@ -0,0 +1,41 @@ +/* @flow */ + +class A {} +class B extends A {} +class C extends B {} + +var React = require('react'); +var Example = React.createClass({ + propTypes: { + x: React.PropTypes.instanceOf(B), + } +}); + +(); // error: A ~> B +(); // OK +(); // OK (C ~> B) +(); // error: string ~> B + +class Poly {x:T} +var PolyExample = React.createClass({ + propTypes: { + x: React.PropTypes.instanceOf(Poly).isRequired, + }, + m() { + (this.props.x.x: empty); // OK, T instantiated to `any` + } +}); + +// Different instantiations don't interact +()} />); // OK +()} />); // OK + +class PolyDefault {x:T} +var PolyDefaultExample = React.createClass({ + propTypes: { + x: React.PropTypes.instanceOf(PolyDefault).isRequired, + }, + m() { + (this.props.x.x: empty); // OK, T instantiated to `any` + } +}); diff --git a/tests/flow/react/proptype_missing.js b/tests/flow/react/proptype_missing.js index 29fa980f..4f672027 100644 --- a/tests/flow/react/proptype_missing.js +++ b/tests/flow/react/proptype_missing.js @@ -13,7 +13,8 @@ * We may change this back to the empty object at some point and fix the * situations where it didn't used to error */ -var React = React.createClass({ +var React = require('react'); +var Foo = React.createClass({ getID(): string { // So this would have been an error in 0.21.0 if we didn't make this.props // Object diff --git a/tests/flow/react/proptype_objectOf.js b/tests/flow/react/proptype_objectOf.js index abead561..9718a1f0 100644 --- a/tests/flow/react/proptype_objectOf.js +++ b/tests/flow/react/proptype_objectOf.js @@ -10,5 +10,36 @@ var Example = React.createClass({ var ok_empty = var ok_numbers = +var fail_missing = var fail_not_object = var fail_mistyped_props = + +/* Since the `number` proptype argument is not required, React will actually + allow `null` and `undefined` elements in the `obj` prop, but Flow has + currently ignores the innter prop type's required flag. */ +var todo_required = + +var OptionalExample = React.createClass({ + propTypes: { + obj: React.PropTypes.objectOf(React.PropTypes.number), + } +}); + +(); // OK +(); // OK +(); // error: string ~> number + +var AnyExample = React.createClass({ + propTypes: { + obj: React.PropTypes.objectOf((0:any)), // OK + }, +}); + +(); // error: still needs to be an object +(); // OK + +var InvalidExample = React.createClass({ + propTypes: { + obj: React.PropTypes.objectOf(0), // error: number not a prop type + }, +}); diff --git a/tests/flow/react/proptype_oneOf.js b/tests/flow/react/proptype_oneOf.js index f7486afb..ecf5f5d2 100644 --- a/tests/flow/react/proptype_oneOf.js +++ b/tests/flow/react/proptype_oneOf.js @@ -3,9 +3,85 @@ var React = require('react'); var Example = React.createClass({ propTypes: { - literal: React.PropTypes.oneOf(["foo"]).isRequired + str: React.PropTypes.oneOf(["foo", "bar"]), + num: React.PropTypes.oneOf([0, 1, 2]), + bool: React.PropTypes.oneOf([true]), + mixed: React.PropTypes.oneOf(["foo", 0, true]), }, }); -var ex1 = ; -var ex2 = ; +(); // OK +(); // error: 'baz' not in enum + +(); // OK +(); // error: 3 not in enum + +(); // OK +(); // error: false ~> true + +(); // OK +(); // OK +(); // error: 'baz' not in enum + +var RequiredExample = React.createClass({ + propTypes: { + p: React.PropTypes.oneOf([]).isRequired, + }, +}); + +(); // error: `p` not found + +var EmptyExample = React.createClass({ + propTypes: { + nil: React.PropTypes.oneOf([]), // i.e., `empty` + }, +}); + +(); // number ~> empty + +var AnyArrayExample = React.createClass({ + propTypes: { + any: React.PropTypes.oneOf((0:any)), + }, +}); + +(); // OK + +var AnyElemExample = React.createClass({ + propTypes: { + any: React.PropTypes.oneOf(["foo", (0:any)]), + }, +}); + +(); // OK + +var DynamicArrayExample = React.createClass({ + propTypes: { + dyn: React.PropTypes.oneOf(([]: Array)), + }, +}); + +(); // OK + +var DynamicElemExample = React.createClass({ + propTypes: { + dyn: React.PropTypes.oneOf(["foo", ("": string)]), + }, +}); + +(); // OK + +var InvalidArrayExample = React.createClass({ + propTypes: { + p: React.PropTypes.oneOf(0), // error: expected array, got 0 + }, +}); + +(); // OK, don't cascade errors + +var NonLiteralElemExample = React.createClass({ + propTypes: { + p: React.PropTypes.oneOf([{}]), // OK: allow non-literals + }, +}); +(); // OK, result is unknown/any diff --git a/tests/flow/react/proptype_oneOfType.js b/tests/flow/react/proptype_oneOfType.js index 3fa66d94..78629e4c 100644 --- a/tests/flow/react/proptype_oneOfType.js +++ b/tests/flow/react/proptype_oneOfType.js @@ -20,4 +20,84 @@ var Example = React.createClass({ var ok_number = ; var ok_string = ; -var fail_bool = +var fail_missing = ; +var fail_bool = ; + +/* Since the proptype arguments are not required, React will actually allow + `null` and `undefined` elements in the `prop` prop, but Flow has currently + ignores the innter prop types' required flags. */ +var todo_required = ; + +var OptionalExample = React.createClass({ + propTypes: { + p: React.PropTypes.oneOfType([ + React.PropTypes.string, + ]), + }, +}); + +(); // OK +(); // OK +(); // error: number ~> string + +var EmptyExample = React.createClass({ + propTypes: { + nil: React.PropTypes.oneOfType([]), // i.e., `empty` + }, +}); + +(); // number ~> empty + +var AnyArrayExample = React.createClass({ + propTypes: { + any: React.PropTypes.oneOfType((0:any)), + }, +}); + +(); // OK + +var AnyElemExample = React.createClass({ + propTypes: { + any: React.PropTypes.oneOfType([ + React.PropTypes.string, + (0:any), + ]), + }, +}); + +(); // OK + +var DynamicArrayExample = React.createClass({ + propTypes: { + dyn: React.PropTypes.oneOfType(([]: Array)), + }, +}); + +(); // OK + +var DynamicElemExample = React.createClass({ + propTypes: { + dyn: React.PropTypes.oneOfType([ + React.PropTypes.string, + (() => {}: Function), + ]), + }, +}); + +(); // OK + +var InvalidArrayExample = React.createClass({ + propTypes: { + p: React.PropTypes.oneOfType(0), // error: expected array, got 0 + }, +}); + +(); // OK, don't cascade errors + +var InvalidElemExample = React.createClass({ + propTypes: { + p: React.PropTypes.oneOfType([{}]), // error: expected prop type, got {} + }, +}); + +(); // OK, don't cascade errors diff --git a/tests/flow/react/proptype_shape.js b/tests/flow/react/proptype_shape.js new file mode 100644 index 00000000..0636bba6 --- /dev/null +++ b/tests/flow/react/proptype_shape.js @@ -0,0 +1,35 @@ +/* Shape should be a sealed, inexact object just like a type annotation. The + * below component's `foo` property should be equivalent to `{ bar: string }`, + * which would forbid reads/writes on an unknown `baz` property. + * + * If you see a single "number incompatible with string" error instead of two + * separate "property `baz` not found" errors, this is broken and we are + * treating the shape like an unsealed object and performing shadow read/writes. + */ + +import React from "react"; + +React.createClass({ + propTypes: { + foo: React.PropTypes.shape({ + bar: React.PropTypes.string.isRequired, + }).isRequired, + }, + + f() { + (this.props.foo.baz: string); + }, + + g() { + this.props.foo.baz = 0; + } +}); + +React.createClass({ + propTypes: { + foo: React.PropTypes.shape(({}: {[string]: any})).isRequired, + }, + f() { + (this.props.foo.bar: empty); // OK + }, +}); diff --git a/tests/flow/react/proptypes_builtins.js b/tests/flow/react/proptypes_builtins.js new file mode 100644 index 00000000..08d96a50 --- /dev/null +++ b/tests/flow/react/proptypes_builtins.js @@ -0,0 +1,27 @@ +import React from "react"; + +type NoFun = mixed => empty; + +// error: mixed ~> ReactPropsCheckType +// error: ReactPropsChainableTypeChecker ~> empty +(React.PropTypes.arrayOf : NoFun); + +// OK: mixed ~> any +// error: ReactPropsChainableTypeChecker ~> empty +(React.PropTypes.instanceOf : NoFun); + +// error: mixed ~> ReactPropsCheckType +// error: ReactPropsChainableTypeChecker ~> empty +(React.PropTypes.objectOf : NoFun); + +// error: mixed ~> Array +// error: ReactPropsChainableTypeChecker ~> empty +(React.PropTypes.oneOf : NoFun); + +// error: mixed ~> Array +// error: ReactPropsChainableTypeChecker ~> empty +(React.PropTypes.oneOfType : NoFun); + +// error: mixed ~> object type +// error: ReactPropsChainableTypeChecker ~> empty +(React.PropTypes.shape : NoFun); diff --git a/tests/flow/react/proptypes_sealed.js b/tests/flow/react/proptypes_sealed.js new file mode 100644 index 00000000..950219f3 --- /dev/null +++ b/tests/flow/react/proptypes_sealed.js @@ -0,0 +1,24 @@ +/* propTypes should be a sealed, inexact object just like a type annotation. The + * below component's propTypes should be equivalent to `{ bar: string }`, which + * would forbid reads/writes on an unknown `baz` property. + * + * If you see a single "number incompatible with string" error instead of two + * separate "property `baz` not found" errors, this is broken and we are + * treating propTypes like an unsealed object and performing shadow read/writes. + */ + +import React from "react"; + +React.createClass({ + propTypes: { + foo: React.PropTypes.string.isRequired, + }, + + f() { + (this.props.baz: string); + }, + + g() { + this.props.baz = 0; + } +}); diff --git a/tests/flow/react_modules/__snapshots__/jsfmt.spec.js.snap b/tests/flow/react_modules/__snapshots__/jsfmt.spec.js.snap index de9496c1..3e35ff12 100644 --- a/tests/flow/react_modules/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/react_modules/__snapshots__/jsfmt.spec.js.snap @@ -90,6 +90,98 @@ module.exports = Hello; " `; +exports[`es6class-proptypes-callsite.js 1`] = ` +"/* @flow */ +import React from 'react'; +import Hello from './es6class-proptypes-module'; + +class HelloLocal extends React.Component { + defaultProps = {}; + propTypes = { + name: React.PropTypes.string.isRequired, + }; + render(): React.Element<*> { + return
{this.props.name}
; + } +} + +class Callsite extends React.Component { + render(): React.Element<*> { + return ( +
+ + +
+ ); + } +} + +module.exports = Callsite; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +import React from \\"react\\"; +import Hello from \\"./es6class-proptypes-module\\"; + +class HelloLocal extends React.Component { + defaultProps = {}; + propTypes = { + name: React.PropTypes.string.isRequired + }; + render(): React.Element<*> { + return
{this.props.name}
; + } +} + +class Callsite extends React.Component { + render(): React.Element<*> { + return ( +
+ + +
+ ); + } +} + +module.exports = Callsite; +" +`; + +exports[`es6class-proptypes-module.js 1`] = ` +"/* @flow */ +import React from 'react'; + +class Hello extends React.Component { + defaultProps = {}; + propTypes = { + name: React.PropTypes.string.isRequired, + }; + + render(): React.Element<*> { + return
{this.props.name}
; + } +} + +module.exports = Hello; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* @flow */ +import React from \\"react\\"; + +class Hello extends React.Component { + defaultProps = {}; + propTypes = { + name: React.PropTypes.string.isRequired + }; + + render(): React.Element<*> { + return
{this.props.name}
; + } +} + +module.exports = Hello; +" +`; + exports[`es6class-types-callsite.js 1`] = ` "/* @flow */ import React from 'react'; diff --git a/tests/flow/react_modules/es6class-proptypes-callsite.js b/tests/flow/react_modules/es6class-proptypes-callsite.js new file mode 100644 index 00000000..6a50aa90 --- /dev/null +++ b/tests/flow/react_modules/es6class-proptypes-callsite.js @@ -0,0 +1,26 @@ +/* @flow */ +import React from 'react'; +import Hello from './es6class-proptypes-module'; + +class HelloLocal extends React.Component { + defaultProps = {}; + propTypes = { + name: React.PropTypes.string.isRequired, + }; + render(): React.Element<*> { + return
{this.props.name}
; + } +} + +class Callsite extends React.Component { + render(): React.Element<*> { + return ( +
+ + +
+ ); + } +} + +module.exports = Callsite; diff --git a/tests/flow/react_modules/es6class-proptypes-module.js b/tests/flow/react_modules/es6class-proptypes-module.js new file mode 100644 index 00000000..86e55da1 --- /dev/null +++ b/tests/flow/react_modules/es6class-proptypes-module.js @@ -0,0 +1,15 @@ +/* @flow */ +import React from 'react'; + +class Hello extends React.Component { + defaultProps = {}; + propTypes = { + name: React.PropTypes.string.isRequired, + }; + + render(): React.Element<*> { + return
{this.props.name}
; + } +} + +module.exports = Hello; diff --git a/tests/flow/recheck-haste/B1.js b/tests/flow/recheck-haste/B1.js new file mode 100644 index 00000000..ffe3df2f --- /dev/null +++ b/tests/flow/recheck-haste/B1.js @@ -0,0 +1,6 @@ +/** + * @flow + */ + +require('B2'); +require('B3'); diff --git a/tests/flow/recheck-haste/B3.js b/tests/flow/recheck-haste/B3.js new file mode 100644 index 00000000..4f0b5ad8 --- /dev/null +++ b/tests/flow/recheck-haste/B3.js @@ -0,0 +1,6 @@ +/** + * @providesModule B3 + * @flow + */ + +require('B2'); diff --git a/tests/flow/recheck-haste/__snapshots__/jsfmt.spec.js.snap b/tests/flow/recheck-haste/__snapshots__/jsfmt.spec.js.snap index cc86c4f5..ad48c9c4 100644 --- a/tests/flow/recheck-haste/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/recheck-haste/__snapshots__/jsfmt.spec.js.snap @@ -23,3 +23,37 @@ require('A'); require(\\"A\\"); " `; + +exports[`B1.js 1`] = ` +"/** + * @flow + */ + +require('B2'); +require('B3'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @flow + */ + +require(\\"B2\\"); +require(\\"B3\\"); +" +`; + +exports[`B3.js 1`] = ` +"/** + * @providesModule B3 + * @flow + */ + +require('B2'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule B3 + * @flow + */ + +require(\\"B2\\"); +" +`; diff --git a/tests/flow/recheck-haste/dir1B/B2.js b/tests/flow/recheck-haste/dir1B/B2.js new file mode 100644 index 00000000..f12b7326 --- /dev/null +++ b/tests/flow/recheck-haste/dir1B/B2.js @@ -0,0 +1,6 @@ +/** + * @providesModule B2 + * @flow + */ + +require('B3'); diff --git a/tests/flow/recheck-haste/dir1B/__snapshots__/jsfmt.spec.js.snap b/tests/flow/recheck-haste/dir1B/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..97784a4a --- /dev/null +++ b/tests/flow/recheck-haste/dir1B/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`B2.js 1`] = ` +"/** + * @providesModule B2 + * @flow + */ + +require('B3'); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @providesModule B2 + * @flow + */ + +require(\\"B3\\"); +" +`; diff --git a/tests/flow/recheck-haste/dir1B/jsfmt.spec.js b/tests/flow/recheck-haste/dir1B/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/recheck-haste/dir1B/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/recheck/__snapshots__/jsfmt.spec.js.snap b/tests/flow/recheck/__snapshots__/jsfmt.spec.js.snap index 03e96024..3e2a6c89 100644 --- a/tests/flow/recheck/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/recheck/__snapshots__/jsfmt.spec.js.snap @@ -349,3 +349,63 @@ import type { Foo } from './h1'; import type { Foo } from \\"./h1\\"; " `; + +exports[`i1.js 1`] = ` +"// @flow + +const foo: { p: number } = { p: 0 }; + +module.exports = foo; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +const foo: { p: number } = { p: 0 }; + +module.exports = foo; +" +`; + +exports[`i2.js 1`] = ` +"// @flow + +const foo = require('./i1'); + +foo.p = 0; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +const foo = require(\\"./i1\\"); + +foo.p = 0; +" +`; + +exports[`j1.js 1`] = ` +"// @flow + +const foo: { [string]: number } = {}; + +module.exports = foo; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +const foo: { [string]: number } = {}; + +module.exports = foo; +" +`; + +exports[`j2.js 1`] = ` +"// @flow + +const foo = require('./j1'); + +foo.p = 0; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +const foo = require(\\"./j1\\"); + +foo.p = 0; +" +`; diff --git a/tests/flow/recheck/i1.js b/tests/flow/recheck/i1.js new file mode 100644 index 00000000..c3eea824 --- /dev/null +++ b/tests/flow/recheck/i1.js @@ -0,0 +1,5 @@ +// @flow + +const foo: { p: number } = { p: 0 }; + +module.exports = foo; diff --git a/tests/flow/recheck/i2.js b/tests/flow/recheck/i2.js new file mode 100644 index 00000000..2b3825df --- /dev/null +++ b/tests/flow/recheck/i2.js @@ -0,0 +1,5 @@ +// @flow + +const foo = require('./i1'); + +foo.p = 0; diff --git a/tests/flow/recheck/j1.js b/tests/flow/recheck/j1.js new file mode 100644 index 00000000..aee12f01 --- /dev/null +++ b/tests/flow/recheck/j1.js @@ -0,0 +1,5 @@ +// @flow + +const foo: { [string]: number } = {}; + +module.exports = foo; diff --git a/tests/flow/recheck/j2.js b/tests/flow/recheck/j2.js new file mode 100644 index 00000000..b58f337a --- /dev/null +++ b/tests/flow/recheck/j2.js @@ -0,0 +1,5 @@ +// @flow + +const foo = require('./j1'); + +foo.p = 0; diff --git a/tests/flow/recheck/tmp1i/__snapshots__/jsfmt.spec.js.snap b/tests/flow/recheck/tmp1i/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..dbbf8c37 --- /dev/null +++ b/tests/flow/recheck/tmp1i/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,16 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`i1.js 1`] = ` +"// @flow + +const foo: { +p: number } = { p: 0 }; + +module.exports = foo; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +const foo: { +p: number } = { p: 0 }; + +module.exports = foo; +" +`; diff --git a/tests/flow/recheck/tmp1i/i1.js b/tests/flow/recheck/tmp1i/i1.js new file mode 100644 index 00000000..4fe468cd --- /dev/null +++ b/tests/flow/recheck/tmp1i/i1.js @@ -0,0 +1,5 @@ +// @flow + +const foo: { +p: number } = { p: 0 }; + +module.exports = foo; diff --git a/tests/flow/recheck/tmp1i/jsfmt.spec.js b/tests/flow/recheck/tmp1i/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/recheck/tmp1i/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/recheck/tmp1j/__snapshots__/jsfmt.spec.js.snap b/tests/flow/recheck/tmp1j/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..bd75dd96 --- /dev/null +++ b/tests/flow/recheck/tmp1j/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,16 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`j1.js 1`] = ` +"// @flow + +const foo: { +[string]: number } = {}; + +module.exports = foo; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// @flow + +const foo: { +[string]: number } = {}; + +module.exports = foo; +" +`; diff --git a/tests/flow/recheck/tmp1j/j1.js b/tests/flow/recheck/tmp1j/j1.js new file mode 100644 index 00000000..852cdbac --- /dev/null +++ b/tests/flow/recheck/tmp1j/j1.js @@ -0,0 +1,5 @@ +// @flow + +const foo: { +[string]: number } = {}; + +module.exports = foo; diff --git a/tests/flow/recheck/tmp1j/jsfmt.spec.js b/tests/flow/recheck/tmp1j/jsfmt.spec.js new file mode 100644 index 00000000..989047bc --- /dev/null +++ b/tests/flow/recheck/tmp1j/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname); diff --git a/tests/flow/refinements/__snapshots__/jsfmt.spec.js.snap b/tests/flow/refinements/__snapshots__/jsfmt.spec.js.snap index 76d1192e..368090f0 100644 --- a/tests/flow/refinements/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/refinements/__snapshots__/jsfmt.spec.js.snap @@ -760,6 +760,18 @@ function foo8(o: { p: mixed }) { if (o.p && o.p.q) {} // this is ok because o.p is truthy, so o.p.q is safe if (o.p && o.p.q && o.p.q.r) {} } + +type Foo9Expected = { + foo: string, +} + +function foo9() { + const actual = {}; + if (actual.foo === undefined) { + actual.foo = 'foo'; + } + (actual: Foo9Expected); +} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // @flow @@ -823,6 +835,18 @@ function foo8(o: { p: mixed }) { if (o.p && o.p.q && o.p.q.r) { } } + +type Foo9Expected = { + foo: string +}; + +function foo9() { + const actual = {}; + if (actual.foo === undefined) { + actual.foo = \\"foo\\"; + } + (actual: Foo9Expected); +} " `; @@ -2468,7 +2492,43 @@ let tests = [ x.y; // error: flow isn't smart enough to figure this out yet x.z; // error } - } + }, + + // null + function(x: { foo: null, y: string } | { foo: 'foo', z: string }) { + if (x.foo === null) { + (x.y: string); + x.z; // error + } else { + (x.z: string); + x.y; // error + } + if (x.foo === 'foo') { + (x.z: string); + x.y; // error + } else { + (x.y: string); + x.z; // error + } + }, + + // void + function(x: { foo: void, y: string } | { foo: 'foo', z: string }) { + if (x.foo === undefined) { + (x.y: string); + x.z; // error + } else { + (x.z: string); + x.y; // error + } + if (x.foo === 'foo') { + (x.z: string); + x.y; // error + } else { + (x.y: string); + x.z; // error + } + }, ]; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // example 1 @@ -2729,6 +2789,42 @@ let tests = [ x.y; // error: flow isn't smart enough to figure this out yet x.z; // error } + }, + + // null + function(x: { foo: null, y: string } | { foo: \\"foo\\", z: string }) { + if (x.foo === null) { + (x.y: string); + x.z; // error + } else { + (x.z: string); + x.y; // error + } + if (x.foo === \\"foo\\") { + (x.z: string); + x.y; // error + } else { + (x.y: string); + x.z; // error + } + }, + + // void + function(x: { foo: void, y: string } | { foo: \\"foo\\", z: string }) { + if (x.foo === undefined) { + (x.y: string); + x.z; // error + } else { + (x.z: string); + x.y; // error + } + if (x.foo === \\"foo\\") { + (x.z: string); + x.y; // error + } else { + (x.y: string); + x.z; // error + } } ]; " diff --git a/tests/flow/refinements/missing-property-cond.js b/tests/flow/refinements/missing-property-cond.js index 944bd0f9..fdbbc1f6 100644 --- a/tests/flow/refinements/missing-property-cond.js +++ b/tests/flow/refinements/missing-property-cond.js @@ -48,3 +48,15 @@ function foo8(o: { p: mixed }) { if (o.p && o.p.q) {} // this is ok because o.p is truthy, so o.p.q is safe if (o.p && o.p.q && o.p.q.r) {} } + +type Foo9Expected = { + foo: string, +} + +function foo9() { + const actual = {}; + if (actual.foo === undefined) { + actual.foo = 'foo'; + } + (actual: Foo9Expected); +} diff --git a/tests/flow/refinements/tagged_union.js b/tests/flow/refinements/tagged_union.js index 19c5c185..9ec0d553 100644 --- a/tests/flow/refinements/tagged_union.js +++ b/tests/flow/refinements/tagged_union.js @@ -228,5 +228,41 @@ let tests = [ x.y; // error: flow isn't smart enough to figure this out yet x.z; // error } - } + }, + + // null + function(x: { foo: null, y: string } | { foo: 'foo', z: string }) { + if (x.foo === null) { + (x.y: string); + x.z; // error + } else { + (x.z: string); + x.y; // error + } + if (x.foo === 'foo') { + (x.z: string); + x.y; // error + } else { + (x.y: string); + x.z; // error + } + }, + + // void + function(x: { foo: void, y: string } | { foo: 'foo', z: string }) { + if (x.foo === undefined) { + (x.y: string); + x.z; // error + } else { + (x.z: string); + x.y; // error + } + if (x.foo === 'foo') { + (x.z: string); + x.y; // error + } else { + (x.y: string); + x.z; // error + } + }, ]; diff --git a/tests/flow/return_new/__snapshots__/jsfmt.spec.js.snap b/tests/flow/return_new/__snapshots__/jsfmt.spec.js.snap index de09bf61..e6005991 100644 --- a/tests/flow/return_new/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/return_new/__snapshots__/jsfmt.spec.js.snap @@ -13,6 +13,17 @@ var qux: number = new Qux(); // error (returns new object) class A { } function B() { return new A(); } var a: A = new B(); // OK (returns new A) + +// type applications should be applied before deciding if object-like +type C = { x: T }; +function makeC(x: T): C { return {x}; } +(new makeC('x'): C); // normally you wouldn't use \`new\`, but you can + +// unions should be split before deciding if object-like +function makeUnion(): number | {x: string} { + return {x: 'x'}; +} +(new makeUnion(): {x: string}); // error: \`number\` returns {}, missing prop x ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ function Foo() { return {}; @@ -32,6 +43,19 @@ function B() { return new A(); } var a: A = new B(); // OK (returns new A) + +// type applications should be applied before deciding if object-like +type C = { x: T }; +function makeC(x: T): C { + return { x }; +} +(new makeC(\\"x\\"): C); // normally you wouldn't use \`new\`, but you can + +// unions should be split before deciding if object-like +function makeUnion(): number | { x: string } { + return { x: \\"x\\" }; +} +(new makeUnion(): { x: string }); // error: \`number\` returns {}, missing prop x " `; diff --git a/tests/flow/return_new/test.js b/tests/flow/return_new/test.js index ba0f36f8..f9654864 100644 --- a/tests/flow/return_new/test.js +++ b/tests/flow/return_new/test.js @@ -10,3 +10,14 @@ var qux: number = new Qux(); // error (returns new object) class A { } function B() { return new A(); } var a: A = new B(); // OK (returns new A) + +// type applications should be applied before deciding if object-like +type C = { x: T }; +function makeC(x: T): C { return {x}; } +(new makeC('x'): C); // normally you wouldn't use `new`, but you can + +// unions should be split before deciding if object-like +function makeUnion(): number | {x: string} { + return {x: 'x'}; +} +(new makeUnion(): {x: string}); // error: `number` returns {}, missing prop x diff --git a/tests/flow/shape/__snapshots__/jsfmt.spec.js.snap b/tests/flow/shape/__snapshots__/jsfmt.spec.js.snap index 3931e96c..b727c503 100644 --- a/tests/flow/shape/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/shape/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,16 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`shadow.js 1`] = ` +"var o = {}; +(o.p: string); +(o: $Shape<{p:string}>); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +var o = {}; +(o.p: string); +(o: $Shape<{ p: string }>); +" +`; + exports[`test.js 1`] = ` "type Foo = { field: number, diff --git a/tests/flow/shape/shadow.js b/tests/flow/shape/shadow.js new file mode 100644 index 00000000..dc178a1c --- /dev/null +++ b/tests/flow/shape/shadow.js @@ -0,0 +1,3 @@ +var o = {}; +(o.p: string); +(o: $Shape<{p:string}>); diff --git a/tests/flow/suppress/D.js b/tests/flow/suppress/D.js new file mode 100644 index 00000000..bd5a7bf1 --- /dev/null +++ b/tests/flow/suppress/D.js @@ -0,0 +1,8 @@ +declare var x: { + x: { foo: string } +}; +declare var y: { + // $FlowFixMe - this location is only mentioned in the extra section + x: { bar: number } +}; +x = y; diff --git a/tests/flow/suppress/__snapshots__/jsfmt.spec.js.snap b/tests/flow/suppress/__snapshots__/jsfmt.spec.js.snap index 62a5e182..003dc2d7 100644 --- a/tests/flow/suppress/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/suppress/__snapshots__/jsfmt.spec.js.snap @@ -83,6 +83,27 @@ function runTest(y: number): void { " `; +exports[`D.js 1`] = ` +"declare var x: { + x: { foo: string } +}; +declare var y: { + // $FlowFixMe - this location is only mentioned in the extra section + x: { bar: number } +}; +x = y; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +declare var x: { + x: { foo: string } +}; +declare var y: { + // $FlowFixMe - this location is only mentioned in the extra section + x: { bar: number } +}; +x = y; +" +`; + exports[`lib.js 1`] = ` "declare var library_num: number; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/flow/type-at-pos/__snapshots__/jsfmt.spec.js.snap b/tests/flow/type-at-pos/__snapshots__/jsfmt.spec.js.snap index 84dd2b20..ae361d06 100644 --- a/tests/flow/type-at-pos/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/type-at-pos/__snapshots__/jsfmt.spec.js.snap @@ -51,9 +51,21 @@ exports[`function_expressions.js 1`] = ` // foo(): void {} // } -const y = { +const a = { bar(): void {} }; + +const b = { + bar: function (): void {} +}; + +const c = { + m(x: T): T { return x; } +}; + +const d = { + m: function(x: T): T { return x; } +}; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // @flow @@ -62,9 +74,25 @@ const y = { // foo(): void {} // } -const y = { +const a = { bar(): void {} }; + +const b = { + bar: function(): void {} +}; + +const c = { + m(x: T): T { + return x; + } +}; + +const d = { + m: function(x: T): T { + return x; + } +}; " `; diff --git a/tests/flow/type-at-pos/function_expressions.js b/tests/flow/type-at-pos/function_expressions.js index 81df4579..5b4dfeef 100644 --- a/tests/flow/type-at-pos/function_expressions.js +++ b/tests/flow/type-at-pos/function_expressions.js @@ -5,6 +5,18 @@ // foo(): void {} // } -const y = { +const a = { bar(): void {} }; + +const b = { + bar: function (): void {} +}; + +const c = { + m(x: T): T { return x; } +}; + +const d = { + m: function(x: T): T { return x; } +}; diff --git a/tests/flow/unicode/__snapshots__/jsfmt.spec.js.snap b/tests/flow/unicode/__snapshots__/jsfmt.spec.js.snap index c1f814f2..4ed4c3db 100644 --- a/tests/flow/unicode/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/unicode/__snapshots__/jsfmt.spec.js.snap @@ -71,10 +71,3 @@ function utf16Length(str, pos) { } " `; - -exports[`keys.js 1`] = ` -"({'この事はつもり素晴らしいことさ': '35jL9V'}) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -({ \\"この事はつもり素晴らしいことさ\\": \\"35jL9V\\" }); -" -`; diff --git a/tests/flow/unicode/keys.js b/tests/flow/unicode/keys.js deleted file mode 100644 index 173e6c79..00000000 --- a/tests/flow/unicode/keys.js +++ /dev/null @@ -1 +0,0 @@ -({'この事はつもり素晴らしいことさ': '35jL9V'}) diff --git a/tests/flow/union/__snapshots__/jsfmt.spec.js.snap b/tests/flow/union/__snapshots__/jsfmt.spec.js.snap index a3621719..ddf5b453 100644 --- a/tests/flow/union/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/union/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,93 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`blowup.js 1`] = ` +"// @flow + +export type Select = { + expression: ArithmeticExpression; + alias: ?string; +} + +export class Query { + _select: Array = []; + + select(expr: ArithmeticExpression): this { + this._select.push({ + expression: expr, + alias: \\"\\" + }); + return this; + } +} + +export class BinaryExpression< + T: ArithmeticExpression, + U: ArithmeticExpression +> {} + +export type ArithmeticExpression = PlusOp | MinusOp | MulOp | DivOp | ModOp; + +export class PlusOp + extends BinaryExpression {} + +export class MinusOp + extends BinaryExpression {} + +export class MulOp + extends BinaryExpression {} + +export class DivOp + extends BinaryExpression {} + +export class ModOp + extends BinaryExpression {} +" +`; + exports[`fields.js 1`] = ` "class C { x: ?number|string; diff --git a/tests/flow/union/blowup.js b/tests/flow/union/blowup.js new file mode 100644 index 00000000..a480219d --- /dev/null +++ b/tests/flow/union/blowup.js @@ -0,0 +1,43 @@ +// @flow + +export type Select = { + expression: ArithmeticExpression; + alias: ?string; +} + +export class Query { + _select: Array