Commit 3f2876bb authored by Kenton Varda's avatar Kenton Varda

Add test of compiler's error-checking.

parent b5e5634a
......@@ -38,8 +38,12 @@ read CAPNP_TEST
echo findProvider file:capnp/testdata/binary; read JUNK
echo findProvider file:capnp/testdata/flat; read JUNK
echo findProvider file:capnp/testdata/packed; read JUNK
echo findProvider file:capnp/testdata/segmented; read JUNK
echo findProvider file:capnp/testdata/segmented-packed; read JUNK
echo findProvider file:capnp/testdata/pretty.txt; read JUNK
echo findProvider file:capnp/testdata/short.txt; read JUNK
echo findProvider file:capnp/testdata/errors.capnp.nobuild; read JUNK
echo findProvider file:capnp/testdata/errors.txt; read JUNK
# Register our interest in the schema files.
echo findProvider file:capnp/c++.capnp
......
......@@ -64,3 +64,6 @@ test_eval 'TestDefaults.structList[1]' '( textField = "structlist 2" )'
test_eval globalStruct '(int32Field = 54321)'
test_eval TestConstants.enumConst corge
test_eval 'TestListDefaults.lists.int32ListList[2][0]' 12341234
$CAPNP compile -ofoo $TESTDATA/errors.capnp.nobuild 2>&1 | sed -e "s,^.*/errors[.]capnp[.]nobuild,file,g" |
cmp src/capnp/testdata/errors.txt || fail error output
......@@ -595,7 +595,7 @@ kj::Maybe<const Compiler::Node&> Compiler::Node::lookup(const DeclName::Reader&
node = &*n;
} else {
module->getErrorReporter().addErrorOn(
absoluteName, kj::str("not defined: ", absoluteName.getValue()));
absoluteName, kj::str("Not defined: ", absoluteName.getValue()));
return nullptr;
}
break;
......@@ -606,7 +606,7 @@ kj::Maybe<const Compiler::Node&> Compiler::Node::lookup(const DeclName::Reader&
node = &*n;
} else {
module->getErrorReporter().addErrorOn(
relativeName, kj::str("not defined: ", relativeName.getValue()));
relativeName, kj::str("Not defined: ", relativeName.getValue()));
return nullptr;
}
break;
......@@ -617,7 +617,7 @@ kj::Maybe<const Compiler::Node&> Compiler::Node::lookup(const DeclName::Reader&
node = &m->getRootNode();
} else {
module->getErrorReporter().addErrorOn(
importName, kj::str("import failed: ", importName.getValue()));
importName, kj::str("Import failed: ", importName.getValue()));
return nullptr;
}
break;
......@@ -631,7 +631,7 @@ kj::Maybe<const Compiler::Node&> Compiler::Node::lookup(const DeclName::Reader&
node = &*member;
} else {
module->getErrorReporter().addErrorOn(
partName, kj::str("no such member: ", partName.getValue()));
partName, kj::str("No such member: ", partName.getValue()));
return nullptr;
}
}
......@@ -689,8 +689,8 @@ void Compiler::Node::traverse(uint eagerness, std::unordered_map<const Node*, ui
}
if (eagerness & CHILDREN) {
for (auto& child: getContent(Content::EXPANDED).nestedNodes) {
child.second->traverse(eagerness, seen);
for (auto& child: getContent(Content::EXPANDED).orderedNestedNodes) {
child->traverse(eagerness, seen);
}
}
}
......
......@@ -724,12 +724,6 @@ void NodeTranslator::DuplicateNameDetector::check(
}
}
void NodeTranslator::disallowNested(List<Declaration>::Reader nestedDecls) {
for (auto decl: nestedDecls) {
errorReporter.addErrorOn(decl, "Nested declaration not allowed here.");
}
}
void NodeTranslator::compileConst(Declaration::Const::Reader decl,
schema::Node::Const::Builder builder) {
auto typeBuilder = builder.initType();
......@@ -1285,13 +1279,13 @@ static kj::String declNameString(DeclName::Reader name) {
switch (name.getBase().which()) {
case DeclName::Base::RELATIVE_NAME:
prefix = kj::str(name.getBase().getRelativeName());
prefix = kj::str(name.getBase().getRelativeName().getValue());
break;
case DeclName::Base::ABSOLUTE_NAME:
prefix = kj::str(".", name.getBase().getAbsoluteName());
prefix = kj::str(".", name.getBase().getAbsoluteName().getValue());
break;
case DeclName::Base::IMPORT_NAME:
prefix = kj::str("import \"", name.getBase().getImportName(), "\"");
prefix = kj::str("import \"", name.getBase().getImportName().getValue(), "\"");
break;
}
......@@ -1686,7 +1680,7 @@ Orphan<DynamicValue> ValueTranslator::compileValueInner(
case ValueExpression::LIST: {
if (!type.isList()) {
errorReporter.addErrorOn(src, "Type mismatch.");
errorReporter.addErrorOn(src, kj::str("Type mismatch; expected ", makeTypeName(type), "."));
return nullptr;
}
auto elementType = type.getList().getElementType();
......@@ -1707,7 +1701,7 @@ Orphan<DynamicValue> ValueTranslator::compileValueInner(
case ValueExpression::STRUCT: {
if (!type.isStruct()) {
errorReporter.addErrorOn(src, "Type mismatch.");
errorReporter.addErrorOn(src, kj::str("Type mismatch; expected ", makeTypeName(type), "."));
return nullptr;
}
KJ_IF_MAYBE(schema, resolver.resolveType(type.getStruct().getTypeId())) {
......@@ -1747,7 +1741,7 @@ void ValueTranslator::fillStructValue(DynamicStruct::Builder builder,
if (value.isStruct()) {
fillStructValue(builder.init(*field).as<DynamicStruct>(), value.getStruct());
} else {
errorReporter.addErrorOn(value, "Type mismatch.");
errorReporter.addErrorOn(value, "Type mismatch; expected group.");
}
break;
}
......
......@@ -132,9 +132,6 @@ private:
void compileNode(Declaration::Reader decl, schema::Node::Builder builder);
void disallowNested(List<Declaration>::Reader nestedDecls);
// Complain if the nested decl list is non-empty.
void compileConst(Declaration::Const::Reader decl, schema::Node::Const::Builder builder);
void compileAnnotation(Declaration::Annotation::Reader decl,
schema::Node::Annotation::Builder builder);
......
......@@ -712,26 +712,13 @@ CapnpParser::CapnpParser(Orphanage orphanageParam, const ErrorReporter& errorRep
[this](Orphan<LocatedInteger>&& ordinal,
kj::Maybe<kj::Tuple<>> exclamation,
kj::Maybe<kj::Tuple<>> colon)
-> kj::Maybe<Orphan<LocatedInteger>> {
if (exclamation == nullptr) {
errorReporter.addErrorOn(ordinal.getReader(),
"As of Cap'n Proto v0.3, it is no longer necessary to assign numbers to "
"unions. However, removing the number will break binary compatibility. "
"If this is an old protocol and you need to retain compatibility, please "
"add an exclamation point after the number to indicate that it is really "
"needed, e.g. `foo @1! :union {`. If this is a new protocol or compatibility "
"doesn't matter, just remove the @n entirely. Sorry for the inconvenience, "
"and thanks for being an early adopter! :)");
}
if (colon == nullptr) {
errorReporter.addErrorOn(ordinal.getReader(),
"As of Cap'n Proto v0.3, the 'union' keyword should be prefixed with a colon "
"for named unions, e.g. `foo :union {`.");
}
return kj::mv(ordinal);
-> kj::Tuple<kj::Maybe<Orphan<LocatedInteger>>, bool, bool> {
return kj::tuple(kj::mv(ordinal), exclamation == nullptr, colon == nullptr);
}),
p::transform(op(":"),
[]() -> kj::Maybe<Orphan<LocatedInteger>>{ return nullptr; })));
[]() -> kj::Tuple<kj::Maybe<Orphan<LocatedInteger>>, bool, bool> {
return kj::tuple(nullptr, false, false);
})));
parsers.unionDecl = arena.copy(p::transform(
// The first branch of this oneOf() matches named unions. The second branch matches unnamed
......@@ -740,22 +727,39 @@ CapnpParser::CapnpParser(Orphanage orphanageParam, const ErrorReporter& errorRep
p::sequence(
identifier, ordinalOrColon,
keyword("union"), p::many(parsers.annotation)),
p::transform(keyword("union"),
[]() {
p::transformWithLocation(p::sequence(keyword("union"), p::endOfInput),
[](kj::parse::Span<List<Token>::Reader::Iterator> location) {
return kj::tuple(
Located<Text::Reader>("", 0, 0),
Located<Text::Reader>("", location.begin()->getStartByte(),
location.begin()->getEndByte()),
kj::Maybe<Orphan<LocatedInteger>>(nullptr),
false, false,
kj::Array<Orphan<Declaration::AnnotationApplication>>(nullptr));
})),
[this](Located<Text::Reader>&& name,
kj::Maybe<Orphan<LocatedInteger>>&& ordinal,
bool missingExclamation, bool missingColon,
kj::Array<Orphan<Declaration::AnnotationApplication>>&& annotations)
-> DeclParserResult {
if (missingExclamation) {
errorReporter.addErrorOn(KJ_ASSERT_NONNULL(ordinal).getReader(),
"As of Cap'n Proto v0.3, it is no longer necessary to assign numbers to "
"unions. However, removing the number will break binary compatibility. "
"If this is an old protocol and you need to retain compatibility, please "
"add an exclamation point after the number to indicate that it is really "
"needed, e.g. `foo @1! :union {`. If this is a new protocol or compatibility "
"doesn't matter, just remove the @n entirely. Sorry for the inconvenience, "
"and thanks for being an early adopter! :)");
}
if (missingColon) {
errorReporter.addErrorOn(KJ_ASSERT_NONNULL(ordinal).getReader(),
"As of Cap'n Proto v0.3, the 'union' keyword should be prefixed with a colon "
"for named unions, e.g. `foo :union {`.");
}
auto decl = orphanage.newOrphan<Declaration>();
auto builder = decl.get();
if (name.value.size() > 0) {
name.copyTo(builder.initName());
}
name.copyTo(builder.initName());
KJ_IF_MAYBE(ord, ordinal) {
builder.getId().adoptOrdinal(kj::mv(*ord));
} else {
......@@ -941,7 +945,7 @@ CapnpParser::CapnpParser(Orphanage orphanageParam, const ErrorReporter& errorRep
parsers.genericDecl, nakedId, nakedAnnotation));
parsers.enumLevelDecl = arena.copy(p::oneOf(parsers.enumerantDecl));
parsers.structLevelDecl = arena.copy(p::oneOf(
parsers.fieldDecl, parsers.unionDecl, parsers.groupDecl, parsers.genericDecl));
parsers.unionDecl, parsers.fieldDecl, parsers.groupDecl, parsers.genericDecl));
parsers.interfaceLevelDecl = arena.copy(p::oneOf(
parsers.methodDecl, parsers.genericDecl));
}
......@@ -969,7 +973,7 @@ kj::Maybe<Orphan<Declaration>> CapnpParser::parseStatement(
case Statement::LINE:
if (output->memberParser != nullptr) {
errorReporter.addError(statement.getStartByte(), statement.getEndByte(),
"This statement should end with a semicolon, not a block.");
"This statement should end with a block, not a semicolon.");
}
break;
......@@ -985,7 +989,7 @@ kj::Maybe<Orphan<Declaration>> CapnpParser::parseStatement(
builder.adoptNestedDecls(arrayToList(orphanage, members.releaseAsArray()));
} else {
errorReporter.addError(statement.getStartByte(), statement.getEndByte(),
"This statement should end with a block, not a semicolon.");
"This statement should end with a semicolon, not a block.");
}
break;
}
......
# Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# This file is intended to test that various error cases are detected as errors. The error
# output is matched against a golden file. The file name has the .nobuild extension to make
# sure that a build system which automatically builds .capnp files does not try to build this one.
@0xccd0890aa4926a9b;
# Can't really test the missing-ID error because the output is intentionally unpredictable.
const notType :Int32 = 123;
annotation notFieldAnnotation(struct) :Int32;
annotation fieldAnnotation(field) :Int32;
struct Foo {
dupName @0 :Int32;
dupName @1 :Int32;
dupNumber1 @2 :Int32;
dupNumber2 @2 :Int32;
missingNumber @4 :Int32;
next @5 :Int32;
emptyUnion :union {}
emptyGroup :group {}
singletonUnion :union {
field @6 :Int32;
}
union {
dupName @7 :Int32;
f8 @8 :Int32;
}
union {
f9 @9 :Int32;
f10 @10 :Int32;
}
struct wrongTypeStyle {}
WrongFieldStyle @11 :Int32;
under_score @12 :Int32;
containsStruct :group {
f13 @13 :Int32;
struct CantNestHere {}
}
retroUnion @16! :union {
f14 @14 :Int32;
f15 @15 :Int32;
}
missingColonAndEclamation @18 union {
f19 @19 :Int32;
f20 @20 :Int32;
}
missingExclamation @21 :union {
f22 @22 :Int32;
f23 @23 :Int32;
}
missingColon @24! union {
f19 @25 :Int32;
f20 @26 :Int32;
}
unnamedInNamed :union {
f27 @27 :Int32;
f28 @28 :Int32;
union {
# content is ignored
}
}
listWithoutParam @31 :List;
listWithTooManyParams @32 :List(Int32, Int64);
listObject @33 :List(Object);
notAType @34 :notType;
noParams @35 :Foo(Int32);
defaultOutOfRange @36 :Int16 = 1234567;
defaultOutOfRange2 @37 :UInt16 = -1;
defaultWrongType @38 :Text = 123;
defaultWrongType2 @39 :Text = [123];
defaultWrongType3 @40 :Text = (foo = 123, bar = 456);
defaultTooBigToBeNegative @41 :Int64 = -0x8000000000000001;
defaultNotConstant @42 :Int32 = .Foo;
defaultConstantNotQualified @43 :Int32 = notType;
notAnnotation @44 :Int32 $Foo(123);
badAnnotation @45 :Int32 $notFieldAnnotation(123);
notVoidAnnotation @46 :Int32 $fieldAnnotation;
undefinedImport @17 :import "noshuchfile.capnp".Bar;
undefinedAbsolute @47 : .NoSuch;
undefinedRelative @29 :NoSuch;
undefinedMember @30 :Foo.NoSuch;
defaultBadStructSyntax @48 :Foo = (123, bar = 456);
}
struct Bar {
someGroup :group {
defaultNoSuchField @0 :Bar = (nosuchfield = 123);
defaultGroupMismatch @1 :Bar = (someGroup = 123);
}
}
using Bar;
enum DupEnumerants {
dupName @0;
dupName @1;
dupNumber1 @2;
dupNumber2 @2;
}
file:74:30-32: error: As of Cap'n Proto v0.3, it is no longer necessary to assign numbers to unions. However, removing the number will break binary compatibility. If this is an old protocol and you need to retain compatibility, please add an exclamation point after the number to indicate that it is really needed, e.g. `foo @1! :union {`. If this is a new protocol or compatibility doesn't matter, just remove the @n entirely. Sorry for the inconvenience, and thanks for being an early adopter! :)
file:74:30-32: error: As of Cap'n Proto v0.3, the 'union' keyword should be prefixed with a colon for named unions, e.g. `foo :union {`.
file:79:23-25: error: As of Cap'n Proto v0.3, it is no longer necessary to assign numbers to unions. However, removing the number will break binary compatibility. If this is an old protocol and you need to retain compatibility, please add an exclamation point after the number to indicate that it is really needed, e.g. `foo @1! :union {`. If this is a new protocol or compatibility doesn't matter, just remove the @n entirely. Sorry for the inconvenience, and thanks for being an early adopter! :)
file:84:17-19: error: As of Cap'n Proto v0.3, the 'union' keyword should be prefixed with a colon for named unions, e.g. `foo :union {`.
file:121:38-41: error: Missing field name.
file:132:7-10: error: 'using' declaration without '=' must use a qualified path.
file:37:3-10: error: 'dupName' is already defined in this scope.
file:36:3-10: error: 'dupName' previously defined here.
file:52:5-12: error: 'dupName' is already defined in this scope.
file:36:3-10: error: 'dupName' previously defined here.
file:55:3-8: error: An unnamed union is already defined in this scope.
file:51:3-8: error: Previously defined here.
file:60:10-24: error: Type names must begin with a capital letter.
file:61:3-18: error: Non-type names must begin with a lower-case letter.
file:62:3-14: error: Cap'n Proto declaration names should use camelCase and must not contain underscores. (Code generators may convert names to the appropriate style for the target language.)
file:66:5-27: error: This kind of declaration doesn't belong here.
file:44:3-23: error: Union must have at least two members.
file:45:3-23: error: Group must have at least one member.
file:47: error: Union must have at least two members.
file:92: error: Unions cannot contain unnamed unions.
file:39:15-16: error: Duplicate ordinal number.
file:38:15-16: error: Ordinal @2 originally used here.
file:41:18-19: error: Skipped ordinal @3. Ordinals must be sequential with no holes.
file:69:15-17: error: Union ordinal, if specified, must be greater than no more than one of its member ordinals (i.e. there can only be one field retroactively unionized).
file:116:31-50: error: Import failed: noshuchfile.capnp
file:118:26-32: error: Not defined: NoSuch
file:119:28-34: error: No such member: NoSuch
file:97:25-29: error: 'List' requires exactly one parameter.
file:98:30-48: error: 'List' requires exactly one parameter.
file:99:19-31: error: 'List(Object)' is not supported.
file:100:17-24: error: 'notType' is not a type.
file:101:17-27: error: 'Foo' does not accept parameters.
file:103:34-41: error: Integer value out of range.
file:104:37-38: error: Integer value out of range.
file:105:32-35: error: Type mismatch; expected Text.
file:106:33-38: error: Type mismatch; expected Text.
file:107:33-55: error: Type mismatch; expected Text.
file:108:43-61: error: Integer is too big to be negative.
file:109:35-39: error: '.Foo' does not refer to a constant.
file:110:44-51: error: Constant names must be qualified to avoid confusion. Please replace 'notType' with '.notType', if that's what you intended.
file:117:28-34: error: Not defined: NoSuch
file:112:29-32: error: 'Foo' is not an annotation.
file:113:29-47: error: 'notFieldAnnotation' cannot be applied to this kind of declaration.
file:114:33-48: error: 'fieldAnnotation' requires a value.
file:126:35-46: error: Struct has no field named 'nosuchfield'.
file:127:49-52: error: Type mismatch; expected group.
file:136:3-10: error: 'dupName' is already defined in this scope.
file:135:3-10: error: 'dupName' previously defined here.
file:138:15-16: error: Duplicate ordinal number.
file:137:15-16: error: Ordinal @2 originally used here.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment