Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

flow shadow-file #2184

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ MODULES=\
src/server\
src/services/autocomplete\
src/services/inference\
src/services/interfaceGenerator\
src/services/port\
src/stubs\
src/typing\
Expand Down
14 changes: 7 additions & 7 deletions hack/utils/tty.ml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ let style_num = function
| NormalWithBG (text, bg) -> (text_num text) ^ ";" ^ (background_num bg)
| BoldWithBG (text, bg) -> (text_num text) ^ ";" ^ (background_num bg) ^ ";1"

let print_one ?(color_mode=Color_Auto) c s =
let print_one ?(color_mode=Color_Auto) ?(out_channel=stdout) c s =
let should_color = match color_mode with
| Color_Always -> true
| Color_Never -> false
Expand All @@ -84,14 +84,14 @@ let print_one ?(color_mode=Color_Auto) c s =
Unix.isatty Unix.stdout && term <> "dumb"
end in
if should_color
then Printf.printf "\x1b[%sm%s\x1b[0m" (style_num c) (s)
else Printf.printf "%s" s
then Printf.fprintf out_channel "\x1b[%sm%s\x1b[0m" (style_num c) (s)
else Printf.fprintf out_channel "%s" s

let cprint ?(color_mode=Color_Auto) strs =
List.iter strs (fun (c, s) -> print_one ~color_mode c s)
let cprint ?(color_mode=Color_Auto) ?(out_channel=stdout) strs =
List.iter strs (fun (c, s) -> print_one ~color_mode ~out_channel c s)

let cprintf ?(color_mode=Color_Auto) c =
Printf.ksprintf (print_one ~color_mode c)
let cprintf ?(color_mode=Color_Auto) ?(out_channel=stdout) c =
Printf.ksprintf (print_one ~color_mode ~out_channel c)

let (spinner, spinner_used) =
let state = ref 0 in
Expand Down
8 changes: 4 additions & 4 deletions hack/utils/tty.mli
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ type color_mode =
| Color_Auto

(*
* Print a sequence of colorized strings to stdout, using ANSI color escapes
* codes.
* Print a sequence of colorized strings to stdout/stderr, using ANSI color
* escapes codes.
*)
val cprint : ?color_mode:color_mode -> (style * string) list -> unit
val cprintf : ?color_mode:color_mode -> style ->
val cprint : ?color_mode:color_mode -> ?out_channel:out_channel -> (style * string) list -> unit
val cprintf : ?color_mode:color_mode -> ?out_channel:out_channel -> style ->
('a, unit, string, unit) format4 -> 'a

(* These two functions provide a four-state TTY-friendly spinner that
Expand Down
7 changes: 7 additions & 0 deletions newtests/shadow_file_command/_flowconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[ignore]

[include]

[libs]

[options]
4 changes: 4 additions & 0 deletions newtests/shadow_file_command/function_exports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @flow

export function mono(a: number, b: {c: number}) { return a + b.c; };
export function poly<T: number, U: T, V: U> (a: V) { return a; }
21 changes: 21 additions & 0 deletions newtests/shadow_file_command/named_class_exports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// @flow

export class Base<A, B, C> {
// Testing infinite type recursion
baseInst: Base<number, string, mixed>;

// Testing forward references
childInst: Child<string, number>;

baseMethod(a: number, b: string) { return a; }
overriddenMethod(a: {b: number, c: number}) { return a.b + a.c; }
};

export class Child<A, B> extends Base<A, B, mixed> {
notExported: NotExportedUsed;

overriddenMethod(a: {b: number}) { return a.b; }
}

class NotExportedUsed {}
class NotExportedNotUsed {}
13 changes: 13 additions & 0 deletions newtests/shadow_file_command/named_variable_exports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// @flow

export const constExport = 42;
export let letExport = 43;
export var varExport = 44;

export type typeExport = number;

type UnexportedT = string;
export const unexportedAlias = ((0: any): UnexportedT);

class C {}
export const unexportedNominal = ((0: any): C);
1 change: 1 addition & 0 deletions newtests/shadow_file_command/non_flow_file.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export function addNum(a: number, b: number) { return a + b; }
14 changes: 14 additions & 0 deletions newtests/shadow_file_command/t.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
declare class Class0 {
}
declare export class Base<A, B, C> {
baseInst: Base<number, string, mixed>;
childInst: Child<string, number>;

baseMethod(a: number, b: string): number;
}

declare export class Child<A, B> extends Base<A, B, mixed> {
notExported: Class0;
}


69 changes: 69 additions & 0 deletions newtests/shadow_file_command/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/* @flow */


import {suite, test} from '../../tsrc/test/Tester';

export default suite(({addFile, flowCmd}) => [
test('class exports', [
addFile('named_class_exports.js'),
flowCmd(['shadow-file', 'named_class_exports.js']).stdout(`
declare class Class0 {
}
declare export class Base<A, B, C> {
baseInst: Base<number, string, mixed>;
childInst: Child<string, number>;

baseMethod(a: number, b: string): number;
overriddenMethod(a: {b: number, c: number}): number;
}
declare export class Child<A, B> extends Base<A, B, mixed> {
notExported: Class0;

overriddenMethod(a: {b: number}): number;
}
`)
.stderr('')
]),

test('named variable exports', [
addFile('named_variable_exports.js'),
flowCmd(['shadow-file', 'named_variable_exports.js']).stderr('').stdout(`
declare class Class0 {
}
declare export var constExport: 42;
declare export var letExport: 43;
export type typeExport = number;
declare export var unexportedAlias: string;
declare export var unexportedNominal: Class0;
declare export var varExport: 44;
`)
]),

test('function exports', [
addFile('function_exports.js'),
flowCmd(['shadow-file', 'function_exports.js']).stderr('').stdout(`
declare export function mono(a: number, b: {c: number}): number;
declare export function poly<T: number, U: T, V: U>(a: V): number;
`)
]),

test('non-@flow files', [
addFile('non_flow_file.js'),
flowCmd(['shadow-file', 'non_flow_file.js']).stderr('').stdout(`
declare export function addNum(a: number, b: number): number;
`)
]),

test('type errors halt and stderr', [
addFile('type_error.js'),
flowCmd(['shadow-file', 'type_error.js']).stdout('').stderr(`
type_error.js:3
3: export var a: string = 42;
^^ number. This type is incompatible with
3: export var a: string = 42;
^^^^^^ string
Found 1 error
There must be no type errors in order to generate a shadow file!
`)
]),
]);
3 changes: 3 additions & 0 deletions newtests/shadow_file_command/type_error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// @flow

export var a: string = 42;
11 changes: 11 additions & 0 deletions ocp_build_flow.ocp.fb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ begin library "flow-commands"
"flow-server-env"
"flow-services-autocomplete"
"flow-services-inference"
"flow-services-interfaceGenerator"
"flow-typing"
"hh-find"
"hh-heap"
Expand Down Expand Up @@ -231,6 +232,16 @@ begin library "flow-services-inference"
]
end

begin library "flow-services-interfaceGenerator"
sort = true
files = begin fb-glob "src/services/interfaceGenerator"
end
requires = [
"flow-typing"
"hh-utils-collections"
]
end

begin library "flow-services-port"
sort = true
files = begin fb-glob "src/services/port"
Expand Down
3 changes: 2 additions & 1 deletion src/commands/commandUtils.ml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ let expand_path file =
if Path.file_exists path
then Path.to_string path
else begin
FlowExitStatus.(exit ~msg:"File not found" Input_error)
let msg = Printf.sprintf "File not found: %s" (Path.to_string path) in
FlowExitStatus.(exit ~msg Input_error)
end

(* line split/transform utils *)
Expand Down
76 changes: 76 additions & 0 deletions src/commands/shadowFileCommand.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
(**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the "flow" directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*)

open CommandUtils
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing copyright header


let spf = Printf.sprintf

let name = "shadow-file"
let spec = {
CommandSpec.
name;
doc = "Given a filename, generate a shadow (.js.flow) file.";
usage = Printf.sprintf
"Usage: %s %s [OPTIONS] [FILE] [FILE] [FILE]...\n\n\
e.g. %s %s foo.js > foo.js.flow\n"
CommandUtils.exe_name
name
CommandUtils.exe_name
name
;
args = CommandSpec.ArgSpec.(
empty
|> server_flags
|> root_flag
|> error_flags
|> strip_root_flag
|> anon "file" (required string)
~doc:"The file for which a shadow file should be generated"
)
}

let main option_values root error_flags strip_root file () = (
let root = guess_root (
match root with
| Some root -> Some root
| None -> Some (expand_path file)
) in
let filename = ServerProt.FileName (expand_path file) in
let (in_chan, out_chan) = connect option_values root in
ServerProt.cmd_to_channel out_chan (ServerProt.GEN_INTERFACES [filename]);
let response =
(Timeout.input_value in_chan: ServerProt.gen_interface_response)
in

match response with
| response::[] -> (
match response with
| Utils_js.Err (ServerProt.GenIface_TypecheckError (_, errors)) ->
let errors = Errors.to_list errors in
Errors.print_error_summary ~out_channel:stderr ~flags:error_flags ~strip_root ~root errors;
FlowExitStatus.exit
~msg:"\nThere must be no type errors in order to generate a shadow file!"
FlowExitStatus.Type_error;
| Utils_js.Err (ServerProt.GenIface_UnexpectedError (file_path, error)) ->
FlowExitStatus.exit
~msg:(spf "Error: %s: %s" file_path error)
FlowExitStatus.Unknown_error
| Utils_js.OK (_file, interface) ->
print_endline interface
)
| response ->
let msg = spf (
"Internal Error: Expected a single interface description from the " ^^
"server, but received %d interfaces!"
) (List.length response) in
prerr_endline msg
)

let command = CommandSpec.command spec main
30 changes: 18 additions & 12 deletions src/common/errors.ml
Original file line number Diff line number Diff line change
Expand Up @@ -238,19 +238,19 @@ let format_info info =
let formatted = format_reason_color msg in
String.concat "" (List.map snd formatted)

let print_reason_color ~first ~one_line ~color (message: message) =
let print_reason_color ?(out_channel=stdout) ~first ~one_line ~color (message: message) =
let to_print = format_reason_color ~first ~one_line message in
(if first then Printf.printf "\n");
Tty.cprint ~color_mode:color to_print
Tty.cprint ~color_mode:color ~out_channel to_print

let print_error_color_old ~one_line ~color (e : error) =
let print_error_color_old ?(out_channel=stdout) ~one_line ~color (e : error) =
let { kind; messages; op; trace; extra } = e in
let messages = prepend_kind_message messages kind in
let messages = prepend_op_reason messages op in
let messages = append_extra_info messages extra in
let messages = append_trace_reasons messages trace in
print_reason_color ~first:true ~one_line ~color (List.hd messages);
List.iter (print_reason_color ~first:false ~one_line ~color) (List.tl messages)
print_reason_color ~out_channel ~first:true ~one_line ~color (List.hd messages);
List.iter (print_reason_color ~out_channel ~first:false ~one_line ~color) (List.tl messages)

let file_location_style text = (Tty.Underline Tty.Default, text)
let default_style text = (Tty.Normal Tty.Default, text)
Expand Down Expand Up @@ -531,10 +531,10 @@ let get_pretty_printed_error_new ~stdin_file:stdin_file ~strip_root ~one_line ~r
else to_print in
(to_print @ [default_style "\n"])

let print_error_color_new ~stdin_file:stdin_file ~strip_root ~one_line ~color ~root (error : error) =
let print_error_color_new ?(out_channel=stdout) ~stdin_file:stdin_file ~strip_root ~one_line ~color ~root (error : error) =
let to_print =
get_pretty_printed_error_new ~stdin_file ~strip_root ~one_line ~root error in
Tty.cprint ~color_mode:color to_print
Tty.cprint ~out_channel ~color_mode:color to_print

(* TODO: deprecate this in favor of Reason.json_of_loc *)
let deprecated_json_props_of_loc loc = Loc.(
Expand Down Expand Up @@ -848,7 +848,7 @@ let print_error_deprecated =
flush oc

(* Human readable output *)
let print_error_summary ~flags ?(stdin_file=None) ~strip_root ~root errors =
let print_error_summary ?(out_channel=stdout) ~flags ?(stdin_file=None) ~strip_root ~root errors =
let error_or_errors n = if n != 1 then "errors" else "error" in
let truncate = not (flags.Options.show_all_errors) in
let one_line = flags.Options.one_line in
Expand All @@ -858,17 +858,23 @@ let print_error_summary ~flags ?(stdin_file=None) ~strip_root ~root errors =
else print_error_color_new ~stdin_file ~strip_root ~root
in
let print_error_if_not_truncated curr e =
(if not(truncate) || curr < 50 then print_error_color ~one_line ~color e);
if not(truncate) || curr < 50
then print_error_color ~one_line ~color ~out_channel e;

curr + 1
in
let total =
List.fold_left print_error_if_not_truncated 0 errors
in
if total > 0 then print_newline ();
if truncate && total > 50 then (
Printf.printf
Printf.fprintf
out_channel
"... %d more %s (only 50 out of %d errors displayed)\n"
(total - 50) (error_or_errors (total - 50)) total;
print_endline "To see all errors, re-run Flow with --show-all-errors"
Printf.fprintf
out_channel
"To see all errors, re-run Flow with --show-all-errors";
flush out_channel
) else
Printf.printf "Found %d %s\n" total (error_or_errors total)
Printf.fprintf out_channel "Found %d %s\n" total (error_or_errors total)
2 changes: 2 additions & 0 deletions src/common/errors.mli
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ val json_of_errors_with_context :
Hh_json.json

val print_error_color_new:
?out_channel:out_channel ->
stdin_file:stdin_file ->
strip_root:bool ->
one_line:bool ->
Expand All @@ -100,6 +101,7 @@ val print_error_json :

(* Human readable output *)
val print_error_summary:
?out_channel:out_channel ->
flags:Options.error_flags ->
?stdin_file:stdin_file ->
strip_root: bool ->
Expand Down
Loading