diff --git a/src/quick-lint-js/util/cast.h b/src/quick-lint-js/util/cast.h index 278e387b9b..970f843365 100644 --- a/src/quick-lint-js/util/cast.h +++ b/src/quick-lint-js/util/cast.h @@ -78,6 +78,20 @@ Derived_Reference derived_cast(Base& base) { "Derived should not be the same type as Base"); return static_cast(base); } + +// Cast a pointer or reference to a pointer/reference to a base class. +// +// In the presence of multiple inheritance, perform pointer adjustments (like +// what static_cast does). +template +Base_Pointer base_cast(Derived* base) { + using Base = std::remove_pointer_t; + static_assert(std::is_base_of_v, + "Derived should derive from Base"); + static_assert(!std::is_base_of_v, + "Derived should not be the same type as Base"); + return base; +} } // quick-lint-js finds bugs in JavaScript programs. diff --git a/test/quick-lint-js/variable-analyzer-support.cpp b/test/quick-lint-js/variable-analyzer-support.cpp index 61104b42d6..565fc8953b 100644 --- a/test/quick-lint-js/variable-analyzer-support.cpp +++ b/test/quick-lint-js/variable-analyzer-support.cpp @@ -60,12 +60,15 @@ void test_parse_and_analyze(String8_View input, Source_Location caller) { Padded_String code(input); - // Our variable analyzer tests should have valid code as input. We don't want - // to accidentally test parse errors when we're testing the variable analyzer. - Failing_Diag_Reporter parse_diag_reporter; - Parser p(&code, &parse_diag_reporter, options.parse_options); - + Failing_Diag_Reporter failing_diag_reporter; Diag_Collector diag_collector; + + Parser p(&code, + options.allow_parse_errors + ? base_cast(&diag_collector) + : base_cast(&failing_diag_reporter), + options.parse_options); + Variable_Analyzer var_analyzer(&diag_collector, &globals, options.analyze_options); diff --git a/test/quick-lint-js/variable-analyzer-support.h b/test/quick-lint-js/variable-analyzer-support.h index 1a9277284f..b83d5527f6 100644 --- a/test/quick-lint-js/variable-analyzer-support.h +++ b/test/quick-lint-js/variable-analyzer-support.h @@ -33,6 +33,17 @@ constexpr Variable_Analyzer_Options typescript_var_options = struct Test_Parse_And_Analyze_Options { Parser_Options parse_options; Variable_Analyzer_Options analyze_options; + + // Our variable analyzer tests should have valid code as input. By default we + // don't want to accidentally test parse errors when we're testing the + // variable analyzer. + bool allow_parse_errors = false; + + Test_Parse_And_Analyze_Options with_allow_parse_errors() const { + Test_Parse_And_Analyze_Options o = *this; + o.allow_parse_errors = true; + return o; + } }; extern const Test_Parse_And_Analyze_Options javascript_analyze_options; diff --git a/test/test-variable-analyzer-parse.cpp b/test/test-variable-analyzer-parse.cpp index 0adc24f691..667d575ebb 100644 --- a/test/test-variable-analyzer-parse.cpp +++ b/test/test-variable-analyzer-parse.cpp @@ -2,7 +2,6 @@ // See end of file for extended copyright information. #include -#include #include #include #include @@ -14,56 +13,30 @@ #include #include -using ::testing::ElementsAreArray; -using ::testing::IsEmpty; - namespace quick_lint_js { namespace { TEST(Test_Variable_Analyzer_Parse, let_variable_use_before_declaration_with_parsing) { - Padded_String input(u8"let x = y, y = x;"_sv); - Diag_Collector v; - Variable_Analyzer l(&v, &default_globals, javascript_var_options); - Parser p(&input, &v, javascript_options); - EXPECT_TRUE(p.parse_and_visit_statement(l)); - l.visit_end_of_module(); - - EXPECT_THAT( - v.errors, - ElementsAreArray({ - DIAG_TYPE_2_OFFSETS(&input, Diag_Variable_Used_Before_Declaration, // - use, 8, u8"y"_sv, declaration, 11, u8"y"_sv), - })); + test_parse_and_analyze( + u8"let x = y, y = x;"_sv, // + u8" ^ Diag_Variable_Used_Before_Declaration.declaration\n"_diag // + u8" ^ .use"_diag, + javascript_analyze_options, default_globals); } TEST(Test_Variable_Analyzer_Parse, generic_parameter_use_before_declaration) { - Padded_String input(u8"function f() {}"_sv); - Diag_Collector v; - Variable_Analyzer l(&v, &default_globals, typescript_var_options); - Parser p(&input, &v, typescript_options); - EXPECT_TRUE(p.parse_and_visit_statement(l)); - l.visit_end_of_module(); - - EXPECT_THAT(v.errors, - ElementsAreArray({ - DIAG_TYPE_2_OFFSETS( - &input, Diag_Cyclic_TypeScript_Type_Definition, // - use, u8"function f() {}"_sv, + u8" ^ Diag_Cyclic_TypeScript_Type_Definition.use\n"_diag + u8" ^ .declaration"_diag, + typescript_analyze_options.with_allow_parse_errors(), default_globals); } TEST( Test_Variable_Analyzer_Parse, variables_with_different_escape_sequences_are_equivalent_after_normalization) { - Padded_String input(u8"let \\u{69} = 0; i += 1; \\u0069;"_sv); - Diag_Collector v; - - Variable_Analyzer l(&v, &default_globals, javascript_var_options); - Parser p(&input, &v, javascript_options); - p.parse_and_visit_module(l); - - EXPECT_THAT(v.errors, IsEmpty()); + test_parse_and_analyze(u8"let \\u{69} = 0; i += 1; \\u0069;"_sv, no_diags, + javascript_analyze_options, default_globals); } TEST(Test_Variable_Analyzer_Parse, @@ -77,38 +50,19 @@ TEST(Test_Variable_Analyzer_Parse, TEST(Test_Variable_Analyzer_Parse, escape_sequences_are_allowed_for_arguments_variable) { - Padded_String input(u8R"(function f() { return \u{61}rgument\u{73}; })"_sv); - Diag_Collector v; - - Variable_Analyzer l(&v, &default_globals, javascript_var_options); - Parser p(&input, &v, javascript_options); - p.parse_and_visit_module(l); - - EXPECT_THAT(v.errors, IsEmpty()); + test_parse_and_analyze(u8R"(function f() { return \u{61}rgument\u{73}; })"_sv, + no_diags, javascript_analyze_options, default_globals); } TEST(Test_Variable_Analyzer_Parse, function_statement_inside_if_does_not_conflict_with_let_variable) { - Padded_String input(u8"let f;\nif (true)\n function f() {}"_sv); - - Diag_Collector v; - Variable_Analyzer l(&v, &default_globals, javascript_var_options); - Parser p(&input, &v, javascript_options); - p.parse_and_visit_module(l); - - EXPECT_THAT(v.errors, IsEmpty()); + test_parse_and_analyze(u8"let f;\nif (true)\n function f() {}"_sv, no_diags, + javascript_analyze_options, default_globals); } TEST(Test_Variable_Analyzer_Parse, typeof_with_conditional_operator) { - { - Padded_String input(u8"typeof x ? 10 : 20;"_sv); - Diag_Collector v; - Variable_Analyzer l(&v, &default_globals, javascript_var_options); - Parser p(&input, &v, javascript_options); - p.parse_and_visit_module(l); - - EXPECT_THAT(v.errors, IsEmpty()); - } + test_parse_and_analyze(u8"typeof x ? 10 : 20;"_sv, no_diags, + javascript_analyze_options, default_globals); } TEST(Test_Variable_Analyzer_Parse, prefix_plusplus_on_const_variable) { @@ -118,27 +72,13 @@ TEST(Test_Variable_Analyzer_Parse, prefix_plusplus_on_const_variable) { u8" ^ .declaration"_diag, javascript_analyze_options, default_globals); - { - Padded_String input(u8"const x = {y : 10};\n ++x.y;"_sv); - Diag_Collector v; - Variable_Analyzer l(&v, &default_globals, javascript_var_options); - Parser p(&input, &v, javascript_options); - p.parse_and_visit_module(l); - - EXPECT_THAT(v.errors, IsEmpty()); - } + test_parse_and_analyze(u8"const x = {y : 10};\n ++x.y;"_sv, no_diags, + javascript_analyze_options, default_globals); } TEST(Test_Variable_Analyzer_Parse, prefix_plusplus_plus_operand) { - { - Padded_String input(u8"const x = [42]; ++x[0];"_sv); - Diag_Collector v; - Variable_Analyzer l(&v, &default_globals, javascript_var_options); - Parser p(&input, &v, javascript_options); - p.parse_and_visit_module(l); - - EXPECT_THAT(v.errors, IsEmpty()); - } + test_parse_and_analyze(u8"const x = [42]; ++x[0];"_sv, no_diags, + javascript_analyze_options, default_globals); test_parse_and_analyze( u8"const x = 42;\n const y =10;\n ++x + y;"_sv, // @@ -148,124 +88,62 @@ TEST(Test_Variable_Analyzer_Parse, prefix_plusplus_plus_operand) { } TEST(Test_Variable_Analyzer_Parse, use_await_label_in_non_async_function) { - Padded_String input(u8"function f() {await: for(;;){break await;}}"_sv); - Diag_Collector v; - Variable_Analyzer l(&v, &default_globals, javascript_var_options); - Parser p(&input, &v, javascript_options); - p.parse_and_visit_module(l); - - EXPECT_THAT(v.errors, IsEmpty()); + test_parse_and_analyze(u8"function f() {await: for(;;){break await;}}"_sv, + no_diags, javascript_analyze_options, default_globals); } TEST(Test_Variable_Analyzer_Parse, use_yield_label_in_non_generator_function) { - Padded_String input(u8"function f() {yield: for(;;){break yield;}}"_sv); - Diag_Collector v; - Variable_Analyzer l(&v, &default_globals, javascript_var_options); - Parser p(&input, &v, javascript_options); - p.parse_and_visit_module(l); - - EXPECT_THAT(v.errors, IsEmpty()); + test_parse_and_analyze(u8"function f() {yield: for(;;){break yield;}}"_sv, + no_diags, javascript_analyze_options, default_globals); } TEST(Test_Variable_Analyzer_Parse, escape_sequence_in_keyword_identifier) { // The parser should not report a stray 'finally' keyword. // The linter should not report that 'finally' is undeclared. - Padded_String input(u8"let which = \\u{66}inally;"_sv); - Diag_Collector v; - Variable_Analyzer l(&v, &default_globals, javascript_var_options); - Parser p(&input, &v, javascript_options); - p.parse_and_visit_module(l); - - EXPECT_THAT(v.errors, - ElementsAreArray({ - DIAG_TYPE(Diag_Keywords_Cannot_Contain_Escape_Sequences), - })); + test_parse_and_analyze(u8"let which = \\u{66}inally;"_sv, + u8"Diag_Keywords_Cannot_Contain_Escape_Sequences"_diag, + javascript_analyze_options.with_allow_parse_errors(), + default_globals); } TEST(Test_Variable_Analyzer_Parse, delete_local_variable) { - Padded_String input( - u8"function f(param) { let v; delete v; delete param; }"_sv); - Diag_Collector v; - Variable_Analyzer l(&v, &default_globals, javascript_var_options); - Parser p(&input, &v, javascript_options); - p.parse_and_visit_module(l); - - EXPECT_THAT(v.errors, - ElementsAreArray({ - DIAG_TYPE(Diag_Redundant_Delete_Statement_On_Variable), - DIAG_TYPE(Diag_Redundant_Delete_Statement_On_Variable), - })); + test_parse_and_analyze( + u8"function f(param) { let v; delete v; delete param; }"_sv, + u8"Diag_Redundant_Delete_Statement_On_Variable"_diag, + u8"Diag_Redundant_Delete_Statement_On_Variable"_diag, + javascript_analyze_options, default_globals); } TEST(Test_Variable_Analyzer_Parse, extends_self) { - { - Padded_String input( - u8"function C() {}\n"_sv - u8"{\n"_sv - u8" class C extends C {}"_sv - u8"}"_sv); - Diag_Collector v; - Variable_Analyzer l(&v, &default_globals, javascript_var_options); - Parser p(&input, &v, javascript_options); - p.parse_and_visit_module(l); - - EXPECT_THAT(v.errors, ElementsAreArray({ - DIAG_TYPE(Diag_Variable_Used_Before_Declaration), - })); - } - - { - Padded_String input( - u8"function C() {}\n"_sv - u8"{\n"_sv - u8" class C extends (null, [C][0], Object) {}"_sv - u8"}"_sv); - Diag_Collector v; - Variable_Analyzer l(&v, &default_globals, javascript_var_options); - Parser p(&input, &v, javascript_options); - p.parse_and_visit_module(l); - - EXPECT_THAT(v.errors, ElementsAreArray({ - DIAG_TYPE(Diag_Variable_Used_Before_Declaration), - })); - } - - { - Padded_String input( - u8"function C() {}\n"_sv - u8"{\n"_sv - u8" (class C extends C {})"_sv - u8"}"_sv); - Diag_Collector v; - Variable_Analyzer l(&v, &default_globals, javascript_var_options); - Parser p(&input, &v, javascript_options); - p.parse_and_visit_module(l); - - EXPECT_THAT(v.errors, ElementsAreArray({ - DIAG_TYPE(Diag_Variable_Used_Before_Declaration), - })); - } + test_parse_and_analyze( + u8"function C() {}\n"_sv + u8"{\n"_sv + u8" class C extends C {}"_sv + u8"}"_sv, + u8"Diag_Variable_Used_Before_Declaration"_diag, + javascript_analyze_options, default_globals); + test_parse_and_analyze( + u8"function C() {}\n"_sv + u8"{\n"_sv + u8" class C extends (null, [C][0], Object) {}"_sv + u8"}"_sv, + u8"Diag_Variable_Used_Before_Declaration"_diag, + javascript_analyze_options, default_globals); + test_parse_and_analyze( + u8"function C() {}\n"_sv + u8"{\n"_sv + u8" (class C extends C {})"_sv + u8"}"_sv, + u8"Diag_Variable_Used_Before_Declaration"_diag, + javascript_analyze_options, default_globals); } TEST(Test_Variable_Analyzer_Parse, typescript_static_block_can_reference_class) { - { - Padded_String input(u8"class C { static { C; } }"_sv); - Diag_Collector v; - Variable_Analyzer l(&v, &default_globals, javascript_var_options); - Parser p(&input, &v, typescript_options); - p.parse_and_visit_module(l); - EXPECT_THAT(v.errors, IsEmpty()); - } - - { - Padded_String input(u8"(class C { static { C; } });"_sv); - Diag_Collector v; - Variable_Analyzer l(&v, &default_globals, javascript_var_options); - Parser p(&input, &v, typescript_options); - p.parse_and_visit_module(l); - EXPECT_THAT(v.errors, IsEmpty()); - } + test_parse_and_analyze(u8"class C { static { C; } }"_sv, no_diags, + javascript_analyze_options, default_globals); + test_parse_and_analyze(u8"(class C { static { C; } });"_sv, no_diags, + javascript_analyze_options, default_globals); } } }