Skip to content

Commit

Permalink
flow shadow-file
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffmo committed Aug 3, 2016
1 parent 858ac40 commit f285c07
Show file tree
Hide file tree
Showing 26 changed files with 853 additions and 24 deletions.
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
15 changes: 8 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) ?(stderr=false) c s =
let should_color = match color_mode with
| Color_Always -> true
| Color_Never -> false
Expand All @@ -83,15 +83,16 @@ let print_one ?(color_mode=Color_Auto) c s =
| Some term ->
Unix.isatty Unix.stdout && term <> "dumb"
end in
let printf = if stderr then Printf.eprintf else Printf.printf in
if should_color
then Printf.printf "\x1b[%sm%s\x1b[0m" (style_num c) (s)
else Printf.printf "%s" s
then printf "\x1b[%sm%s\x1b[0m" (style_num c) (s)
else printf "%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) ?(stderr=false) strs =
List.iter strs (fun (c, s) -> print_one ~color_mode ~stderr c s)

let cprintf ?(color_mode=Color_Auto) c =
Printf.ksprintf (print_one ~color_mode c)
let cprintf ?(color_mode=Color_Auto) ?(stderr=false) c =
Printf.ksprintf (print_one ~color_mode ~stderr c)

let (spinner, spinner_used) =
let state = ref 0 in
Expand Down
4 changes: 2 additions & 2 deletions hack/utils/tty.mli
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ type color_mode =
* Print a sequence of colorized strings to stdout, 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 -> ?stderr:bool -> (style * string) list -> unit
val cprintf : ?color_mode:color_mode -> ?stderr:bool -> 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;
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
66 changes: 66 additions & 0 deletions src/commands/shadowFileCommand.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
open CommandUtils

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 ~stderr:true ~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
29 changes: 17 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 ?(stderr=false) ~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 ~stderr to_print

let print_error_color_old ~one_line ~color (e : error) =
let print_error_color_old ?(stderr=false) ~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 ~stderr ~first:true ~one_line ~color (List.hd messages);
List.iter (print_reason_color ~stderr ~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 ?(stderr=false) ~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 ~stderr ~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 ?(stderr=false) ~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,22 @@ 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 ~stderr e);
curr + 1
in
let total =
List.fold_left print_error_if_not_truncated 0 errors
in
if total > 0 then print_newline ();
let (printf, p_endline) =
if stderr
then (Printf.eprintf, prerr_endline)
else (Printf.printf, print_endline)
in
if truncate && total > 50 then (
Printf.printf
printf
"... %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"
p_endline "To see all errors, re-run Flow with --show-all-errors"
) else
Printf.printf "Found %d %s\n" total (error_or_errors total)
printf "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:
?stderr:bool ->
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:
?stderr:bool ->
flags:Options.error_flags ->
?stdin_file:stdin_file ->
strip_root: bool ->
Expand Down
1 change: 1 addition & 0 deletions src/flow.ml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ end = struct
CoverageCommand.command;
FindModuleCommand.command;
ForceRecheckCommand.command;
ShadowFileCommand.command;
GetDefCommand.command;
GetImportersCommand.command;
GetImportsCommand.command;
Expand Down
Loading

0 comments on commit f285c07

Please sign in to comment.