blob: 7e76bb4d600e135039f4748b98324cb7cce78864 [file] [log] [blame]
// Copyright 2020 The Fuchsia 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 <gtest/gtest.h>
#include "tools/fidl/fidlc/src/diagnostics.h"
#include "tools/fidl/fidlc/tests/test_library.h"
namespace fidlc {
namespace {
TEST(RecoverableParsingTests, BadUnexpectedToken) {
TestLibrary library;
library.AddFile("bad/fi-0007.noformat.test.fidl");
library.ExpectFail(ErrUnexpectedToken);
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST(RecoverableParsingTests, BadRecoverAtEndOfFile) {
TestLibrary library(R"FIDL(
library example;
type Enum = enum {
ONE; // First error
};
type Bits = bits {
CONSTANT = ; // Second error
};
)FIDL");
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kSemicolon),
Token::KindAndSubkind(Token::Kind::kEqual));
library.ExpectFail(ErrUnexpectedToken);
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST(RecoverableParsingTests, BadRecoverAtEndOfDecl) {
TestLibrary library(R"FIDL(
library example;
type Enum = enum {
VARIANT = 0;
MISSING_EQUALS 5;
};
type Union = union {
1: string_value string;
2 missing_colon uint16;
};
type Struct = struct {
value string;
};
)FIDL");
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kNumericLiteral),
Token::KindAndSubkind(Token::Kind::kEqual));
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kIdentifier),
Token::KindAndSubkind(Token::Kind::kColon));
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST(RecoverableParsingTests, BadRecoverAtEndOfMember) {
TestLibrary library(R"FIDL(
library example;
type SettingType = enum {
UNKNOWN = 0;
TIME_ZONE = 1;
CONNECTIVITY 2; // Error: missing equals
};
type SettingData = union {
1: string_value string;
2 time_zone_value ConnectedState; // Error: missing colon
/// Unattached doc comment. // erroneous doc comment is skipped during recovery
};
type LoginOverride = { // Error: missing keyword
NONE = 0;
AUTH.PROVIDER = 2, // Error: '.' in identifier
};
type AccountSettings = table {
1: mo.de LoginOverride; // Error: '.' in identifier
3: setting OtherSetting;
};
type TimeZoneInfo = struct {
current TimeZone:optional;
available vector<<TimeZone>; // Error: extra <
};
type TimeZone = struct {
id string;
name string;
region vector<string>;
};
)FIDL");
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kNumericLiteral),
Token::KindAndSubkind(Token::Kind::kEqual));
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kIdentifier),
Token::KindAndSubkind(Token::Kind::kColon));
library.ExpectFail(ErrMissingOrdinalBeforeMember);
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kLeftCurly),
Token::KindAndSubkind(Token::Kind::kIdentifier));
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kDot),
Token::KindAndSubkind(Token::Kind::kIdentifier));
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kLeftAngle),
Token::KindAndSubkind(Token::Kind::kIdentifier));
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST(RecoverableParsingTests, BadDoNotCompileAfterParsingFails) {
TestLibrary library(R"FIDL(
library example;
const compound.identifier uint8 = 0; // Syntax error
type NameCollision = struct {};
type NameCollision = struct {}; // This name collision error will not be
// reported, because if parsing fails
// compilation is skipped
)FIDL");
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kDot),
Token::KindAndSubkind(Token::Kind::kIdentifier));
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST(RecoverableParsingTests, BadRecoverToNextBitsMember) {
TestLibrary library(R"FIDL(
library example;
type Bits = bits {
ONE 0x1; // First error
TWO = 0x2;
FOUR = 0x4 // Second error
EIGHT = 0x8;
};
)FIDL");
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kNumericLiteral),
Token::KindAndSubkind(Token::Kind::kEqual));
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kIdentifier),
Token::KindAndSubkind(Token::Kind::kSemicolon));
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST(RecoverableParsingTests, BadRecoverToNextEnumMember) {
TestLibrary library(R"FIDL(
library example;
type Enum = enum {
ONE 1; // First error
TWO = 2;
THREE = 3 // Second error
FOUR = 4;
};
)FIDL");
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kNumericLiteral),
Token::KindAndSubkind(Token::Kind::kEqual));
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kIdentifier),
Token::KindAndSubkind(Token::Kind::kSemicolon));
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST(RecoverableParsingTests, BadRecoverToNextProtocolMember) {
TestLibrary library(R"FIDL(
library example;
protocol P {
compose A B; // 2 Errors (on 'B', ';')
MethodWithoutSemicolon()
ValidMethod(); // Error (expecting ';')
-> Event(struct { TypeWithoutParamName; }); // Error
MissingParen server_end:Protocol protocol); // Error
-> Event(struct { missing_paren T }; // 2 Errors (on '}', ';')
ValidMethod();
Method() -> (struct { num uint16; }) error; // Error
};
)FIDL");
// NOTE(https://fxbug.dev/42152439): the difference in errors is due to the change in
// test input (for the TypeWithoutParams and MissingParen cases) rather than
// any real behavior change
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kIdentifier),
Token::KindAndSubkind(Token::Kind::kSemicolon));
library.ExpectFail(ErrInvalidProtocolMember);
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kIdentifier),
Token::KindAndSubkind(Token::Kind::kSemicolon));
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kSemicolon),
Token::KindAndSubkind(Token::Kind::kIdentifier));
library.ExpectFail(ErrInvalidProtocolMember);
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kRightCurly),
Token::KindAndSubkind(Token::Kind::kSemicolon));
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kSemicolon),
Token::KindAndSubkind(Token::Kind::kRightParen));
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kSemicolon),
Token::KindAndSubkind(Token::Kind::kIdentifier));
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST(RecoverableParsingTests, BadRecoverableParamListParsing) {
TestLibrary library(R"FIDL(
library example;
protocol Example {
Method(/// Doc comment
struct { b bool; }) -> (/// Doc comment
struct { b bool; });
};
)FIDL");
library.ExpectFail(ErrDocCommentOnParameters);
library.ExpectFail(ErrDocCommentOnParameters);
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST(RecoverableParsingTests, BadRecoverableUnmatchedDelimiterInParamList) {
TestLibrary library(R"FIDL(
library example;
protocol Example {
Method() -> (vector<);
};
)FIDL");
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kRightParen),
Token::KindAndSubkind(Token::Kind::kIdentifier));
library.ExpectFail(ErrUnexpectedToken);
library.ExpectFail(ErrUnexpectedToken);
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST(RecoverableParsingTests, BadRecoverToNextServiceMember) {
TestLibrary library(R"FIDL(
library example;
protocol P {};
protocol Q {};
protocol R {};
service Service {
p P extra_token; // First error
q Q // Second error
r R;
};
)FIDL");
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kIdentifier),
Token::KindAndSubkind(Token::Kind::kSemicolon));
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kSemicolon),
Token::KindAndSubkind(Token::Kind::kIdentifier));
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST(RecoverableParsingTests, BadRecoverToNextStructMember) {
TestLibrary library(R"FIDL(
library example;
type Struct = struct {
string_value string extra_token; // Error
uint_value uint8;
vector_value vector<handle> // Error
int_value int32;
};
)FIDL");
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kIdentifier),
Token::KindAndSubkind(Token::Kind::kSemicolon));
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kSemicolon),
Token::KindAndSubkind(Token::Kind::kIdentifier));
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kIdentifier),
Token::KindAndSubkind(Token::Kind::kSemicolon));
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST(RecoverableParsingTests, BadRecoverToNextTableMember) {
TestLibrary library(R"FIDL(
library example;
type Table = table {
1: string_value string // Error
2: uint_value uint8;
3: value_with space vector<handle>; // Error
4: int_value int32;
};
)FIDL");
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kNumericLiteral),
Token::KindAndSubkind(Token::Kind::kSemicolon));
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Subkind::kVector),
Token::KindAndSubkind(Token::Kind::kSemicolon));
// NOTE(https://fxbug.dev/42152439): the difference here is just due to the type/member
// reordering, not a behavior change
library.ExpectFail(ErrMissingOrdinalBeforeMember);
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST(RecoverableParsingTests, BadRecoverToNextUnionMember) {
TestLibrary library(R"FIDL(
library example;
type Union = union {
1 missing_colon string; // First error
3: uint_value uint8;
4: missing_semicolon string // Second error
5: int_value int16;
};
)FIDL");
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kIdentifier),
Token::KindAndSubkind(Token::Kind::kColon));
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kNumericLiteral),
Token::KindAndSubkind(Token::Kind::kSemicolon));
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST(RecoverableParsingTests, BadRecoverFinalMemberMissingSemicolon) {
TestLibrary library(R"FIDL(
library example;
type Struct = struct {
uint_value uint8;
foo string // First error
};
// Recovered back to top-level parsing.
type Good = struct {};
extra_token // Second error
)FIDL");
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kRightCurly),
Token::KindAndSubkind(Token::Kind::kSemicolon));
library.ExpectFail(ErrExpectedDeclaration, "extra_token");
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST(RecoverableParsingTests, BadRecoverFinalMemberMissingNameAndSemicolon) {
TestLibrary library(R"FIDL(
library example;
type Struct = struct {
uint_value uint8;
string_value }; // First error
// Does not recover back to top-level parsing. End the struct.
};
// Back to top-level parsing.
type Good = struct {};
extra_token // Second error
)FIDL");
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kRightCurly),
Token::KindAndSubkind(Token::Kind::kIdentifier));
library.ExpectFail(ErrExpectedDeclaration, "extra_token");
ASSERT_COMPILER_DIAGNOSTICS(library);
}
// This test ensures that recoverable parsing works as intended for constraints,
// and returns useful and actionable information back to users.
TEST(RecoverableParsingTests, BadConstraintsRecoverability) {
TestLibrary library(R"FIDL(
library example;
type TypeDecl = struct {
// errors[0]: no constraints specified
f0 vector<uint16>:;
// errors[1]: no constraints specified
f1 vector<uint16>:<>;
// errors[2]: leading comma
f2 vector<uint16>:<,16,optional>;
// errors[3]: trailing comma
f3 vector<uint16>:<16,optional,>;
// errors[4]: double comma
f4 vector<uint16>:<16,,optional>;
// errors[5]: missing comma, errors[6], errors[7]: consume > and ; trying
// to get to next member
f5 vector<uint16>:<16 optional>;
// errors[8] missing close bracket
f7 vector<uint16>:<16;
// errors[10]: invalid constant
f8 vector<uint16>:1~6,optional;
// errors[11]: unexpected token
f9 vector<uint16>:,16,,optional,;
};
)FIDL");
library.ExpectFail(ErrUnexpectedToken);
library.ExpectFail(ErrUnexpectedToken);
library.ExpectFail(ErrUnexpectedToken);
library.ExpectFail(ErrUnexpectedToken);
library.ExpectFail(ErrUnexpectedToken);
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kIdentifier),
Token::KindAndSubkind(Token::Kind::kRightAngle));
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kIdentifier),
Token::KindAndSubkind(Token::Kind::kSemicolon));
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kRightAngle),
Token::KindAndSubkind(Token::Kind::kIdentifier));
library.ExpectFail(ErrUnexpectedTokenOfKind, Token::KindAndSubkind(Token::Kind::kSemicolon),
Token::KindAndSubkind(Token::Kind::kRightAngle));
library.ExpectFail(ErrInvalidCharacter, "~");
library.ExpectFail(ErrUnexpectedToken);
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST(RecoverableParsingTests, UnexpectedLineBreakInLiteral) {
TestLibrary library;
library.AddFile("bad/fi-0002.noformat.test.fidl");
library.ExpectFail(ErrUnexpectedLineBreak);
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST(RecoverableParsingTests, UnexpectedControlCharacter) {
TestLibrary library;
library.AddFile("bad/fi-0184.noformat.test.fidl");
library.ExpectFail(ErrUnexpectedControlCharacter, "9");
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST(RecoverableParsingTests, InvalidEscapeSequenceInLiteral) {
TestLibrary library;
library.AddFile("bad/fi-0003.noformat.test.fidl");
// TODO(https://fxbug.dev/42063301): fidlc should recover from all three failures
library.ExpectFail(ErrInvalidEscapeSequence, "\\ ");
library.ExpectFail(ErrInvalidEscapeSequence, "\\i");
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST(RecoverableParsingTests, InvalidHexDigit) {
TestLibrary library;
library.AddFile("bad/fi-0004.noformat.test.fidl");
library.ExpectFail(ErrInvalidHexDigit, 'G');
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST(RecoverableParsingTests, UnicodeEscapeMissingBraces) {
TestLibrary library;
library.AddFile("bad/fi-0185.noformat.test.fidl");
library.ExpectFail(ErrUnicodeEscapeMissingBraces);
ASSERT_COMPILER_DIAGNOSTICS(library);
EXPECT_EQ(library.errors()[0]->span.data(), "\\u");
}
TEST(RecoverableParsingTests, UnicodeEscapeUnterminated) {
TestLibrary library;
library.AddFile("bad/fi-0186.noformat.test.fidl");
library.ExpectFail(ErrUnicodeEscapeUnterminated);
ASSERT_COMPILER_DIAGNOSTICS(library);
EXPECT_EQ(library.errors()[0]->span.data(), "\\u{1F600");
}
TEST(RecoverableParsingTests, UnicodeEscapeEmpty) {
TestLibrary library;
library.AddFile("bad/fi-0187.noformat.test.fidl");
library.ExpectFail(ErrUnicodeEscapeEmpty);
ASSERT_COMPILER_DIAGNOSTICS(library);
EXPECT_EQ(library.errors()[0]->span.data(), "\\u{}");
}
TEST(RecoverableParsingTests, UnicodeEscapeTooLong) {
TestLibrary library;
library.AddFile("bad/fi-0188.noformat.test.fidl");
library.ExpectFail(ErrUnicodeEscapeTooLong);
ASSERT_COMPILER_DIAGNOSTICS(library);
EXPECT_EQ(library.errors()[0]->span.data(), "001F600");
}
TEST(RecoverableParsingTests, UnicodeEscapeTooLarge) {
TestLibrary library;
library.AddFile("bad/fi-0189.noformat.test.fidl");
library.ExpectFail(ErrUnicodeEscapeTooLarge, "110000");
ASSERT_COMPILER_DIAGNOSTICS(library);
EXPECT_EQ(library.errors()[0]->span.data(), "110000");
}
TEST(RecoverableParsingTests, ExpectedDeclaration) {
TestLibrary library;
library.AddFile("bad/fi-0006.noformat.test.fidl");
library.ExpectFail(ErrExpectedDeclaration, "cosnt");
ASSERT_COMPILER_DIAGNOSTICS(library);
}
} // namespace
} // namespace fidlc