da6543108d
This CL adds a lint error for variables that are unnecessarily bound with 'let' when they could be bound using 'const. This test is skipped for struct types. For struct types, the "constness" also depends on the struct methods called and whether these methods write to the struct or not. This is not straight-forward to detect. Drive-by: Fix all the newly introduced lint errors. Bug: v8:7793 Change-Id: I0522ffcc4321350eef2e9573b8430bc78200ddce Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1645322 Commit-Queue: Simon Zünd <szuend@chromium.org> Reviewed-by: Tobias Tebbi <tebbi@chromium.org> Cr-Commit-Position: refs/heads/master@{#62085}
320 lines
8.3 KiB
C++
320 lines
8.3 KiB
C++
// Copyright 2018 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "src/torque/torque-compiler.h"
|
|
#include "src/torque/utils.h"
|
|
#include "test/unittests/test-utils.h"
|
|
#include "testing/gmock-support.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace torque {
|
|
|
|
namespace {
|
|
|
|
// This is a simplified version of the basic Torque type definitions.
|
|
// Some class types are replaced by abstact types to keep it self-contained and
|
|
// small.
|
|
constexpr const char* kTestTorquePrelude = R"(
|
|
type void;
|
|
type never;
|
|
|
|
type Tagged generates 'TNode<Object>' constexpr 'ObjectPtr';
|
|
type Smi extends Tagged generates 'TNode<Smi>' constexpr 'Smi';
|
|
|
|
@abstract
|
|
extern class HeapObject extends Tagged {
|
|
map: Map;
|
|
}
|
|
type Map extends HeapObject generates 'TNode<Map>';
|
|
type Object = Smi | HeapObject;
|
|
type JSReceiver extends HeapObject generates 'TNode<JSReceiver>';
|
|
type JSObject extends JSReceiver generates 'TNode<JSObject>';
|
|
type int32 generates 'TNode<Int32T>' constexpr 'int32_t';
|
|
type uint32 generates 'TNode<Uint32T>' constexpr 'uint32_t';
|
|
type int31 extends int32
|
|
generates 'TNode<Int32T>' constexpr 'int31_t';
|
|
type uint31 extends uint32
|
|
generates 'TNode<Uint32T>' constexpr 'uint31_t';
|
|
type int16 extends int31
|
|
generates 'TNode<Int16T>' constexpr 'int16_t';
|
|
type uint16 extends uint31
|
|
generates 'TNode<Uint16T>' constexpr 'uint16_t';
|
|
type int8 extends int16 generates 'TNode<Int8T>' constexpr 'int8_t';
|
|
type uint8 extends uint16
|
|
generates 'TNode<Uint8T>' constexpr 'uint8_t';
|
|
type int64 generates 'TNode<Int64T>' constexpr 'int64_t';
|
|
type intptr generates 'TNode<IntPtrT>' constexpr 'intptr_t';
|
|
type uintptr generates 'TNode<UintPtrT>' constexpr 'uintptr_t';
|
|
type float32 generates 'TNode<Float32T>' constexpr 'float';
|
|
type float64 generates 'TNode<Float64T>' constexpr 'double';
|
|
type bool generates 'TNode<BoolT>' constexpr 'bool';
|
|
type bint generates 'TNode<BInt>' constexpr 'BInt';
|
|
type string constexpr 'const char*';
|
|
type RawPtr generates 'TNode<RawPtrT>' constexpr 'void*';
|
|
type Code extends HeapObject generates 'TNode<Code>';
|
|
type BuiltinPtr extends Smi generates 'TNode<BuiltinPtr>';
|
|
type Context extends HeapObject generates 'TNode<Context>';
|
|
type NativeContext extends Context;
|
|
)";
|
|
|
|
TorqueCompilerResult TestCompileTorque(std::string source) {
|
|
TorqueCompilerOptions options;
|
|
options.output_directory = "";
|
|
options.collect_language_server_data = false;
|
|
options.force_assert_statements = false;
|
|
options.v8_root = ".";
|
|
|
|
source = kTestTorquePrelude + source;
|
|
return CompileTorque(source, options);
|
|
}
|
|
|
|
void ExpectSuccessfulCompilation(std::string source) {
|
|
TorqueCompilerResult result = TestCompileTorque(std::move(source));
|
|
std::vector<std::string> messages;
|
|
for (const auto& message : result.messages) {
|
|
messages.push_back(message.message);
|
|
}
|
|
EXPECT_EQ(messages, std::vector<std::string>{});
|
|
}
|
|
|
|
template <class T>
|
|
void ExpectFailingCompilation(
|
|
std::string source, ::testing::PolymorphicMatcher<T> message_pattern) {
|
|
TorqueCompilerResult result = TestCompileTorque(std::move(source));
|
|
ASSERT_FALSE(result.messages.empty());
|
|
EXPECT_THAT(result.messages[0].message, message_pattern);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST(Torque, Prelude) { ExpectSuccessfulCompilation(""); }
|
|
|
|
TEST(Torque, StackDeleteRange) {
|
|
Stack<int> stack = {1, 2, 3, 4, 5, 6, 7};
|
|
stack.DeleteRange(StackRange{BottomOffset{2}, BottomOffset{4}});
|
|
Stack<int> result = {1, 2, 5, 6, 7};
|
|
ASSERT_TRUE(stack == result);
|
|
}
|
|
|
|
using ::testing::HasSubstr;
|
|
TEST(Torque, TypeNamingConventionLintError) {
|
|
ExpectFailingCompilation(R"(
|
|
type foo generates 'TNode<Foo>';
|
|
)",
|
|
HasSubstr("\"foo\""));
|
|
}
|
|
|
|
TEST(Torque, StructNamingConventionLintError) {
|
|
ExpectFailingCompilation(R"(
|
|
struct foo {}
|
|
)",
|
|
HasSubstr("\"foo\""));
|
|
}
|
|
|
|
TEST(Torque, ClassDefinition) {
|
|
ExpectSuccessfulCompilation(R"(
|
|
extern class TestClassWithAllTypes extends HeapObject {
|
|
a: int8;
|
|
b: uint8;
|
|
b2: uint8;
|
|
b3: uint8;
|
|
c: int16;
|
|
d: uint16;
|
|
e: int32;
|
|
f: uint32;
|
|
g: RawPtr;
|
|
h: intptr;
|
|
i: uintptr;
|
|
}
|
|
|
|
macro TestClassWithAllTypesLoadsAndStores(
|
|
t: TestClassWithAllTypes, r: RawPtr, v1: int8, v2: uint8, v3: int16,
|
|
v4: uint16, v5: int32, v6: uint32, v7: intptr, v8: uintptr) {
|
|
t.a = v1;
|
|
t.b = v2;
|
|
t.c = v3;
|
|
t.d = v4;
|
|
t.e = v5;
|
|
t.f = v6;
|
|
t.g = r;
|
|
t.h = v7;
|
|
t.i = v8;
|
|
t.a = t.a;
|
|
t.b = t.b;
|
|
t.c = t.c;
|
|
t.d = t.d;
|
|
t.e = t.e;
|
|
t.f = t.f;
|
|
t.g = t.g;
|
|
t.h = t.h;
|
|
t.i = t.i;
|
|
}
|
|
)");
|
|
}
|
|
|
|
TEST(Torque, TypeDeclarationOrder) {
|
|
ExpectSuccessfulCompilation(R"(
|
|
type Baztype = Foo | FooType;
|
|
|
|
@abstract
|
|
@noVerifier
|
|
extern class Foo extends HeapObject {
|
|
fooField: FooType;
|
|
}
|
|
|
|
@noVerifier
|
|
extern class Bar extends Foo {
|
|
barField: Bartype;
|
|
bazfield: Baztype;
|
|
}
|
|
|
|
type Bartype = FooType;
|
|
|
|
type FooType = Smi | Bar;
|
|
)");
|
|
}
|
|
|
|
TEST(Torque, ConditionalFields) {
|
|
// This class should throw alignment errors if @if decorators aren't
|
|
// working.
|
|
ExpectSuccessfulCompilation(R"(
|
|
@noVerifier
|
|
extern class PreprocessingTest extends HeapObject {
|
|
@if(FALSE_FOR_TESTING) a: int8;
|
|
@if(TRUE_FOR_TESTING) a: int16;
|
|
b: int16;
|
|
d: int32;
|
|
@ifnot(TRUE_FOR_TESTING) e: int8;
|
|
@ifnot(FALSE_FOR_TESTING) f: int16;
|
|
g: int16;
|
|
h: int32;
|
|
}
|
|
)");
|
|
ExpectFailingCompilation(R"(
|
|
@noVerifier
|
|
extern class PreprocessingTest extends HeapObject {
|
|
@if(TRUE_FOR_TESTING) a: int8;
|
|
@if(FALSE_FOR_TESTING) a: int16;
|
|
b: int16;
|
|
d: int32;
|
|
@ifnot(FALSE_FOR_TESTING) e: int8;
|
|
@ifnot(TRUE_FOR_TESTING) f: int16;
|
|
g: int16;
|
|
h: int32;
|
|
}
|
|
)",
|
|
HasSubstr("aligned"));
|
|
}
|
|
|
|
TEST(Torque, ConstexprLetBindingDoesNotCrash) {
|
|
ExpectFailingCompilation(
|
|
R"(macro FooBar() { let foo = 0; check(foo >= 0); })",
|
|
HasSubstr("Use 'const' instead of 'let' for variable 'foo'"));
|
|
}
|
|
|
|
TEST(Torque, DoubleUnderScorePrefixIllegalForIdentifiers) {
|
|
ExpectFailingCompilation(R"(
|
|
macro Foo() {
|
|
let __x;
|
|
}
|
|
)",
|
|
HasSubstr("Lexer Error"));
|
|
}
|
|
|
|
TEST(Torque, UnusedLetBindingLintError) {
|
|
ExpectFailingCompilation(R"(
|
|
macro Foo(y: Smi) {
|
|
let x: Smi = y;
|
|
}
|
|
)",
|
|
HasSubstr("Variable 'x' is never used."));
|
|
}
|
|
|
|
TEST(Torque, UnderscorePrefixSilencesUnusedWarning) {
|
|
ExpectSuccessfulCompilation(R"(
|
|
macro Foo(y: Smi) {
|
|
let _x: Smi = y;
|
|
}
|
|
)");
|
|
}
|
|
|
|
TEST(Torque, UsingUnderscorePrefixedIdentifierError) {
|
|
ExpectFailingCompilation(R"(
|
|
macro Foo(y: Smi) {
|
|
let _x: Smi = y;
|
|
check(_x == y);
|
|
}
|
|
)",
|
|
HasSubstr("Trying to reference '_x'"));
|
|
}
|
|
|
|
TEST(Torque, UnusedArgumentLintError) {
|
|
ExpectFailingCompilation(R"(
|
|
macro Foo(x: Smi) {}
|
|
)",
|
|
HasSubstr("Variable 'x' is never used."));
|
|
}
|
|
|
|
TEST(Torque, UsingUnderscorePrefixedArgumentSilencesWarning) {
|
|
ExpectSuccessfulCompilation(R"(
|
|
macro Foo(_y: Smi) {}
|
|
)");
|
|
}
|
|
|
|
TEST(Torque, UnusedLabelLintError) {
|
|
ExpectFailingCompilation(R"(
|
|
macro Foo() labels Bar {}
|
|
)",
|
|
HasSubstr("Label 'Bar' is never used."));
|
|
}
|
|
|
|
TEST(Torque, UsingUnderScorePrefixLabelSilencesWarning) {
|
|
ExpectSuccessfulCompilation(R"(
|
|
macro Foo() labels _Bar {}
|
|
)");
|
|
}
|
|
|
|
TEST(Torque, NoUnusedWarningForImplicitArguments) {
|
|
ExpectSuccessfulCompilation(R"(
|
|
macro Foo(implicit c: Context, r: JSReceiver)() {}
|
|
)");
|
|
}
|
|
|
|
TEST(Torque, NoUnusedWarningForVariablesOnlyUsedInAsserts) {
|
|
ExpectSuccessfulCompilation(R"(
|
|
macro Foo(x: bool) {
|
|
assert(x);
|
|
}
|
|
)");
|
|
}
|
|
|
|
TEST(Torque, ImportNonExistentFile) {
|
|
ExpectFailingCompilation(R"(import "foo/bar.tq")",
|
|
HasSubstr("File 'foo/bar.tq' not found."));
|
|
}
|
|
|
|
TEST(Torque, LetShouldBeConstLintError) {
|
|
ExpectFailingCompilation(R"(
|
|
macro Foo(y: Smi): Smi {
|
|
let x: Smi = y;
|
|
return x;
|
|
})",
|
|
HasSubstr("Variable 'x' is never assigned to."));
|
|
}
|
|
|
|
TEST(Torque, LetShouldBeConstIsSkippedForStructs) {
|
|
ExpectSuccessfulCompilation(R"(
|
|
struct Foo{ a: Smi; }
|
|
macro Bar(x: Smi): Foo {
|
|
let foo = Foo{a: x};
|
|
return foo;
|
|
}
|
|
)");
|
|
}
|
|
|
|
} // namespace torque
|
|
} // namespace internal
|
|
} // namespace v8
|