| Index: third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc
|
| diff --git a/third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc b/third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc
|
| index 990aca248c20b40221064ef82b9d2734632a19a3..196b39dd93207e5529a8e5a0701afcb72b658c2d 100644
|
| --- a/third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc
|
| +++ b/third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc
|
| @@ -58,6 +58,14 @@ namespace protobuf {
|
| namespace compiler {
|
| namespace objectivec {
|
|
|
| +Options::Options() {
|
| + // Default is the value of the env for the package prefixes.
|
| + const char* file_path = getenv("GPB_OBJC_EXPECTED_PACKAGE_PREFIXES");
|
| + if (file_path) {
|
| + expected_prefixes_path = file_path;
|
| + }
|
| +}
|
| +
|
| namespace {
|
|
|
| hash_set<string> MakeWordsMap(const char* const words[], size_t num_words) {
|
| @@ -116,9 +124,14 @@ string UnderscoresToCamelCase(const string& input, bool first_capitalized) {
|
| }
|
| values.push_back(current);
|
|
|
| + string result;
|
| + bool first_segment_forces_upper = false;
|
| for (vector<string>::iterator i = values.begin(); i != values.end(); ++i) {
|
| string value = *i;
|
| bool all_upper = (kUpperSegments.count(value) > 0);
|
| + if (all_upper && (result.length() == 0)) {
|
| + first_segment_forces_upper = true;
|
| + }
|
| for (int j = 0; j < value.length(); j++) {
|
| if (j == 0 || all_upper) {
|
| value[j] = ascii_toupper(value[j]);
|
| @@ -126,13 +139,11 @@ string UnderscoresToCamelCase(const string& input, bool first_capitalized) {
|
| // Nothing, already in lower.
|
| }
|
| }
|
| - *i = value;
|
| + result += value;
|
| }
|
| - string result;
|
| - for (vector<string>::iterator i = values.begin(); i != values.end(); ++i) {
|
| - result += *i;
|
| - }
|
| - if ((result.length() != 0) && !first_capitalized) {
|
| + if ((result.length() != 0) &&
|
| + !first_capitalized &&
|
| + !first_segment_forces_upper) {
|
| result[0] = ascii_tolower(result[0]);
|
| }
|
| return result;
|
| @@ -209,11 +220,6 @@ string NameFromFieldDescriptor(const FieldDescriptor* field) {
|
| }
|
| }
|
|
|
| -// Escape C++ trigraphs by escaping question marks to \?
|
| -string EscapeTrigraphs(const string& to_escape) {
|
| - return StringReplace(to_escape, "?", "\\?", true);
|
| -}
|
| -
|
| void PathSplit(const string& path, string* directory, string* basename) {
|
| string::size_type last_slash = path.rfind('/');
|
| if (last_slash == string::npos) {
|
| @@ -253,6 +259,11 @@ bool IsSpecialName(const string& name, const string* special_names,
|
|
|
| } // namespace
|
|
|
| +// Escape C++ trigraphs by escaping question marks to \?
|
| +string EscapeTrigraphs(const string& to_escape) {
|
| + return StringReplace(to_escape, "?", "\\?", true);
|
| +}
|
| +
|
| string StripProto(const string& filename) {
|
| if (HasSuffixString(filename, ".protodevel")) {
|
| return StripSuffixString(filename, ".protodevel");
|
| @@ -723,7 +734,7 @@ string DefaultValue(const FieldDescriptor* field) {
|
| uint32 length = ghtonl(default_string.length());
|
| string bytes((const char*)&length, sizeof(length));
|
| bytes.append(default_string);
|
| - return "(NSData*)\"" + CEscape(bytes) + "\"";
|
| + return "(NSData*)\"" + EscapeTrigraphs(CEscape(bytes)) + "\"";
|
| } else {
|
| return "@\"" + EscapeTrigraphs(CEscape(default_string)) + "\"";
|
| }
|
| @@ -740,6 +751,51 @@ string DefaultValue(const FieldDescriptor* field) {
|
| return NULL;
|
| }
|
|
|
| +bool HasNonZeroDefaultValue(const FieldDescriptor* field) {
|
| + // Repeated fields don't have defaults.
|
| + if (field->is_repeated()) {
|
| + return false;
|
| + }
|
| +
|
| + // As much as checking field->has_default_value() seems useful, it isn't
|
| + // because of enums. proto2 syntax allows the first item in an enum (the
|
| + // default) to be non zero. So checking field->has_default_value() would
|
| + // result in missing this non zero default. See MessageWithOneBasedEnum in
|
| + // objectivec/Tests/unittest_objc.proto for a test Message to confirm this.
|
| +
|
| + // Some proto file set the default to the zero value, so make sure the value
|
| + // isn't the zero case.
|
| + switch (field->cpp_type()) {
|
| + case FieldDescriptor::CPPTYPE_INT32:
|
| + return field->default_value_int32() != 0;
|
| + case FieldDescriptor::CPPTYPE_UINT32:
|
| + return field->default_value_uint32() != 0U;
|
| + case FieldDescriptor::CPPTYPE_INT64:
|
| + return field->default_value_int64() != 0LL;
|
| + case FieldDescriptor::CPPTYPE_UINT64:
|
| + return field->default_value_uint64() != 0ULL;
|
| + case FieldDescriptor::CPPTYPE_DOUBLE:
|
| + return field->default_value_double() != 0.0;
|
| + case FieldDescriptor::CPPTYPE_FLOAT:
|
| + return field->default_value_float() != 0.0f;
|
| + case FieldDescriptor::CPPTYPE_BOOL:
|
| + return field->default_value_bool();
|
| + case FieldDescriptor::CPPTYPE_STRING: {
|
| + const string& default_string = field->default_value_string();
|
| + return default_string.length() != 0;
|
| + }
|
| + case FieldDescriptor::CPPTYPE_ENUM:
|
| + return field->default_value_enum()->number() != 0;
|
| + case FieldDescriptor::CPPTYPE_MESSAGE:
|
| + return false;
|
| + }
|
| +
|
| + // Some compilers report reaching end of function even though all cases of
|
| + // the enum are handed in the switch.
|
| + GOOGLE_LOG(FATAL) << "Can't get here.";
|
| + return false;
|
| +}
|
| +
|
| string BuildFlagsString(const vector<string>& strings) {
|
| if (strings.size() == 0) {
|
| return "0";
|
| @@ -763,16 +819,14 @@ string BuildCommentsString(const SourceLocation& location) {
|
| while (!lines.empty() && lines.back().empty()) {
|
| lines.pop_back();
|
| }
|
| - string prefix("//");
|
| + string prefix("///");
|
| string suffix("\n");
|
| string final_comments;
|
| for (int i = 0; i < lines.size(); i++) {
|
| - // We use $ for delimiters, so replace comments with dollars with
|
| - // html escaped version.
|
| - // None of the other compilers handle this (as of this writing) but we
|
| - // ran into it once, so just to be safe.
|
| + // HeaderDoc uses '\' and '@' for markers; escape them.
|
| + const string line = StringReplace(lines[i], "\\", "\\\\", true);
|
| final_comments +=
|
| - prefix + StringReplace(lines[i], "$", "$", true) + suffix;
|
| + prefix + StringReplace(line, "@", "\\@", true) + suffix;
|
| }
|
| return final_comments;
|
| }
|
| @@ -883,33 +937,33 @@ bool Parser::ParseLoop() {
|
| StringPiece prefix(line, offset + 1, line.length() - offset - 1);
|
| TrimWhitespace(&package);
|
| TrimWhitespace(&prefix);
|
| - // Don't really worry about error checking the the package/prefix for
|
| + // Don't really worry about error checking the package/prefix for
|
| // being valid. Assume the file is validated when it is created/edited.
|
| (*prefix_map_)[package.ToString()] = prefix.ToString();
|
| }
|
| return true;
|
| }
|
|
|
| -bool LoadExpectedPackagePrefixes(map<string, string>* prefix_map,
|
| - string* out_expect_file_path,
|
| +bool LoadExpectedPackagePrefixes(const Options &generation_options,
|
| + map<string, string>* prefix_map,
|
| string* out_error) {
|
| - const char* file_path = getenv("GPB_OBJC_EXPECTED_PACKAGE_PREFIXES");
|
| - if (file_path == NULL) {
|
| + if (generation_options.expected_prefixes_path.empty()) {
|
| return true;
|
| }
|
|
|
| int fd;
|
| do {
|
| - fd = open(file_path, O_RDONLY);
|
| + fd = open(generation_options.expected_prefixes_path.c_str(), O_RDONLY);
|
| } while (fd < 0 && errno == EINTR);
|
| if (fd < 0) {
|
| *out_error =
|
| - string(file_path) + ":0:0: error: Unable to open." + strerror(errno);
|
| + string("error: Unable to open \"") +
|
| + generation_options.expected_prefixes_path +
|
| + "\", " + strerror(errno);
|
| return false;
|
| }
|
| io::FileInputStream file_stream(fd);
|
| file_stream.SetCloseOnDelete(true);
|
| - *out_expect_file_path = file_path;
|
|
|
| Parser parser(prefix_map);
|
| const void* buf;
|
| @@ -920,8 +974,9 @@ bool LoadExpectedPackagePrefixes(map<string, string>* prefix_map,
|
| }
|
|
|
| if (!parser.ParseChunk(StringPiece(static_cast<const char*>(buf), buf_len))) {
|
| - *out_error = string(file_path) + ":" + SimpleItoa(parser.last_line()) +
|
| - ":0: error: " + parser.error_str();
|
| + *out_error =
|
| + string("error: ") + generation_options.expected_prefixes_path +
|
| + " Line " + SimpleItoa(parser.last_line()) + ", " + parser.error_str();
|
| return false;
|
| }
|
| }
|
| @@ -930,7 +985,9 @@ bool LoadExpectedPackagePrefixes(map<string, string>* prefix_map,
|
|
|
| } // namespace
|
|
|
| -bool ValidateObjCClassPrefix(const FileDescriptor* file, string* out_error) {
|
| +bool ValidateObjCClassPrefix(const FileDescriptor* file,
|
| + const Options& generation_options,
|
| + string* out_error) {
|
| const string prefix = file->options().objc_class_prefix();
|
| const string package = file->package();
|
|
|
| @@ -939,11 +996,10 @@ bool ValidateObjCClassPrefix(const FileDescriptor* file, string* out_error) {
|
|
|
| // Load any expected package prefixes to validate against those.
|
| map<string, string> expected_package_prefixes;
|
| - string expect_file_path;
|
| - if (!LoadExpectedPackagePrefixes(&expected_package_prefixes,
|
| - &expect_file_path, out_error)) {
|
| - // Any error, clear the entries that were read.
|
| - expected_package_prefixes.clear();
|
| + if (!LoadExpectedPackagePrefixes(generation_options,
|
| + &expected_package_prefixes,
|
| + out_error)) {
|
| + return false;
|
| }
|
|
|
| // Check: Error - See if there was an expected prefix for the package and
|
| @@ -957,8 +1013,9 @@ bool ValidateObjCClassPrefix(const FileDescriptor* file, string* out_error) {
|
| return true;
|
| } else {
|
| // ...it didn't match!
|
| - *out_error = "protoc:0: error: Expected 'option objc_class_prefix = \"" +
|
| - package_match->second + "\";' in '" + file->name() + "'";
|
| + *out_error = "error: Expected 'option objc_class_prefix = \"" +
|
| + package_match->second + "\";' for package '" + package +
|
| + "' in '" + file->name() + "'";
|
| if (prefix.length()) {
|
| *out_error += "; but found '" + prefix + "' instead";
|
| }
|
| @@ -980,11 +1037,11 @@ bool ValidateObjCClassPrefix(const FileDescriptor* file, string* out_error) {
|
| i != expected_package_prefixes.end(); ++i) {
|
| if (i->second == prefix) {
|
| *out_error =
|
| - "protoc:0: error: Found 'option objc_class_prefix = \"" + prefix +
|
| + "error: Found 'option objc_class_prefix = \"" + prefix +
|
| "\";' in '" + file->name() +
|
| "'; that prefix is already used for 'package " + i->first +
|
| ";'. It can only be reused by listing it in the expected file (" +
|
| - expect_file_path + ").";
|
| + generation_options.expected_prefixes_path + ").";
|
| return false; // Only report first usage of the prefix.
|
| }
|
| }
|
| @@ -1017,7 +1074,7 @@ bool ValidateObjCClassPrefix(const FileDescriptor* file, string* out_error) {
|
| << "protoc:0: warning: Found unexpected 'option objc_class_prefix = \""
|
| << prefix << "\";' in '" << file->name() << "';"
|
| << " consider adding it to the expected prefixes file ("
|
| - << expect_file_path << ")." << endl;
|
| + << generation_options.expected_prefixes_path << ")." << endl;
|
| cerr.flush();
|
| }
|
|
|
|
|