From f6a57df44b1150d302dbe4e6872382bb85b7eff1 Mon Sep 17 00:00:00 2001 From: Jamie Kyle Date: Tue, 5 Oct 2021 23:06:34 -0700 Subject: [PATCH] feat(parser): temp clone rslint parser/etc into repo Temporarily check in the rslint to make some changes we need right away. We're not planning on forking the parser. --- .github/workflows/ci.yml | 19 + .gitignore | 4 +- .gitmodules | 3 + Cargo.lock | 1067 +++- crates/rslint_errors/CHANGELOG.md | 30 + crates/rslint_errors/Cargo.toml | 19 + crates/rslint_errors/src/codespan/LICENSE | 201 + crates/rslint_errors/src/codespan/README.md | 7 + .../rslint_errors/src/codespan/diagnostic.rs | 205 + crates/rslint_errors/src/codespan/files.rs | 396 ++ crates/rslint_errors/src/codespan/mod.rs | 7 + crates/rslint_errors/src/codespan/term.rs | 75 + .../rslint_errors/src/codespan/term/config.rs | 281 + .../src/codespan/term/renderer.rs | 1066 ++++ .../rslint_errors/src/codespan/term/views.rs | 491 ++ crates/rslint_errors/src/diagnostic.rs | 414 ++ crates/rslint_errors/src/emit.rs | 270 + crates/rslint_errors/src/file.rs | 274 + crates/rslint_errors/src/formatters.rs | 155 + crates/rslint_errors/src/lib.rs | 56 + crates/rslint_errors/src/lsp.rs | 325 + crates/rslint_errors/src/suggestion.rs | 42 + crates/rslint_lexer/CHANGELOG.md | 48 + crates/rslint_lexer/Cargo.toml | 24 + crates/rslint_lexer/scripts/gen.py | 171 + crates/rslint_lexer/src/highlight.rs | 144 + crates/rslint_lexer/src/lib.rs | 1539 +++++ crates/rslint_lexer/src/state.rs | 169 + crates/rslint_lexer/src/tables.rs | 448 ++ crates/rslint_lexer/src/tests.rs | 1050 ++++ crates/rslint_lexer/src/token.rs | 34 + crates/rslint_parser/CHANGELOG.md | 117 + crates/rslint_parser/Cargo.toml | 19 + crates/rslint_parser/src/ast.rs | 104 + crates/rslint_parser/src/ast/expr_ext.rs | 878 +++ crates/rslint_parser/src/ast/generated.rs | 4 + .../rslint_parser/src/ast/generated/nodes.rs | 5452 +++++++++++++++++ .../rslint_parser/src/ast/generated/tokens.rs | 70 + crates/rslint_parser/src/ast/stmt_ext.rs | 456 ++ crates/rslint_parser/src/ast/ts_ext.rs | 97 + crates/rslint_parser/src/event.rs | 113 + crates/rslint_parser/src/lib.rs | 244 + .../rslint_parser/src/lossless_tree_sink.rs | 239 + crates/rslint_parser/src/lossy_tree_sink.rs | 167 + crates/rslint_parser/src/numbers.rs | 143 + crates/rslint_parser/src/parse.rs | 255 + crates/rslint_parser/src/parser.rs | 723 +++ crates/rslint_parser/src/state.rs | 178 + crates/rslint_parser/src/syntax.rs | 16 + crates/rslint_parser/src/syntax/decl.rs | 1178 ++++ crates/rslint_parser/src/syntax/expr.rs | 1423 +++++ crates/rslint_parser/src/syntax/pat.rs | 255 + crates/rslint_parser/src/syntax/program.rs | 551 ++ crates/rslint_parser/src/syntax/stmt.rs | 1135 ++++ crates/rslint_parser/src/syntax/typescript.rs | 1335 ++++ crates/rslint_parser/src/syntax/util.rs | 364 ++ crates/rslint_parser/src/syntax_node.rs | 60 + crates/rslint_parser/src/tests.rs | 125 + crates/rslint_parser/src/token_set.rs | 39 + crates/rslint_parser/src/token_source.rs | 169 + crates/rslint_parser/src/util.rs | 400 ++ .../err/async_arrow_expr_await_parameter.js | 1 + .../err/async_arrow_expr_await_parameter.rast | 36 + .../inline/err/binary_expressions_err.js | 3 + .../inline/err/binary_expressions_err.rast | 62 + .../inline/err/binding_identifier_invalid.js | 5 + .../err/binding_identifier_invalid.rast | 107 + .../inline/err/block_stmt_in_class.js | 1 + .../inline/err/block_stmt_in_class.rast | 37 + .../test_data/inline/err/bracket_expr_err.js | 3 + .../inline/err/bracket_expr_err.rast | 55 + .../test_data/inline/err/class_decl_err.js | 5 + .../test_data/inline/err/class_decl_err.rast | 136 + .../inline/err/conditional_expr_err.js | 2 + .../inline/err/conditional_expr_err.rast | 55 + .../test_data/inline/err/do_while_stmt_err.js | 3 + .../inline/err/do_while_stmt_err.rast | 156 + .../inline/err/export_decl_not_top_level.js | 3 + .../inline/err/export_decl_not_top_level.rast | 35 + .../test_data/inline/err/for_stmt_err.js | 2 + .../test_data/inline/err/for_stmt_err.rast | 82 + .../err/formal_params_no_binding_element.js | 1 + .../err/formal_params_no_binding_element.rast | 25 + .../test_data/inline/err/function_decl_err.js | 7 + .../inline/err/function_decl_err.rast | 142 + .../test_data/inline/err/if_stmt_err.js | 4 + .../test_data/inline/err/if_stmt_err.rast | 108 + .../inline/err/import_call_no_arg.js | 2 + .../inline/err/import_call_no_arg.rast | 36 + .../inline/err/import_decl_not_top_level.js | 3 + .../inline/err/import_decl_not_top_level.rast | 29 + .../test_data/inline/err/import_no_meta.js | 2 + .../test_data/inline/err/import_no_meta.rast | 32 + .../test_data/inline/err/invalid_arg_list.js | 2 + .../inline/err/invalid_arg_list.rast | 64 + .../inline/err/invalid_method_recover.js | 5 + .../inline/err/invalid_method_recover.rast | 79 + .../test_data/inline/err/method_getter_err.js | 3 + .../inline/err/method_getter_err.rast | 60 + .../inline/err/object_expr_error_prop_name.js | 2 + .../err/object_expr_error_prop_name.rast | 66 + .../err/object_expr_non_ident_literal_prop.js | 1 + .../object_expr_non_ident_literal_prop.rast | 34 + .../err/paren_or_arrow_expr_invalid_params.js | 1 + .../paren_or_arrow_expr_invalid_params.rast | 69 + .../err/primary_expr_invalid_recovery.js | 1 + .../err/primary_expr_invalid_recovery.rast | 33 + .../test_data/inline/err/return_stmt_err.js | 2 + .../test_data/inline/err/return_stmt_err.rast | 29 + .../test_data/inline/err/semicolons_err.js | 1 + .../test_data/inline/err/semicolons_err.rast | 31 + .../test_data/inline/err/subscripts_err.js | 2 + .../test_data/inline/err/subscripts_err.rast | 40 + .../test_data/inline/err/switch_stmt_err.js | 2 + .../test_data/inline/err/switch_stmt_err.rast | 56 + .../err/template_literal_unterminated.js | 1 + .../err/template_literal_unterminated.rast | 28 + .../test_data/inline/err/throw_stmt_err.js | 2 + .../test_data/inline/err/throw_stmt_err.rast | 29 + .../err/unterminated_unicode_codepoint.js | 1 + .../err/unterminated_unicode_codepoint.rast | 24 + .../test_data/inline/err/var_decl_err.js | 2 + .../test_data/inline/err/var_decl_err.rast | 59 + .../test_data/inline/err/while_stmt_err.js | 4 + .../test_data/inline/err/while_stmt_err.rast | 104 + .../test_data/inline/ok/array_expr.js | 6 + .../test_data/inline/ok/array_expr.rast | 72 + .../inline/ok/arrow_expr_single_param.js | 5 + .../inline/ok/arrow_expr_single_param.rast | 45 + .../test_data/inline/ok/assign_expr.js | 5 + .../test_data/inline/ok/assign_expr.rast | 115 + .../test_data/inline/ok/async_arrow_expr.js | 3 + .../test_data/inline/ok/async_arrow_expr.rast | 77 + .../inline/ok/async_function_expr.js | 2 + .../inline/ok/async_function_expr.rast | 50 + .../test_data/inline/ok/async_ident.js | 1 + .../test_data/inline/ok/async_ident.rast | 15 + .../test_data/inline/ok/async_method.js | 4 + .../test_data/inline/ok/async_method.rast | 39 + .../test_data/inline/ok/binary_expressions.js | 10 + .../inline/ok/binary_expressions.rast | 158 + .../test_data/inline/ok/block_stmt.js | 3 + .../test_data/inline/ok/block_stmt.rast | 34 + .../test_data/inline/ok/bracket_expr.js | 5 + .../test_data/inline/ok/bracket_expr.rast | 58 + .../test_data/inline/ok/break_stmt.js | 5 + .../test_data/inline/ok/break_stmt.rast | 36 + .../test_data/inline/ok/class_decl.js | 3 + .../test_data/inline/ok/class_decl.rast | 45 + .../inline/ok/class_empty_element.js | 1 + .../inline/ok/class_empty_element.rast | 53 + .../test_data/inline/ok/class_expr.js | 5 + .../test_data/inline/ok/class_expr.rast | 64 + .../test_data/inline/ok/conditional_expr.js | 2 + .../test_data/inline/ok/conditional_expr.rast | 42 + .../test_data/inline/ok/continue_stmt.js | 6 + .../test_data/inline/ok/continue_stmt.rast | 38 + .../test_data/inline/ok/debugger_stmt.js | 1 + .../test_data/inline/ok/debugger_stmt.rast | 5 + .../test_data/inline/ok/dot_expr.js | 6 + .../test_data/inline/ok/dot_expr.rast | 49 + .../test_data/inline/ok/empty_stmt.js | 1 + .../test_data/inline/ok/empty_stmt.rast | 4 + .../test_data/inline/ok/export.js | 1 + .../test_data/inline/ok/export.rast | 19 + .../test_data/inline/ok/for_stmt.js | 4 + .../test_data/inline/ok/for_stmt.rast | 107 + .../test_data/inline/ok/function_decl.js | 8 + .../test_data/inline/ok/function_decl.rast | 98 + .../test_data/inline/ok/function_expr.js | 2 + .../test_data/inline/ok/function_expr.rast | 44 + .../test_data/inline/ok/grouping_expr.js | 2 + .../test_data/inline/ok/grouping_expr.rast | 18 + .../inline/ok/identifier_reference.js | 3 + .../inline/ok/identifier_reference.rast | 16 + .../test_data/inline/ok/if_stmt.js | 4 + .../test_data/inline/ok/if_stmt.rast | 80 + .../test_data/inline/ok/import_call.js | 1 + .../test_data/inline/ok/import_call.rast | 9 + .../test_data/inline/ok/import_decl.js | 1 + .../test_data/inline/ok/import_decl.rast | 18 + .../test_data/inline/ok/import_meta.js | 1 + .../test_data/inline/ok/import_meta.rast | 7 + .../test_data/inline/ok/literals.js | 7 + .../test_data/inline/ok/literals.rast | 29 + .../test_data/inline/ok/method_getter.js | 3 + .../test_data/inline/ok/method_getter.rast | 25 + .../test_data/inline/ok/method_setter.js | 3 + .../test_data/inline/ok/method_setter.rast | 25 + .../test_data/inline/ok/new_exprs.js | 5 + .../test_data/inline/ok/new_exprs.rast | 107 + .../inline/ok/object_binding_prop.js | 2 + .../inline/ok/object_binding_prop.rast | 60 + .../test_data/inline/ok/object_expr.js | 2 + .../test_data/inline/ok/object_expr.rast | 34 + .../inline/ok/object_expr_assign_prop.js | 1 + .../inline/ok/object_expr_assign_prop.rast | 35 + .../inline/ok/object_expr_async_method.js | 4 + .../inline/ok/object_expr_async_method.rast | 44 + .../inline/ok/object_expr_generator_method.js | 1 + .../ok/object_expr_generator_method.rast | 28 + .../inline/ok/object_expr_getter_setter.js | 5 + .../inline/ok/object_expr_getter_setter.rast | 37 + .../ok/object_expr_getter_setter_as_prop.js | 1 + .../ok/object_expr_getter_setter_as_prop.rast | 26 + .../ok/object_expr_getter_setter_computed.js | 5 + .../object_expr_getter_setter_computed.rast | 40 + .../ok/object_expr_ident_literal_prop.js | 1 + .../ok/object_expr_ident_literal_prop.rast | 24 + .../inline/ok/object_expr_ident_prop.js | 1 + .../inline/ok/object_expr_ident_prop.rast | 18 + .../test_data/inline/ok/object_expr_method.js | 3 + .../inline/ok/object_expr_method.rast | 28 + .../inline/ok/object_expr_spread_prop.js | 1 + .../inline/ok/object_expr_spread_prop.rast | 19 + .../test_data/inline/ok/object_prop_name.js | 1 + .../test_data/inline/ok/object_prop_name.rast | 58 + .../inline/ok/paren_or_arrow_expr.js | 5 + .../inline/ok/paren_or_arrow_expr.rast | 105 + .../test_data/inline/ok/postfix_expr.js | 2 + .../test_data/inline/ok/postfix_expr.rast | 13 + .../test_data/inline/ok/return_stmt.js | 5 + .../test_data/inline/ok/return_stmt.rast | 28 + .../test_data/inline/ok/semicolons.js | 6 + .../test_data/inline/ok/semicolons.rast | 74 + .../test_data/inline/ok/sequence_expr.js | 1 + .../test_data/inline/ok/sequence_expr.rast | 22 + .../test_data/inline/ok/static_method.js | 6 + .../test_data/inline/ok/static_method.rast | 73 + .../test_data/inline/ok/subscripts.js | 2 + .../test_data/inline/ok/subscripts.rast | 36 + .../test_data/inline/ok/switch_stmt.js | 4 + .../test_data/inline/ok/switch_stmt.rast | 25 + .../test_data/inline/ok/template_literal.js | 4 + .../test_data/inline/ok/template_literal.rast | 73 + .../test_data/inline/ok/this_expr.js | 2 + .../test_data/inline/ok/this_expr.rast | 13 + .../test_data/inline/ok/throw_stmt.js | 2 + .../test_data/inline/ok/throw_stmt.rast | 22 + .../test_data/inline/ok/try_stmt.js | 2 + .../test_data/inline/ok/try_stmt.rast | 42 + .../test_data/inline/ok/var_decl.js | 5 + .../test_data/inline/ok/var_decl.rast | 101 + .../test_data/inline/ok/while_stmt.js | 2 + .../test_data/inline/ok/while_stmt.rast | 27 + .../test_data/inline/ok/yield_expr.js | 4 + .../test_data/inline/ok/yield_expr.rast | 33 + .../async-rest-optional-parameter/input.ts | 1 + .../err/class/declare-private-name/input.ts | 3 + .../test_data/ts/err/class/input.ts | 3 + .../ts/err/class/method-readonly/input.ts | 3 + .../input.ts | 3 + .../err/class/parameter-properties/input.ts | 11 + .../ts/err/custom/const-enum-2/input.ts | 5 + .../ts/err/custom/const-enum/input.ts | 5 + .../ts/err/custom/issue-380/input.ts | 8 + .../top-level-await-jsc-target/input.ts | 1 + .../type-arguments-invalid/input.ts | 2 + .../test_data/ts/err/issue-1170-1/input.ts | 1 + .../no-paren-and-nullish/input.ts | 1 + .../no-paren-nullish-and/input.ts | 1 + .../no-paren-nullish-or/input.ts | 1 + .../no-paren-or-nullish/input.ts | 1 + .../test_data/ts/err/type-arguments/input.ts | 1 + .../err/types/tuple-optional-invalid/input.ts | 1 + .../definite-assignment-not-allowed/input.ts | 1 + .../ts/ok/arrow-function/annotated/input.ts | 1 + .../async-generic-false-positive/input.ts | 2 + .../ts/ok/arrow-function/async-rest/input.ts | 1 + .../ts/ok/arrow-function/async/input.ts | 1 + .../default-parameter-values/input.ts | 1 + .../ok/arrow-function/destructuring/input.ts | 1 + .../ts/ok/arrow-function/generic-tsx/input.ts | 2 + .../ts/ok/arrow-function/generic/input.ts | 1 + .../inside-conditional-expr/input.ts | 1 + .../optional-parameter/input.ts | 1 + .../arrow-function/predicate-types/input.ts | 1 + .../test_data/ts/ok/cast/as/input.ts | 4 + .../ts/ok/cast/assert-and-assign/input.ts | 1 + .../ts/ok/cast/false-positive/input.ts | 1 + .../cast/multiple-assert-and-assign/input.ts | 1 + .../ts/ok/cast/need-parentheses/input.ts | 3 + .../ts/ok/cast/null-assertion-3/input.ts | 1 + .../cast/null-assertion-and-assign-2/input.ts | 1 + .../cast/null-assertion-and-assign/input.ts | 1 + .../null-assertion-false-positive/input.ts | 2 + .../input.ts | 1 + .../ts/ok/cast/null-assertion/input.ts | 1 + .../type-assertion-after-operator/input.ts | 1 + .../cast/type-assertion-and-assign/input.ts | 1 + .../type-assertion-before-operator/input.ts | 1 + .../ts/ok/cast/type-assertion/input.ts | 1 + .../ok/class/abstract-false-positive/input.ts | 2 + .../test_data/ts/ok/class/abstract/input.ts | 7 + .../constructor-with-modifier-names/input.ts | 4 + .../ts/ok/class/constructor/input.ts | 5 + .../test_data/ts/ok/class/declare/input.ts | 7 + .../test_data/ts/ok/class/decorators/input.ts | 18 + .../expression-extends-implements/input.ts | 2 + .../ts/ok/class/expression-extends/input.ts | 2 + .../ts/ok/class/expression-generic/input.ts | 2 + .../ok/class/expression-implements/input.ts | 2 + .../ts/ok/class/extends-implements/input.ts | 1 + .../test_data/ts/ok/class/extends/input.ts | 1 + .../test_data/ts/ok/class/generic/input.ts | 1 + .../ts/ok/class/get-generic/input.ts | 3 + .../test_data/ts/ok/class/implements/input.ts | 1 + .../ts/ok/class/index-signature/input.ts | 4 + .../members-with-modifier-names/input.ts | 6 + .../members-with-reserved-names/input.ts | 3 + .../ts/ok/class/method-computed/input.ts | 4 + .../ts/ok/class/method-generic/input.ts | 4 + .../ts/ok/class/method-no-body/input.ts | 4 + .../ts/ok/class/method-optional/input.ts | 3 + .../ts/ok/class/method-return-type/input.ts | 6 + .../method-with-newline-with-body/input.ts | 6 + .../method-with-newline-without-body/input.ts | 5 + .../ts/ok/class/modifiers-accessors/input.ts | 11 + .../ok/class/modifiers-methods-async/input.ts | 11 + .../ts/ok/class/modifiers-properties/input.ts | 23 + .../test_data/ts/ok/class/properties/input.ts | 8 + .../ts/ok/class/property-computed/input.ts | 4 + .../ts/ok/class/property-private/input.ts | 8 + .../test_data/ts/ok/class/static/input.ts | 6 + .../ts/ok/custom/arrow/complex-tsc/input.ts | 7 + .../ts/ok/custom/arrow/complex/input.ts | 6 + .../test_data/ts/ok/custom/bigint/input.ts | 1 + .../ts/ok/custom/const-enum/input.ts | 5 + .../default-followed-by-type/arrow/input.ts | 3 + .../function/input.ts | 3 + .../custom/dynamic-import-expr-ctx/input.ts | 5 + .../custom/dynamic-import-top-level/input.ts | 1 + .../ok/custom/import-meta-expr-ctx/input.ts | 5 + .../ts/ok/custom/import-meta-top-ctx/input.ts | 1 + .../ok/custom/import-type/typeof/as/input.ts | 1 + .../custom/import-type/typeof/simple/input.ts | 1 + .../ts/ok/custom/issue-196-01/input.ts | 1 + .../ts/ok/custom/issue-196-02/input.ts | 2 + .../test_data/ts/ok/custom/issue-236/input.ts | 4 + .../ts/ok/custom/issue-236/input.ts.stdout | 426 ++ .../test_data/ts/ok/custom/issue-259/input.ts | 1 + .../ts/ok/custom/issue-327-1/input.ts | 1 + .../ts/ok/custom/issue-327-2/input.ts | 1 + .../test_data/ts/ok/custom/issue-338/input.ts | 1 + .../test_data/ts/ok/custom/issue-370/input.ts | 5 + .../test_data/ts/ok/custom/issue-374/input.ts | 2 + .../input.ts | 1 + .../issue-401-const-assertion-array/input.ts | 1 + .../input.ts | 1 + .../input.ts | 1 + .../input.ts | 1 + .../issue-401-const-assertion-object/input.ts | 1 + .../ts/ok/custom/issue-410-1/input.ts | 1 + .../ts/ok/custom/issue-410-2/input.ts | 1 + .../custom/issue-432-readonly-array/input.ts | 1 + .../ts/ok/custom/issue-433-1/input.ts | 1 + .../ts/ok/custom/issue-433-2/input.ts | 1 + .../test_data/ts/ok/custom/issue-461/input.ts | 3 + .../test_data/ts/ok/custom/issue-496/input.ts | 11 + .../test_data/ts/ok/custom/issue-535/input.ts | 3 + .../test_data/ts/ok/custom/issue-538/input.ts | 1 + .../custom/issue-612-async-generator/input.ts | 1 + .../test_data/ts/ok/custom/issue-623/input.ts | 4 + .../ts/ok/custom/issue-709-2/input.ts | 3 + .../ts/ok/custom/issue-709-3/input.ts | 3 + .../ts/ok/custom/issue-709/input.d.ts | 13 + .../test_data/ts/ok/custom/issue-716/input.ts | 25 + .../test_data/ts/ok/custom/issue-717/input.ts | 3 + .../test_data/ts/ok/custom/issue-720/input.ts | 3 + .../ts/ok/custom/ternary-paren/input.ts | 1 + .../test_data/ts/ok/custom/ternary/input.ts | 1 + .../custom/type-only/export/aliased/input.ts | 1 + .../custom/type-only/export/specific/input.ts | 1 + .../custom/type-only/import/aliased/input.ts | 1 + .../custom/type-only/import/default/input.ts | 2 + .../custom/type-only/import/specific/input.ts | 2 + .../ts/ok/custom/yield-object-paren/input.ts | 3 + .../ts/ok/custom/yield-object/input.ts | 3 + .../test_data/ts/ok/declare/const/input.ts | 1 + .../ts/ok/declare/destructure/input.ts | 1 + .../ts/ok/declare/interface/input.ts | 1 + .../test_data/ts/ok/declare/let/input.ts | 1 + .../test_data/ts/ok/declare/var/input.ts | 2 + .../ts/ok/decorators/parameter/input.ts | 7 + .../ts/ok/decorators/type-arguments/input.ts | 2 + .../test_data/ts/ok/enum/const/input.ts | 1 + .../ts/ok/enum/declare-const/input.ts | 1 + .../test_data/ts/ok/enum/declare/input.ts | 1 + .../ts/ok/enum/export-const/input.ts | 1 + .../ts/ok/enum/export-declare-const/input.ts | 1 + .../test_data/ts/ok/enum/export/input.ts | 1 + .../ok/enum/members-reserved-words/input.ts | 4 + .../ts/ok/enum/members-strings/input.ts | 4 + .../input.ts | 3 + .../ok/enum/members-trailing-comma/input.ts | 3 + .../test_data/ts/ok/enum/members/input.ts | 4 + .../ts/ok/es2019/from-entries/input.ts | 5 + .../shorthand-ambient-module/input.ts | 1 + .../ts/ok/export-default-interface/index.ts | 1 + .../ts/ok/export/as-namespace/input.ts | 1 + .../test_data/ts/ok/export/declare/input.ts | 7 + .../test_data/ts/ok/export/equals/input.ts | 1 + .../ts/ok/export/namespace-from/input.ts | 1 + .../ts/ok/export/nested-same-name/input.ts | 4 + .../ts/ok/function/annotated/input.ts | 1 + .../ts/ok/function/anonymous/input.ts | 1 + .../test_data/ts/ok/function/declare/input.ts | 2 + .../ts/ok/function/export-default/input.ts | 1 + .../ts/ok/function/overloads/input.ts | 2 + .../ts/ok/function/predicate-types/input.ts | 4 + .../ts/ok/import/equals-require/input.ts | 1 + .../test_data/ts/ok/import/equals/input.ts | 1 + .../ok/import/export-import-require/input.ts | 1 + .../ts/ok/import/export-import/input.ts | 1 + .../ts/ok/import/not-top-level/input.ts | 3 + .../ts/ok/interface/call-signature/input.ts | 3 + .../ok/interface/construct-signature/input.ts | 3 + .../test_data/ts/ok/interface/export/input.ts | 2 + .../ts/ok/interface/extends/input.ts | 1 + .../ts/ok/interface/generic/input.ts | 1 + .../ts/ok/interface/index-signature/input.ts | 3 + .../ts/ok/interface/method-computed/input.ts | 4 + .../ts/ok/interface/method-generic/input.ts | 3 + .../ts/ok/interface/method-optional/input.ts | 3 + .../ts/ok/interface/method-plain/input.ts | 4 + .../ts/ok/interface/modifiers/input.ts | 3 + .../ts/ok/interface/properties/input.ts | 5 + .../ok/interface/property-computed/input.ts | 4 + .../interface/property-named-public/input.ts | 3 + .../interface/reserved-method-name/input.ts | 3 + .../ts/ok/interface/separators/input.ts | 6 + .../test_data/ts/ok/issue-812/input.ts | 3 + .../test_data/ts/ok/issue-814-2/input.ts | 12 + .../test_data/ts/ok/issue-814/input.ts | 5 + .../test_data/ts/ok/issue-818-1/input.ts | 5 + .../test_data/ts/ok/issue-818-2/input.ts | 4 + .../test_data/ts/ok/issue-861/input.ts | 1 + .../test_data/ts/ok/issue-874/input.ts | 1 + .../test_data/ts/ok/issue-896/input.ts | 2 + .../test_data/ts/ok/issue-913/input.ts | 337 + .../test_data/ts/ok/issue-915/input.ts | 3 + .../test_data/ts/ok/issue-944/input.ts | 3 + .../test_data/ts/ok/issue-947/input.ts | 1 + .../ok/module-namespace/body-declare/input.ts | 3 + .../body-nested-declare/input.ts | 5 + .../ok/module-namespace/body-nested/input.ts | 4 + .../ts/ok/module-namespace/body/input.ts | 3 + .../declare-shorthand/input.ts | 1 + .../global-in-module/input.ts | 5 + .../ok/module-namespace/head-declare/input.ts | 4 + .../ok/module-namespace/head-export/input.ts | 2 + .../ts/ok/module-namespace/head/input.ts | 4 + .../and-nullish/input.ts | 1 + .../associativity/input.ts | 1 + .../expression/input.ts | 1 + .../multiline/input.ts | 3 + .../nullish-and/input.ts | 1 + .../nullish-or/input.ts | 1 + .../or-nullish/input.ts | 1 + .../ts/ok/object/getter-prop/input.ts | 8 + .../ok/object/method-async-generator/input.ts | 4 + .../destructuring-in-function-type/input.ts | 1 + .../ok/regression/is-default-export/input.ts | 3 + .../ts/ok/regression/tsx-issue-7742/input.ts | 3 + .../test_data/ts/ok/stack-size/input.ts | 25 + .../ts/ok/template-literal-type/input.ts | 9 + .../ts/ok/type-alias/declare/input.ts | 1 + .../ts/ok/type-alias/export/input.ts | 2 + .../ts/ok/type-alias/generic-complex/input.ts | 1 + .../ts/ok/type-alias/generic/input.ts | 1 + .../test_data/ts/ok/type-alias/plain/input.ts | 1 + .../ts/ok/type-arguments/call/input.ts | 2 + .../new-false-positive/input.ts | 1 + .../ts/ok/type-arguments/new/input.ts | 2 + .../tagged-template-no-asi/input.ts | 2 + .../type-arguments/tagged-template/input.ts | 1 + .../ts/ok/type-arguments/whitespace/input.ts | 1 + .../test_data/ts/ok/types/array/input.ts | 1 + .../ts/ok/types/conditional-infer/input.ts | 1 + .../ts/ok/types/conditional/input.ts | 1 + .../ts/ok/types/function-generic/input.ts | 1 + .../ts/ok/types/function-in-generic/input.ts | 1 + .../ts/ok/types/function-with-this/input.ts | 1 + .../test_data/ts/ok/types/function/input.ts | 1 + .../test_data/ts/ok/types/indexed/input.ts | 1 + .../test_data/ts/ok/types/keywords/input.ts | 13 + .../ts/ok/types/literal-boolean/input.ts | 2 + .../ok/types/literal-number-negative/input.ts | 1 + .../ts/ok/types/literal-number/input.ts | 1 + .../ts/ok/types/literal-string/input.ts | 1 + .../test_data/ts/ok/types/mapped/input.ts | 5 + .../ts/ok/types/object-shorthand/input.ts | 5 + .../ts/ok/types/parenthesized/input.ts | 1 + .../types/reference-generic-nested/input.ts | 1 + .../ts/ok/types/reference-generic/input.ts | 1 + .../test_data/ts/ok/types/reference/input.ts | 1 + .../ts/ok/types/tuple-empty/input.ts | 1 + .../ts/ok/types/tuple-optional/input.ts | 1 + .../types/tuple-rest-after-optional/input.ts | 1 + .../test_data/ts/ok/types/tuple-rest/input.ts | 1 + .../test_data/ts/ok/types/tuple/input.ts | 1 + .../ts/ok/types/type-literal/input.ts | 2 + .../ts/ok/types/type-operator/input.ts | 2 + .../test_data/ts/ok/types/typeof/input.ts | 1 + .../ts/ok/types/union-intersection/input.ts | 10 + .../test_data/ts/ok/v4/issue-864/input.ts | 5 + .../test_data/ts/ok/v4/issue-865/input.ts | 1 + .../test_data/ts/ok/v4/issue-866/input.ts | 3 + .../test_data/ts/ok/v4/issue-941/input.ts | 3 + .../ts/ok/v4/optional-tuple-element/input.ts | 1 + .../definite-assignment/input.ts | 1 + crates/rslint_regex/Cargo.toml | 20 + crates/rslint_regex/benches/regex.rs | 21 + crates/rslint_regex/src/ir.rs | 345 ++ crates/rslint_regex/src/lib.rs | 510 ++ crates/rslint_regex/src/parser.rs | 1259 ++++ crates/rslint_regex/src/tests.rs | 1133 ++++ crates/rslint_regex/src/unicode.rs | 610 ++ crates/rslint_rowan/Cargo.toml | 22 + crates/rslint_rowan/README.md | 21 + crates/rslint_rowan/src/api.rs | 403 ++ crates/rslint_rowan/src/arc.rs | 1002 +++ crates/rslint_rowan/src/cursor.rs | 904 +++ crates/rslint_rowan/src/green.rs | 41 + crates/rslint_rowan/src/green/builder.rs | 200 + crates/rslint_rowan/src/green/element.rs | 212 + crates/rslint_rowan/src/green/node.rs | 220 + crates/rslint_rowan/src/green/token.rs | 115 + crates/rslint_rowan/src/lib.rs | 35 + crates/rslint_rowan/src/serde_impls.rs | 68 + crates/rslint_rowan/src/syntax_text.rs | 324 + crates/rslint_rowan/src/utility_types.rs | 142 + crates/rslint_syntax/Cargo.toml | 10 + crates/rslint_syntax/src/generated.rs | 522 ++ crates/rslint_syntax/src/lib.rs | 27 + crates/rslint_text_edit/Cargo.toml | 10 + crates/rslint_text_edit/src/lib.rs | 202 + xtask/Cargo.toml | 29 +- xtask/src/ast.rs | 1705 ++++++ xtask/src/codegen.rs | 45 + xtask/src/codegen/parser_tests.rs | 170 + xtask/src/codegen/syntax.rs | 463 ++ xtask/src/coverage/files.rs | 167 + xtask/src/coverage/mod.rs | 260 + xtask/src/coverage/test262 | 1 + xtask/src/docgen/extract.rs | 253 + xtask/src/docgen/mod.rs | 212 + xtask/src/glue.rs | 213 + xtask/src/lib.rs | 62 + xtask/src/main.rs | 63 +- 550 files changed, 43824 insertions(+), 26 deletions(-) create mode 100644 .gitmodules create mode 100644 crates/rslint_errors/CHANGELOG.md create mode 100644 crates/rslint_errors/Cargo.toml create mode 100644 crates/rslint_errors/src/codespan/LICENSE create mode 100644 crates/rslint_errors/src/codespan/README.md create mode 100644 crates/rslint_errors/src/codespan/diagnostic.rs create mode 100644 crates/rslint_errors/src/codespan/files.rs create mode 100644 crates/rslint_errors/src/codespan/mod.rs create mode 100644 crates/rslint_errors/src/codespan/term.rs create mode 100644 crates/rslint_errors/src/codespan/term/config.rs create mode 100644 crates/rslint_errors/src/codespan/term/renderer.rs create mode 100644 crates/rslint_errors/src/codespan/term/views.rs create mode 100644 crates/rslint_errors/src/diagnostic.rs create mode 100644 crates/rslint_errors/src/emit.rs create mode 100644 crates/rslint_errors/src/file.rs create mode 100644 crates/rslint_errors/src/formatters.rs create mode 100644 crates/rslint_errors/src/lib.rs create mode 100644 crates/rslint_errors/src/lsp.rs create mode 100644 crates/rslint_errors/src/suggestion.rs create mode 100644 crates/rslint_lexer/CHANGELOG.md create mode 100644 crates/rslint_lexer/Cargo.toml create mode 100644 crates/rslint_lexer/scripts/gen.py create mode 100644 crates/rslint_lexer/src/highlight.rs create mode 100644 crates/rslint_lexer/src/lib.rs create mode 100644 crates/rslint_lexer/src/state.rs create mode 100644 crates/rslint_lexer/src/tables.rs create mode 100644 crates/rslint_lexer/src/tests.rs create mode 100644 crates/rslint_lexer/src/token.rs create mode 100644 crates/rslint_parser/CHANGELOG.md create mode 100644 crates/rslint_parser/Cargo.toml create mode 100644 crates/rslint_parser/src/ast.rs create mode 100644 crates/rslint_parser/src/ast/expr_ext.rs create mode 100644 crates/rslint_parser/src/ast/generated.rs create mode 100644 crates/rslint_parser/src/ast/generated/nodes.rs create mode 100644 crates/rslint_parser/src/ast/generated/tokens.rs create mode 100644 crates/rslint_parser/src/ast/stmt_ext.rs create mode 100644 crates/rslint_parser/src/ast/ts_ext.rs create mode 100644 crates/rslint_parser/src/event.rs create mode 100644 crates/rslint_parser/src/lib.rs create mode 100644 crates/rslint_parser/src/lossless_tree_sink.rs create mode 100644 crates/rslint_parser/src/lossy_tree_sink.rs create mode 100644 crates/rslint_parser/src/numbers.rs create mode 100644 crates/rslint_parser/src/parse.rs create mode 100644 crates/rslint_parser/src/parser.rs create mode 100644 crates/rslint_parser/src/state.rs create mode 100644 crates/rslint_parser/src/syntax.rs create mode 100644 crates/rslint_parser/src/syntax/decl.rs create mode 100644 crates/rslint_parser/src/syntax/expr.rs create mode 100644 crates/rslint_parser/src/syntax/pat.rs create mode 100644 crates/rslint_parser/src/syntax/program.rs create mode 100644 crates/rslint_parser/src/syntax/stmt.rs create mode 100644 crates/rslint_parser/src/syntax/typescript.rs create mode 100644 crates/rslint_parser/src/syntax/util.rs create mode 100644 crates/rslint_parser/src/syntax_node.rs create mode 100644 crates/rslint_parser/src/tests.rs create mode 100644 crates/rslint_parser/src/token_set.rs create mode 100644 crates/rslint_parser/src/token_source.rs create mode 100644 crates/rslint_parser/src/util.rs create mode 100644 crates/rslint_parser/test_data/inline/err/async_arrow_expr_await_parameter.js create mode 100644 crates/rslint_parser/test_data/inline/err/async_arrow_expr_await_parameter.rast create mode 100644 crates/rslint_parser/test_data/inline/err/binary_expressions_err.js create mode 100644 crates/rslint_parser/test_data/inline/err/binary_expressions_err.rast create mode 100644 crates/rslint_parser/test_data/inline/err/binding_identifier_invalid.js create mode 100644 crates/rslint_parser/test_data/inline/err/binding_identifier_invalid.rast create mode 100644 crates/rslint_parser/test_data/inline/err/block_stmt_in_class.js create mode 100644 crates/rslint_parser/test_data/inline/err/block_stmt_in_class.rast create mode 100644 crates/rslint_parser/test_data/inline/err/bracket_expr_err.js create mode 100644 crates/rslint_parser/test_data/inline/err/bracket_expr_err.rast create mode 100644 crates/rslint_parser/test_data/inline/err/class_decl_err.js create mode 100644 crates/rslint_parser/test_data/inline/err/class_decl_err.rast create mode 100644 crates/rslint_parser/test_data/inline/err/conditional_expr_err.js create mode 100644 crates/rslint_parser/test_data/inline/err/conditional_expr_err.rast create mode 100644 crates/rslint_parser/test_data/inline/err/do_while_stmt_err.js create mode 100644 crates/rslint_parser/test_data/inline/err/do_while_stmt_err.rast create mode 100644 crates/rslint_parser/test_data/inline/err/export_decl_not_top_level.js create mode 100644 crates/rslint_parser/test_data/inline/err/export_decl_not_top_level.rast create mode 100644 crates/rslint_parser/test_data/inline/err/for_stmt_err.js create mode 100644 crates/rslint_parser/test_data/inline/err/for_stmt_err.rast create mode 100644 crates/rslint_parser/test_data/inline/err/formal_params_no_binding_element.js create mode 100644 crates/rslint_parser/test_data/inline/err/formal_params_no_binding_element.rast create mode 100644 crates/rslint_parser/test_data/inline/err/function_decl_err.js create mode 100644 crates/rslint_parser/test_data/inline/err/function_decl_err.rast create mode 100644 crates/rslint_parser/test_data/inline/err/if_stmt_err.js create mode 100644 crates/rslint_parser/test_data/inline/err/if_stmt_err.rast create mode 100644 crates/rslint_parser/test_data/inline/err/import_call_no_arg.js create mode 100644 crates/rslint_parser/test_data/inline/err/import_call_no_arg.rast create mode 100644 crates/rslint_parser/test_data/inline/err/import_decl_not_top_level.js create mode 100644 crates/rslint_parser/test_data/inline/err/import_decl_not_top_level.rast create mode 100644 crates/rslint_parser/test_data/inline/err/import_no_meta.js create mode 100644 crates/rslint_parser/test_data/inline/err/import_no_meta.rast create mode 100644 crates/rslint_parser/test_data/inline/err/invalid_arg_list.js create mode 100644 crates/rslint_parser/test_data/inline/err/invalid_arg_list.rast create mode 100644 crates/rslint_parser/test_data/inline/err/invalid_method_recover.js create mode 100644 crates/rslint_parser/test_data/inline/err/invalid_method_recover.rast create mode 100644 crates/rslint_parser/test_data/inline/err/method_getter_err.js create mode 100644 crates/rslint_parser/test_data/inline/err/method_getter_err.rast create mode 100644 crates/rslint_parser/test_data/inline/err/object_expr_error_prop_name.js create mode 100644 crates/rslint_parser/test_data/inline/err/object_expr_error_prop_name.rast create mode 100644 crates/rslint_parser/test_data/inline/err/object_expr_non_ident_literal_prop.js create mode 100644 crates/rslint_parser/test_data/inline/err/object_expr_non_ident_literal_prop.rast create mode 100644 crates/rslint_parser/test_data/inline/err/paren_or_arrow_expr_invalid_params.js create mode 100644 crates/rslint_parser/test_data/inline/err/paren_or_arrow_expr_invalid_params.rast create mode 100644 crates/rslint_parser/test_data/inline/err/primary_expr_invalid_recovery.js create mode 100644 crates/rslint_parser/test_data/inline/err/primary_expr_invalid_recovery.rast create mode 100644 crates/rslint_parser/test_data/inline/err/return_stmt_err.js create mode 100644 crates/rslint_parser/test_data/inline/err/return_stmt_err.rast create mode 100644 crates/rslint_parser/test_data/inline/err/semicolons_err.js create mode 100644 crates/rslint_parser/test_data/inline/err/semicolons_err.rast create mode 100644 crates/rslint_parser/test_data/inline/err/subscripts_err.js create mode 100644 crates/rslint_parser/test_data/inline/err/subscripts_err.rast create mode 100644 crates/rslint_parser/test_data/inline/err/switch_stmt_err.js create mode 100644 crates/rslint_parser/test_data/inline/err/switch_stmt_err.rast create mode 100644 crates/rslint_parser/test_data/inline/err/template_literal_unterminated.js create mode 100644 crates/rslint_parser/test_data/inline/err/template_literal_unterminated.rast create mode 100644 crates/rslint_parser/test_data/inline/err/throw_stmt_err.js create mode 100644 crates/rslint_parser/test_data/inline/err/throw_stmt_err.rast create mode 100644 crates/rslint_parser/test_data/inline/err/unterminated_unicode_codepoint.js create mode 100644 crates/rslint_parser/test_data/inline/err/unterminated_unicode_codepoint.rast create mode 100644 crates/rslint_parser/test_data/inline/err/var_decl_err.js create mode 100644 crates/rslint_parser/test_data/inline/err/var_decl_err.rast create mode 100644 crates/rslint_parser/test_data/inline/err/while_stmt_err.js create mode 100644 crates/rslint_parser/test_data/inline/err/while_stmt_err.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/array_expr.js create mode 100644 crates/rslint_parser/test_data/inline/ok/array_expr.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/arrow_expr_single_param.js create mode 100644 crates/rslint_parser/test_data/inline/ok/arrow_expr_single_param.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/assign_expr.js create mode 100644 crates/rslint_parser/test_data/inline/ok/assign_expr.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/async_arrow_expr.js create mode 100644 crates/rslint_parser/test_data/inline/ok/async_arrow_expr.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/async_function_expr.js create mode 100644 crates/rslint_parser/test_data/inline/ok/async_function_expr.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/async_ident.js create mode 100644 crates/rslint_parser/test_data/inline/ok/async_ident.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/async_method.js create mode 100644 crates/rslint_parser/test_data/inline/ok/async_method.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/binary_expressions.js create mode 100644 crates/rslint_parser/test_data/inline/ok/binary_expressions.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/block_stmt.js create mode 100644 crates/rslint_parser/test_data/inline/ok/block_stmt.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/bracket_expr.js create mode 100644 crates/rslint_parser/test_data/inline/ok/bracket_expr.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/break_stmt.js create mode 100644 crates/rslint_parser/test_data/inline/ok/break_stmt.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/class_decl.js create mode 100644 crates/rslint_parser/test_data/inline/ok/class_decl.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/class_empty_element.js create mode 100644 crates/rslint_parser/test_data/inline/ok/class_empty_element.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/class_expr.js create mode 100644 crates/rslint_parser/test_data/inline/ok/class_expr.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/conditional_expr.js create mode 100644 crates/rslint_parser/test_data/inline/ok/conditional_expr.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/continue_stmt.js create mode 100644 crates/rslint_parser/test_data/inline/ok/continue_stmt.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/debugger_stmt.js create mode 100644 crates/rslint_parser/test_data/inline/ok/debugger_stmt.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/dot_expr.js create mode 100644 crates/rslint_parser/test_data/inline/ok/dot_expr.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/empty_stmt.js create mode 100644 crates/rslint_parser/test_data/inline/ok/empty_stmt.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/export.js create mode 100644 crates/rslint_parser/test_data/inline/ok/export.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/for_stmt.js create mode 100644 crates/rslint_parser/test_data/inline/ok/for_stmt.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/function_decl.js create mode 100644 crates/rslint_parser/test_data/inline/ok/function_decl.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/function_expr.js create mode 100644 crates/rslint_parser/test_data/inline/ok/function_expr.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/grouping_expr.js create mode 100644 crates/rslint_parser/test_data/inline/ok/grouping_expr.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/identifier_reference.js create mode 100644 crates/rslint_parser/test_data/inline/ok/identifier_reference.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/if_stmt.js create mode 100644 crates/rslint_parser/test_data/inline/ok/if_stmt.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/import_call.js create mode 100644 crates/rslint_parser/test_data/inline/ok/import_call.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/import_decl.js create mode 100644 crates/rslint_parser/test_data/inline/ok/import_decl.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/import_meta.js create mode 100644 crates/rslint_parser/test_data/inline/ok/import_meta.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/literals.js create mode 100644 crates/rslint_parser/test_data/inline/ok/literals.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/method_getter.js create mode 100644 crates/rslint_parser/test_data/inline/ok/method_getter.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/method_setter.js create mode 100644 crates/rslint_parser/test_data/inline/ok/method_setter.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/new_exprs.js create mode 100644 crates/rslint_parser/test_data/inline/ok/new_exprs.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/object_binding_prop.js create mode 100644 crates/rslint_parser/test_data/inline/ok/object_binding_prop.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr.js create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr_assign_prop.js create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr_assign_prop.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr_async_method.js create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr_async_method.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr_generator_method.js create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr_generator_method.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr_getter_setter.js create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr_getter_setter.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr_getter_setter_as_prop.js create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr_getter_setter_as_prop.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr_getter_setter_computed.js create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr_getter_setter_computed.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr_ident_literal_prop.js create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr_ident_literal_prop.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr_ident_prop.js create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr_ident_prop.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr_method.js create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr_method.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr_spread_prop.js create mode 100644 crates/rslint_parser/test_data/inline/ok/object_expr_spread_prop.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/object_prop_name.js create mode 100644 crates/rslint_parser/test_data/inline/ok/object_prop_name.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/paren_or_arrow_expr.js create mode 100644 crates/rslint_parser/test_data/inline/ok/paren_or_arrow_expr.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/postfix_expr.js create mode 100644 crates/rslint_parser/test_data/inline/ok/postfix_expr.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/return_stmt.js create mode 100644 crates/rslint_parser/test_data/inline/ok/return_stmt.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/semicolons.js create mode 100644 crates/rslint_parser/test_data/inline/ok/semicolons.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/sequence_expr.js create mode 100644 crates/rslint_parser/test_data/inline/ok/sequence_expr.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/static_method.js create mode 100644 crates/rslint_parser/test_data/inline/ok/static_method.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/subscripts.js create mode 100644 crates/rslint_parser/test_data/inline/ok/subscripts.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/switch_stmt.js create mode 100644 crates/rslint_parser/test_data/inline/ok/switch_stmt.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/template_literal.js create mode 100644 crates/rslint_parser/test_data/inline/ok/template_literal.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/this_expr.js create mode 100644 crates/rslint_parser/test_data/inline/ok/this_expr.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/throw_stmt.js create mode 100644 crates/rslint_parser/test_data/inline/ok/throw_stmt.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/try_stmt.js create mode 100644 crates/rslint_parser/test_data/inline/ok/try_stmt.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/var_decl.js create mode 100644 crates/rslint_parser/test_data/inline/ok/var_decl.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/while_stmt.js create mode 100644 crates/rslint_parser/test_data/inline/ok/while_stmt.rast create mode 100644 crates/rslint_parser/test_data/inline/ok/yield_expr.js create mode 100644 crates/rslint_parser/test_data/inline/ok/yield_expr.rast create mode 100644 crates/rslint_parser/test_data/ts/err/arrow-function/async-rest-optional-parameter/input.ts create mode 100644 crates/rslint_parser/test_data/ts/err/class/declare-private-name/input.ts create mode 100644 crates/rslint_parser/test_data/ts/err/class/input.ts create mode 100644 crates/rslint_parser/test_data/ts/err/class/method-readonly/input.ts create mode 100644 crates/rslint_parser/test_data/ts/err/class/parameter-properties-binding-patterns/input.ts create mode 100644 crates/rslint_parser/test_data/ts/err/class/parameter-properties/input.ts create mode 100644 crates/rslint_parser/test_data/ts/err/custom/const-enum-2/input.ts create mode 100644 crates/rslint_parser/test_data/ts/err/custom/const-enum/input.ts create mode 100644 crates/rslint_parser/test_data/ts/err/custom/issue-380/input.ts create mode 100644 crates/rslint_parser/test_data/ts/err/custom/top-level-await-jsc-target/input.ts create mode 100644 crates/rslint_parser/test_data/ts/err/decorators/type-arguments-invalid/input.ts create mode 100644 crates/rslint_parser/test_data/ts/err/issue-1170-1/input.ts create mode 100644 crates/rslint_parser/test_data/ts/err/nullish-coalescing-operator/no-paren-and-nullish/input.ts create mode 100644 crates/rslint_parser/test_data/ts/err/nullish-coalescing-operator/no-paren-nullish-and/input.ts create mode 100644 crates/rslint_parser/test_data/ts/err/nullish-coalescing-operator/no-paren-nullish-or/input.ts create mode 100644 crates/rslint_parser/test_data/ts/err/nullish-coalescing-operator/no-paren-or-nullish/input.ts create mode 100644 crates/rslint_parser/test_data/ts/err/type-arguments/input.ts create mode 100644 crates/rslint_parser/test_data/ts/err/types/tuple-optional-invalid/input.ts create mode 100644 crates/rslint_parser/test_data/ts/err/variable-declarator/definite-assignment-not-allowed/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/arrow-function/annotated/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/arrow-function/async-generic-false-positive/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/arrow-function/async-rest/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/arrow-function/async/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/arrow-function/default-parameter-values/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/arrow-function/destructuring/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/arrow-function/generic-tsx/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/arrow-function/generic/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/arrow-function/inside-conditional-expr/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/arrow-function/optional-parameter/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/arrow-function/predicate-types/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/cast/as/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/cast/assert-and-assign/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/cast/false-positive/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/cast/multiple-assert-and-assign/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/cast/need-parentheses/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/cast/null-assertion-3/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/cast/null-assertion-and-assign-2/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/cast/null-assertion-and-assign/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/cast/null-assertion-false-positive/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/cast/null-assertion-then-property-access/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/cast/null-assertion/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/cast/type-assertion-after-operator/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/cast/type-assertion-and-assign/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/cast/type-assertion-before-operator/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/cast/type-assertion/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/abstract-false-positive/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/abstract/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/constructor-with-modifier-names/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/constructor/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/declare/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/decorators/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/expression-extends-implements/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/expression-extends/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/expression-generic/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/expression-implements/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/extends-implements/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/extends/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/generic/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/get-generic/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/implements/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/index-signature/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/members-with-modifier-names/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/members-with-reserved-names/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/method-computed/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/method-generic/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/method-no-body/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/method-optional/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/method-return-type/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/method-with-newline-with-body/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/method-with-newline-without-body/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/modifiers-accessors/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/modifiers-methods-async/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/modifiers-properties/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/properties/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/property-computed/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/property-private/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/class/static/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/arrow/complex-tsc/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/arrow/complex/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/bigint/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/const-enum/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/default-followed-by-type/arrow/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/default-followed-by-type/function/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/dynamic-import-expr-ctx/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/dynamic-import-top-level/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/import-meta-expr-ctx/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/import-meta-top-ctx/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/import-type/typeof/as/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/import-type/typeof/simple/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-196-01/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-196-02/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-236/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-236/input.ts.stdout create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-259/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-327-1/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-327-2/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-338/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-370/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-374/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-401-const-assertion-array-ts/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-401-const-assertion-array/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-401-const-assertion-literal-ts/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-401-const-assertion-literal/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-401-const-assertion-object-ts/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-401-const-assertion-object/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-410-1/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-410-2/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-432-readonly-array/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-433-1/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-433-2/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-461/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-496/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-535/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-538/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-612-async-generator/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-623/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-709-2/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-709-3/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-709/input.d.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-716/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-717/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/issue-720/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/ternary-paren/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/ternary/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/type-only/export/aliased/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/type-only/export/specific/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/type-only/import/aliased/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/type-only/import/default/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/type-only/import/specific/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/yield-object-paren/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/custom/yield-object/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/declare/const/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/declare/destructure/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/declare/interface/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/declare/let/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/declare/var/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/decorators/parameter/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/decorators/type-arguments/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/enum/const/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/enum/declare-const/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/enum/declare/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/enum/export-const/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/enum/export-declare-const/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/enum/export/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/enum/members-reserved-words/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/enum/members-strings/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/enum/members-trailing-comma-with-initializer/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/enum/members-trailing-comma/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/enum/members/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/es2019/from-entries/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/estree-compat/shorthand-ambient-module/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/export-default-interface/index.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/export/as-namespace/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/export/declare/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/export/equals/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/export/namespace-from/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/export/nested-same-name/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/function/annotated/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/function/anonymous/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/function/declare/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/function/export-default/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/function/overloads/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/function/predicate-types/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/import/equals-require/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/import/equals/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/import/export-import-require/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/import/export-import/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/import/not-top-level/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/interface/call-signature/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/interface/construct-signature/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/interface/export/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/interface/extends/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/interface/generic/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/interface/index-signature/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/interface/method-computed/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/interface/method-generic/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/interface/method-optional/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/interface/method-plain/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/interface/modifiers/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/interface/properties/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/interface/property-computed/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/interface/property-named-public/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/interface/reserved-method-name/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/interface/separators/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/issue-812/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/issue-814-2/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/issue-814/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/issue-818-1/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/issue-818-2/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/issue-861/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/issue-874/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/issue-896/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/issue-913/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/issue-915/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/issue-944/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/issue-947/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/module-namespace/body-declare/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/module-namespace/body-nested-declare/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/module-namespace/body-nested/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/module-namespace/body/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/module-namespace/declare-shorthand/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/module-namespace/global-in-module/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/module-namespace/head-declare/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/module-namespace/head-export/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/module-namespace/head/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/nullish-coalescing-operator/and-nullish/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/nullish-coalescing-operator/associativity/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/nullish-coalescing-operator/expression/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/nullish-coalescing-operator/multiline/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/nullish-coalescing-operator/nullish-and/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/nullish-coalescing-operator/nullish-or/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/nullish-coalescing-operator/or-nullish/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/object/getter-prop/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/object/method-async-generator/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/regression/destructuring-in-function-type/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/regression/is-default-export/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/regression/tsx-issue-7742/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/stack-size/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/template-literal-type/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/type-alias/declare/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/type-alias/export/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/type-alias/generic-complex/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/type-alias/generic/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/type-alias/plain/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/type-arguments/call/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/type-arguments/new-false-positive/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/type-arguments/new/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/type-arguments/tagged-template-no-asi/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/type-arguments/tagged-template/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/type-arguments/whitespace/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/array/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/conditional-infer/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/conditional/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/function-generic/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/function-in-generic/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/function-with-this/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/function/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/indexed/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/keywords/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/literal-boolean/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/literal-number-negative/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/literal-number/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/literal-string/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/mapped/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/object-shorthand/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/parenthesized/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/reference-generic-nested/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/reference-generic/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/reference/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/tuple-empty/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/tuple-optional/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/tuple-rest-after-optional/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/tuple-rest/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/tuple/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/type-literal/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/type-operator/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/typeof/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/types/union-intersection/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/v4/issue-864/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/v4/issue-865/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/v4/issue-866/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/v4/issue-941/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/v4/optional-tuple-element/input.ts create mode 100644 crates/rslint_parser/test_data/ts/ok/variable-declarator/definite-assignment/input.ts create mode 100644 crates/rslint_regex/Cargo.toml create mode 100644 crates/rslint_regex/benches/regex.rs create mode 100644 crates/rslint_regex/src/ir.rs create mode 100644 crates/rslint_regex/src/lib.rs create mode 100644 crates/rslint_regex/src/parser.rs create mode 100644 crates/rslint_regex/src/tests.rs create mode 100644 crates/rslint_regex/src/unicode.rs create mode 100644 crates/rslint_rowan/Cargo.toml create mode 100644 crates/rslint_rowan/README.md create mode 100644 crates/rslint_rowan/src/api.rs create mode 100644 crates/rslint_rowan/src/arc.rs create mode 100644 crates/rslint_rowan/src/cursor.rs create mode 100644 crates/rslint_rowan/src/green.rs create mode 100644 crates/rslint_rowan/src/green/builder.rs create mode 100644 crates/rslint_rowan/src/green/element.rs create mode 100644 crates/rslint_rowan/src/green/node.rs create mode 100644 crates/rslint_rowan/src/green/token.rs create mode 100644 crates/rslint_rowan/src/lib.rs create mode 100644 crates/rslint_rowan/src/serde_impls.rs create mode 100644 crates/rslint_rowan/src/syntax_text.rs create mode 100644 crates/rslint_rowan/src/utility_types.rs create mode 100644 crates/rslint_syntax/Cargo.toml create mode 100644 crates/rslint_syntax/src/generated.rs create mode 100644 crates/rslint_syntax/src/lib.rs create mode 100644 crates/rslint_text_edit/Cargo.toml create mode 100644 crates/rslint_text_edit/src/lib.rs create mode 100644 xtask/src/ast.rs create mode 100644 xtask/src/codegen.rs create mode 100644 xtask/src/codegen/parser_tests.rs create mode 100644 xtask/src/codegen/syntax.rs create mode 100644 xtask/src/coverage/files.rs create mode 100644 xtask/src/coverage/mod.rs create mode 160000 xtask/src/coverage/test262 create mode 100644 xtask/src/docgen/extract.rs create mode 100644 xtask/src/docgen/mod.rs create mode 100644 xtask/src/glue.rs create mode 100644 xtask/src/lib.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 80c62e4f62c..2fa075bb56b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,3 +76,22 @@ jobs: with: command: test args: --workspace --verbose + + coverage: + name: Test262 coverage + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + submodules: recursive + - name: Install toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + - name: Cache + uses: Swatinem/rust-cache@v1 + - name: Run Test262 suite + continue-on-error: true + run: cargo xtask coverage diff --git a/.gitignore b/.gitignore index ce8a008d6ee..ab8fd27eac4 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ Profile-*.json .vscode .idea # Release artifacts -/target +**/target # Ignore test files that contributors could create locally /test.* # Ignore lockfiles @@ -12,3 +12,5 @@ yarn.lock package-lock.json npm-shrinkwrap.json pnpm-lock.yaml +**/node_modules +**/dist diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000000..89d0d41eb9e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "xtask/src/coverage/test262"] + path = xtask/src/coverage/test262 + url = https://github.com/tc39/test262 diff --git a/Cargo.lock b/Cargo.lock index ead5b6805b9..199bfa1bb19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,12 +11,33 @@ dependencies = [ "memchr", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "anyhow" version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "ascii_table" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f0a58f3b1453981b910b603a4a7346be14fccd50f8edd7c954725a9210c24f" + [[package]] name = "atty" version = "0.2.14" @@ -34,18 +55,74 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cast" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" +dependencies = [ + "rustc_version", +] + [[package]] name = "cc" version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "bitflags", + "textwrap 0.11.0", + "unicode-width", +] + [[package]] name = "clap" version = "3.0.0-beta.4" @@ -60,7 +137,7 @@ dependencies = [ "os_str_bytes", "strsim", "termcolor", - "textwrap", + "textwrap 0.14.2", "vec_map", ] @@ -81,16 +158,245 @@ dependencies = [ name = "cli" version = "0.0.0" dependencies = [ - "clap", + "clap 3.0.0-beta.4", "rome-formatter", ] +[[package]] +name = "colored" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +dependencies = [ + "atty", + "lazy_static", + "winapi", +] + +[[package]] +name = "console" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "regex", + "terminal_size", + "unicode-width", + "winapi", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "countme" version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "328b822bdcba4d4e402be8d9adb6eebf269f969f8eadef977a553ff3c4fbcb58" +[[package]] +name = "criterion" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10" +dependencies = [ + "atty", + "cast", + "clap 2.33.3", + "criterion-plot", + "csv", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +dependencies = [ + "cfg-if", + "lazy_static", +] + +[[package]] +name = "csv" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +dependencies = [ + "bstr", + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + +[[package]] +name = "dissimilar" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ad93652f40969dead8d4bf897a41e9462095152eb21c56e5830537e41179dd" + +[[package]] +name = "dtoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" + +[[package]] +name = "dyn-clone" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf" + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "expect-test" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2300477aab3a378f2ca00a4fbd4dc713654ab7ed790e4017493cb33656280633" +dependencies = [ + "dissimilar", + "once_cell", +] + +[[package]] +name = "flume" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24c3fd473b3a903a62609e413ed7538f99e10b665ecb502b5e481a95283f8ab4" +dependencies = [ + "spin", +] + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "half" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" + [[package]] name = "hashbrown" version = "0.11.2" @@ -115,6 +421,17 @@ dependencies = [ "libc", ] +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indexmap" version = "1.7.0" @@ -126,24 +443,132 @@ dependencies = [ "serde", ] +[[package]] +name = "indicatif" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7baab56125e25686df467fe470785512329883aab42696d661247aca2a2896e4" +dependencies = [ + "console", + "lazy_static", + "number_prefix", + "regex", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "itertools" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" +[[package]] +name = "js-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lexical" +version = "5.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f404a90a744e32e8be729034fc33b90cf2a56418fbf594d69aa3c0214ad414e5" +dependencies = [ + "cfg-if", + "lexical-core", +] + +[[package]] +name = "lexical-core" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if", + "ryu", + "static_assertions", +] + [[package]] name = "libc" version = "0.2.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2a5ac8f984bfcf3a823267e5fde638acc3325f6496633a5da6bb6eb2171e103" +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "lsp-types" +version = "0.81.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e02724627e9ef8ba91f461ebc01d48aebbd13a4b7c9dc547a0a2890f53e2171" +dependencies = [ + "base64", + "bitflags", + "serde", + "serde_json", + "serde_repr", + "url", +] + +[[package]] +name = "m_lexer" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7e51ebf91162d585a5bae05e4779efc4a276171cb880d61dd6fab11c98467a7" +dependencies = [ + "regex", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + [[package]] name = "memchr" version = "2.4.1" @@ -159,6 +584,70 @@ dependencies = [ "autocfg", ] +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "num-bigint" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "number_prefix" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b02fc0ff9a9e4b35b3342880f48e896ebf69f2967921fe8646bf5b7125956a" + +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + [[package]] name = "os_str_bytes" version = "3.1.0" @@ -174,6 +663,52 @@ dependencies = [ "tree-sitter-typescript", ] +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pico-args" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1" + +[[package]] +name = "plotters" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" + +[[package]] +name = "plotters-svg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -188,32 +723,121 @@ dependencies = [ ] [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quickcheck" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" +dependencies = [ + "env_logger", + "log", + "rand", + "rand_core", +] + +[[package]] +name = "quickcheck_macros" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608c156fd8e97febc07dc9c2e2c80bf74cfc6ef26893eae3daf8bc2bc94a4b7f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "proc-macro2", - "quote", - "version_check", + "rand_core", ] [[package]] -name = "proc-macro2" -version = "1.0.29" +name = "rayon" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" dependencies = [ - "unicode-xid", + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", ] [[package]] -name = "quote" -version = "1.0.9" +name = "rayon-core" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" dependencies = [ - "proc-macro2", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", ] [[package]] @@ -227,6 +851,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + [[package]] name = "regex-syntax" version = "0.6.25" @@ -254,18 +884,145 @@ dependencies = [ "text-size", ] +[[package]] +name = "rslint_errors" +version = "0.2.0" +dependencies = [ + "colored", + "lsp-types", + "rslint_rowan", + "rslint_text_edit", + "serde", + "termcolor", + "unicode-width", +] + +[[package]] +name = "rslint_lexer" +version = "0.2.0" +dependencies = [ + "ansi_term", + "atty", + "quickcheck", + "quickcheck_macros", + "rslint_errors", + "rslint_syntax", +] + +[[package]] +name = "rslint_parser" +version = "0.3.0" +dependencies = [ + "expect-test", + "lexical", + "num-bigint", + "rslint_errors", + "rslint_lexer", + "rslint_rowan", + "rslint_syntax", +] + +[[package]] +name = "rslint_regex" +version = "0.3.0" +dependencies = [ + "bitflags", + "criterion", + "once_cell", + "rslint_errors", +] + +[[package]] +name = "rslint_rowan" +version = "0.10.0" +dependencies = [ + "fxhash", + "m_lexer", + "nodrop", + "serde", + "smallvec", + "smol_str", + "stable_deref_trait", + "text-size", +] + +[[package]] +name = "rslint_syntax" +version = "0.1.4" + +[[package]] +name = "rslint_text_edit" +version = "0.1.0" +dependencies = [ + "rslint_rowan", +] + [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "ryu" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schemars" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a48d098c2a7fdf5740b19deb1181b4fb8a9e68e03ae517c14cde04b5725409" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9ea2a613fe4cd7118b2bb101a25d8ae6192e1975179b67b2f17afd11e70ac8" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" + [[package]] name = "serde" version = "1.0.130" @@ -275,6 +1032,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + [[package]] name = "serde_derive" version = "1.0.130" @@ -286,6 +1053,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_derive_internals" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dbab34ca63057a1f15280bdf3c39f2b1eb1b54c17e98360e511637aef7418c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "serde_json" version = "1.0.68" @@ -298,6 +1076,62 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_yaml" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8c608a35705a5d3cdc9fbe403147647ff34b921f8e833e49306df898f9b20af" +dependencies = [ + "dtoa", + "indexmap", + "serde", + "yaml-rust", +] + +[[package]] +name = "smallvec" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" + +[[package]] +name = "smol_str" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b203e79e90905594272c1c97c7af701533d42adaab0beb3859018e477d54a3b0" + +[[package]] +name = "spin" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.10.0" @@ -348,11 +1182,33 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "text-size" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a" +dependencies = [ + "serde", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] [[package]] name = "textwrap" @@ -363,6 +1219,31 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tinyvec" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + [[package]] name = "tree-sitter" version = "0.19.5" @@ -383,6 +1264,21 @@ dependencies = [ "tree-sitter", ] +[[package]] +name = "unicode-bidi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246f4c42e67e7a4e3c6106ff716a5d067d4132a642840b242e357e468a2a0085" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-segmentation" version = "1.8.0" @@ -401,6 +1297,25 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "unindent" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", + "serde", +] + [[package]] name = "vec_map" version = "0.8.2" @@ -413,6 +1328,87 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasm-bindgen" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" + +[[package]] +name = "web-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -464,4 +1460,43 @@ name = "xtask" version = "0.0.0" dependencies = [ "anyhow", + "ascii_table", + "colored", + "convert_case", + "indicatif", + "num_cpus", + "once_cell", + "pico-args", + "proc-macro2", + "quote", + "regex", + "rslint_errors", + "rslint_parser", + "schemars", + "serde", + "serde_json", + "serde_yaml", + "syn", + "unindent", + "walkdir", + "yastl", +] + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "yastl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca6c5a4d66c1a9ea261811cf4773c27343de7e5033e1b75ea3f297dc7db3c1a" +dependencies = [ + "flume", + "scopeguard", ] diff --git a/crates/rslint_errors/CHANGELOG.md b/crates/rslint_errors/CHANGELOG.md new file mode 100644 index 00000000000..71d31024418 --- /dev/null +++ b/crates/rslint_errors/CHANGELOG.md @@ -0,0 +1,30 @@ +# Changelog + +All notable changes to this crate will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), + +## [Unreleased] + +## [0.2.0] - 2021-09-16 + +### Fixed + +- Fixed lsp diagnostic conversion not working correctly + +### Added + +- Added the `Formatter` trait for describing structs which can emit diagnostics in a certain way +- Added the `ShortFormatter` which emits diagnostics in an eslint-like style + +### Changed + +- Changed codespan backend to render notes with severity correctly +- Improved note rendering to account for severity length and indent correctly, as well as add a border + between lines if the note spans multiple lines + +## [0.1.1] + +### Added + +- Added multiple utility methods to `Span` diff --git a/crates/rslint_errors/Cargo.toml b/crates/rslint_errors/Cargo.toml new file mode 100644 index 00000000000..d56c79ac34c --- /dev/null +++ b/crates/rslint_errors/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "rslint_errors" +version = "0.2.0" +authors = ["RSLint developers"] +edition = "2018" +license = "MIT" +description = "Pretty error reporting library based on codespan-reporting built for the RSLint project" + +[dependencies] +rslint_rowan = { path = "../rslint_rowan", version = "0.10.0" } +unicode-width = "0.1.8" +serde = { version = "1.0.117", optional = true, features = ["derive"] } +lsp-types = { version = ">=0.79, <0.82", optional = true } +rslint_text_edit = { version = "0.1", path = "../rslint_text_edit" } +termcolor = "1" +colored = "2.0.0" + +[features] +lsp = ["lsp-types"] diff --git a/crates/rslint_errors/src/codespan/LICENSE b/crates/rslint_errors/src/codespan/LICENSE new file mode 100644 index 00000000000..261eeb9e9f8 --- /dev/null +++ b/crates/rslint_errors/src/codespan/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/crates/rslint_errors/src/codespan/README.md b/crates/rslint_errors/src/codespan/README.md new file mode 100644 index 00000000000..b784330fa91 --- /dev/null +++ b/crates/rslint_errors/src/codespan/README.md @@ -0,0 +1,7 @@ +# emitter backend + +This folder contains the emitter and renderer for rslint_errors. The emitter is essentially +codespan-reporting but with some hacks here and there to get it to display custom things for us and act as a module +and not a standalone crate. + +**All credit goes to the authors of [codespan](https://github.com/brendanzab/codespan)** diff --git a/crates/rslint_errors/src/codespan/diagnostic.rs b/crates/rslint_errors/src/codespan/diagnostic.rs new file mode 100644 index 00000000000..e267a71aa9c --- /dev/null +++ b/crates/rslint_errors/src/codespan/diagnostic.rs @@ -0,0 +1,205 @@ +//! Diagnostic data structures. + +#[cfg(feature = "serialization")] +use serde::{Deserialize, Serialize}; +use std::ops::Range; + +/// A severity level for diagnostic messages. +/// +/// These are ordered in the following way: +#[derive(Copy, Clone, PartialEq, Hash, Debug)] +#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] +pub enum Severity { + /// An unexpected bug. + Bug, + /// An error. + Error, + /// A warning. + Warning, + /// A note. + Note, + /// A help message. + Help, +} + +impl Severity { + /// We want bugs to be the maximum severity, errors next, etc... + fn to_cmp_int(self) -> u8 { + match self { + Severity::Bug => 5, + Severity::Error => 4, + Severity::Warning => 3, + Severity::Note => 2, + Severity::Help => 1, + } + } +} + +impl PartialOrd for Severity { + fn partial_cmp(&self, other: &Severity) -> Option { + u8::partial_cmp(&self.to_cmp_int(), &other.to_cmp_int()) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)] +#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] +pub enum LabelStyle { + /// Labels that describe the primary cause of a diagnostic. + Primary, + /// Labels that provide additional context for a diagnostic. + Secondary, +} + +/// A label describing an underlined region of code associated with a diagnostic. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] +pub struct Label { + /// The style of the label. + pub style: LabelStyle, + /// The file that we are labelling. + pub file_id: FileId, + /// The range in bytes we are going to include in the final snippet. + pub range: Range, + /// An optional message to provide some additional information for the + /// underlined code. These should not include line breaks. + pub message: String, +} + +impl Label { + /// Create a new label. + pub fn new( + style: LabelStyle, + file_id: FileId, + range: impl Into>, + ) -> Label { + Label { + style, + file_id, + range: range.into(), + message: String::new(), + } + } + + /// Create a new label with a style of [`LabelStyle::Primary`]. + /// + /// [`LabelStyle::Primary`]: LabelStyle::Primary + pub fn primary(file_id: FileId, range: impl Into>) -> Label { + Label::new(LabelStyle::Primary, file_id, range) + } + + /// Create a new label with a style of [`LabelStyle::Secondary`]. + /// + /// [`LabelStyle::Secondary`]: LabelStyle::Secondary + pub fn secondary(file_id: FileId, range: impl Into>) -> Label { + Label::new(LabelStyle::Secondary, file_id, range) + } + + /// Add a message to the diagnostic. + pub fn with_message(mut self, message: impl Into) -> Label { + self.message = message.into(); + self + } +} + +/// Represents a diagnostic message that can provide information like errors and +/// warnings to the user. +/// +/// The position of a Diagnostic is considered to be the position of the [`Label`] that has the earliest starting position and has the highest style which appears in all the labels of the diagnostic. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] +pub struct Diagnostic { + /// The overall severity of the diagnostic + pub severity: Severity, + /// An optional code that identifies this diagnostic. + pub code: Option, + /// The main message associated with this diagnostic. + /// + /// These should not include line breaks, and in order support the 'short' + /// diagnostic display mod, the message should be specific enough to make + /// sense on its own, without additional context provided by labels and notes. + pub message: String, + /// Source labels that describe the cause of the diagnostic. + /// The order of the labels inside the vector does not have any meaning. + /// The labels are always arranged in the order they appear in the source code. + pub labels: Vec>, + /// Notes that are associated with the primary cause of the diagnostic. + /// These can include line breaks for improved formatting. + pub notes: Vec, + pub anonymous: bool, + pub render_extra_empty: bool, +} + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] +pub struct Note { + pub severity: Option, + pub message: String, +} + +impl Diagnostic { + /// Create a new diagnostic. + pub fn new(severity: Severity) -> Diagnostic { + Diagnostic { + severity, + code: None, + message: String::new(), + labels: Vec::new(), + notes: Vec::new(), + anonymous: false, + render_extra_empty: false, + } + } + + /// Create a new diagnostic with a severity of [`Severity::Bug`]. + /// + /// [`Severity::Bug`]: Severity::Bug + pub fn bug() -> Diagnostic { + Diagnostic::new(Severity::Bug) + } + + /// Create a new diagnostic with a severity of [`Severity::Error`]. + /// + /// [`Severity::Error`]: Severity::Error + pub fn error() -> Diagnostic { + Diagnostic::new(Severity::Error) + } + + /// Create a new diagnostic with a severity of [`Severity::Warning`]. + /// + /// [`Severity::Warning`]: Severity::Warning + pub fn warning() -> Diagnostic { + Diagnostic::new(Severity::Warning) + } + + /// Create a new diagnostic with a severity of [`Severity::Note`]. + /// + /// [`Severity::Note`]: Severity::Note + pub fn note() -> Diagnostic { + Diagnostic::new(Severity::Note) + } + + /// Create a new diagnostic with a severity of [`Severity::Help`]. + /// + /// [`Severity::Help`]: Severity::Help + pub fn help() -> Diagnostic { + Diagnostic::new(Severity::Help) + } + + /// Add an error code to the diagnostic. + pub fn with_code(mut self, code: impl Into) -> Diagnostic { + self.code = Some(code.into()); + self + } + + /// Add a message to the diagnostic. + pub fn with_message(mut self, message: impl Into) -> Diagnostic { + self.message = message.into(); + self + } + + /// Add some labels to the diagnostic. + pub fn with_labels(mut self, labels: Vec>) -> Diagnostic { + self.labels = labels; + self + } +} diff --git a/crates/rslint_errors/src/codespan/files.rs b/crates/rslint_errors/src/codespan/files.rs new file mode 100644 index 00000000000..cf2485ce078 --- /dev/null +++ b/crates/rslint_errors/src/codespan/files.rs @@ -0,0 +1,396 @@ +//! Source file support for diagnostic reporting. +//! +//! The main trait defined in this module is the [`Files`] trait, which provides +//! provides the minimum amount of functionality required for printing [`Diagnostics`] +//! with the [`term::emit`] function. +//! +//! Simple implementations of this trait are implemented: +//! +//! - [`SimpleFile`]: For single-file use-cases +//! - [`SimpleFiles`]: For multi-file use-cases +//! +//! These data structures provide a pretty minimal API, however, +//! so end-users are encouraged to create their own implementations for their +//! own specific use-cases, such as an implementation that accesses the file +//! system directly (and caches the line start locations), or an implementation +//! using an incremental compilation library like [`salsa`]. +//! +//! [`term::emit`]: crate::term::emit +//! [`Diagnostics`]: crate::diagnostic::Diagnostic +//! [`Files`]: Files +//! [`SimpleFile`]: SimpleFile +//! [`SimpleFiles`]: SimpleFiles +//! +//! [`salsa`]: https://crates.io/crates/salsa + +use std::ops::Range; + +/// An enum representing an error that happened while looking up a file or a piece of content in that file. +#[derive(Debug)] +#[non_exhaustive] +pub enum Error { + /// A required file is not in the file database. + FileMissing, + /// The file is present, but does not contain the specified byte index. + IndexTooLarge { given: usize, max: usize }, + /// The file is present, but does not contain the specified line index. + LineTooLarge { given: usize, max: usize }, + /// The file is present and contains the specified line index, but the line does not contain the specified column index. + ColumnTooLarge { given: usize, max: usize }, + /// The given index is contained in the file, but is not a boundary of a UTF-8 code point. + InvalidCharBoundary { given: usize }, + /// There was a error while doing IO. + Io(std::io::Error), +} + +impl From for Error { + fn from(err: std::io::Error) -> Error { + Error::Io(err) + } +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::FileMissing => write!(f, "file missing"), + Error::IndexTooLarge { given, max } => { + write!(f, "invalid index {}, maximum index is {}", given, max) + } + Error::LineTooLarge { given, max } => { + write!(f, "invalid line {}, maximum line is {}", given, max) + } + Error::ColumnTooLarge { given, max } => { + write!(f, "invalid column {}, maximum column {}", given, max) + } + Error::InvalidCharBoundary { .. } => write!(f, "index is not a code point boundary"), + Error::Io(err) => write!(f, "{}", err), + } + } +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + Error::Io(err) => Some(err), + _ => None, + } + } +} + +/// A minimal interface for accessing source files when rendering diagnostics. +/// +/// A lifetime parameter `'a` is provided to allow any of the returned values to returned by reference. +/// This is to workaround the lack of higher kinded lifetime parameters. +/// This can be ignored if this is not needed, however. +pub trait Files<'a> { + /// A unique identifier for files in the file provider. This will be used + /// for rendering `diagnostic::Label`s in the corresponding source files. + type FileId: 'a + Copy + PartialEq; + /// The user-facing name of a file, to be displayed in diagnostics. + type Name: 'a + std::fmt::Display; + /// The source code of a file. + type Source: 'a + AsRef; + + /// The user-facing name of a file. + fn name(&'a self, id: Self::FileId) -> Result; + + /// The source code of a file. + fn source(&'a self, id: Self::FileId) -> Result; + + /// The index of the line at the given byte index. + /// If the byte index is past the end of the file, returns the maximum line index in the file. + /// This means that this function only fails if the file is not present. + /// + /// # Note for trait implementors + /// + /// This can be implemented efficiently by performing a binary search over + /// a list of line starts that was computed by calling the [`line_starts`] + /// function that is exported from the [`files`] module. It might be useful + /// to pre-compute and cache these line starts. + /// + /// [`line_starts`]: crate::files::line_starts + /// [`files`]: crate::files + fn line_index(&'a self, id: Self::FileId, byte_index: usize) -> Result; + + /// The user-facing line number at the given line index. + /// It is not necessarily checked that the specified line index + /// is actually in the file. + /// + /// # Note for trait implementors + /// + /// This is usually 1-indexed from the beginning of the file, but + /// can be useful for implementing something like the + /// [C preprocessor's `#line` macro][line-macro]. + /// + /// [line-macro]: https://en.cppreference.com/w/c/preprocessor/line + #[allow(unused_variables)] + fn line_number(&'a self, id: Self::FileId, line_index: usize) -> Result { + Ok(line_index + 1) + } + + /// The user-facing column number at the given line index and byte index. + /// + /// # Note for trait implementors + /// + /// This is usually 1-indexed from the the start of the line. + /// A default implementation is provided, based on the [`column_index`] + /// function that is exported from the [`files`] module. + /// + /// [`files`]: crate::files + /// [`column_index`]: crate::files::column_index + fn column_number( + &'a self, + id: Self::FileId, + line_index: usize, + byte_index: usize, + ) -> Result { + let source = self.source(id)?; + let line_range = self.line_range(id, line_index)?; + let column_index = column_index(source.as_ref(), line_range, byte_index); + + Ok(column_index + 1) + } + + /// Convenience method for returning line and column number at the given + /// byte index in the file. + fn location(&'a self, id: Self::FileId, byte_index: usize) -> Result { + let line_index = self.line_index(id, byte_index)?; + + Ok(Location { + line_number: self.line_number(id, line_index)?, + column_number: self.column_number(id, line_index, byte_index)?, + }) + } + + /// The byte range of line in the source of the file. + fn line_range(&'a self, id: Self::FileId, line_index: usize) -> Result, Error>; +} + +/// A user-facing location in a source file. +/// +/// Returned by [`Files::location`]. +/// +/// [`Files::location`]: Files::location +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Location { + /// The user-facing line number. + pub line_number: usize, + /// The user-facing column number. + pub column_number: usize, +} + +/// The column index at the given byte index in the source file. +/// This is the number of characters to the given byte index. +/// +/// If the byte index is smaller than the start of the line, then `0` is returned. +/// If the byte index is past the end of the line, the column index of the last +/// character `+ 1` is returned. +pub fn column_index(source: &str, line_range: Range, byte_index: usize) -> usize { + let end_index = std::cmp::min(byte_index, std::cmp::min(line_range.end, source.len())); + + (line_range.start..end_index) + .filter(|byte_index| source.is_char_boundary(byte_index + 1)) + .count() +} + +/// Return the starting byte index of each line in the source string. +/// +/// This can make it easier to implement [`Files::line_index`] by allowing +/// implementors of [`Files`] to pre-compute the line starts, then search for +/// the corresponding line range, as shown in the example below. +/// +/// [`Files`]: Files +/// [`Files::line_index`]: Files::line_index +pub fn line_starts<'source>(source: &'source str) -> impl 'source + Iterator { + std::iter::once(0).chain(source.match_indices('\n').map(|(i, _)| i + 1)) +} + +/// A file database that contains a single source file. +/// +/// Because there is only single file in this database we use `()` as a [`FileId`]. +/// +/// This is useful for simple language tests, but it might be worth creating a +/// custom implementation when a language scales beyond a certain size. +/// +/// [`FileId`]: Files::FileId +#[derive(Debug, Clone)] +pub struct SimpleFile { + /// The name of the file. + name: Name, + /// The source code of the file. + source: Source, + /// The starting byte indices in the source code. + line_starts: Vec, +} + +impl SimpleFile +where + Name: std::fmt::Display, + Source: AsRef, +{ + /// Create a new source file. + pub fn new(name: Name, source: Source) -> SimpleFile { + SimpleFile { + name, + line_starts: line_starts(source.as_ref()).collect(), + source, + } + } + + /// Return the name of the file. + pub fn name(&self) -> &Name { + &self.name + } + + /// Return the source of the file. + pub fn source(&self) -> &Source { + &self.source + } + + /// Return the starting byte index of the line with the specified line index. + /// Convenience method that already generates errors if necessary. + fn line_start(&self, line_index: usize) -> Result { + use std::cmp::Ordering; + + match line_index.cmp(&self.line_starts.len()) { + Ordering::Less => Ok(self + .line_starts + .get(line_index) + .cloned() + .expect("failed despite previous check")), + Ordering::Equal => Ok(self.source.as_ref().len()), + Ordering::Greater => Err(Error::LineTooLarge { + given: line_index, + max: self.line_starts.len() - 1, + }), + } + } +} + +impl<'a, Name, Source> Files<'a> for SimpleFile +where + Name: 'a + std::fmt::Display + Clone, + Source: 'a + AsRef, +{ + type FileId = (); + type Name = Name; + type Source = &'a str; + + fn name(&self, (): ()) -> Result { + Ok(self.name.clone()) + } + + fn source(&self, (): ()) -> Result<&str, Error> { + Ok(self.source.as_ref()) + } + + fn line_index(&self, (): (), byte_index: usize) -> Result { + Ok(self + .line_starts + .binary_search(&byte_index) + .unwrap_or_else(|next_line| next_line - 1)) + } + + fn line_range(&self, (): (), line_index: usize) -> Result, Error> { + let line_start = self.line_start(line_index)?; + let next_line_start = self.line_start(line_index + 1)?; + + Ok(line_start..next_line_start) + } +} + +/// A file database that can store multiple source files. +/// +/// This is useful for simple language tests, but it might be worth creating a +/// custom implementation when a language scales beyond a certain size. +/// It is a glorified `Vec` that implements the `Files` trait. +#[derive(Debug, Clone)] +pub struct SimpleFiles { + files: Vec>, +} + +impl SimpleFiles +where + Name: std::fmt::Display, + Source: AsRef, +{ + /// Create a new files database. + pub fn new() -> SimpleFiles { + SimpleFiles { files: Vec::new() } + } + + /// Add a file to the database, returning the handle that can be used to + /// refer to it again. + pub fn add(&mut self, name: Name, source: Source) -> usize { + let file_id = self.files.len(); + self.files.push(SimpleFile::new(name, source)); + file_id + } + + /// Get the file corresponding to the given id. + pub fn get(&self, file_id: usize) -> Result<&SimpleFile, Error> { + self.files.get(file_id).ok_or(Error::FileMissing) + } +} + +impl<'a, Name, Source> Files<'a> for SimpleFiles +where + Name: 'a + std::fmt::Display + Clone, + Source: 'a + AsRef, +{ + type FileId = usize; + type Name = Name; + type Source = &'a str; + + fn name(&self, file_id: usize) -> Result { + Ok(self.get(file_id)?.name().clone()) + } + + fn source(&self, file_id: usize) -> Result<&str, Error> { + Ok(self.get(file_id)?.source().as_ref()) + } + + fn line_index(&self, file_id: usize, byte_index: usize) -> Result { + self.get(file_id)?.line_index((), byte_index) + } + + fn line_range(&self, file_id: usize, line_index: usize) -> Result, Error> { + self.get(file_id)?.line_range((), line_index) + } +} + +#[cfg(test)] +mod test { + use super::*; + + const TEST_SOURCE: &str = "foo\nbar\r\n\nbaz"; + + #[test] + fn line_starts() { + let file = SimpleFile::new("test", TEST_SOURCE); + + assert_eq!( + file.line_starts, + [ + 0, // "foo\n" + 4, // "bar\r\n" + 9, // "" + 10, // "baz" + ], + ); + } + + #[test] + fn line_span_sources() { + let file = SimpleFile::new("test", TEST_SOURCE); + + let line_sources = (0..4) + .map(|line| { + let line_range = file.line_range((), line).unwrap(); + &file.source[line_range] + }) + .collect::>(); + + assert_eq!(line_sources, ["foo\n", "bar\r\n", "\n", "baz"]); + } +} diff --git a/crates/rslint_errors/src/codespan/mod.rs b/crates/rslint_errors/src/codespan/mod.rs new file mode 100644 index 00000000000..b870277cc98 --- /dev/null +++ b/crates/rslint_errors/src/codespan/mod.rs @@ -0,0 +1,7 @@ +//! Diagnostic reporting support for the codespan crate. + +#![allow(clippy::all, warnings)] + +pub mod diagnostic; +pub mod files; +pub mod term; diff --git a/crates/rslint_errors/src/codespan/term.rs b/crates/rslint_errors/src/codespan/term.rs new file mode 100644 index 00000000000..9fd96730428 --- /dev/null +++ b/crates/rslint_errors/src/codespan/term.rs @@ -0,0 +1,75 @@ +//! Terminal back-end for emitting diagnostics. + +use std::str::FromStr; +use termcolor::{ColorChoice, WriteColor}; + +use super::diagnostic::Diagnostic; +use super::files::Files; + +mod config; +mod renderer; +mod views; + +pub use termcolor; + +pub use self::config::{Chars, Config, DisplayStyle, Styles}; + +/// A command line argument that configures the coloring of the output. +/// +/// This can be used with command line argument parsers like [`clap`] or [`structopt`]. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct ColorArg(pub ColorChoice); + +impl ColorArg { + /// Allowed values the argument. + /// + /// This is useful for generating documentation via [`clap`] or `structopt`'s + /// `possible_values` configuration. + /// + /// [`clap`]: https://crates.io/crates/clap + /// [`structopt`]: https://crates.io/crates/structopt + pub const VARIANTS: &'static [&'static str] = &["auto", "always", "ansi", "never"]; +} + +impl FromStr for ColorArg { + type Err = &'static str; + + fn from_str(src: &str) -> Result { + match src { + _ if src.eq_ignore_ascii_case("auto") => Ok(ColorArg(ColorChoice::Auto)), + _ if src.eq_ignore_ascii_case("always") => Ok(ColorArg(ColorChoice::Always)), + _ if src.eq_ignore_ascii_case("ansi") => Ok(ColorArg(ColorChoice::AlwaysAnsi)), + _ if src.eq_ignore_ascii_case("never") => Ok(ColorArg(ColorChoice::Never)), + _ => Err("valid values: auto, always, ansi, never"), + } + } +} + +impl Into for ColorArg { + fn into(self) -> ColorChoice { + self.0 + } +} + +/// Emit a diagnostic using the given writer, context, config, and files. +/// +/// The return value covers all error cases. These error case can arise if: +/// * a file was removed from the file database. +/// * a file was changed so that it is too small to have an index +/// * IO fails +pub fn emit<'files, F: Files<'files>>( + writer: &mut dyn WriteColor, + config: &Config, + files: &'files F, + diagnostic: &Diagnostic, +) -> Result<(), super::files::Error> { + use self::renderer::Renderer; + use self::views::{RichDiagnostic, ShortDiagnostic}; + + let mut renderer = Renderer::new(writer, config); + match config.display_style { + DisplayStyle::Rich => RichDiagnostic::new(diagnostic, config).render(files, &mut renderer), + DisplayStyle::Medium => ShortDiagnostic::new(diagnostic, true).render(files, &mut renderer), + DisplayStyle::Short => ShortDiagnostic::new(diagnostic, false).render(files, &mut renderer), + } +} diff --git a/crates/rslint_errors/src/codespan/term/config.rs b/crates/rslint_errors/src/codespan/term/config.rs new file mode 100644 index 00000000000..573a379b2b8 --- /dev/null +++ b/crates/rslint_errors/src/codespan/term/config.rs @@ -0,0 +1,281 @@ +use termcolor::{Color, ColorSpec}; + +use super::super::diagnostic::{LabelStyle, Severity}; + +/// Configures how a diagnostic is rendered. +#[derive(Clone, Debug)] +pub struct Config { + /// The display style to use when rendering diagnostics. + /// Defaults to: [`DisplayStyle::Rich`]. + /// + /// [`DisplayStyle::Rich`]: DisplayStyle::Rich + pub display_style: DisplayStyle, + /// Column width of tabs. + /// Defaults to: `4`. + pub tab_width: usize, + /// Styles to use when rendering the diagnostic. + pub styles: Styles, + /// Characters to use when rendering the diagnostic. + pub chars: Chars, + /// The minimum number of lines to be shown after the line on which a multiline [`Label`] begins. + /// + /// Defaults to: `3`. + pub start_context_lines: usize, + /// The minimum number of lines to be shown before the line on which a multiline [`Label`] ends. + /// + /// Defaults to: `1`. + pub end_context_lines: usize, +} + +impl Default for Config { + fn default() -> Config { + Config { + display_style: DisplayStyle::Rich, + tab_width: 4, + styles: Styles::default(), + chars: Chars::default(), + start_context_lines: 3, + end_context_lines: 1, + } + } +} + +/// The display style to use when rendering diagnostics. +#[derive(Clone, Debug)] +pub enum DisplayStyle { + /// Output a richly formatted diagnostic, with source code previews. + /// + /// ```text + /// error[E0001]: unexpected type in `+` application + /// ┌─ test:2:9 + /// │ + /// 2 │ (+ test "") + /// │ ^^ expected `Int` but found `String` + /// │ + /// = expected type `Int` + /// found type `String` + /// + /// error[E0002]: Bad config found + /// + /// ``` + Rich, + /// Output a condensed diagnostic, with a line number, severity, message and notes (if any). + /// + /// ```text + /// test:2:9: error[E0001]: unexpected type in `+` application + /// = expected type `Int` + /// found type `String` + /// + /// error[E0002]: Bad config found + /// ``` + Medium, + /// Output a short diagnostic, with a line number, severity, and message. + /// + /// ```text + /// test:2:9: error[E0001]: unexpected type in `+` application + /// error[E0002]: Bad config found + /// ``` + Short, +} + +/// Styles to use when rendering the diagnostic. +#[derive(Clone, Debug)] +pub struct Styles { + /// The style to use when rendering bug headers. + /// Defaults to `fg:red bold intense`. + pub header_bug: ColorSpec, + /// The style to use when rendering error headers. + /// Defaults to `fg:red bold intense`. + pub header_error: ColorSpec, + /// The style to use when rendering warning headers. + /// Defaults to `fg:yellow bold intense`. + pub header_warning: ColorSpec, + /// The style to use when rendering note headers. + /// Defaults to `fg:green bold intense`. + pub header_note: ColorSpec, + /// The style to use when rendering help headers. + /// Defaults to `fg:cyan bold intense`. + pub header_help: ColorSpec, + /// The style to use when the main diagnostic message. + /// Defaults to `bold intense`. + pub header_message: ColorSpec, + + /// The style to use when rendering bug labels. + /// Defaults to `fg:red`. + pub primary_label_bug: ColorSpec, + /// The style to use when rendering error labels. + /// Defaults to `fg:red`. + pub primary_label_error: ColorSpec, + /// The style to use when rendering warning labels. + /// Defaults to `fg:yellow`. + pub primary_label_warning: ColorSpec, + /// The style to use when rendering note labels. + /// Defaults to `fg:green`. + pub primary_label_note: ColorSpec, + /// The style to use when rendering help labels. + /// Defaults to `fg:cyan`. + pub primary_label_help: ColorSpec, + /// The style to use when rendering secondary labels. + /// Defaults `fg:blue` (or `fg:cyan` on windows). + pub secondary_label: ColorSpec, + + /// The style to use when rendering the line numbers. + /// Defaults `fg:blue` (or `fg:cyan` on windows). + pub line_number: ColorSpec, + /// The style to use when rendering the source code borders. + /// Defaults `fg:blue` (or `fg:cyan` on windows). + pub source_border: ColorSpec, + /// The style to use when rendering the note bullets. + /// Defaults `fg:blue` (or `fg:cyan` on windows). + pub note_bullet: ColorSpec, +} + +impl Styles { + /// The style used to mark a header at a given severity. + pub fn header(&self, severity: Severity) -> &ColorSpec { + match severity { + Severity::Bug => &self.header_bug, + Severity::Error => &self.header_error, + Severity::Warning => &self.header_warning, + Severity::Note => &self.header_note, + Severity::Help => &self.header_help, + } + } + + /// The style used to mark a primary or secondary label at a given severity. + pub fn label(&self, severity: Severity, label_style: LabelStyle) -> &ColorSpec { + match (label_style, severity) { + (LabelStyle::Primary, Severity::Bug) => &self.primary_label_bug, + (LabelStyle::Primary, Severity::Error) => &self.primary_label_error, + (LabelStyle::Primary, Severity::Warning) => &self.primary_label_warning, + (LabelStyle::Primary, Severity::Note) => &self.primary_label_note, + (LabelStyle::Primary, Severity::Help) => &self.primary_label_help, + (LabelStyle::Secondary, _) => &self.secondary_label, + } + } + + #[doc(hidden)] + pub fn with_blue(blue: Color) -> Styles { + let header = ColorSpec::new().set_bold(true).set_intense(true).clone(); + + Styles { + header_bug: header.clone().set_fg(Some(Color::Red)).clone(), + header_error: header.clone().set_fg(Some(Color::Red)).clone(), + header_warning: header.clone().set_fg(Some(Color::Yellow)).clone(), + header_note: header.clone().set_fg(Some(Color::Green)).clone(), + header_help: header.clone().set_fg(Some(Color::Cyan)).clone(), + header_message: header, + + primary_label_bug: ColorSpec::new().set_fg(Some(Color::Red)).clone(), + primary_label_error: ColorSpec::new().set_fg(Some(Color::Red)).clone(), + primary_label_warning: ColorSpec::new().set_fg(Some(Color::Yellow)).clone(), + primary_label_note: ColorSpec::new().set_fg(Some(Color::Green)).clone(), + primary_label_help: ColorSpec::new().set_fg(Some(Color::Cyan)).clone(), + secondary_label: ColorSpec::new().set_fg(Some(blue)).clone(), + + line_number: ColorSpec::new().set_fg(Some(blue)).clone(), + source_border: ColorSpec::new().set_fg(Some(blue)).clone(), + note_bullet: ColorSpec::new().set_fg(Some(blue)).clone(), + } + } +} + +impl Default for Styles { + fn default() -> Styles { + // Blue is really difficult to see on the standard windows command line + #[cfg(windows)] + const BLUE: Color = Color::Cyan; + #[cfg(not(windows))] + const BLUE: Color = Color::Blue; + + Self::with_blue(BLUE) + } +} + +/// Characters to use when rendering the diagnostic. +#[derive(Clone, Debug)] +pub struct Chars { + /// The character to use for the top-left border of the source. + /// Defaults to: `'┌'`. + pub source_border_top_left: char, + /// The character to use for the top border of the source. + /// Defaults to: `'─'`. + pub source_border_top: char, + /// The character to use for the left border of the source. + /// Defaults to: `'│'`. + pub source_border_left: char, + /// The character to use for the left border break of the source. + /// Defaults to: `'·'`. + pub source_border_left_break: char, + + pub note_bullet_middle: char, + pub note_bullet_end: char, + + /// The character to use for marking a single-line primary label. + /// Defaults to: `'^'`. + pub single_primary_caret: char, + /// The character to use for marking a single-line secondary label. + /// Defaults to: `'-'`. + pub single_secondary_caret: char, + + /// The character to use for marking the start of a multi-line primary label. + /// Defaults to: `'^'`. + pub multi_primary_caret_start: char, + /// The character to use for marking the end of a multi-line primary label. + /// Defaults to: `'^'`. + pub multi_primary_caret_end: char, + /// The character to use for marking the start of a multi-line secondary label. + /// Defaults to: `'\''`. + pub multi_secondary_caret_start: char, + /// The character to use for marking the end of a multi-line secondary label. + /// Defaults to: `'\''`. + pub multi_secondary_caret_end: char, + /// The character to use for the top-left corner of a multi-line label. + /// Defaults to: `'╭'`. + pub multi_top_left: char, + /// The character to use for the top of a multi-line label. + /// Defaults to: `'─'`. + pub multi_top: char, + /// The character to use for the bottom-left corner of a multi-line label. + /// Defaults to: `'╰'`. + pub multi_bottom_left: char, + /// The character to use when marking the bottom of a multi-line label. + /// Defaults to: `'─'`. + pub multi_bottom: char, + /// The character to use for the left of a multi-line label. + /// Defaults to: `'│'`. + pub multi_left: char, + + /// The character to use for the left of a pointer underneath a caret. + /// Defaults to: `'│'`. + pub pointer_left: char, +} + +impl Default for Chars { + fn default() -> Chars { + Chars { + source_border_top_left: '┌', + source_border_top: '─', + source_border_left: '│', + source_border_left_break: '·', + + note_bullet_middle: '╪', + note_bullet_end: '╧', + + single_primary_caret: '^', + single_secondary_caret: '-', + + multi_primary_caret_start: '^', + multi_primary_caret_end: '^', + multi_secondary_caret_start: '\'', + multi_secondary_caret_end: '\'', + multi_top_left: '╭', + multi_top: '─', + multi_bottom_left: '╰', + multi_bottom: '─', + multi_left: '│', + + pointer_left: '│', + } + } +} diff --git a/crates/rslint_errors/src/codespan/term/renderer.rs b/crates/rslint_errors/src/codespan/term/renderer.rs new file mode 100644 index 00000000000..541f80091ec --- /dev/null +++ b/crates/rslint_errors/src/codespan/term/renderer.rs @@ -0,0 +1,1066 @@ +use std::io::{self, Write}; +use std::ops::Range; +use termcolor::{ColorSpec, WriteColor}; + +use super::super::diagnostic::{LabelStyle, Note, Severity}; +use super::super::files::{Error, Location}; +use super::super::term::{Chars, Config, Styles}; + +/// The 'location focus' of a source code snippet. +pub struct Locus { + /// The user-facing name of the file. + pub name: String, + /// The location. + pub location: Location, +} + +/// Single-line label, with an optional message. +/// +/// ```text +/// ^^^^^^^^^ blah blah +/// ``` +pub type SingleLabel<'diagnostic> = (LabelStyle, Range, &'diagnostic str); + +/// A multi-line label to render. +/// +/// Locations are relative to the start of where the source code is rendered. +pub enum MultiLabel<'diagnostic> { + /// Multi-line label top. + /// The contained value indicates where the label starts. + /// + /// ```text + /// ╭────────────^ + /// ``` + /// + /// Can also be rendered at the beginning of the line + /// if there is only whitespace before the label starts. + /// + /// /// ```text + /// ╭ + /// ``` + Top(usize), + /// Left vertical labels for multi-line labels. + /// + /// ```text + /// │ + /// ``` + Left, + /// Multi-line label bottom, with an optional message. + /// The first value indicates where the label ends. + /// + /// ```text + /// ╰────────────^ blah blah + /// ``` + Bottom(usize, &'diagnostic str), +} + +#[derive(Copy, Clone)] +enum VerticalBound { + Top, + Bottom, +} + +type Underline = (LabelStyle, VerticalBound); + +/// A renderer of display list entries. +/// +/// The following diagram gives an overview of each of the parts of the renderer's output: +/// +/// ```text +/// ┌ outer gutter +/// │ ┌ left border +/// │ │ ┌ inner gutter +/// │ │ │ ┌─────────────────────────── source ─────────────────────────────┐ +/// │ │ │ │ │ +/// ┌──────────────────────────────────────────────────────────────────────────── +/// header ── │ error[0001]: oh noes, a cupcake has occurred! +/// snippet start ── │ ┌─ test:9:0 +/// snippet empty ── │ │ +/// snippet line ── │ 9 │ ╭ Cupcake ipsum dolor. Sit amet marshmallow topping cheesecake +/// snippet line ── │ 10 │ │ muffin. Halvah croissant candy canes bonbon candy. Apple pie jelly +/// │ │ ╭─│─────────^ +/// snippet break ── │ · │ │ +/// snippet line ── │ 33 │ │ │ Muffin danish chocolate soufflé pastry icing bonbon oat cake. +/// snippet line ── │ 34 │ │ │ Powder cake jujubes oat cake. Lemon drops tootsie roll marshmallow +/// │ │ │ ╰─────────────────────────────^ blah blah +/// snippet break ── │ · │ +/// snippet line ── │ 38 │ │ Brownie lemon drops chocolate jelly-o candy canes. Danish marzipan +/// snippet line ── │ 39 │ │ jujubes soufflé carrot cake marshmallow tiramisu caramels candy canes. +/// │ │ │ ^^^^^^^^^^^^^^^^^^^ -------------------- blah blah +/// │ │ │ │ +/// │ │ │ blah blah +/// │ │ │ note: this is a note +/// snippet line ── │ 40 │ │ Fruitcake jelly-o danish toffee. Tootsie roll pastry cheesecake +/// snippet line ── │ 41 │ │ soufflé marzipan. Chocolate bar oat cake jujubes lollipop pastry +/// snippet line ── │ 42 │ │ cupcake. Candy canes cupcake toffee gingerbread candy canes muffin +/// │ │ │ ^^^^^^^^^^^^^^^^^^ blah blah +/// │ │ ╰──────────^ blah blah +/// snippet break ── │ · +/// snippet line ── │ 82 │ gingerbread toffee chupa chups chupa chups jelly-o cotton candy. +/// │ │ ^^^^^^ ------- blah blah +/// snippet empty ── │ │ +/// snippet note ── │ = blah blah +/// snippet note ── │ = blah blah blah +/// │ blah blah +/// snippet note ── │ = blah blah blah +/// │ blah blah +/// empty ── │ +/// ``` +/// +/// Filler text from http://www.cupcakeipsum.com +pub struct Renderer<'writer, 'config> { + writer: &'writer mut dyn WriteColor, + config: &'config Config, +} + +impl<'writer, 'config> Renderer<'writer, 'config> { + /// Construct a renderer from the given writer and config. + pub fn new( + writer: &'writer mut dyn WriteColor, + config: &'config Config, + ) -> Renderer<'writer, 'config> { + Renderer { writer, config } + } + + fn chars(&self) -> &'config Chars { + &self.config.chars + } + + fn styles(&self) -> &'config Styles { + &self.config.styles + } + + /// Diagnostic header, with severity, code, and message. + /// + /// ```text + /// error[E0001]: unexpected type in `+` application + /// ``` + pub fn render_header( + &mut self, + locus: Option<&Locus>, + severity: Severity, + code: Option<&str>, + message: &str, + ) -> Result<(), Error> { + // Write locus + // + // ```text + // test:2:9: + // ``` + if let Some(locus) = locus { + self.snippet_locus(locus)?; + write!(self, ": ")?; + } + + // Write severity name + // + // ```text + // error + // ``` + self.set_color(self.styles().header(severity))?; + match severity { + Severity::Bug => write!(self, "bug")?, + Severity::Error => write!(self, "error")?, + Severity::Warning => write!(self, "warning")?, + Severity::Help => write!(self, "help")?, + Severity::Note => write!(self, "note")?, + } + + // Write error code + // + // ```text + // [E0001] + // ``` + if let Some(code) = &code.filter(|code| !code.is_empty()) { + write!(self, "[{}]", code)?; + } + + // Write diagnostic message + // + // ```text + // : unexpected type in `+` application + // ``` + self.set_color(&self.styles().header_message)?; + write!(self, ": {}", message)?; + self.reset()?; + + writeln!(self)?; + + Ok(()) + } + + /// Empty line. + pub fn render_empty(&mut self) -> Result<(), Error> { + writeln!(self)?; + Ok(()) + } + + /// Top left border and locus. + /// + /// ```text + /// ┌─ test:2:9 + /// ``` + pub fn render_snippet_start( + &mut self, + outer_padding: usize, + locus: &Locus, + ) -> Result<(), Error> { + self.outer_gutter(outer_padding)?; + + self.set_color(&self.styles().source_border)?; + write!(self, "{}", self.chars().source_border_top_left)?; + write!(self, "{0}", self.chars().source_border_top)?; + self.reset()?; + + write!(self, " ")?; + self.snippet_locus(&locus)?; + + writeln!(self)?; + + Ok(()) + } + + /// A line of source code. + /// + /// ```text + /// 10 │ │ muffin. Halvah croissant candy canes bonbon candy. Apple pie jelly + /// │ ╭─│─────────^ + /// ``` + pub fn render_snippet_source( + &mut self, + outer_padding: usize, + line_number: usize, + source: &str, + severity: Severity, + single_labels: &[SingleLabel<'_>], + num_multi_labels: usize, + multi_labels: &[(usize, LabelStyle, MultiLabel<'_>)], + ) -> Result<(), Error> { + // Trim trailing newlines, linefeeds, and null chars from source, if they exist. + // FIXME: Use the number of trimmed placeholders when rendering single line carets + let source = source.trim_end_matches(['\n', '\r', '\0'].as_ref()); + + // Write source line + // + // ```text + // 10 │ │ muffin. Halvah croissant candy canes bonbon candy. Apple pie jelly + // ``` + { + // Write outer gutter (with line number) and border + self.outer_gutter_number(line_number, outer_padding)?; + self.border_left()?; + + // Write inner gutter (with multi-line continuations on the left if necessary) + let mut multi_labels_iter = multi_labels.iter().peekable(); + for label_column in 0..num_multi_labels { + match multi_labels_iter.peek() { + Some((label_index, label_style, label)) if *label_index == label_column => { + match label { + MultiLabel::Top(start) + if *start <= source.len() - source.trim_start().len() => + { + self.label_multi_top_left(severity, *label_style)?; + } + MultiLabel::Top(..) => self.inner_gutter_space()?, + MultiLabel::Left | MultiLabel::Bottom(..) => { + self.label_multi_left(severity, *label_style, None)?; + } + } + multi_labels_iter.next(); + } + Some((_, _, _)) | None => self.inner_gutter_space()?, + } + } + + // Write source text + write!(self, " ")?; + let mut in_primary = false; + for (metrics, ch) in self.char_metrics(source.char_indices()) { + let column_range = metrics.byte_index..(metrics.byte_index + ch.len_utf8()); + + // Check if we are overlapping a primary label + let is_primary = single_labels.iter().any(|(ls, range, _)| { + *ls == LabelStyle::Primary && is_overlapping(range, &column_range) + }) || multi_labels.iter().any(|(_, ls, label)| { + *ls == LabelStyle::Primary + && match label { + MultiLabel::Top(start) => column_range.start >= *start, + MultiLabel::Left => true, + MultiLabel::Bottom(start, _) => column_range.end <= *start, + } + }); + + // Set the source color if we are in a primary label + if is_primary && !in_primary { + self.set_color(self.styles().label(severity, LabelStyle::Primary))?; + in_primary = true; + } else if !is_primary && in_primary { + self.reset()?; + in_primary = false; + } + + match ch { + '\t' => (0..metrics.unicode_width).try_for_each(|_| write!(self, " "))?, + _ => write!(self, "{}", ch)?, + } + } + if in_primary { + self.reset()?; + } + writeln!(self)?; + } + + // Write single labels underneath source + // + // ```text + // │ - ---- ^^^ second mutable borrow occurs here + // │ │ │ + // │ │ first mutable borrow occurs here + // │ first borrow later used by call + // │ help: some help here + // ``` + if !single_labels.is_empty() { + // Our plan is as follows: + // + // 1. Do an initial scan to find: + // - The number of non-empty messages. + // - The right-most start and end positions of labels. + // - A candidate for a trailing label (where the label's message + // is printed to the left of the caret). + // 2. Check if the trailing label candidate overlaps another label - + // if so we print it underneath the carets with the other labels. + // 3. Print a line of carets, and (possibly) the trailing message + // to the left. + // 4. Print vertical lines pointing to the carets, and the messages + // for those carets. + // + // We try our best avoid introducing new dynamic allocations, + // instead preferring to iterate over the labels multiple times. It + // is unclear what the performance tradeoffs are however, so further + // investigation may be required. + + // The number of non-empty messages to print. + let mut num_messages = 0; + // The right-most start position, eg: + // + // ```text + // -^^^^---- ^^^^^^^ + // │ + // right-most start position + // ``` + let mut max_label_start = 0; + // The right-most end position, eg: + // + // ```text + // -^^^^---- ^^^^^^^ + // │ + // right-most end position + // ``` + let mut max_label_end = 0; + // A trailing message, eg: + // + // ```text + // ^^^ second mutable borrow occurs here + // ``` + let mut trailing_label = None; + + for (label_index, label) in single_labels.iter().enumerate() { + let (_, range, message) = label; + if !message.is_empty() { + num_messages += 1; + } + max_label_start = std::cmp::max(max_label_start, range.start); + max_label_end = std::cmp::max(max_label_end, range.end); + // This is a candidate for the trailing label, so let's record it. + if range.end == max_label_end { + if message.is_empty() { + trailing_label = None; + } else { + trailing_label = Some((label_index, label)); + } + } + } + if let Some((trailing_label_index, (_, trailing_range, _))) = trailing_label { + // Check to see if the trailing label candidate overlaps any of + // the other labels on the current line. + if single_labels + .iter() + .enumerate() + .filter(|(label_index, _)| *label_index != trailing_label_index) + .any(|(_, (_, range, _))| is_overlapping(trailing_range, range)) + { + // If it does, we'll instead want to render it below the + // carets along with the other hanging labels. + trailing_label = None; + } + } + + // Write a line of carets + // + // ```text + // │ ^^^^^^ -------^^^^^^^^^-------^^^^^----- ^^^^ trailing label message + // ``` + self.outer_gutter(outer_padding)?; + self.border_left()?; + self.inner_gutter(severity, num_multi_labels, multi_labels)?; + write!(self, " ")?; + + let mut previous_label_style = None; + let placeholder_metrics = Metrics { + byte_index: source.len(), + unicode_width: 1, + }; + for (metrics, ch) in self + .char_metrics(source.char_indices()) + // Add a placeholder source column at the end to allow for + // printing carets at the end of lines, eg: + // + // ```text + // 1 │ Hello world! + // │ ^ + // ``` + .chain(std::iter::once((placeholder_metrics, '\0'))) + { + // Find the current label style at this column + let column_range = metrics.byte_index..(metrics.byte_index + ch.len_utf8()); + let current_label_style = single_labels + .iter() + .filter(|(_, range, _)| is_overlapping(range, &column_range)) + .map(|(label_style, _, _)| *label_style) + .max_by_key(label_priority_key); + + // Update writer style if necessary + if previous_label_style != current_label_style { + match current_label_style { + None => self.reset()?, + Some(label_style) => { + self.set_color(self.styles().label(severity, label_style))?; + } + } + } + + let caret_ch = match current_label_style { + Some(LabelStyle::Primary) => Some(self.chars().single_primary_caret), + Some(LabelStyle::Secondary) => Some(self.chars().single_secondary_caret), + // Only print padding if we are before the end of the last single line caret + None if metrics.byte_index < max_label_end => Some(' '), + None => None, + }; + if let Some(caret_ch) = caret_ch { + // FIXME: improve rendering of carets between character boundaries + (0..metrics.unicode_width).try_for_each(|_| write!(self, "{}", caret_ch))?; + } + + previous_label_style = current_label_style; + } + // Reset style if it was previously set + if previous_label_style.is_some() { + self.reset()?; + } + // Write first trailing label message + if let Some((_, (label_style, _, message))) = trailing_label { + write!(self, " ")?; + self.set_color(self.styles().label(severity, *label_style))?; + write!(self, "{}", message)?; + self.reset()?; + } + writeln!(self)?; + + // Write hanging labels pointing to carets + // + // ```text + // │ │ │ + // │ │ first mutable borrow occurs here + // │ first borrow later used by call + // │ help: some help here + // ``` + if num_messages > trailing_label.iter().count() { + // Write first set of vertical lines before hanging labels + // + // ```text + // │ │ │ + // ``` + self.outer_gutter(outer_padding)?; + self.border_left()?; + self.inner_gutter(severity, num_multi_labels, multi_labels)?; + write!(self, " ")?; + self.caret_pointers( + severity, + max_label_start, + single_labels, + trailing_label, + source.char_indices(), + )?; + writeln!(self)?; + + // Write hanging labels pointing to carets + // + // ```text + // │ │ first mutable borrow occurs here + // │ first borrow later used by call + // │ help: some help here + // ``` + for (label_style, range, message) in + hanging_labels(single_labels, trailing_label).rev() + { + self.outer_gutter(outer_padding)?; + self.border_left()?; + self.inner_gutter(severity, num_multi_labels, multi_labels)?; + write!(self, " ")?; + self.caret_pointers( + severity, + max_label_start, + single_labels, + trailing_label, + source + .char_indices() + .take_while(|(byte_index, _)| *byte_index < range.start), + )?; + self.set_color(self.styles().label(severity, *label_style))?; + write!(self, "{}", message)?; + self.reset()?; + writeln!(self)?; + } + } + } + + // Write top or bottom label carets underneath source + // + // ```text + // │ ╰───│──────────────────^ woops + // │ ╭─│─────────^ + // ``` + for (multi_label_index, (_, label_style, label)) in multi_labels.iter().enumerate() { + let (label_style, range, bottom_message) = match label { + MultiLabel::Left => continue, // no label caret needed + // no label caret needed if this can be started in front of the line + MultiLabel::Top(start) if *start <= source.len() - source.trim_start().len() => { + continue + } + MultiLabel::Top(range) => (*label_style, range, None), + MultiLabel::Bottom(range, message) => (*label_style, range, Some(message)), + }; + + self.outer_gutter(outer_padding)?; + self.border_left()?; + + // Write inner gutter. + // + // ```text + // │ ╭─│───│ + // ``` + let mut underline = None; + let mut multi_labels_iter = multi_labels.iter().enumerate().peekable(); + for label_column in 0..num_multi_labels { + match multi_labels_iter.peek() { + Some((i, (label_index, ls, label))) if *label_index == label_column => { + match label { + MultiLabel::Left => { + self.label_multi_left(severity, *ls, underline.map(|(s, _)| s))?; + } + MultiLabel::Top(..) if multi_label_index > *i => { + self.label_multi_left(severity, *ls, underline.map(|(s, _)| s))?; + } + MultiLabel::Bottom(..) if multi_label_index < *i => { + self.label_multi_left(severity, *ls, underline.map(|(s, _)| s))?; + } + MultiLabel::Top(..) if multi_label_index == *i => { + underline = Some((*ls, VerticalBound::Top)); + self.label_multi_top_left(severity, label_style)? + } + MultiLabel::Bottom(..) if multi_label_index == *i => { + underline = Some((*ls, VerticalBound::Bottom)); + self.label_multi_bottom_left(severity, label_style)?; + } + MultiLabel::Top(..) | MultiLabel::Bottom(..) => { + self.inner_gutter_column(severity, underline)?; + } + } + multi_labels_iter.next(); + } + Some((_, _)) | None => self.inner_gutter_column(severity, underline)?, + } + } + + // Finish the top or bottom caret + match bottom_message { + None => self.label_multi_top_caret(severity, label_style, source, *range)?, + Some(message) => { + self.label_multi_bottom_caret(severity, label_style, source, *range, message)? + } + } + } + + Ok(()) + } + + /// An empty source line, for providing additional whitespace to source snippets. + /// + /// ```text + /// │ │ │ + /// ``` + pub fn render_snippet_empty( + &mut self, + outer_padding: usize, + severity: Severity, + num_multi_labels: usize, + multi_labels: &[(usize, LabelStyle, MultiLabel<'_>)], + ) -> Result<(), Error> { + self.outer_gutter(outer_padding)?; + self.border_left()?; + self.inner_gutter(severity, num_multi_labels, multi_labels)?; + writeln!(self)?; + Ok(()) + } + + /// A broken source line, for labeling skipped sections of source. + /// + /// ```text + /// · │ │ + /// ``` + pub fn render_snippet_break( + &mut self, + outer_padding: usize, + severity: Severity, + num_multi_labels: usize, + multi_labels: &[(usize, LabelStyle, MultiLabel<'_>)], + ) -> Result<(), Error> { + self.outer_gutter(outer_padding)?; + self.border_left_break()?; + self.inner_gutter(severity, num_multi_labels, multi_labels)?; + writeln!(self)?; + Ok(()) + } + + /// Additional notes. + /// + /// ```text + /// = expected type `Int` + /// found type `String` + /// ``` + pub fn render_snippet_note( + &mut self, + outer_padding: usize, + note: Note, + has_another_note_after: bool, + ) -> Result<(), Error> { + let mut offset = 0; + for (note_line_index, line) in note.message.lines().enumerate() { + match note_line_index { + 0 => { + self.outer_gutter(outer_padding)?; + self.set_color(&self.styles().note_bullet)?; + write!( + self, + "{}", + if !has_another_note_after { + self.chars().note_bullet_end + } else { + self.chars().note_bullet_middle + } + )?; + self.reset()?; + if let Some(severity) = note.severity { + write!(self, " "); + if severity == Severity::Note { + self.set_color(&self.styles().header_help)?; + } else { + self.set_color(&self.styles().header(severity))?; + } + let string = match severity { + Severity::Bug => "bug", + Severity::Error => "error", + Severity::Warning => "warning", + Severity::Help => "help", + Severity::Note => "note", + }; + offset += string.len(); + write!(self, "{}", string)?; + self.reset()?; + write!(self, ":")?; + offset += 1; + } + } + _ => { + if has_another_note_after { + // write a gutter between a new line and the next note bullet if there is another note + // so its not disjointed + // ``` + // = help: foo bar baz + // | bar + // = note: foo + ///``` + self.outer_gutter(outer_padding)?; + self.border_left()?; + } else { + self.outer_gutter(outer_padding)?; + } + write!(self, " {}", " ".repeat(offset))? + } + } + // Write line of message + writeln!(self, " {}", line)?; + } + + Ok(()) + } + + /// Adds tab-stop aware unicode-width computations to an iterator over + /// character indices. Assumes that the character indices begin at the start + /// of the line. + fn char_metrics( + &self, + char_indices: impl Iterator, + ) -> impl Iterator { + use unicode_width::UnicodeWidthChar; + + let tab_width = self.config.tab_width; + let mut unicode_column = 0; + + char_indices.map(move |(byte_index, ch)| { + let metrics = Metrics { + byte_index, + unicode_width: match (ch, tab_width) { + ('\t', 0) => 0, // Guard divide-by-zero + ('\t', _) => tab_width - (unicode_column % tab_width), + (ch, _) => ch.width().unwrap_or(0), + }, + }; + unicode_column += metrics.unicode_width; + + (metrics, ch) + }) + } + + /// Location focus. + fn snippet_locus(&mut self, locus: &Locus) -> Result<(), Error> { + write!( + self, + "{name}:{line_number}:{column_number}", + name = locus.name, + line_number = locus.location.line_number, + column_number = locus.location.column_number, + )?; + Ok(()) + } + + /// The outer gutter of a source line. + fn outer_gutter(&mut self, outer_padding: usize) -> Result<(), Error> { + write!(self, "{space: >width$} ", space = "", width = outer_padding)?; + Ok(()) + } + + /// The outer gutter of a source line, with line number. + fn outer_gutter_number( + &mut self, + line_number: usize, + outer_padding: usize, + ) -> Result<(), Error> { + self.set_color(&self.styles().line_number)?; + write!( + self, + "{line_number: >width$}", + line_number = line_number, + width = outer_padding, + )?; + self.reset()?; + write!(self, " ")?; + Ok(()) + } + + /// The left-hand border of a source line. + fn border_left(&mut self) -> Result<(), Error> { + self.set_color(&self.styles().source_border)?; + write!(self, "{}", self.chars().source_border_left)?; + self.reset()?; + Ok(()) + } + + /// The broken left-hand border of a source line. + fn border_left_break(&mut self) -> Result<(), Error> { + self.set_color(&self.styles().source_border)?; + write!(self, "{}", self.chars().source_border_left_break)?; + self.reset()?; + Ok(()) + } + + /// Write vertical lines pointing to carets. + fn caret_pointers( + &mut self, + severity: Severity, + max_label_start: usize, + single_labels: &[SingleLabel<'_>], + trailing_label: Option<(usize, &SingleLabel<'_>)>, + char_indices: impl Iterator, + ) -> Result<(), Error> { + for (metrics, ch) in self.char_metrics(char_indices) { + let column_range = metrics.byte_index..(metrics.byte_index + ch.len_utf8()); + let label_style = hanging_labels(single_labels, trailing_label) + .filter(|(_, range, _)| column_range.contains(&range.start)) + .map(|(label_style, _, _)| *label_style) + .max_by_key(label_priority_key); + + let mut spaces = match label_style { + None => 0..metrics.unicode_width, + Some(label_style) => { + self.set_color(self.styles().label(severity, label_style))?; + write!(self, "{}", self.chars().pointer_left)?; + self.reset()?; + 1..metrics.unicode_width + } + }; + // Only print padding if we are before the end of the last single line caret + if metrics.byte_index <= max_label_start { + spaces.try_for_each(|_| write!(self, " "))?; + } + } + + Ok(()) + } + + /// The left of a multi-line label. + /// + /// ```text + /// │ + /// ``` + fn label_multi_left( + &mut self, + severity: Severity, + label_style: LabelStyle, + underline: Option, + ) -> Result<(), Error> { + match underline { + None => write!(self, " ")?, + // Continue an underline horizontally + Some(label_style) => { + self.set_color(self.styles().label(severity, label_style))?; + write!(self, "{}", self.chars().multi_top)?; + self.reset()?; + } + } + self.set_color(self.styles().label(severity, label_style))?; + write!(self, "{}", self.chars().multi_left)?; + self.reset()?; + Ok(()) + } + + /// The top-left of a multi-line label. + /// + /// ```text + /// ╭ + /// ``` + fn label_multi_top_left( + &mut self, + severity: Severity, + label_style: LabelStyle, + ) -> Result<(), Error> { + write!(self, " ")?; + self.set_color(self.styles().label(severity, label_style))?; + write!(self, "{}", self.chars().multi_top_left)?; + self.reset()?; + Ok(()) + } + + /// The bottom left of a multi-line label. + /// + /// ```text + /// ╰ + /// ``` + fn label_multi_bottom_left( + &mut self, + severity: Severity, + label_style: LabelStyle, + ) -> Result<(), Error> { + write!(self, " ")?; + self.set_color(self.styles().label(severity, label_style))?; + write!(self, "{}", self.chars().multi_bottom_left)?; + self.reset()?; + Ok(()) + } + + /// Multi-line label top. + /// + /// ```text + /// ─────────────^ + /// ``` + fn label_multi_top_caret( + &mut self, + severity: Severity, + label_style: LabelStyle, + source: &str, + start: usize, + ) -> Result<(), Error> { + self.set_color(self.styles().label(severity, label_style))?; + + for (metrics, _) in self + .char_metrics(source.char_indices()) + .take_while(|(metrics, _)| metrics.byte_index < start + 1) + { + // FIXME: improve rendering of carets between character boundaries + (0..metrics.unicode_width) + .try_for_each(|_| write!(self, "{}", self.chars().multi_top))?; + } + + let caret_start = match label_style { + LabelStyle::Primary => self.config.chars.multi_primary_caret_start, + LabelStyle::Secondary => self.config.chars.multi_secondary_caret_start, + }; + write!(self, "{}", caret_start)?; + self.reset()?; + writeln!(self)?; + Ok(()) + } + + /// Multi-line label bottom, with a message. + /// + /// ```text + /// ─────────────^ expected `Int` but found `String` + /// ``` + fn label_multi_bottom_caret( + &mut self, + severity: Severity, + label_style: LabelStyle, + source: &str, + start: usize, + message: &str, + ) -> Result<(), Error> { + self.set_color(self.styles().label(severity, label_style))?; + + for (metrics, _) in self + .char_metrics(source.char_indices()) + .take_while(|(metrics, _)| metrics.byte_index < start) + { + // FIXME: improve rendering of carets between character boundaries + (0..metrics.unicode_width) + .try_for_each(|_| write!(self, "{}", self.chars().multi_bottom))?; + } + + let caret_end = match label_style { + LabelStyle::Primary => self.config.chars.multi_primary_caret_start, + LabelStyle::Secondary => self.config.chars.multi_secondary_caret_start, + }; + write!(self, "{}", caret_end)?; + if !message.is_empty() { + write!(self, " {}", message)?; + } + self.reset()?; + writeln!(self)?; + Ok(()) + } + + /// Writes an empty gutter space, or continues an underline horizontally. + fn inner_gutter_column( + &mut self, + severity: Severity, + underline: Option, + ) -> Result<(), Error> { + match underline { + None => self.inner_gutter_space(), + Some((label_style, vertical_bound)) => { + self.set_color(self.styles().label(severity, label_style))?; + let ch = match vertical_bound { + VerticalBound::Top => self.config.chars.multi_top, + VerticalBound::Bottom => self.config.chars.multi_bottom, + }; + write!(self, "{0}{0}", ch)?; + self.reset()?; + Ok(()) + } + } + } + + /// Writes an empty gutter space. + fn inner_gutter_space(&mut self) -> Result<(), Error> { + write!(self, " ")?; + Ok(()) + } + + /// Writes an inner gutter, with the left lines if necessary. + fn inner_gutter( + &mut self, + severity: Severity, + num_multi_labels: usize, + multi_labels: &[(usize, LabelStyle, MultiLabel<'_>)], + ) -> Result<(), Error> { + let mut multi_labels_iter = multi_labels.iter().peekable(); + for label_column in 0..num_multi_labels { + match multi_labels_iter.peek() { + Some((label_index, ls, label)) if *label_index == label_column => match label { + MultiLabel::Left | MultiLabel::Bottom(..) => { + self.label_multi_left(severity, *ls, None)?; + multi_labels_iter.next(); + } + MultiLabel::Top(..) => { + self.inner_gutter_space()?; + multi_labels_iter.next(); + } + }, + Some((_, _, _)) | None => self.inner_gutter_space()?, + } + } + + Ok(()) + } +} + +impl<'writer, 'config> Write for Renderer<'writer, 'config> { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.writer.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.writer.flush() + } +} + +impl<'writer, 'config> WriteColor for Renderer<'writer, 'config> { + fn supports_color(&self) -> bool { + self.writer.supports_color() + } + + fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { + self.writer.set_color(spec) + } + + fn reset(&mut self) -> io::Result<()> { + self.writer.reset() + } + + fn is_synchronous(&self) -> bool { + self.writer.is_synchronous() + } +} + +struct Metrics { + byte_index: usize, + unicode_width: usize, +} + +/// Check if two ranges overlap +fn is_overlapping(range0: &Range, range1: &Range) -> bool { + let start = std::cmp::max(range0.start, range1.start); + let end = std::cmp::min(range0.end, range1.end); + start < end +} + +/// For prioritizing primary labels over secondary labels when rendering carets. +fn label_priority_key(label_style: &LabelStyle) -> u8 { + match label_style { + LabelStyle::Secondary => 0, + LabelStyle::Primary => 1, + } +} + +/// Return an iterator that yields the labels that require hanging messages +/// rendered underneath them. +fn hanging_labels<'labels, 'diagnostic>( + single_labels: &'labels [SingleLabel<'diagnostic>], + trailing_label: Option<(usize, &'labels SingleLabel<'diagnostic>)>, +) -> impl 'labels + DoubleEndedIterator> { + single_labels + .iter() + .enumerate() + .filter(|(_, (_, _, message))| !message.is_empty()) + .filter(move |(i, _)| trailing_label.map_or(true, |(j, _)| *i != j)) + .map(|(_, label)| label) +} diff --git a/crates/rslint_errors/src/codespan/term/views.rs b/crates/rslint_errors/src/codespan/term/views.rs new file mode 100644 index 00000000000..dbae14e8731 --- /dev/null +++ b/crates/rslint_errors/src/codespan/term/views.rs @@ -0,0 +1,491 @@ +use std::ops::Range; + +use super::super::diagnostic::{Diagnostic, LabelStyle}; +use super::super::files::{Error, Files, Location}; +use super::super::term::renderer::{Locus, MultiLabel, Renderer, SingleLabel}; +use super::super::term::Config; + +/// Count the number of decimal digits in `n`. +fn count_digits(mut n: usize) -> usize { + let mut count = 0; + while n != 0 { + count += 1; + n /= 10; // remove last digit + } + count +} + +/// Output a richly formatted diagnostic, with source code previews. +pub struct RichDiagnostic<'diagnostic, 'config, FileId> { + diagnostic: &'diagnostic Diagnostic, + config: &'config Config, +} + +impl<'diagnostic, 'config, FileId> RichDiagnostic<'diagnostic, 'config, FileId> +where + FileId: Copy + PartialEq, +{ + pub fn new( + diagnostic: &'diagnostic Diagnostic, + config: &'config Config, + ) -> RichDiagnostic<'diagnostic, 'config, FileId> { + RichDiagnostic { diagnostic, config } + } + + pub fn render<'files>( + &self, + files: &'files impl Files<'files, FileId = FileId>, + renderer: &mut Renderer<'_, '_>, + ) -> Result<(), Error> + where + FileId: 'files, + { + use std::collections::BTreeMap; + + struct LabeledFile<'diagnostic, FileId> { + file_id: FileId, + start: usize, + name: String, + location: Location, + num_multi_labels: usize, + lines: BTreeMap>, + max_label_style: LabelStyle, + } + + impl<'diagnostic, FileId> LabeledFile<'diagnostic, FileId> { + fn get_or_insert_line( + &mut self, + line_index: usize, + line_range: Range, + line_number: usize, + ) -> &mut Line<'diagnostic> { + self.lines.entry(line_index).or_insert_with(|| Line { + range: line_range, + number: line_number, + single_labels: vec![], + multi_labels: vec![], + // This has to be false by default so we know if it must be rendered by another condition already. + must_render: false, + }) + } + } + + struct Line<'diagnostic> { + number: usize, + range: std::ops::Range, + // TODO: How do we reuse these allocations? + single_labels: Vec>, + multi_labels: Vec<(usize, LabelStyle, MultiLabel<'diagnostic>)>, + must_render: bool, + } + + // TODO: Make this data structure external, to allow for allocation reuse + let mut labeled_files = Vec::>::new(); + // Keep track of the outer padding to use when rendering the + // snippets of source code. + let mut outer_padding = 0; + + // Group labels by file + for label in &self.diagnostic.labels { + let start_line_index = files.line_index(label.file_id, label.range.start)?; + let start_line_number = files.line_number(label.file_id, start_line_index)?; + let start_line_range = files.line_range(label.file_id, start_line_index)?; + let end_line_index = files.line_index(label.file_id, label.range.end)?; + let end_line_number = files.line_number(label.file_id, end_line_index)?; + let end_line_range = files.line_range(label.file_id, end_line_index)?; + + outer_padding = std::cmp::max(outer_padding, count_digits(start_line_number)); + outer_padding = std::cmp::max(outer_padding, count_digits(end_line_number)); + + // NOTE: This could be made more efficient by using an associative + // data structure like a hashmap or B-tree, but we use a vector to + // preserve the order that unique files appear in the list of labels. + let labeled_file = match labeled_files + .iter_mut() + .find(|labeled_file| label.file_id == labeled_file.file_id) + { + Some(labeled_file) => { + // another diagnostic also referenced this file + if labeled_file.max_label_style > label.style + || (labeled_file.max_label_style == label.style + && labeled_file.start > label.range.start) + { + // this label has a higher style or has the same style but starts earlier + labeled_file.start = label.range.start; + labeled_file.location = files.location(label.file_id, label.range.start)?; + labeled_file.max_label_style = label.style; + } + labeled_file + } + None => { + // no other diagnostic referenced this file yet + labeled_files.push(LabeledFile { + file_id: label.file_id, + start: label.range.start, + name: files.name(label.file_id)?.to_string(), + location: files.location(label.file_id, label.range.start)?, + num_multi_labels: 0, + lines: BTreeMap::new(), + max_label_style: label.style, + }); + // this unwrap should never fail because we just pushed an element + labeled_files + .last_mut() + .expect("just pushed an element that disappeared") + } + }; + + if start_line_index == end_line_index { + // Single line + // + // ```text + // 2 │ (+ test "") + // │ ^^ expected `Int` but found `String` + // ``` + let label_start = label.range.start - start_line_range.start; + // Ensure that we print at least one caret, even when we + // have a zero-length source range. + let label_end = + usize::max(label.range.end - start_line_range.start, label_start + 1); + + let line = labeled_file.get_or_insert_line( + start_line_index, + start_line_range, + start_line_number, + ); + + // Ensure that the single line labels are lexicographically + // sorted by the range of source code that they cover. + let index = match line.single_labels.binary_search_by(|(_, range, _)| { + // `Range` doesn't implement `Ord`, so convert to `(usize, usize)` + // to piggyback off its lexicographic comparison implementation. + (range.start, range.end).cmp(&(label_start, label_end)) + }) { + // If the ranges are the same, order the labels in reverse + // to how they were originally specified in the diagnostic. + // This helps with printing in the renderer. + Ok(index) | Err(index) => index, + }; + + line.single_labels + .insert(index, (label.style, label_start..label_end, &label.message)); + + // If this line is not rendered, the SingleLabel is not visible. + line.must_render = true; + } else { + // Multiple lines + // + // ```text + // 4 │ fizz₁ num = case (mod num 5) (mod num 3) of + // │ ╭─────────────^ + // 5 │ │ 0 0 => "FizzBuzz" + // 6 │ │ 0 _ => "Fizz" + // 7 │ │ _ 0 => "Buzz" + // 8 │ │ _ _ => num + // │ ╰──────────────^ `case` clauses have incompatible types + // ``` + + let label_index = labeled_file.num_multi_labels; + labeled_file.num_multi_labels += 1; + + // First labeled line + let label_start = label.range.start - start_line_range.start; + + let start_line = labeled_file.get_or_insert_line( + start_line_index, + start_line_range.clone(), + start_line_number, + ); + + start_line.multi_labels.push(( + label_index, + label.style, + MultiLabel::Top(label_start), + )); + + // The first line has to be rendered so the start of the label is visible. + start_line.must_render = true; + + // Marked lines + // + // ```text + // 5 │ │ 0 0 => "FizzBuzz" + // 6 │ │ 0 _ => "Fizz" + // 7 │ │ _ 0 => "Buzz" + // ``` + for line_index in (start_line_index + 1)..end_line_index { + let line_range = files.line_range(label.file_id, line_index)?; + let line_number = files.line_number(label.file_id, line_index)?; + + outer_padding = std::cmp::max(outer_padding, count_digits(line_number)); + + let line = labeled_file.get_or_insert_line(line_index, line_range, line_number); + + line.multi_labels + .push((label_index, label.style, MultiLabel::Left)); + + // The line should be rendered to match the configuration of how much context to show. + line.must_render |= + // Is this line part of the context after the start of the label? + line_index - start_line_index <= self.config.start_context_lines + || + // Is this line part of the context before the end of the label? + end_line_index - line_index <= self.config.end_context_lines; + } + + // Last labeled line + // + // ```text + // 8 │ │ _ _ => num + // │ ╰──────────────^ `case` clauses have incompatible types + // ``` + let label_end = label.range.end - end_line_range.start; + + let end_line = labeled_file.get_or_insert_line( + end_line_index, + end_line_range, + end_line_number, + ); + + end_line.multi_labels.push(( + label_index, + label.style, + MultiLabel::Bottom(label_end, &label.message), + )); + + // The last line has to be rendered so the end of the label is visible. + end_line.must_render = true; + } + } + + // Header and message + // + // ```text + // error[E0001]: unexpected type in `+` application + // ``` + renderer.render_header( + None, + self.diagnostic.severity, + self.diagnostic.code.as_deref(), + self.diagnostic.message.as_str(), + )?; + + // Source snippets + // + // ```text + // ┌─ test:2:9 + // │ + // 2 │ (+ test "") + // │ ^^ expected `Int` but found `String` + // │ + // ``` + let mut labeled_files = labeled_files.into_iter().peekable(); + while let Some(labeled_file) = labeled_files.next() { + let source = files.source(labeled_file.file_id)?; + let source = source.as_ref(); + + // Top left border and locus. + // + // ```text + // ┌─ test:2:9 + // ``` + if !labeled_file.lines.is_empty() { + if !self.diagnostic.anonymous { + renderer.render_snippet_start( + outer_padding, + &Locus { + name: labeled_file.name, + location: labeled_file.location, + }, + )?; + } + renderer.render_snippet_empty( + outer_padding, + self.diagnostic.severity, + labeled_file.num_multi_labels, + &[], + )?; + } + + let mut lines = labeled_file + .lines + .iter() + .filter(|(_, line)| line.must_render) + .peekable(); + + while let Some((line_index, line)) = lines.next() { + renderer.render_snippet_source( + outer_padding, + line.number, + &source[line.range.clone()], + self.diagnostic.severity, + &line.single_labels, + labeled_file.num_multi_labels, + &line.multi_labels, + )?; + + // Check to see if we need to render any intermediate stuff + // before rendering the next line. + if let Some((next_line_index, _)) = lines.peek() { + match next_line_index.checked_sub(*line_index) { + // Consecutive lines + Some(1) => {} + // One line between the current line and the next line + Some(2) => { + // Write a source line + let file_id = labeled_file.file_id; + + // This line was not intended to be rendered initially. + // To render the line right, we have to get back the original labels. + let labels = labeled_file + .lines + .get(&(line_index + 1)) + .map_or(&[][..], |line| &line.multi_labels[..]); + + renderer.render_snippet_source( + outer_padding, + files.line_number(file_id, line_index + 1)?, + &source[files.line_range(file_id, line_index + 1)?], + self.diagnostic.severity, + &[], + labeled_file.num_multi_labels, + labels, + )?; + } + // More than one line between the current line and the next line. + Some(_) | None => { + // Source break + // + // ```text + // · + // ``` + renderer.render_snippet_break( + outer_padding, + self.diagnostic.severity, + labeled_file.num_multi_labels, + &line.multi_labels, + )?; + } + } + } + } + + // Check to see if we should render a trailing border after the + // final line of the snippet. + if labeled_files.peek().is_none() && self.diagnostic.notes.is_empty() { + // We don't render a border if we are at the final newline + // without trailing notes, because it would end up looking too + // spaced-out in combination with the final new line. + } else { + // Render the trailing snippet border. + renderer.render_snippet_empty( + outer_padding, + self.diagnostic.severity, + labeled_file.num_multi_labels, + &[], + )?; + } + } + if self.diagnostic.render_extra_empty { + renderer.render_snippet_empty(outer_padding, self.diagnostic.severity, 0, &[])?; + } + + // Additional notes + // + // ```text + // = expected type `Int` + // found type `String` + // ``` + for (idx, note) in self.diagnostic.notes.iter().enumerate() { + renderer.render_snippet_note( + outer_padding, + note.to_owned(), + idx != self.diagnostic.notes.len() - 1, + )?; + } + Ok(()) + } +} + +/// Output a short diagnostic, with a line number, severity, and message. +pub struct ShortDiagnostic<'diagnostic, FileId> { + diagnostic: &'diagnostic Diagnostic, + show_notes: bool, +} + +impl<'diagnostic, FileId> ShortDiagnostic<'diagnostic, FileId> +where + FileId: Copy + PartialEq, +{ + pub fn new( + diagnostic: &'diagnostic Diagnostic, + show_notes: bool, + ) -> ShortDiagnostic<'diagnostic, FileId> { + ShortDiagnostic { + diagnostic, + show_notes, + } + } + + pub fn render<'files>( + &self, + files: &'files impl Files<'files, FileId = FileId>, + renderer: &mut Renderer<'_, '_>, + ) -> Result<(), Error> + where + FileId: 'files, + { + // Located headers + // + // ```text + // test:2:9: error[E0001]: unexpected type in `+` application + // ``` + let mut primary_labels_encountered = 0; + let labels = self.diagnostic.labels.iter(); + for label in labels.filter(|label| label.style == LabelStyle::Primary) { + primary_labels_encountered += 1; + + renderer.render_header( + Some(&Locus { + name: files.name(label.file_id)?.to_string(), + location: files.location(label.file_id, label.range.start)?, + }), + self.diagnostic.severity, + self.diagnostic.code.as_deref(), + self.diagnostic.message.as_str(), + )?; + } + + // Fallback to printing a non-located header if no primary labels were encountered + // + // ```text + // error[E0002]: Bad config found + // ``` + if primary_labels_encountered == 0 { + renderer.render_header( + None, + self.diagnostic.severity, + self.diagnostic.code.as_deref(), + self.diagnostic.message.as_str(), + )?; + } + + if self.show_notes { + // Additional notes + // + // ```text + // = expected type `Int` + // found type `String` + // ``` + for (idx, note) in self.diagnostic.notes.iter().enumerate() { + renderer.render_snippet_note( + 0, + note.to_owned(), + idx != self.diagnostic.notes.len() - 1, + )?; + } + } + + Ok(()) + } +} diff --git a/crates/rslint_errors/src/diagnostic.rs b/crates/rslint_errors/src/diagnostic.rs new file mode 100644 index 00000000000..fc5c3727626 --- /dev/null +++ b/crates/rslint_errors/src/diagnostic.rs @@ -0,0 +1,414 @@ +use crate::suggestion::SuggestionChange; +use crate::{ + file::{FileId, FileSpan, Span}, + Applicability, CodeSuggestion, DiagnosticTag, Severity, SuggestionStyle, +}; +use rslint_text_edit::*; + +/// A diagnostic message that can give information +/// like errors or warnings. +#[derive(Debug, Clone, PartialEq, Hash)] +pub struct Diagnostic { + pub file_id: FileId, + + pub severity: Severity, + pub code: Option, + pub title: String, + pub tag: Option, + + pub primary: Option, + pub children: Vec, + pub suggestions: Vec, + pub footers: Vec