Skip to content

Commit 561ab61

Browse files
arthaudfacebook-github-bot
authored andcommitted
Return a result type in Parser.parse
Summary: This function use to throw an exception on parse errors. To force error handling at the call site, let's return a result type. This will be used in a follow-up to convert parse error in pysa models into normal validation errors, which is necessary for editor plugins. Reviewed By: grievejia Differential Revision: D28948404 fbshipit-source-id: 32b1d6d16c02d39e98ca25c17aa2aa3beabe1bbb
1 parent 3c4ab6b commit 561ab61

File tree

9 files changed

+106
-81
lines changed

9 files changed

+106
-81
lines changed

source/analysis/astEnvironment.ml

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,9 @@ type parse_result =
117117
let parse_source ~configuration ({ SourcePath.relative; qualifier; _ } as source_path) =
118118
let parse_lines lines =
119119
let metadata = Source.Metadata.parse ~qualifier lines in
120-
try
121-
let statements = Parser.parse ~relative lines in
122-
Success (Source.create_from_source_path ~metadata ~source_path statements)
123-
with
124-
| Parser.Error error
125-
| Failure error ->
120+
match Parser.parse ~relative lines with
121+
| Ok statements -> Success (Source.create_from_source_path ~metadata ~source_path statements)
122+
| Error error ->
126123
let is_suppressed =
127124
let { Source.Metadata.local_mode; ignore_codes; _ } = metadata in
128125
match Source.mode ~configuration ~local_mode with
@@ -131,7 +128,7 @@ let parse_source ~configuration ({ SourcePath.relative; qualifier; _ } as source
131128
(* NOTE: The number needs to be updated when the error code changes. *)
132129
List.exists ignore_codes ~f:(Int.equal 404)
133130
in
134-
Error { message = error; is_suppressed }
131+
Error { message = Parser.Error.show error; is_suppressed }
135132
in
136133
let path = SourcePath.full_path ~configuration source_path in
137134
try File.lines_exn (File.create path) |> parse_lines with
@@ -282,7 +279,7 @@ let get_and_preprocess_source
282279
(* Files that have parser errors fall back into getattr-any. *)
283280
let fallback_source = ["import typing"; "def __getattr__(name: str) -> typing.Any: ..."] in
284281
let metadata = Source.Metadata.parse ~qualifier fallback_source in
285-
let statements = Parser.parse ~relative fallback_source in
282+
let statements = Parser.parse_exn ~relative fallback_source in
286283
Source.create_from_source_path ~metadata ~source_path statements |> preprocessing
287284

288285

source/analysis/preprocessing.ml

Lines changed: 24 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -88,24 +88,19 @@ let transform_string_annotation_expression ~relative =
8888
arguments = variable_name :: List.map ~f:transform_argument remaining_arguments;
8989
}
9090
| String { StringLiteral.value = string_value; _ } -> (
91-
try
92-
let parsed =
93-
(* Start at column + 1 since parsing begins after the opening quote of the string
94-
literal. *)
95-
Parser.parse
96-
~start_line
97-
~start_column:(start_column + 1)
98-
[string_value ^ "\n"]
99-
~relative
100-
in
101-
match parsed with
102-
| [{ Node.value = Expression { Node.value = Name _ as expression; _ }; _ }]
103-
| [{ Node.value = Expression { Node.value = Call _ as expression; _ }; _ }] ->
104-
expression
105-
| _ -> failwith "Invalid annotation"
91+
(* Start at column + 1 since parsing begins after the opening quote of the string literal. *)
92+
match
93+
Parser.parse
94+
~start_line
95+
~start_column:(start_column + 1)
96+
[string_value ^ "\n"]
97+
~relative
10698
with
107-
| Parser.Error _
108-
| Failure _ ->
99+
| Ok [{ Node.value = Expression { Node.value = Name _ as expression; _ }; _ }]
100+
| Ok [{ Node.value = Expression { Node.value = Call _ as expression; _ }; _ }] ->
101+
expression
102+
| Ok _
103+
| Error _ ->
109104
Log.debug
110105
"Invalid string annotation `%s` at %s:%a"
111106
string_value
@@ -283,14 +278,11 @@ let expand_format_string ({ Source.source_path = { SourcePath.relative; _ }; _ }
283278
List.fold substrings ~init:[] ~f:gather_expressions_in_substring |> List.rev
284279
in
285280
let parse ((start_line, start_column), input_string) =
286-
try
287-
let string = input_string ^ "\n" in
288-
match Parser.parse [string ^ "\n"] ~start_line ~start_column ~relative with
289-
| [{ Node.value = Expression expression; _ }] -> [expression]
290-
| _ -> failwith "Not an expression"
291-
with
292-
| Parser.Error _
293-
| Failure _ ->
281+
let string = input_string ^ "\n" in
282+
match Parser.parse [string ^ "\n"] ~start_line ~start_column ~relative with
283+
| Ok [{ Node.value = Expression expression; _ }] -> [expression]
284+
| Ok _
285+
| Error _ ->
294286
Log.debug
295287
"Pyre could not parse format string `%s` at %s:%a"
296288
input_string
@@ -1142,16 +1134,13 @@ let qualify
11421134
| _ -> kind
11431135
in
11441136
if qualify_strings then (
1145-
try
1146-
match Parser.parse [value ^ "\n"] ~relative with
1147-
| [{ Node.value = Expression expression; _ }] ->
1148-
qualify_expression ~qualify_strings ~scope expression
1149-
|> Expression.show
1150-
|> fun value -> Expression.String { StringLiteral.value; kind }
1151-
| _ -> failwith "Not an expression"
1152-
with
1153-
| Parser.Error _
1154-
| Failure _ ->
1137+
match Parser.parse [value ^ "\n"] ~relative with
1138+
| Ok [{ Node.value = Expression expression; _ }] ->
1139+
qualify_expression ~qualify_strings ~scope expression
1140+
|> Expression.show
1141+
|> fun value -> Expression.String { StringLiteral.value; kind }
1142+
| Ok _
1143+
| Error _ ->
11551144
Log.debug
11561145
"Invalid string annotation `%s` at %s:%a"
11571146
value

source/analysis/type.ml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3262,7 +3262,10 @@ let rec create_logic ~resolve_aliases ~variable_aliases { Node.value = expressio
32623262
let expression =
32633263
try
32643264
let parsed =
3265-
Parser.parse [value] |> Source.create |> Preprocessing.preprocess |> Source.statements
3265+
Parser.parse_exn [value]
3266+
|> Source.create
3267+
|> Preprocessing.preprocess
3268+
|> Source.statements
32663269
in
32673270
match parsed with
32683271
| [{ Node.value = Expression { Node.value; _ }; _ }] -> Some value

source/ast/test/statementTest.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ let test_pp _ =
384384
let pretty_print_of_source =
385385
source
386386
|> String.split_on_chars ~on:['\n']
387-
|> Parser.parse
387+
|> Parser.parse_exn
388388
|> List.map ~f:show
389389
|> String.concat ~sep:"\n"
390390
|> String.rstrip ~drop:(Char.equal '\n')

source/interprocedural_analyses/taint/modelParser.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2376,7 +2376,7 @@ let create ~resolution ~path ~configuration ~rule_filter source =
23762376
in
23772377
let signatures_and_queries, errors =
23782378
String.split ~on:'\n' source
2379-
|> Parser.parse
2379+
|> Parser.parse_exn
23802380
|> Source.create
23812381
|> Source.statements
23822382
|> List.map ~f:(parse_statement ~resolution ~path ~configuration)

source/parser/parser.ml

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,31 @@ open Ast
1010

1111
exception Error of string
1212

13+
module Error = struct
14+
type t = {
15+
location: Location.t;
16+
file_name: string;
17+
content: string option;
18+
}
19+
20+
let pp formatter { location; file_name; content } =
21+
let column = location.Location.start.Location.column in
22+
let header = Format.asprintf "Could not parse file at %s:%a" file_name Location.pp location in
23+
match content with
24+
| Some content ->
25+
let indicator =
26+
if column > 0 then
27+
String.make (column - 1) ' ' ^ "^"
28+
else
29+
"^"
30+
in
31+
Format.fprintf formatter "%s\n %s\n %s" header content indicator
32+
| None -> Format.fprintf formatter "%s" header
33+
34+
35+
let show = Format.asprintf "%a" pp
36+
end
37+
1338
let sanitize_input lines =
1439
(* Remove byte order mark from first line if it exists. *)
1540
let lines =
@@ -44,27 +69,19 @@ let parse ?start_line ?start_column ?relative lines =
4469
buffer
4570
in
4671
let state = Lexer.State.initial () in
47-
try Generator.parse (Lexer.read state) buffer with
72+
try Ok (Generator.parse (Lexer.read state) buffer) with
4873
| Pyre.ParserError _
4974
| Generator.Error
5075
| Failure _ ->
5176
let location =
5277
Location.create ~start:buffer.Lexing.lex_curr_p ~stop:buffer.Lexing.lex_curr_p
5378
in
54-
let line = location.Location.start.Location.line - 1
55-
and column = location.Location.start.Location.column in
56-
let error =
57-
let header =
58-
Format.asprintf "Could not parse file at %s:%a" file_name Location.pp location
59-
in
60-
let indicator =
61-
if column > 0 then
62-
String.make (column - 1) ' ' ^ "^"
63-
else
64-
"^"
65-
in
66-
match List.nth (String.split ~on:'\n' input) line with
67-
| Some line -> Format.sprintf "%s\n %s\n %s" header line indicator
68-
| None -> Format.sprintf "%s" header
69-
in
70-
raise (Error error)
79+
let line_number = location.Location.start.Location.line - 1 in
80+
let content = List.nth (String.split ~on:'\n' input) line_number in
81+
Error { Error.location; file_name; content }
82+
83+
84+
let parse_exn ?start_line ?start_column ?relative lines =
85+
match parse ?start_line ?start_column ?relative lines with
86+
| Ok statements -> statements
87+
| Error error -> raise (Error (Error.show error))

source/parser/parser.mli

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,26 @@ open Ast
99

1010
exception Error of string
1111

12+
module Error : sig
13+
type t = {
14+
location: Location.t;
15+
file_name: string;
16+
content: string option;
17+
}
18+
[@@deriving show]
19+
end
20+
1221
val parse
1322
: ?start_line:int ->
1423
?start_column:int ->
1524
?relative:string ->
1625
string list ->
17-
Statement.t list
26+
(Statement.t list, Error.t) Result.t
1827
(** Parse python source. ?handle is path relative to the file's source root, if any. *)
28+
29+
val parse_exn
30+
: ?start_line:int ->
31+
?start_column:int ->
32+
?relative:string ->
33+
string list ->
34+
Statement.t list

source/parser/test/generatorTest.ml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5464,12 +5464,13 @@ let test_setitem _ =
54645464

54655465

54665466
let test_byte_order_mark _ =
5467+
let parse lines = PyreParser.Parser.parse_exn lines |> ignore in
54675468
let byte_order_mark = [0xEF; 0xBB; 0xBF] |> List.map ~f:Char.of_int_exn |> String.of_char_list in
54685469
(* Ensure that we can parse UTF-8 files with byte order marks properly. *)
5469-
PyreParser.Parser.parse [byte_order_mark ^ "1"] |> ignore;
5470+
parse [byte_order_mark ^ "1"];
54705471
assert_raises
54715472
(PyreParser.Parser.Error "Could not parse file at $invalid_path:2:0-2:0\n \239\187\1912\n ^")
5472-
(fun () -> PyreParser.Parser.parse ["1"; byte_order_mark ^ "2"])
5473+
(fun () -> parse ["1"; byte_order_mark ^ "2"])
54735474

54745475

54755476
let test_walrus_operator _ =

source/server/query.ml

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -294,17 +294,19 @@ let help () =
294294
let rec parse_request_exn query =
295295
let open Expression in
296296
match PyreParser.Parser.parse [query] with
297-
| [
298-
{
299-
Node.value =
300-
Expression
301-
{
302-
Node.value = Call { callee = { Node.value = Name (Name.Identifier name); _ }; arguments };
303-
_;
304-
};
305-
_;
306-
};
307-
] -> (
297+
| Ok
298+
[
299+
{
300+
Node.value =
301+
Expression
302+
{
303+
Node.value =
304+
Call { callee = { Node.value = Name (Name.Identifier name); _ }; arguments };
305+
_;
306+
};
307+
_;
308+
};
309+
] -> (
308310
let expression { Call.Argument.value; _ } = value in
309311
let access = function
310312
| { Call.Argument.value; _ } when has_identifier_base value -> value
@@ -352,9 +354,9 @@ let rec parse_request_exn query =
352354
| "validate_taint_models", [] -> ValidateTaintModels None
353355
| "validate_taint_models", [argument] -> Request.ValidateTaintModels (Some (string argument))
354356
| _ -> raise (InvalidQuery "unexpected query") )
355-
| _ when String.equal query "help" -> Help (help ())
356-
| _ -> raise (InvalidQuery "unexpected query")
357-
| exception PyreParser.Parser.Error _ -> raise (InvalidQuery "failed to parse query")
357+
| Ok _ when String.equal query "help" -> Help (help ())
358+
| Ok _ -> raise (InvalidQuery "unexpected query")
359+
| Error _ -> raise (InvalidQuery "failed to parse query")
358360

359361

360362
let parse_request query =

0 commit comments

Comments
 (0)