Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(31)

Unified Diff: third_party/protobuf/src/google/protobuf/compiler/parser_unittest.cc

Issue 1842653006: Update //third_party/protobuf to version 3. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: merge Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/protobuf/src/google/protobuf/compiler/parser_unittest.cc
diff --git a/third_party/protobuf/src/google/protobuf/compiler/parser_unittest.cc b/third_party/protobuf/src/google/protobuf/compiler/parser_unittest.cc
index c61ac60ec05406245ea15ef8b29b681b4e14edd8..1d623dd90b21ec4f3b6fef9643e530491b72d554 100644
--- a/third_party/protobuf/src/google/protobuf/compiler/parser_unittest.cc
+++ b/third_party/protobuf/src/google/protobuf/compiler/parser_unittest.cc
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
@@ -32,6 +32,10 @@
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
#include <vector>
#include <algorithm>
#include <map>
@@ -47,7 +51,7 @@
#include <google/protobuf/unittest_custom_options.pb.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/substitute.h>
-#include <google/protobuf/stubs/map-util.h>
+#include <google/protobuf/stubs/map_util.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
@@ -174,9 +178,9 @@ class ParserTest : public testing::Test {
MockErrorCollector error_collector_;
DescriptorPool pool_;
- scoped_ptr<io::ZeroCopyInputStream> raw_input_;
- scoped_ptr<io::Tokenizer> input_;
- scoped_ptr<Parser> parser_;
+ google::protobuf::scoped_ptr<io::ZeroCopyInputStream> raw_input_;
+ google::protobuf::scoped_ptr<io::Tokenizer> input_;
+ google::protobuf::scoped_ptr<Parser> parser_;
bool require_syntax_identifier_;
};
@@ -212,10 +216,45 @@ TEST_F(ParserTest, StopAfterSyntaxIdentifierWithErrors) {
EXPECT_EQ("1:9: Expected syntax identifier.\n", error_collector_.text_);
}
+TEST_F(ParserTest, WarnIfSyntaxIdentifierOmmitted) {
+ SetupParser("message A {}");
+ FileDescriptorProto file;
+ CaptureTestStderr();
+ EXPECT_TRUE(parser_->Parse(input_.get(), &file));
+ EXPECT_TRUE(
+ GetCapturedTestStderr().find("No syntax specified") != string::npos);
+}
+
// ===================================================================
typedef ParserTest ParseMessageTest;
+TEST_F(ParseMessageTest, IgnoreBOM) {
+ char input[] = " message TestMessage {\n"
+ " required int32 foo = 1;\n"
+ "}\n";
+ // Set UTF-8 BOM.
+ input[0] = (char)0xEF;
+ input[1] = (char)0xBB;
+ input[2] = (char)0xBF;
+ ExpectParsesTo(input,
+ "message_type {"
+ " name: \"TestMessage\""
+ " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }"
+ "}");
+}
+
+TEST_F(ParseMessageTest, BOMError) {
+ char input[] = " message TestMessage {\n"
+ " required int32 foo = 1;\n"
+ "}\n";
+ input[0] = (char)0xEF;
+ ExpectHasErrors(input,
+ "0:1: Proto file starts with 0xEF but not UTF-8 BOM. "
+ "Only UTF-8 is accepted for proto file.\n"
+ "0:0: Expected top-level statement (e.g. \"message\").\n");
+}
+
TEST_F(ParseMessageTest, SimpleMessage) {
ExpectParsesTo(
"message TestMessage {\n"
@@ -249,6 +288,7 @@ TEST_F(ParseMessageTest, ExplicitSyntaxIdentifier) {
" required int32 foo = 1;\n"
"}\n",
+ "syntax: 'proto2' "
"message_type {"
" name: \"TestMessage\""
" field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }"
@@ -264,6 +304,7 @@ TEST_F(ParseMessageTest, ExplicitRequiredSyntaxIdentifier) {
" required int32 foo = 1;\n"
"}\n",
+ "syntax: 'proto2' "
"message_type {"
" name: \"TestMessage\""
" field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }"
@@ -364,39 +405,67 @@ TEST_F(ParseMessageTest, FieldDefaults) {
#define ETC "name:\"foo\" label:LABEL_REQUIRED number:1"
"message_type {"
" name: \"TestMessage\""
- " field { type:TYPE_INT32 default_value:\"1\" "ETC" }"
- " field { type:TYPE_INT32 default_value:\"-2\" "ETC" }"
- " field { type:TYPE_INT64 default_value:\"3\" "ETC" }"
- " field { type:TYPE_INT64 default_value:\"-4\" "ETC" }"
- " field { type:TYPE_UINT32 default_value:\"5\" "ETC" }"
- " field { type:TYPE_UINT64 default_value:\"6\" "ETC" }"
- " field { type:TYPE_FLOAT default_value:\"7.5\" "ETC" }"
- " field { type:TYPE_FLOAT default_value:\"-8.5\" "ETC" }"
- " field { type:TYPE_FLOAT default_value:\"9\" "ETC" }"
- " field { type:TYPE_DOUBLE default_value:\"10.5\" "ETC" }"
- " field { type:TYPE_DOUBLE default_value:\"-11.5\" "ETC" }"
- " field { type:TYPE_DOUBLE default_value:\"12\" "ETC" }"
- " field { type:TYPE_DOUBLE default_value:\"inf\" "ETC" }"
- " field { type:TYPE_DOUBLE default_value:\"-inf\" "ETC" }"
- " field { type:TYPE_DOUBLE default_value:\"nan\" "ETC" }"
- " field { type:TYPE_STRING default_value:\"13\\001\" "ETC" }"
- " field { type:TYPE_STRING default_value:\"abc\" "ETC" }"
- " field { type:TYPE_BYTES default_value:\"14\\\\002\" "ETC" }"
- " field { type:TYPE_BYTES default_value:\"abc\" "ETC" }"
- " field { type:TYPE_BOOL default_value:\"true\" "ETC" }"
- " field { type_name:\"Foo\" default_value:\"FOO\" "ETC" }"
-
- " field { type:TYPE_INT32 default_value:\"2147483647\" "ETC" }"
- " field { type:TYPE_INT32 default_value:\"-2147483648\" "ETC" }"
- " field { type:TYPE_UINT32 default_value:\"4294967295\" "ETC" }"
- " field { type:TYPE_INT64 default_value:\"9223372036854775807\" "ETC" }"
- " field { type:TYPE_INT64 default_value:\"-9223372036854775808\" "ETC" }"
- " field { type:TYPE_UINT64 default_value:\"18446744073709551615\" "ETC" }"
- " field { type:TYPE_DOUBLE default_value:\"43981\" "ETC" }"
+ " field { type:TYPE_INT32 default_value:\"1\" " ETC " }"
+ " field { type:TYPE_INT32 default_value:\"-2\" " ETC " }"
+ " field { type:TYPE_INT64 default_value:\"3\" " ETC " }"
+ " field { type:TYPE_INT64 default_value:\"-4\" " ETC " }"
+ " field { type:TYPE_UINT32 default_value:\"5\" " ETC " }"
+ " field { type:TYPE_UINT64 default_value:\"6\" " ETC " }"
+ " field { type:TYPE_FLOAT default_value:\"7.5\" " ETC " }"
+ " field { type:TYPE_FLOAT default_value:\"-8.5\" " ETC " }"
+ " field { type:TYPE_FLOAT default_value:\"9\" " ETC " }"
+ " field { type:TYPE_DOUBLE default_value:\"10.5\" " ETC " }"
+ " field { type:TYPE_DOUBLE default_value:\"-11.5\" " ETC " }"
+ " field { type:TYPE_DOUBLE default_value:\"12\" " ETC " }"
+ " field { type:TYPE_DOUBLE default_value:\"inf\" " ETC " }"
+ " field { type:TYPE_DOUBLE default_value:\"-inf\" " ETC " }"
+ " field { type:TYPE_DOUBLE default_value:\"nan\" " ETC " }"
+ " field { type:TYPE_STRING default_value:\"13\\001\" " ETC " }"
+ " field { type:TYPE_STRING default_value:\"abc\" " ETC " }"
+ " field { type:TYPE_BYTES default_value:\"14\\\\002\" " ETC " }"
+ " field { type:TYPE_BYTES default_value:\"abc\" " ETC " }"
+ " field { type:TYPE_BOOL default_value:\"true\" " ETC " }"
+ " field { type_name:\"Foo\" default_value:\"FOO\" " ETC " }"
+
+ " field {"
+ " type:TYPE_INT32 default_value:\"2147483647\" " ETC
+ " }"
+ " field {"
+ " type:TYPE_INT32 default_value:\"-2147483648\" " ETC
+ " }"
+ " field {"
+ " type:TYPE_UINT32 default_value:\"4294967295\" " ETC
+ " }"
+ " field {"
+ " type:TYPE_INT64 default_value:\"9223372036854775807\" " ETC
+ " }"
+ " field {"
+ " type:TYPE_INT64 default_value:\"-9223372036854775808\" " ETC
+ " }"
+ " field {"
+ " type:TYPE_UINT64 default_value:\"18446744073709551615\" " ETC
+ " }"
+ " field {"
+ " type:TYPE_DOUBLE default_value:\"43981\" " ETC
+ " }"
"}");
#undef ETC
}
+TEST_F(ParseMessageTest, FieldJsonName) {
+ ExpectParsesTo(
+ "message TestMessage {\n"
+ " optional string foo = 1 [json_name = \"@type\"];\n"
+ "}\n",
+ "message_type {"
+ " name: \"TestMessage\""
+ " field {\n"
+ " name: \"foo\" label: LABEL_OPTIONAL type: TYPE_STRING number: 1"
+ " json_name: \"@type\"\n"
+ " }\n"
+ "}\n");
+}
+
TEST_F(ParseMessageTest, FieldOptions) {
ExpectParsesTo(
"message TestMessage {\n"
@@ -436,6 +505,117 @@ TEST_F(ParseMessageTest, FieldOptions) {
"}");
}
+TEST_F(ParseMessageTest, Oneof) {
+ ExpectParsesTo(
+ "message TestMessage {\n"
+ " oneof foo {\n"
+ " int32 a = 1;\n"
+ " string b = 2;\n"
+ " TestMessage c = 3;\n"
+ " group D = 4 { optional int32 i = 5; }\n"
+ " }\n"
+ "}\n",
+
+ "message_type {"
+ " name: \"TestMessage\""
+ " field { name:\"a\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 "
+ " oneof_index:0 }"
+ " field { name:\"b\" label:LABEL_OPTIONAL type:TYPE_STRING number:2 "
+ " oneof_index:0 }"
+ " field { name:\"c\" label:LABEL_OPTIONAL type_name:\"TestMessage\" "
+ " number:3 oneof_index:0 }"
+ " field { name:\"d\" label:LABEL_OPTIONAL type:TYPE_GROUP "
+ " type_name:\"D\" number:4 oneof_index:0 }"
+ " oneof_decl {"
+ " name: \"foo\""
+ " }"
+ " nested_type {"
+ " name: \"D\""
+ " field { name:\"i\" label:LABEL_OPTIONAL type:TYPE_INT32 number:5 }"
+ " }"
+ "}");
+}
+
+TEST_F(ParseMessageTest, MultipleOneofs) {
+ ExpectParsesTo(
+ "message TestMessage {\n"
+ " oneof foo {\n"
+ " int32 a = 1;\n"
+ " string b = 2;\n"
+ " }\n"
+ " oneof bar {\n"
+ " int32 c = 3;\n"
+ " string d = 4;\n"
+ " }\n"
+ "}\n",
+
+ "message_type {"
+ " name: \"TestMessage\""
+ " field { name:\"a\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 "
+ " oneof_index:0 }"
+ " field { name:\"b\" label:LABEL_OPTIONAL type:TYPE_STRING number:2 "
+ " oneof_index:0 }"
+ " field { name:\"c\" label:LABEL_OPTIONAL type:TYPE_INT32 number:3 "
+ " oneof_index:1 }"
+ " field { name:\"d\" label:LABEL_OPTIONAL type:TYPE_STRING number:4 "
+ " oneof_index:1 }"
+ " oneof_decl {"
+ " name: \"foo\""
+ " }"
+ " oneof_decl {"
+ " name: \"bar\""
+ " }"
+ "}");
+}
+
+TEST_F(ParseMessageTest, Maps) {
+ ExpectParsesTo(
+ "message TestMessage {\n"
+ " map<int32, string> primitive_type_map = 1;\n"
+ " map<KeyType, ValueType> composite_type_map = 2;\n"
+ "}\n",
+
+ "message_type {"
+ " name: \"TestMessage\""
+ " nested_type {"
+ " name: \"PrimitiveTypeMapEntry\""
+ " field { "
+ " name: \"key\" number: 1 label:LABEL_OPTIONAL"
+ " type:TYPE_INT32"
+ " }"
+ " field { "
+ " name: \"value\" number: 2 label:LABEL_OPTIONAL"
+ " type:TYPE_STRING"
+ " }"
+ " options { map_entry: true }"
+ " }"
+ " nested_type {"
+ " name: \"CompositeTypeMapEntry\""
+ " field { "
+ " name: \"key\" number: 1 label:LABEL_OPTIONAL"
+ " type_name: \"KeyType\""
+ " }"
+ " field { "
+ " name: \"value\" number: 2 label:LABEL_OPTIONAL"
+ " type_name: \"ValueType\""
+ " }"
+ " options { map_entry: true }"
+ " }"
+ " field {"
+ " name: \"primitive_type_map\""
+ " label: LABEL_REPEATED"
+ " type_name: \"PrimitiveTypeMapEntry\""
+ " number: 1"
+ " }"
+ " field {"
+ " name: \"composite_type_map\""
+ " label: LABEL_REPEATED"
+ " type_name: \"CompositeTypeMapEntry\""
+ " number: 2"
+ " }"
+ "}");
+}
+
TEST_F(ParseMessageTest, Group) {
ExpectParsesTo(
"message TestMessage {\n"
@@ -480,6 +660,36 @@ TEST_F(ParseMessageTest, NestedEnum) {
"}");
}
+TEST_F(ParseMessageTest, ReservedRange) {
+ ExpectParsesTo(
+ "message TestMessage {\n"
+ " required int32 foo = 1;\n"
+ " reserved 2, 15, 9 to 11, 3;\n"
+ "}\n",
+
+ "message_type {"
+ " name: \"TestMessage\""
+ " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }"
+ " reserved_range { start:2 end:3 }"
+ " reserved_range { start:15 end:16 }"
+ " reserved_range { start:9 end:12 }"
+ " reserved_range { start:3 end:4 }"
+ "}");
+}
+
+TEST_F(ParseMessageTest, ReservedNames) {
+ ExpectParsesTo(
+ "message TestMessage {\n"
+ " reserved \"foo\", \"bar\";\n"
+ "}\n",
+
+ "message_type {"
+ " name: \"TestMessage\""
+ " reserved_name: \"foo\""
+ " reserved_name: \"bar\""
+ "}");
+}
+
TEST_F(ParseMessageTest, ExtensionRange) {
ExpectParsesTo(
"message TestMessage {\n"
@@ -575,6 +785,19 @@ TEST_F(ParseMessageTest, MultipleExtensionsOneExtendee) {
" type_name:\"TestMessage\" extendee: \"Extendee1\" }");
}
+TEST_F(ParseMessageTest, OptionalLabelProto3) {
+ ExpectParsesTo(
+ "syntax = \"proto3\";\n"
+ "message TestMessage {\n"
+ " int32 foo = 1;\n"
+ "}\n",
+
+ "syntax: \"proto3\" "
+ "message_type {"
+ " name: \"TestMessage\""
+ " field { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 } }");
+}
+
// ===================================================================
typedef ParserTest ParseEnumTest;
@@ -681,6 +904,8 @@ TEST_F(ParseServiceTest, MethodsAndStreams) {
"}");
}
+
+
// ===================================================================
// imports and packages
@@ -761,9 +986,9 @@ typedef ParserTest ParseErrorTest;
TEST_F(ParseErrorTest, MissingSyntaxIdentifier) {
require_syntax_identifier_ = true;
- ExpectHasEarlyExitErrors(
- "message TestMessage {}",
- "0:0: File must begin with 'syntax = \"proto2\";'.\n");
+ ExpectHasEarlyExitErrors("message TestMessage {}",
+ "0:0: File must begin with a syntax statement, e.g. "
+ "'syntax = \"proto2\";'.\n");
EXPECT_EQ("", parser_->GetSyntaxIdentifier());
}
@@ -771,7 +996,7 @@ TEST_F(ParseErrorTest, UnknownSyntaxIdentifier) {
ExpectHasEarlyExitErrors(
"syntax = \"no_such_syntax\";",
"0:9: Unrecognized syntax identifier \"no_such_syntax\". This parser "
- "only recognizes \"proto2\".\n");
+ "only recognizes \"proto2\" and \"proto3\".\n");
EXPECT_EQ("no_such_syntax", parser_->GetSyntaxIdentifier());
}
@@ -870,7 +1095,7 @@ TEST_F(ParseErrorTest, DefaultValueTypeMismatch) {
"message TestMessage {\n"
" optional uint32 foo = 1 [default=true];\n"
"}\n",
- "1:35: Expected integer.\n");
+ "1:35: Expected integer for field default value.\n");
}
TEST_F(ParseErrorTest, DefaultValueNotBoolean) {
@@ -886,7 +1111,7 @@ TEST_F(ParseErrorTest, DefaultValueNotString) {
"message TestMessage {\n"
" optional string foo = 1 [default=1];\n"
"}\n",
- "1:35: Expected string.\n");
+ "1:35: Expected string for field default value.\n");
}
TEST_F(ParseErrorTest, DefaultValueUnsignedNegative) {
@@ -915,6 +1140,22 @@ TEST_F(ParseErrorTest, DefaultValueTooLarge) {
"6:36: Integer out of range.\n");
}
+TEST_F(ParseErrorTest, JsonNameNotString) {
+ ExpectHasErrors(
+ "message TestMessage {\n"
+ " optional string foo = 1 [json_name=1];\n"
+ "}\n",
+ "1:37: Expected string for JSON name.\n");
+}
+
+TEST_F(ParseErrorTest, DuplicateJsonName) {
+ ExpectHasErrors(
+ "message TestMessage {\n"
+ " optional uint32 foo = 1 [json_name=\"a\",json_name=\"b\"];\n"
+ "}\n",
+ "1:41: Already set option \"json_name\".\n");
+}
+
TEST_F(ParseErrorTest, EnumValueOutOfRange) {
ExpectHasErrors(
"enum TestEnum {\n"
@@ -929,12 +1170,35 @@ TEST_F(ParseErrorTest, EnumValueOutOfRange) {
"4:19: Integer out of range.\n");
}
+TEST_F(ParseErrorTest, EnumAllowAliasFalse) {
+ ExpectHasErrors(
+ "enum Foo {\n"
+ " option allow_alias = false;\n"
+ " BAR = 1;\n"
+ " BAZ = 2;\n"
+ "}\n",
+ "5:0: \"Foo\" declares 'option allow_alias = false;' which has no effect. "
+ "Please remove the declaration.\n");
+}
+
+TEST_F(ParseErrorTest, UnnecessaryEnumAllowAlias) {
+ ExpectHasErrors(
+ "enum Foo {\n"
+ " option allow_alias = true;\n"
+ " BAR = 1;\n"
+ " BAZ = 2;\n"
+ "}\n",
+ "5:0: \"Foo\" declares support for enum aliases but no enum values share "
+ "field numbers. Please remove the unnecessary 'option allow_alias = true;' "
+ "declaration.\n");
+}
+
TEST_F(ParseErrorTest, DefaultValueMissing) {
ExpectHasErrors(
"message TestMessage {\n"
" optional uint32 foo = 1 [default=];\n"
"}\n",
- "1:35: Expected integer.\n");
+ "1:35: Expected integer for field default value.\n");
}
TEST_F(ParseErrorTest, DefaultValueForGroup) {
@@ -953,6 +1217,76 @@ TEST_F(ParseErrorTest, DuplicateDefaultValue) {
"1:37: Already set option \"default\".\n");
}
+TEST_F(ParseErrorTest, MissingOneofName) {
+ ExpectHasErrors(
+ "message TestMessage {\n"
+ " oneof {\n"
+ " int32 bar = 1;\n"
+ " }\n"
+ "}\n",
+ "1:8: Expected oneof name.\n");
+}
+
+TEST_F(ParseErrorTest, LabelInOneof) {
+ ExpectHasErrors(
+ "message TestMessage {\n"
+ " oneof foo {\n"
+ " optional int32 bar = 1;\n"
+ " }\n"
+ "}\n",
+ "2:4: Fields in oneofs must not have labels (required / optional "
+ "/ repeated).\n");
+}
+
+TEST_F(ParseErrorTest, MapInOneof) {
+ ExpectHasErrors(
+ "message TestMessage {\n"
+ " oneof foo {\n"
+ " map<int32, int32> foo_map = 1;\n"
+ " map message_field = 2;\n" // a normal message field is OK
+ " }\n"
+ "}\n",
+ "2:7: Map fields are not allowed in oneofs.\n");
+}
+
+TEST_F(ParseErrorTest, LabelForMap) {
+ ExpectHasErrors(
+ "message TestMessage {\n"
+ " optional map<int32, int32> int_map = 1;\n"
+ " required map<int32, int32> int_map2 = 2;\n"
+ " repeated map<int32, int32> int_map3 = 3;\n"
+ " optional map map_message = 4;\n" // a normal message field is OK
+ "}\n",
+ "1:14: Field labels (required/optional/repeated) are not allowed on map "
+ "fields.\n"
+ "2:14: Field labels (required/optional/repeated) are not allowed on map "
+ "fields.\n"
+ "3:14: Field labels (required/optional/repeated) are not allowed on map "
+ "fields.\n");
+}
+
+TEST_F(ParseErrorTest, MalformedMaps) {
+ ExpectHasErrors(
+ "message TestMessage {\n"
+ " map map_message = 1;\n" // a normal message field lacking label
+ " map<string> str_map = 2;\n"
+ " map<string,> str_map2 = 3;\n"
+ " map<,string> str_map3 = 4;\n"
+ " map<> empty_map = 5;\n"
+ " map<string,string str_map6 = 6;\n"
+ "}"
+ "extend SomeMessage {\n"
+ " map<int32, int32> int_map = 1;\n"
+ "}",
+ "1:6: Expected \"required\", \"optional\", or \"repeated\".\n"
+ "2:12: Expected \",\".\n"
+ "3:13: Expected type name.\n"
+ "4:6: Expected type name.\n"
+ "5:6: Expected type name.\n"
+ "6:20: Expected \">\".\n"
+ "8:5: Map fields are not allowed to be extensions.\n");
+}
+
TEST_F(ParseErrorTest, GroupNotCapitalized) {
ExpectHasErrors(
"message TestMessage {\n"
@@ -1003,6 +1337,18 @@ TEST_F(ParseErrorTest, EofInAggregateValue) {
"1:0: Unexpected end of stream while parsing aggregate value.\n");
}
+TEST_F(ParseErrorTest, ExplicitOptionalLabelProto3) {
+ ExpectHasErrors(
+ "syntax = 'proto3';\n"
+ "message TestMessage {\n"
+ " optional int32 foo = 1;\n"
+ "}\n",
+ "2:11: Explicit 'optional' labels are disallowed in the Proto3 syntax. "
+ "To define 'optional' fields in Proto3, simply remove the 'optional' "
+ "label, as fields are 'optional' by default.\n");
+}
+
+
// -------------------------------------------------------------------
// Enum errors
@@ -1021,6 +1367,33 @@ TEST_F(ParseErrorTest, EnumValueMissingNumber) {
}
// -------------------------------------------------------------------
+// Reserved field number errors
+
+TEST_F(ParseErrorTest, ReservedMaxNotAllowed) {
+ ExpectHasErrors(
+ "message Foo {\n"
+ " reserved 10 to max;\n"
+ "}\n",
+ "1:17: Expected integer.\n");
+}
+
+TEST_F(ParseErrorTest, ReservedMixNameAndNumber) {
+ ExpectHasErrors(
+ "message Foo {\n"
+ " reserved 10, \"foo\";\n"
+ "}\n",
+ "1:15: Expected field number range.\n");
+}
+
+TEST_F(ParseErrorTest, ReservedMissingQuotes) {
+ ExpectHasErrors(
+ "message Foo {\n"
+ " reserved foo;\n"
+ "}\n",
+ "1:11: Expected field name or number range.\n");
+}
+
+// -------------------------------------------------------------------
// Service errors
TEST_F(ParseErrorTest, EofInService) {
@@ -1244,17 +1617,93 @@ TEST_F(ParserValidationErrorTest, MethodOutputTypeError) {
}
+TEST_F(ParserValidationErrorTest, ResovledUndefinedError) {
+ // Create another file which defines symbol ".base.bar".
+ FileDescriptorProto other_file;
+ other_file.set_name("base.proto");
+ other_file.set_package("base");
+ other_file.add_message_type()->set_name("bar");
+ EXPECT_TRUE(pool_.BuildFile(other_file) != NULL);
+
+ // Define "foo.base" and try "base.bar".
+ // "base.bar" is resolved to "foo.base.bar" which is not defined.
+ ExpectHasValidationErrors(
+ "package foo.base;\n"
+ "import \"base.proto\";\n"
+ "message qux {\n"
+ " optional base.bar baz = 1;\n"
+ " optional .base.bar quz = 2;\n"
+ "}\n",
+ "3:11: \"base.bar\" is resolved to \"foo.base.bar\","
+ " which is not defined. The innermost scope is searched first "
+ "in name resolution. Consider using a leading '.'(i.e., \".base.bar\")"
+ " to start from the outermost scope.\n");
+}
+
+TEST_F(ParserValidationErrorTest, ResovledUndefinedOptionError) {
+ // Build descriptor message in test pool
+ FileDescriptorProto descriptor_proto;
+ DescriptorProto::descriptor()->file()->CopyTo(&descriptor_proto);
+ ASSERT_TRUE(pool_.BuildFile(descriptor_proto) != NULL);
+
+ // base2.proto:
+ // package baz
+ // import google/protobuf/descriptor.proto
+ // message Bar { optional int32 foo = 1; }
+ // extend FileOptions { optional Bar bar = 7672757; }
+ FileDescriptorProto other_file;
+ other_file.set_name("base2.proto");
+ other_file.set_package("baz");
+ other_file.add_dependency();
+ other_file.set_dependency(0, descriptor_proto.name());
+
+ DescriptorProto* message(other_file.add_message_type());
+ message->set_name("Bar");
+ FieldDescriptorProto* field(message->add_field());
+ field->set_name("foo");
+ field->set_number(1);
+ field->set_label(FieldDescriptorProto_Label_LABEL_OPTIONAL);
+ field->set_type(FieldDescriptorProto_Type_TYPE_INT32);
+
+ FieldDescriptorProto* extension(other_file.add_extension());
+ extension->set_name("bar");
+ extension->set_number(7672757);
+ extension->set_label(FieldDescriptorProto_Label_LABEL_OPTIONAL);
+ extension->set_type(FieldDescriptorProto_Type_TYPE_MESSAGE);
+ extension->set_type_name("Bar");
+ extension->set_extendee("google.protobuf.FileOptions");
+
+ EXPECT_TRUE(pool_.BuildFile(other_file) != NULL);
+
+ // qux.proto:
+ // package qux.baz
+ // option (baz.bar).foo = 1;
+ //
+ // Although "baz.bar" is already defined, the lookup code will try
+ // "qux.baz.bar", since it's the match from the innermost scope,
+ // which will cause a symbol not defined error.
+ ExpectHasValidationErrors(
+ "package qux.baz;\n"
+ "import \"base2.proto\";\n"
+ "option (baz.bar).foo = 1;\n",
+ "2:7: Option \"(baz.bar)\" is resolved to \"(qux.baz.bar)\","
+ " which is not defined. The innermost scope is searched first "
+ "in name resolution. Consider using a leading '.'(i.e., \"(.baz.bar)\")"
+ " to start from the outermost scope.\n");
+}
+
// ===================================================================
// Test that the output from FileDescriptor::DebugString() (and all other
// descriptor types) is parseable, and results in the same Descriptor
-// definitions again afoter parsing (not, however, that the order of messages
+// definitions again afoter parsing (note, however, that the order of messages
// cannot be guaranteed to be the same)
-typedef ParserTest ParseDecriptorDebugTest;
+typedef ParserTest ParseDescriptorDebugTest;
class CompareDescriptorNames {
public:
- bool operator()(const DescriptorProto* left, const DescriptorProto* right) {
+ bool operator()(const DescriptorProto* left,
+ const DescriptorProto* right) const {
return left->name() < right->name();
}
};
@@ -1268,7 +1717,7 @@ void SortMessages(DescriptorProto *descriptor_proto) {
}
DescriptorProto **data =
descriptor_proto->mutable_nested_type()->mutable_data();
- sort(data, data + size, CompareDescriptorNames());
+ std::sort(data, data + size, CompareDescriptorNames());
}
// Sorts DescriptorProtos belonging to a FileDescriptorProto, by name.
@@ -1280,10 +1729,31 @@ void SortMessages(FileDescriptorProto *file_descriptor_proto) {
}
DescriptorProto **data =
file_descriptor_proto->mutable_message_type()->mutable_data();
- sort(data, data + size, CompareDescriptorNames());
+ std::sort(data, data + size, CompareDescriptorNames());
}
-TEST_F(ParseDecriptorDebugTest, TestAllDescriptorTypes) {
+// Strips the message and enum field type names for comparison purpose only.
+void StripFieldTypeName(DescriptorProto* proto) {
+ for (int i = 0; i < proto->field_size(); ++i) {
+ string type_name = proto->field(i).type_name();
+ string::size_type pos = type_name.find_last_of(".");
+ if (pos != string::npos) {
+ proto->mutable_field(i)->mutable_type_name()->assign(
+ type_name.begin() + pos + 1, type_name.end());
+ }
+ }
+ for (int i = 0; i < proto->nested_type_size(); ++i) {
+ StripFieldTypeName(proto->mutable_nested_type(i));
+ }
+}
+
+void StripFieldTypeName(FileDescriptorProto* file_proto) {
+ for (int i = 0; i < file_proto->message_type_size(); ++i) {
+ StripFieldTypeName(file_proto->mutable_message_type(i));
+ }
+}
+
+TEST_F(ParseDescriptorDebugTest, TestAllDescriptorTypes) {
const FileDescriptor* original_file =
protobuf_unittest::TestAllTypes::descriptor()->file();
FileDescriptorProto expected;
@@ -1298,7 +1768,8 @@ TEST_F(ParseDecriptorDebugTest, TestAllDescriptorTypes) {
FileDescriptorProto parsed;
parser_->Parse(input_.get(), &parsed);
EXPECT_EQ(io::Tokenizer::TYPE_END, input_->current().type);
- ASSERT_EQ("", error_collector_.text_);
+ ASSERT_EQ("", error_collector_.text_)
+ << "Failed to parse:\n" << debug_string;
// We now have a FileDescriptorProto, but to compare with the expected we
// need to link to a FileDecriptor, then output back to a proto. We'll
@@ -1317,6 +1788,8 @@ TEST_F(ParseDecriptorDebugTest, TestAllDescriptorTypes) {
ASSERT_TRUE(pool_.BuildFile(import_proto) != NULL);
const FileDescriptor* actual = pool_.BuildFile(parsed);
parsed.Clear();
+ ASSERT_TRUE(actual != NULL)
+ << "Failed to validate:\n" << debug_string;
actual->CopyTo(&parsed);
ASSERT_TRUE(actual != NULL);
@@ -1332,7 +1805,7 @@ TEST_F(ParseDecriptorDebugTest, TestAllDescriptorTypes) {
EXPECT_EQ(expected.DebugString(), parsed.DebugString());
}
-TEST_F(ParseDecriptorDebugTest, TestCustomOptions) {
+TEST_F(ParseDescriptorDebugTest, TestCustomOptions) {
const FileDescriptor* original_file =
protobuf_unittest::AggregateMessage::descriptor()->file();
FileDescriptorProto expected;
@@ -1371,6 +1844,160 @@ TEST_F(ParseDecriptorDebugTest, TestCustomOptions) {
EXPECT_EQ(expected.DebugString(), parsed.DebugString());
}
+// Ensure that DebugStringWithOptions(), with |include_comments| set to true,
+// includes comments from the original parser input in all of the appropriate
+// places.
+TEST_F(ParseDescriptorDebugTest, TestCommentsInDebugString) {
+ SetupParser(
+ "// Detached comment before syntax.\n"
+ "\n"
+ "// Syntax comment.\n"
+ "syntax = \"proto2\";\n"
+ "\n"
+ "// Detached comment before package.\n"
+ "\n"
+ "// Package comment.\n"
+ "package comment_test;\n"
+ "\n"
+ "// Detached comment before TestMessage1.\n"
+ "\n"
+ "// Message comment.\n"
+ "//\n"
+ "// More detail in message comment.\n"
+ "message TestMessage1 {\n"
+ "\n"
+ " // Detached comment before foo.\n"
+ "\n"
+ " // Field comment.\n"
+ " optional int32 foo = 1;\n"
+ "\n"
+ " // Detached comment before NestedMessage.\n"
+ "\n"
+ " // Nested-message comment.\n"
+ " message NestedMessage {\n"
+ " optional int32 bar = 1;\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "// Detached comment before MyEnumType.\n"
+ "\n"
+ "// Enum comment.\n"
+ "enum MyEnumType {\n"
+ "\n"
+ " // Detached comment before ASDF.\n"
+ "\n"
+ " // Enum-value comment.\n"
+ " ASDF = 1;\n"
+ "}\n"
+ "\n"
+ "// Detached comment before MyService.\n"
+ "\n"
+ "// Service comment.\n"
+ "service MyService {\n"
+ "\n"
+ " // Detached comment before MyRPCCall.\n"
+ "\n"
+ " // RPC comment.\n"
+ " rpc MyRPCCall(TestMessage1) returns (TestMessage1) { }\n"
+ "}\n");
+
+ FileDescriptorProto parsed_desc;
+ parsed_desc.set_name("foo.proto");
+ SourceLocationTable source_locations;
+ parser_->RecordSourceLocationsTo(&source_locations);
+ parser_->Parse(input_.get(), &parsed_desc);
+ EXPECT_EQ(io::Tokenizer::TYPE_END, input_->current().type);
+ ASSERT_EQ("", error_collector_.text_);
+
+ // We need to import the FileDescriptorProto to get a FileDescriptor.
+ MockValidationErrorCollector collector(source_locations, &error_collector_);
+ const FileDescriptor* descriptor =
+ pool_.BuildFileCollectingErrors(parsed_desc, &collector);
+ ASSERT_TRUE(descriptor != NULL);
+
+ // Ensure that each of the comments appears somewhere in the DebugString().
+ // We don't test the exact comment placement or formatting, because we do not
+ // want to be too fragile here.
+ const char* expected_comments[] = {
+ "Detached comment before syntax.",
+ "Syntax comment.",
+ "Detached comment before package.",
+ "Package comment.",
+ "Detached comment before TestMessage1.",
+ "Message comment.",
+ "More detail in message comment.",
+ "Detached comment before foo.",
+ "Field comment",
+ "Detached comment before NestedMessage.",
+ "Nested-message comment",
+ "Detached comment before MyEnumType.",
+ "Enum comment",
+ "Detached comment before ASDF.",
+ "Enum-value comment",
+ "Detached comment before MyService.",
+ "Service comment",
+ "Detached comment before MyRPCCall.",
+ "RPC comment",
+ };
+
+ DebugStringOptions debug_string_options;
+ debug_string_options.include_comments = true;
+
+ {
+ const string debug_string =
+ descriptor->DebugStringWithOptions(debug_string_options);
+
+ for (int i = 0; i < GOOGLE_ARRAYSIZE(expected_comments); ++i) {
+ string::size_type found_pos = debug_string.find(expected_comments[i]);
+ EXPECT_TRUE(found_pos != string::npos)
+ << "\"" << expected_comments[i] << "\" not found.";
+ }
+
+ // Result of DebugStringWithOptions should be parseable.
+ SetupParser(debug_string.c_str());
+ FileDescriptorProto parsed;
+ parser_->Parse(input_.get(), &parsed);
+ EXPECT_EQ(io::Tokenizer::TYPE_END, input_->current().type);
+ ASSERT_EQ("", error_collector_.text_)
+ << "Failed to parse:\n" << debug_string;
+ }
+
+}
+
+TEST_F(ParseDescriptorDebugTest, TestMaps) {
+ SetupParser(
+ "syntax = \"proto3\"; "
+ "message Foo { "
+ " message Bar { } "
+ " map<int32, Bar> enum_message_map = 1; "
+ " map<string, float> primitive_map = 2; "
+ "} ");
+ FileDescriptorProto original;
+ EXPECT_TRUE(parser_->Parse(input_.get(), &original));
+ original.set_name("foo.proto");
+ const FileDescriptor* file = pool_.BuildFile(original);
+ ASSERT_TRUE(file != NULL);
+
+ // Make sure the debug string uses map syntax and does not have the auto
+ // generated entry.
+ string debug_string = file->DebugString();
+ EXPECT_TRUE(debug_string.find("map<") != string::npos);
+ EXPECT_TRUE(debug_string.find("option map_entry") == string::npos);
+ EXPECT_TRUE(debug_string.find("MapEntry") == string::npos);
+
+ // Make sure the descriptor debug string is parsable.
+ FileDescriptorProto parsed;
+ SetupParser(debug_string.c_str());
+ parsed.set_name("foo.proto");
+ ASSERT_TRUE(parser_->Parse(input_.get(), &parsed));
+
+ original.clear_source_code_info();
+ parsed.clear_source_code_info();
+ StripFieldTypeName(&original);
+ StripFieldTypeName(&parsed);
+ EXPECT_EQ(original.DebugString(), parsed.DebugString());
+}
+
// ===================================================================
// SourceCodeInfo tests.
@@ -1503,8 +2130,8 @@ class SourceInfoTest : public ParserTest {
return false;
}
- spans_.insert(make_pair(SpanKey(*descriptor_proto, field, index),
- &location));
+ spans_.insert(
+ std::make_pair(SpanKey(*descriptor_proto, field, index), &location));
}
return true;
@@ -1525,16 +2152,18 @@ class SourceInfoTest : public ParserTest {
bool HasSpan(char start_marker, char end_marker,
const Message& descriptor_proto) {
return HasSpanWithComment(
- start_marker, end_marker, descriptor_proto, NULL, -1, NULL, NULL);
+ start_marker, end_marker, descriptor_proto, NULL, -1, NULL, NULL, NULL);
}
bool HasSpanWithComment(char start_marker, char end_marker,
const Message& descriptor_proto,
const char* expected_leading_comments,
- const char* expected_trailing_comments) {
+ const char* expected_trailing_comments,
+ const char* expected_leading_detached_comments) {
return HasSpanWithComment(
start_marker, end_marker, descriptor_proto, NULL, -1,
- expected_leading_comments, expected_trailing_comments);
+ expected_leading_comments, expected_trailing_comments,
+ expected_leading_detached_comments);
}
bool HasSpan(char start_marker, char end_marker,
@@ -1546,14 +2175,15 @@ class SourceInfoTest : public ParserTest {
const Message& descriptor_proto, const string& field_name,
int index) {
return HasSpan(start_marker, end_marker, descriptor_proto,
- field_name, index, NULL, NULL);
+ field_name, index, NULL, NULL, NULL);
}
bool HasSpan(char start_marker, char end_marker,
const Message& descriptor_proto,
const string& field_name, int index,
const char* expected_leading_comments,
- const char* expected_trailing_comments) {
+ const char* expected_trailing_comments,
+ const char* expected_leading_detached_comments) {
const FieldDescriptor* field =
descriptor_proto.GetDescriptor()->FindFieldByName(field_name);
if (field == NULL) {
@@ -1564,12 +2194,13 @@ class SourceInfoTest : public ParserTest {
return HasSpanWithComment(
start_marker, end_marker, descriptor_proto, field, index,
- expected_leading_comments, expected_trailing_comments);
+ expected_leading_comments, expected_trailing_comments,
+ expected_leading_detached_comments);
}
bool HasSpan(const Message& descriptor_proto) {
return HasSpanWithComment(
- '\0', '\0', descriptor_proto, NULL, -1, NULL, NULL);
+ '\0', '\0', descriptor_proto, NULL, -1, NULL, NULL, NULL);
}
bool HasSpan(const Message& descriptor_proto, const string& field_name) {
@@ -1581,11 +2212,12 @@ class SourceInfoTest : public ParserTest {
return HasSpan('\0', '\0', descriptor_proto, field_name, index);
}
- bool HasSpanWithComment(char start_marker, char end_marker,
- const Message& descriptor_proto,
- const FieldDescriptor* field, int index,
- const char* expected_leading_comments,
- const char* expected_trailing_comments) {
+ bool HasSpanWithComment(
+ char start_marker, char end_marker, const Message& descriptor_proto,
+ const FieldDescriptor* field, int index,
+ const char* expected_leading_comments,
+ const char* expected_trailing_comments,
+ const char* expected_leading_detached_comments) {
pair<SpanMap::iterator, SpanMap::iterator> range =
spans_.equal_range(SpanKey(descriptor_proto, field, index));
@@ -1624,6 +2256,13 @@ class SourceInfoTest : public ParserTest {
EXPECT_EQ(expected_trailing_comments,
iter->second->trailing_comments());
}
+ if (expected_leading_detached_comments == NULL) {
+ EXPECT_EQ(0, iter->second->leading_detached_comments_size());
+ } else {
+ EXPECT_EQ(
+ expected_leading_detached_comments,
+ Join(iter->second->leading_detached_comments(), "\n"));
+ }
spans_.erase(iter);
return true;
@@ -1674,7 +2313,7 @@ class SourceInfoTest : public ParserTest {
text_without_markers_ += '$';
++column;
} else {
- markers_[*text] = make_pair(line, column);
+ markers_[*text] = std::make_pair(line, column);
++text;
GOOGLE_CHECK_EQ('$', *text);
}
@@ -1693,7 +2332,7 @@ class SourceInfoTest : public ParserTest {
TEST_F(SourceInfoTest, BasicFileDecls) {
EXPECT_TRUE(Parse(
- "$a$syntax = \"proto2\";\n"
+ "$a$syntax = \"proto2\";$i$\n"
"package $b$foo.bar$c$;\n"
"import $d$\"baz.proto\"$e$;\n"
"import $f$\"qux.proto\"$g$;$h$\n"
@@ -1704,6 +2343,7 @@ TEST_F(SourceInfoTest, BasicFileDecls) {
EXPECT_TRUE(HasSpan('b', 'c', file_, "package"));
EXPECT_TRUE(HasSpan('d', 'e', file_, "dependency", 0));
EXPECT_TRUE(HasSpan('f', 'g', file_, "dependency", 1));
+ EXPECT_TRUE(HasSpan('a', 'i', file_, "syntax"));
}
TEST_F(SourceInfoTest, Messages) {
@@ -1871,6 +2511,31 @@ TEST_F(SourceInfoTest, ExtensionRanges) {
EXPECT_TRUE(HasSpan(file_.message_type(0), "name"));
}
+TEST_F(SourceInfoTest, Oneofs) {
+ EXPECT_TRUE(Parse(
+ "message Foo {\n"
+ " $a$oneof $c$foo$d$ {\n"
+ " $e$int32$f$ $g$a$h$ = $i$1$j$;$k$\n"
+ " }$r$\n"
+ "}\n"));
+
+ const OneofDescriptorProto& oneof_decl = file_.message_type(0).oneof_decl(0);
+ const FieldDescriptorProto& field = file_.message_type(0).field(0);
+
+ EXPECT_TRUE(HasSpan('a', 'r', oneof_decl));
+ EXPECT_TRUE(HasSpan('c', 'd', oneof_decl, "name"));
+
+ EXPECT_TRUE(HasSpan('e', 'k', field));
+ EXPECT_TRUE(HasSpan('e', 'f', field, "type"));
+ EXPECT_TRUE(HasSpan('g', 'h', field, "name"));
+ EXPECT_TRUE(HasSpan('i', 'j', field, "number"));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+ EXPECT_TRUE(HasSpan(file_.message_type(0)));
+ EXPECT_TRUE(HasSpan(file_.message_type(0), "name"));
+}
+
TEST_F(SourceInfoTest, NestedMessages) {
EXPECT_TRUE(Parse(
"message Foo {\n"
@@ -2029,6 +2694,7 @@ TEST_F(SourceInfoTest, MethodsAndStreams) {
EXPECT_TRUE(HasSpan(file_.service(0), "name"));
}
+
TEST_F(SourceInfoTest, Options) {
EXPECT_TRUE(Parse(
"$a$option $b$foo$c$.$d$($e$bar.baz$f$)$g$ = "
@@ -2108,6 +2774,9 @@ TEST_F(SourceInfoTest, ScopedOptions) {
" rpc M(X) returns(Y) {\n"
" $g$option mopt = 1;$h$\n"
" }\n"
+ " rpc MS4($1$stream$2$ X) returns($3$stream$4$ Y) {\n"
+ " $k$option mopt = 1;$l$\n"
+ " }\n"
"}\n"));
EXPECT_TRUE(HasSpan('a', 'b', file_.message_type(0).options()));
@@ -2167,6 +2836,26 @@ TEST_F(SourceInfoTest, ScopedOptions) {
.uninterpreted_option(0).name(0), "name_part"));
EXPECT_TRUE(HasSpan(file_.service(0).method(0).options()
.uninterpreted_option(0), "positive_int_value"));
+
+ EXPECT_TRUE(HasSpan('k', 'l', file_.service(0).method(1).options()));
+ EXPECT_TRUE(HasSpan(file_.service(0).method(1)));
+ EXPECT_TRUE(HasSpan(file_.service(0).method(1), "name"));
+ EXPECT_TRUE(HasSpan(file_.service(0).method(1), "input_type"));
+ EXPECT_TRUE(HasSpan(file_.service(0).method(1), "output_type"));
+ EXPECT_TRUE(HasSpan(file_.service(0).method(1).options()
+ .uninterpreted_option(0)));
+ EXPECT_TRUE(HasSpan(file_.service(0).method(1).options()
+ .uninterpreted_option(0), "name"));
+ EXPECT_TRUE(HasSpan(file_.service(0).method(1).options()
+ .uninterpreted_option(0).name(0)));
+ EXPECT_TRUE(HasSpan(file_.service(0).method(1).options()
+ .uninterpreted_option(0).name(0), "name_part"));
+ EXPECT_TRUE(HasSpan(file_.service(0).method(1).options()
+ .uninterpreted_option(0), "positive_int_value"));
+ EXPECT_TRUE(HasSpan('1', '2', file_.service(0).method(1),
+ "client_streaming"));
+ EXPECT_TRUE(HasSpan('3', '4', file_.service(0).method(1),
+ "server_streaming"));
}
TEST_F(SourceInfoTest, FieldOptions) {
@@ -2252,7 +2941,7 @@ TEST_F(SourceInfoTest, DocComments) {
" // Foo trailing\n"
" // line 2\n"
"\n"
- " // ignored\n"
+ " // detached\n"
"\n"
" // bar leading\n"
" $b$optional int32 bar = 1;$c$\n"
@@ -2266,10 +2955,12 @@ TEST_F(SourceInfoTest, DocComments) {
EXPECT_TRUE(HasSpanWithComment('a', 'd', foo,
" Foo leading\n line 2\n",
- " Foo trailing\n line 2\n"));
+ " Foo trailing\n line 2\n",
+ NULL));
EXPECT_TRUE(HasSpanWithComment('b', 'c', bar,
" bar leading\n",
- " bar trailing\n"));
+ " bar trailing\n",
+ " detached\n"));
// Ignore these.
EXPECT_TRUE(HasSpan(file_));
@@ -2282,21 +2973,23 @@ TEST_F(SourceInfoTest, DocComments) {
TEST_F(SourceInfoTest, DocComments2) {
EXPECT_TRUE(Parse(
- "// ignored\n"
- "syntax = \"proto2\";\n"
+ "// detached before message.\n"
+ "\n"
"// Foo leading\n"
"// line 2\n"
"$a$message Foo {\n"
" /* Foo trailing\n"
" * line 2 */\n"
- " // ignored\n"
+ " // detached\n"
" /* bar leading\n"
" */"
" $b$optional int32 bar = 1;$c$ // bar trailing\n"
- " // ignored\n"
+ " // ignored detached\n"
"}$d$\n"
"// ignored\n"
"\n"
+ "// detached before option\n"
+ "\n"
"// option leading\n"
"$e$option baz = 123;$f$\n"
"// option trailing\n"
@@ -2308,13 +3001,16 @@ TEST_F(SourceInfoTest, DocComments2) {
EXPECT_TRUE(HasSpanWithComment('a', 'd', foo,
" Foo leading\n line 2\n",
- " Foo trailing\n line 2 "));
+ " Foo trailing\n line 2 ",
+ " detached before message.\n"));
EXPECT_TRUE(HasSpanWithComment('b', 'c', bar,
" bar leading\n",
- " bar trailing\n"));
+ " bar trailing\n",
+ " detached\n"));
EXPECT_TRUE(HasSpanWithComment('e', 'f', baz,
" option leading\n",
- " option trailing\n"));
+ " option trailing\n",
+ " detached before option\n"));
// Ignore these.
EXPECT_TRUE(HasSpan(file_));
@@ -2345,7 +3041,8 @@ TEST_F(SourceInfoTest, DocComments3) {
EXPECT_TRUE(HasSpanWithComment('b', 'c', bar,
" bar leading\n",
- " bar trailing\n"));
+ " bar trailing\n",
+ NULL));
// Ignore these.
EXPECT_TRUE(HasSpan(file_));
@@ -2365,6 +3062,92 @@ TEST_F(SourceInfoTest, DocComments3) {
bar.options().uninterpreted_option(0), "aggregate_value"));
}
+TEST_F(SourceInfoTest, DocCommentsTopLevel) {
+ EXPECT_TRUE(Parse(
+ "// detached before syntax paragraph 1\n"
+ "\n"
+ "// detached before syntax paragraph 2\n"
+ "\n"
+ "// syntax leading\n"
+ "$a$syntax = \"proto2\";$b$\n"
+ "// syntax trailing\n"
+ "\n"
+ "// syntax-package detached comments\n"
+ "\n"
+ ";\n"
+ "\n"
+ "// detached after empty before package\n"
+ "\n"
+ "// package leading\n"
+ "package $c$foo$d$;\n"
+ "// package trailing\n"
+ "\n"
+ "// ignored detach\n"
+ "\n"));
+
+ EXPECT_TRUE(HasSpan('a', 'b', file_, "syntax", -1,
+ " syntax leading\n",
+ " syntax trailing\n",
+ " detached before syntax paragraph 1\n"
+ "\n"
+ " detached before syntax paragraph 2\n"));
+ EXPECT_TRUE(HasSpan('c', 'd', file_, "package", -1,
+ " package leading\n",
+ " package trailing\n",
+ " syntax-package detached comments\n"
+ "\n"
+ " detached after empty before package\n"));
+
+ // ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+}
+
+TEST_F(SourceInfoTest, DocCommentsOneof) {
+ EXPECT_TRUE(Parse(
+ "// Foo leading\n"
+ "$a$message Foo {\n"
+ " /* Foo trailing\n"
+ " */\n"
+ " // detached before oneof\n"
+ " /* bar leading\n"
+ " * line 2 */\n"
+ " $b$oneof bar {\n"
+ " /* bar trailing\n"
+ " * line 2 */\n"
+ " // detached before bar_int\n"
+ " /* bar_int leading\n"
+ " */\n"
+ " $c$int32 bar_int = 1;$d$ // bar_int trailing\n"
+ " // detach comment ignored\n"
+ " }$e$\n"
+ "}$f$\n"));
+
+ const DescriptorProto& foo = file_.message_type(0);
+ const OneofDescriptorProto& bar = foo.oneof_decl(0);
+ const FieldDescriptorProto& bar_int = foo.field(0);
+
+ EXPECT_TRUE(HasSpanWithComment('a', 'f', foo,
+ " Foo leading\n",
+ " Foo trailing\n",
+ NULL));
+ EXPECT_TRUE(HasSpanWithComment('b', 'e', bar,
+ " bar leading\n line 2 ",
+ " bar trailing\n line 2 ",
+ " detached before oneof\n"));
+ EXPECT_TRUE(HasSpanWithComment('c', 'd', bar_int,
+ " bar_int leading\n",
+ " bar_int trailing\n",
+ " detached before bar_int\n"));
+
+ // Ignore these.
+ EXPECT_TRUE(HasSpan(file_));
+ EXPECT_TRUE(HasSpan(foo, "name"));
+ EXPECT_TRUE(HasSpan(bar, "name"));
+ EXPECT_TRUE(HasSpan(bar_int, "type"));
+ EXPECT_TRUE(HasSpan(bar_int, "name"));
+ EXPECT_TRUE(HasSpan(bar_int, "number"));
+}
+
// ===================================================================
} // anonymous namespace

Powered by Google App Engine
This is Rietveld 408576698