Skip to content

Commit

Permalink
refactor(test): use test_parse_and_analyze for more tests
Browse files Browse the repository at this point in the history
Tidy up the test code.

Add an option to test_parse_and_analyze to allow parse errors. This is
needed for a few of the tests.
  • Loading branch information
strager committed Jan 7, 2024
1 parent eaaee16 commit 9d46145
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 187 deletions.
14 changes: 14 additions & 0 deletions src/quick-lint-js/util/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,20 @@ Derived_Reference derived_cast(Base& base) {
"Derived should not be the same type as Base");
return static_cast<Derived_Reference>(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 <class Base_Pointer, class Derived>
Base_Pointer base_cast(Derived* base) {
using Base = std::remove_pointer_t<Base_Pointer>;
static_assert(std::is_base_of_v<Base, Derived>,
"Derived should derive from Base");
static_assert(!std::is_base_of_v<Derived, Base>,
"Derived should not be the same type as Base");
return base;
}
}

// quick-lint-js finds bugs in JavaScript programs.
Expand Down
13 changes: 8 additions & 5 deletions test/quick-lint-js/variable-analyzer-support.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_Reporter*>(&diag_collector)
: base_cast<Diag_Reporter*>(&failing_diag_reporter),
options.parse_options);

Variable_Analyzer var_analyzer(&diag_collector, &globals,
options.analyze_options);

Expand Down
11 changes: 11 additions & 0 deletions test/quick-lint-js/variable-analyzer-support.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
242 changes: 60 additions & 182 deletions test/test-variable-analyzer-parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// See end of file for extended copyright information.

#include <cstring>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <quick-lint-js/container/padded-string.h>
#include <quick-lint-js/diag-collector.h>
Expand All @@ -14,56 +13,30 @@
#include <quick-lint-js/port/char8.h>
#include <quick-lint-js/variable-analyzer-support.h>

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<T extends T>() {}"_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<T extends "_sv.size(), u8"T"_sv, //
declaration, u8"function f<"_sv.size(), u8"T"_sv),
}));
test_parse_and_analyze(
u8"function f<T extends T>() {}"_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,
Expand All @@ -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) {
Expand All @@ -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, //
Expand All @@ -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);
}
}
}
Expand Down

0 comments on commit 9d46145

Please sign in to comment.