Browse Source

source code info for interpreted options; fix source code info for extension range options (#4342)

* when interpreting options, rewrite file descriptor's source code info
- so that interpreted option paths have correct location information
- so that corresponding uninterpreted option paths are removed
also includes a fix to source code locations for extension range options
Joshua Humphries 7 năm trước cách đây
mục cha
commit
1156ee7ea7

+ 51 - 14
src/google/protobuf/compiler/parser.cc

@@ -337,31 +337,42 @@ void Parser::AddError(const string& error) {
 
 Parser::LocationRecorder::LocationRecorder(Parser* parser)
   : parser_(parser),
+    source_code_info_(parser->source_code_info_),
     location_(parser_->source_code_info_->add_location()) {
   location_->add_span(parser_->input_->current().line);
   location_->add_span(parser_->input_->current().column);
 }
 
 Parser::LocationRecorder::LocationRecorder(const LocationRecorder& parent) {
-  Init(parent);
+  Init(parent, parent.source_code_info_);
+}
+
+Parser::LocationRecorder::LocationRecorder(const LocationRecorder& parent,
+                                           int path1,
+                                           SourceCodeInfo* source_code_info) {
+  Init(parent, source_code_info);
+  AddPath(path1);
 }
 
 Parser::LocationRecorder::LocationRecorder(const LocationRecorder& parent,
                                            int path1) {
-  Init(parent);
+  Init(parent, parent.source_code_info_);
   AddPath(path1);
 }
 
 Parser::LocationRecorder::LocationRecorder(const LocationRecorder& parent,
                                            int path1, int path2) {
-  Init(parent);
+  Init(parent, parent.source_code_info_);
   AddPath(path1);
   AddPath(path2);
 }
 
-void Parser::LocationRecorder::Init(const LocationRecorder& parent) {
+void Parser::LocationRecorder::Init(const LocationRecorder& parent,
+                                    SourceCodeInfo* source_code_info) {
   parser_ = parent.parser_;
-  location_ = parser_->source_code_info_->add_location();
+  source_code_info_ = source_code_info;
+
+  location_ = source_code_info_->add_location();
   location_->mutable_path()->CopyFrom(parent.location_->path());
 
   location_->add_span(parser_->input_->current().line);
@@ -403,6 +414,10 @@ void Parser::LocationRecorder::RecordLegacyLocation(const Message* descriptor,
   }
 }
 
+int Parser::LocationRecorder::CurrentPathSize() const {
+  return location_->path_size();
+}
+
 void Parser::LocationRecorder::AttachComments(
     string* leading, string* trailing,
     std::vector<string>* detached_comments) const {
@@ -1496,21 +1511,30 @@ bool Parser::ParseExtensions(DescriptorProto* message,
     range->set_end(end);
   } while (TryConsume(","));
 
-  if (LookingAt("[")) {
-    LocationRecorder location(
-        extensions_location,
-        DescriptorProto::ExtensionRange::kOptionsFieldNumber);
 
-    DO(Consume("["));
+  if (LookingAt("[")) {
+    int range_number_index = extensions_location.CurrentPathSize();
+    SourceCodeInfo info;
 
     // Parse extension range options in the first range.
     ExtensionRangeOptions* options =
         message->mutable_extension_range(old_range_size)->mutable_options();
-    do {
-      DO(ParseOption(options, location, containing_file, OPTION_ASSIGNMENT));
-    } while (TryConsume(","));
 
-    DO(Consume("]"));
+    {
+      LocationRecorder index_location(
+          extensions_location, 0 /* we fill this in w/ actual index below */,
+          &info);
+      LocationRecorder location(
+          index_location,
+          DescriptorProto::ExtensionRange::kOptionsFieldNumber);
+      DO(Consume("["));
+
+      do {
+        DO(ParseOption(options, location, containing_file, OPTION_ASSIGNMENT));
+      } while (TryConsume(","));
+
+      DO(Consume("]"));
+    }
 
     // Then copy the extension range options to all of the other ranges we've
     // parsed.
@@ -1518,6 +1542,19 @@ bool Parser::ParseExtensions(DescriptorProto* message,
       message->mutable_extension_range(i)->mutable_options()
           ->CopyFrom(*options);
     }
+    // and copy source locations to the other ranges, too
+    for (int i = old_range_size; i < message->extension_range_size(); i++) {
+      for (int j = 0; j < info.location_size(); j++) {
+        if (info.location(j).path_size() == range_number_index + 1) {
+          // this location's path is up to the extension range index, but doesn't
+          // include options; so it's redundant with location above
+          continue;
+        }
+        SourceCodeInfo_Location* dest = source_code_info_->add_location();
+        dest->CopyFrom(info.location(j));
+        dest->set_path(range_number_index, i);
+      }
+    }
   }
 
   DO(ConsumeEndOfDeclaration(";", &extensions_location));

+ 9 - 1
src/google/protobuf/compiler/parser.h

@@ -224,6 +224,10 @@ class LIBPROTOBUF_EXPORT Parser {
     LocationRecorder(const LocationRecorder& parent, int path1);
     LocationRecorder(const LocationRecorder& parent, int path1, int path2);
 
+    // Creates a recorder that generates locations into given source code info.
+    LocationRecorder(const LocationRecorder& parent, int path1,
+                    SourceCodeInfo* source_code_info);
+
     ~LocationRecorder();
 
     // Add a path component.  See SourceCodeInfo.Location.path in
@@ -250,6 +254,9 @@ class LIBPROTOBUF_EXPORT Parser {
     void RecordLegacyLocation(const Message* descriptor,
         DescriptorPool::ErrorCollector::ErrorLocation location);
 
+    // Returns the number of path components in the recorder's current location.
+    int CurrentPathSize() const;
+
     // Attaches leading and trailing comments to the location.  The two strings
     // will be swapped into place, so after this is called *leading and
     // *trailing will be empty.
@@ -264,9 +271,10 @@ class LIBPROTOBUF_EXPORT Parser {
     // SourceCodeInfo.location repeated field. For top-level elements,
     // parent_index_ is -1.
     Parser* parser_;
+    SourceCodeInfo* source_code_info_;
     SourceCodeInfo::Location* location_;
 
-    void Init(const LocationRecorder& parent);
+    void Init(const LocationRecorder& parent, SourceCodeInfo* source_code_info);
   };
 
   // =================================================================

+ 196 - 34
src/google/protobuf/descriptor.cc

@@ -3118,15 +3118,18 @@ namespace {
 struct OptionsToInterpret {
   OptionsToInterpret(const string& ns,
                      const string& el,
+                     std::vector<int>& path,
                      const Message* orig_opt,
                      Message* opt)
       : name_scope(ns),
         element_name(el),
+        element_path(path),
         original_options(orig_opt),
         options(opt) {
   }
   string name_scope;
   string element_name;
+  std::vector<int> element_path;
   const Message* original_options;
   Message* options;
 };
@@ -3293,7 +3296,7 @@ class DescriptorBuilder {
   // descriptor.proto.
   template<class DescriptorT> void AllocateOptions(
       const typename DescriptorT::OptionsType& orig_options,
-      DescriptorT* descriptor);
+      DescriptorT* descriptor, int options_field_tag);
   // Specialization for FileOptions.
   void AllocateOptions(const FileOptions& orig_options,
                        FileDescriptor* descriptor);
@@ -3303,7 +3306,8 @@ class DescriptorBuilder {
       const string& name_scope,
       const string& element_name,
       const typename DescriptorT::OptionsType& orig_options,
-      DescriptorT* descriptor);
+      DescriptorT* descriptor,
+      std::vector<int>& options_path);
 
   // These methods all have the same signature for the sake of the BUILD_ARRAY
   // macro, below.
@@ -3392,13 +3396,21 @@ class DescriptorBuilder {
     // Otherwise returns true.
     bool InterpretOptions(OptionsToInterpret* options_to_interpret);
 
+    // Updates the given source code info by re-writing uninterpreted option
+    // locations to refer to the corresponding interpreted option.
+    void UpdateSourceCodeInfo(SourceCodeInfo* info);
+
     class AggregateOptionFinder;
 
    private:
     // Interprets uninterpreted_option_ on the specified message, which
     // must be the mutable copy of the original options message to which
-    // uninterpreted_option_ belongs.
-    bool InterpretSingleOption(Message* options);
+    // uninterpreted_option_ belongs. The given src_path is the source
+    // location path to the uninterpreted option, and options_path is the
+    // source location path to the options message. The location paths are
+    // recorded and then used in UpdateSourceCodeInfo.
+    bool InterpretSingleOption(Message* options, std::vector<int>& src_path,
+                               std::vector<int>& options_path);
 
     // Adds the uninterpreted_option to the given options message verbatim.
     // Used when AllowUnknownDependencies() is in effect and we can't find
@@ -3473,6 +3485,16 @@ class DescriptorBuilder {
     // can use it to find locations recorded by the parser.
     const UninterpretedOption* uninterpreted_option_;
 
+    // This maps the element path of uninterpreted options to the element path
+    // of the resulting interpreted option. This is used to modify a file's
+    // source code info to account for option interpretation.
+    std::map<std::vector<int>, std::vector<int>> interpreted_paths_;
+
+    // This maps the path to a repeated option field to the known number of
+    // elements the field contains. This is used to track the compute the
+    // index portion of the element path when interpreting a single option.
+    std::map<std::vector<int>, int> repeated_option_counts_;
+
     // Factory used to create the dynamic messages we need to parse
     // any aggregate option values we encounter.
     DynamicMessageFactory dynamic_factory_;
@@ -4090,24 +4112,30 @@ void DescriptorBuilder::ValidateSymbolName(
 // FileDescriptor.
 template<class DescriptorT> void DescriptorBuilder::AllocateOptions(
     const typename DescriptorT::OptionsType& orig_options,
-    DescriptorT* descriptor) {
+    DescriptorT* descriptor, int options_field_tag) {
+  std::vector<int> options_path;
+  descriptor->GetLocationPath(&options_path);
+  options_path.push_back(options_field_tag);
   AllocateOptionsImpl(descriptor->full_name(), descriptor->full_name(),
-                      orig_options, descriptor);
+                      orig_options, descriptor, options_path);
 }
 
 // We specialize for FileDescriptor.
 void DescriptorBuilder::AllocateOptions(const FileOptions& orig_options,
                                         FileDescriptor* descriptor) {
+  std::vector<int> options_path;
+  options_path.push_back(FileDescriptorProto::kOptionsFieldNumber);
   // We add the dummy token so that LookupSymbol does the right thing.
   AllocateOptionsImpl(descriptor->package() + ".dummy", descriptor->name(),
-                      orig_options, descriptor);
+                      orig_options, descriptor, options_path);
 }
 
 template<class DescriptorT> void DescriptorBuilder::AllocateOptionsImpl(
     const string& name_scope,
     const string& element_name,
     const typename DescriptorT::OptionsType& orig_options,
-    DescriptorT* descriptor) {
+    DescriptorT* descriptor,
+    std::vector<int>& options_path) {
   // We need to use a dummy pointer to work around a bug in older versions of
   // GCC.  Otherwise, the following two lines could be replaced with:
   //   typename DescriptorT::OptionsType* options =
@@ -4130,7 +4158,8 @@ template<class DescriptorT> void DescriptorBuilder::AllocateOptionsImpl(
   // we're still trying to build it.
   if (options->uninterpreted_option_size() > 0) {
     options_to_interpret_.push_back(
-        OptionsToInterpret(name_scope, element_name, &orig_options, options));
+        OptionsToInterpret(name_scope, element_name, options_path,
+            &orig_options, options));
   }
 }
 
@@ -4268,8 +4297,9 @@ FileDescriptor* DescriptorBuilder::BuildFileImpl(
 
   result->is_placeholder_ = false;
   result->finished_building_ = false;
+  SourceCodeInfo *info = NULL;
   if (proto.has_source_code_info()) {
-    SourceCodeInfo *info = tables_->AllocateMessage<SourceCodeInfo>();
+    info = tables_->AllocateMessage<SourceCodeInfo>();
     info->CopyFrom(proto.source_code_info());
     result->source_code_info_ = info;
   } else {
@@ -4467,6 +4497,10 @@ FileDescriptor* DescriptorBuilder::BuildFileImpl(
       option_interpreter.InterpretOptions(&(*iter));
     }
     options_to_interpret_.clear();
+
+    if (info != NULL) {
+      option_interpreter.UpdateSourceCodeInfo(info);
+    }
   }
 
   // Validate options. See comments at InternalSetLazilyBuildDependencies about
@@ -4538,7 +4572,8 @@ void DescriptorBuilder::BuildMessage(const DescriptorProto& proto,
   if (!proto.has_options()) {
     result->options_ = NULL;  // Will set to default_instance later.
   } else {
-    AllocateOptions(proto.options(), result);
+    AllocateOptions(proto.options(), result,
+        DescriptorProto::kOptionsFieldNumber);
   }
 
   AddSymbol(result->full_name(), parent, result->name(),
@@ -4918,7 +4953,8 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto,
   if (!proto.has_options()) {
     result->options_ = NULL;  // Will set to default_instance later.
   } else {
-    AllocateOptions(proto.options(), result);
+    AllocateOptions(proto.options(), result,
+        FieldDescriptorProto::kOptionsFieldNumber);
   }
 
 
@@ -4952,8 +4988,16 @@ void DescriptorBuilder::BuildExtensionRange(
   if (!proto.has_options()) {
     result->options_ = NULL;  // Will set to default_instance later.
   } else {
+    std::vector<int> options_path;
+    parent->GetLocationPath(&options_path);
+    options_path.push_back(DescriptorProto::kExtensionRangeFieldNumber);
+    // find index of this extension range in order to compute path
+    int index;
+    for (index = 0; parent->extension_ranges_ + index != result; index++);
+    options_path.push_back(index);
+    options_path.push_back(DescriptorProto_ExtensionRange::kOptionsFieldNumber);
     AllocateOptionsImpl(parent->full_name(), parent->full_name(),
-                        proto.options(), result);
+                        proto.options(), result, options_path);
   }
 }
 
@@ -5005,7 +5049,8 @@ void DescriptorBuilder::BuildOneof(const OneofDescriptorProto& proto,
   if (!proto.has_options()) {
     result->options_ = NULL;  // Will set to default_instance later.
   } else {
-    AllocateOptions(proto.options(), result);
+    AllocateOptions(proto.options(), result,
+        OneofDescriptorProto::kOptionsFieldNumber);
   }
 
   AddSymbol(result->full_name(), parent, result->name(),
@@ -5122,7 +5167,8 @@ void DescriptorBuilder::BuildEnum(const EnumDescriptorProto& proto,
   if (!proto.has_options()) {
     result->options_ = NULL;  // Will set to default_instance later.
   } else {
-    AllocateOptions(proto.options(), result);
+    AllocateOptions(proto.options(), result,
+        EnumDescriptorProto::kOptionsFieldNumber);
   }
 
   AddSymbol(result->full_name(), parent, result->name(),
@@ -5199,7 +5245,8 @@ void DescriptorBuilder::BuildEnumValue(const EnumValueDescriptorProto& proto,
   if (!proto.has_options()) {
     result->options_ = NULL;  // Will set to default_instance later.
   } else {
-    AllocateOptions(proto.options(), result);
+    AllocateOptions(proto.options(), result,
+        EnumValueDescriptorProto::kOptionsFieldNumber);
   }
 
   // Again, enum values are weird because we makes them appear as siblings
@@ -5266,7 +5313,8 @@ void DescriptorBuilder::BuildService(const ServiceDescriptorProto& proto,
   if (!proto.has_options()) {
     result->options_ = NULL;  // Will set to default_instance later.
   } else {
-    AllocateOptions(proto.options(), result);
+    AllocateOptions(proto.options(), result,
+        ServiceDescriptorProto::kOptionsFieldNumber);
   }
 
   AddSymbol(result->full_name(), NULL, result->name(),
@@ -5294,7 +5342,8 @@ void DescriptorBuilder::BuildMethod(const MethodDescriptorProto& proto,
   if (!proto.has_options()) {
     result->options_ = NULL;  // Will set to default_instance later.
   } else {
-    AllocateOptions(proto.options(), result);
+    AllocateOptions(proto.options(), result,
+        MethodDescriptorProto::kOptionsFieldNumber);
   }
 
   result->client_streaming_ = proto.client_streaming();
@@ -6266,6 +6315,9 @@ bool DescriptorBuilder::OptionInterpreter::InterpretOptions(
       << "No field named \"uninterpreted_option\" in the Options proto.";
   options->GetReflection()->ClearField(options, uninterpreted_options_field);
 
+  std::vector<int> src_path = options_to_interpret->element_path;
+  src_path.push_back(uninterpreted_options_field->number());
+
   // Find the uninterpreted_option field in the original options.
   const FieldDescriptor* original_uninterpreted_options_field =
       original_options->GetDescriptor()->
@@ -6276,14 +6328,17 @@ bool DescriptorBuilder::OptionInterpreter::InterpretOptions(
   const int num_uninterpreted_options = original_options->GetReflection()->
       FieldSize(*original_options, original_uninterpreted_options_field);
   for (int i = 0; i < num_uninterpreted_options; ++i) {
+    src_path.push_back(i);
     uninterpreted_option_ = down_cast<const UninterpretedOption*>(
         &original_options->GetReflection()->GetRepeatedMessage(
             *original_options, original_uninterpreted_options_field, i));
-    if (!InterpretSingleOption(options)) {
+    if (!InterpretSingleOption(options, src_path,
+                               options_to_interpret->element_path)) {
       // Error already added by InterpretSingleOption().
       failed = true;
       break;
     }
+    src_path.pop_back();
   }
   // Reset these, so we don't have any dangling pointers.
   uninterpreted_option_ = NULL;
@@ -6316,7 +6371,7 @@ bool DescriptorBuilder::OptionInterpreter::InterpretOptions(
 }
 
 bool DescriptorBuilder::OptionInterpreter::InterpretSingleOption(
-    Message* options) {
+    Message* options, std::vector<int>& src_path, std::vector<int>& opts_path) {
   // First do some basic validation.
   if (uninterpreted_option_->name_size() == 0) {
     // This should never happen unless the parser has gone seriously awry or
@@ -6362,6 +6417,8 @@ bool DescriptorBuilder::OptionInterpreter::InterpretSingleOption(
   std::vector<const FieldDescriptor*> intermediate_fields;
   string debug_msg_name = "";
 
+  std::vector<int> dest_path = opts_path;
+
   for (int i = 0; i < uninterpreted_option_->name_size(); ++i) {
     const string& name_part = uninterpreted_option_->name(i).name_part();
     if (debug_msg_name.size() > 0) {
@@ -6424,19 +6481,24 @@ bool DescriptorBuilder::OptionInterpreter::InterpretSingleOption(
                             "\" is not a field or extension of message \"" +
                             descriptor->name() + "\".");
       }
-    } else if (i < uninterpreted_option_->name_size() - 1) {
-      if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
-        return AddNameError("Option \"" +  debug_msg_name +
-                            "\" is an atomic type, not a message.");
-      } else if (field->is_repeated()) {
-        return AddNameError("Option field \"" + debug_msg_name +
-                            "\" is a repeated message. Repeated message "
-                            "options must be initialized using an "
-                            "aggregate value.");
-      } else {
-        // Drill down into the submessage.
-        intermediate_fields.push_back(field);
-        descriptor = field->message_type();
+    } else {
+      // accumulate field numbers to form path to interpreted option
+      dest_path.push_back(field->number());
+
+      if (i < uninterpreted_option_->name_size() - 1) {
+        if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
+          return AddNameError("Option \"" +  debug_msg_name +
+                              "\" is an atomic type, not a message.");
+        } else if (field->is_repeated()) {
+          return AddNameError("Option field \"" + debug_msg_name +
+                              "\" is a repeated message. Repeated message "
+                              "options must be initialized using an "
+                              "aggregate value.");
+        } else {
+          // Drill down into the submessage.
+          intermediate_fields.push_back(field);
+          descriptor = field->message_type();
+        }
       }
     }
   }
@@ -6457,7 +6519,6 @@ bool DescriptorBuilder::OptionInterpreter::InterpretSingleOption(
     return false;  // ExamineIfOptionIsSet() already added the error.
   }
 
-
   // First set the value on the UnknownFieldSet corresponding to the
   // innermost message.
   std::unique_ptr<UnknownFieldSet> unknown_fields(new UnknownFieldSet());
@@ -6503,9 +6564,110 @@ bool DescriptorBuilder::OptionInterpreter::InterpretSingleOption(
   options->GetReflection()->MutableUnknownFields(options)->MergeFrom(
       *unknown_fields);
 
+  // record the element path of the interpreted option
+  if (field->is_repeated()) {
+    int index = repeated_option_counts_[dest_path]++;
+    dest_path.push_back(index);
+  }
+  interpreted_paths_[src_path] = dest_path;
+
   return true;
 }
 
+void DescriptorBuilder::OptionInterpreter::UpdateSourceCodeInfo(
+    SourceCodeInfo* info) {
+
+  if (interpreted_paths_.empty()) {
+    // nothing to do!
+    return;
+  }
+
+  // We find locations that match keys in interpreted_paths_ and
+  // 1) replace the path with the corresponding value in interpreted_paths_
+  // 2) remove any subsequent sub-locations (sub-location is one whose path
+  //    has the parent path as a prefix)
+  //
+  // To avoid quadratic behavior of removing interior rows as we go,
+  // we keep a copy. But we don't actually copy anything until we've
+  // found the first match (so if the source code info has no locations
+  // that need to be changed, there is zero copy overhead).
+
+  RepeatedPtrField<SourceCodeInfo_Location>* locs = info->mutable_location();
+  RepeatedPtrField<SourceCodeInfo_Location> new_locs;
+  bool copying = false;
+
+  std::vector<int> pathv;
+  bool matched = false;
+
+  for (RepeatedPtrField<SourceCodeInfo_Location>::iterator loc = locs->begin();
+       loc != locs->end(); loc++) {
+
+    if (matched) {
+      // see if this location is in the range to remove
+      bool loc_matches = true;
+      if (loc->path_size() < pathv.size()) {
+        loc_matches = false;
+      } else {
+        for (int j = 0; j < pathv.size(); j++) {
+          if (loc->path(j) != pathv[j]) {
+            loc_matches = false;
+            break;
+          }
+        }
+      }
+
+      if (loc_matches) {
+        // don't copy this row since it is a sub-location that we're removing
+        continue;
+      }
+
+      matched = false;
+    }
+
+    pathv.clear();
+    for (int j = 0; j < loc->path_size(); j++) {
+      pathv.push_back(loc->path(j));
+    }
+
+    std::map<std::vector<int>, std::vector<int>>::iterator entry =
+        interpreted_paths_.find(pathv);
+
+    if (entry == interpreted_paths_.end()) {
+      // not a match
+      if (copying) {
+        new_locs.Add()->CopyFrom(*loc);
+      }
+      continue;
+    }
+
+    matched = true;
+
+    if (!copying) {
+      // initialize the copy we are building
+      copying = true;
+      new_locs.Reserve(locs->size());
+      for (RepeatedPtrField<SourceCodeInfo_Location>::iterator it =
+           locs->begin(); it != loc; it++) {
+        new_locs.Add()->CopyFrom(*it);
+      }
+    }
+
+    // add replacement and update its path
+    SourceCodeInfo_Location* replacement = new_locs.Add();
+    replacement->CopyFrom(*loc);
+    replacement->clear_path();
+    for (std::vector<int>::iterator rit = entry->second.begin();
+         rit != entry->second.end(); rit++) {
+      replacement->add_path(*rit);
+    }
+  }
+
+  // if we made a changed copy, put it in place
+  if (copying) {
+    *locs = new_locs;
+  }
+}
+
 void DescriptorBuilder::OptionInterpreter::AddWithoutInterpreting(
     const UninterpretedOption& uninterpreted_option, Message* options) {
   const FieldDescriptor* field =

+ 492 - 19
src/google/protobuf/descriptor_unittest.cc

@@ -6918,40 +6918,95 @@ class SingletonSourceTree : public compiler::SourceTree {
 
 const char *const kSourceLocationTestInput =
   "syntax = \"proto2\";\n"
+  "option java_package = \"com.foo.bar\";\n"
+  "option (test_file_opt) = \"foobar\";\n"
   "message A {\n"
-  "  optional int32 a = 1;\n"
+  "  option (test_msg_opt) = \"foobar\";\n"
+  "  optional int32 a = 1 [deprecated = true];\n"
   "  message B {\n"
-  "    required double b = 1;\n"
+  "    required double b = 1 [(test_field_opt) = \"foobar\"];\n"
+  "  }\n"
+  "  oneof c {\n"
+  "    option (test_oneof_opt) = \"foobar\";\n"
+  "    string d = 2;\n"
+  "    string e = 3;\n"
+  "    string f = 4;\n"
   "  }\n"
   "}\n"
   "enum Indecision {\n"
-  "  YES   = 1;\n"
-  "  NO    = 2;\n"
+  "  option (test_enum_opt) = 21;\n"
+  "  option (test_enum_opt) = 42;\n"
+  "  option (test_enum_opt) = 63;\n"
+  "  YES   = 1 [(test_enumval_opt).a = 100];\n"
+  "  NO    = 2 [(test_enumval_opt) = {a:200}];\n"
   "  MAYBE = 3;\n"
   "}\n"
   "service S {\n"
+  "  option (test_svc_opt) = {a:100};\n"
+  "  option (test_svc_opt) = {a:200};\n"
+  "  option (test_svc_opt) = {a:300};\n"
   "  rpc Method(A) returns (A.B);\n"
   // Put an empty line here to make the source location range match.
   "\n"
+  "  rpc OtherMethod(A) returns (A) {\n"
+  "    option deprecated = true;\n"
+  "    option (test_method_opt) = \"foobar\";\n"
+  "  }\n"
   "}\n"
   "message MessageWithExtensions {\n"
-  "  extensions 1000 to max;\n"
+  "  extensions 1000 to 2000, 2001 to max [(test_ext_opt) = \"foobar\"];\n"
   "}\n"
   "extend MessageWithExtensions {\n"
-  "  optional int32 int32_extension = 1001;\n"
+  "  repeated int32 int32_extension = 1001 [packed=true];\n"
   "}\n"
   "message C {\n"
   "  extend MessageWithExtensions {\n"
   "    optional C message_extension = 1002;\n"
   "  }\n"
-  "}\n";
+  "}\n"
+  "import \"google/protobuf/descriptor.proto\";\n"
+  "extend google.protobuf.FileOptions {\n"
+  "  optional string test_file_opt = 10101;\n"
+  "}\n"
+  "extend google.protobuf.MessageOptions {\n"
+  "  optional string test_msg_opt = 10101;\n"
+  "}\n"
+  "extend google.protobuf.FieldOptions {\n"
+  "  optional string test_field_opt = 10101;\n"
+  "}\n"
+  "extend google.protobuf.EnumOptions {\n"
+  "  repeated int32 test_enum_opt = 10101;\n"
+  "}\n"
+  "extend google.protobuf.EnumValueOptions {\n"
+  "  optional A test_enumval_opt = 10101;\n"
+  "}\n"
+  "extend google.protobuf.ServiceOptions {\n"
+  "  repeated A test_svc_opt = 10101;\n"
+  "}\n"
+  "extend google.protobuf.MethodOptions {\n"
+  "  optional string test_method_opt = 10101;\n"
+  "}\n"
+  "extend google.protobuf.OneofOptions {\n"
+  "  optional string test_oneof_opt = 10101;\n"
+  "}\n"
+  "extend google.protobuf.ExtensionRangeOptions {\n"
+  "  optional string test_ext_opt = 10101;\n"
+  "}\n"
+  ;
 
 class SourceLocationTest : public testing::Test {
  public:
   SourceLocationTest()
       : source_tree_("/test/test.proto", kSourceLocationTestInput),
-        db_(&source_tree_),
-        pool_(&db_, &collector_) {}
+        simple_db_(),
+        source_tree_db_(&source_tree_),
+        merged_db_(&simple_db_, &source_tree_db_),
+        pool_(&merged_db_, &collector_) {
+    // we need descriptor.proto to be accessible by the pool
+    // since our test file imports it
+    FileDescriptorProto::descriptor()->file()->CopyTo(&file_proto_);
+    simple_db_.Add(file_proto_);
+  }
 
   static string PrintSourceLocation(const SourceLocation &loc) {
     return strings::Substitute("$0:$1-$2:$3",
@@ -6962,12 +7017,20 @@ class SourceLocationTest : public testing::Test {
   }
 
  private:
+  FileDescriptorProto file_proto_;
   AbortingErrorCollector collector_;
   SingletonSourceTree source_tree_;
-  compiler::SourceTreeDescriptorDatabase db_;
+  SimpleDescriptorDatabase simple_db_; // contains descriptor.proto
+  compiler::SourceTreeDescriptorDatabase source_tree_db_; // loads test.proto
+  MergedDescriptorDatabase merged_db_; // combines above two dbs
 
  protected:
   DescriptorPool pool_;
+
+  // tag number of all custom options in above test file
+  static const int kCustomOptionFieldNumber = 10101;
+  // tag number of field "a" in message type "A" in above test file
+  static const int kA_aFieldNumber = 1;
 };
 
 // TODO(adonovan): implement support for option fields and for
@@ -6981,27 +7044,27 @@ TEST_F(SourceLocationTest, GetSourceLocation) {
 
   const Descriptor *a_desc = file_desc->FindMessageTypeByName("A");
   EXPECT_TRUE(a_desc->GetSourceLocation(&loc));
-  EXPECT_EQ("2:1-7:2", PrintSourceLocation(loc));
+  EXPECT_EQ("4:1-16:2", PrintSourceLocation(loc));
 
   const Descriptor *a_b_desc = a_desc->FindNestedTypeByName("B");
   EXPECT_TRUE(a_b_desc->GetSourceLocation(&loc));
-  EXPECT_EQ("4:3-6:4", PrintSourceLocation(loc));
+  EXPECT_EQ("7:3-9:4", PrintSourceLocation(loc));
 
   const EnumDescriptor *e_desc = file_desc->FindEnumTypeByName("Indecision");
   EXPECT_TRUE(e_desc->GetSourceLocation(&loc));
-  EXPECT_EQ("8:1-12:2", PrintSourceLocation(loc));
+  EXPECT_EQ("17:1-24:2", PrintSourceLocation(loc));
 
   const EnumValueDescriptor *yes_desc = e_desc->FindValueByName("YES");
   EXPECT_TRUE(yes_desc->GetSourceLocation(&loc));
-  EXPECT_EQ("9:3-9:13", PrintSourceLocation(loc));
+  EXPECT_EQ("21:3-21:42", PrintSourceLocation(loc));
 
   const ServiceDescriptor *s_desc = file_desc->FindServiceByName("S");
   EXPECT_TRUE(s_desc->GetSourceLocation(&loc));
-  EXPECT_EQ("13:1-16:2", PrintSourceLocation(loc));
+  EXPECT_EQ("25:1-35:2", PrintSourceLocation(loc));
 
   const MethodDescriptor *m_desc = s_desc->FindMethodByName("Method");
   EXPECT_TRUE(m_desc->GetSourceLocation(&loc));
-  EXPECT_EQ("14:3-14:31", PrintSourceLocation(loc));
+  EXPECT_EQ("29:3-29:31", PrintSourceLocation(loc));
 
 }
 
@@ -7014,16 +7077,426 @@ TEST_F(SourceLocationTest, ExtensionSourceLocation) {
   const FieldDescriptor *int32_extension_desc =
       file_desc->FindExtensionByName("int32_extension");
   EXPECT_TRUE(int32_extension_desc->GetSourceLocation(&loc));
-  EXPECT_EQ("21:3-21:41", PrintSourceLocation(loc));
+  EXPECT_EQ("40:3-40:55", PrintSourceLocation(loc));
 
   const Descriptor *c_desc = file_desc->FindMessageTypeByName("C");
   EXPECT_TRUE(c_desc->GetSourceLocation(&loc));
-  EXPECT_EQ("23:1-27:2", PrintSourceLocation(loc));
+  EXPECT_EQ("42:1-46:2", PrintSourceLocation(loc));
 
   const FieldDescriptor *message_extension_desc =
       c_desc->FindExtensionByName("message_extension");
   EXPECT_TRUE(message_extension_desc->GetSourceLocation(&loc));
-  EXPECT_EQ("25:5-25:41", PrintSourceLocation(loc));
+  EXPECT_EQ("44:5-44:41", PrintSourceLocation(loc));
+}
+
+TEST_F(SourceLocationTest, InterpretedOptionSourceLocation) {
+  // This one's a doozy. It checks every kind of option, including
+  // extension range options.
+
+  // We are verifying that the file's source info contains correct
+  // info for interpreted options and that it does *not* contain
+  // any info for corresponding uninterpreted option path.
+
+  SourceLocation loc;
+
+  const FileDescriptor *file_desc =
+      GOOGLE_CHECK_NOTNULL(pool_.FindFileByName("/test/test.proto"));
+
+  // File options
+  {
+    int path[]  = {FileDescriptorProto::kOptionsFieldNumber,
+                   FileOptions::kJavaPackageFieldNumber};
+    int unint[] = {FileDescriptorProto::kOptionsFieldNumber,
+                   FileOptions::kUninterpretedOptionFieldNumber,
+                   0};
+
+    std::vector<int> vpath(path, path + 2);
+    EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
+    EXPECT_EQ("2:1-2:37", PrintSourceLocation(loc));
+
+    std::vector<int> vunint(unint, unint + 3);
+    EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
+  }
+  {
+    int path[]  = {FileDescriptorProto::kOptionsFieldNumber,
+                   kCustomOptionFieldNumber};
+    int unint[] = {FileDescriptorProto::kOptionsFieldNumber,
+                   FileOptions::kUninterpretedOptionFieldNumber,
+                   1};
+    std::vector<int> vpath(path, path + 2);
+    EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
+    EXPECT_EQ("3:1-3:35", PrintSourceLocation(loc));
+
+    std::vector<int> vunint(unint, unint + 3);
+    EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
+  }
+
+  // Message option
+  {
+    int path[]  = {FileDescriptorProto::kMessageTypeFieldNumber,
+                   0,
+                   DescriptorProto::kOptionsFieldNumber,
+                   kCustomOptionFieldNumber};
+    int unint[] = {FileDescriptorProto::kMessageTypeFieldNumber,
+                   0,
+                   DescriptorProto::kOptionsFieldNumber,
+                   MessageOptions::kUninterpretedOptionFieldNumber,
+                   0};
+    std::vector<int> vpath(path, path + 4);
+    EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
+    EXPECT_EQ("5:3-5:36", PrintSourceLocation(loc));
+
+    std::vector<int> vunint(unint, unint + 5);
+    EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
+  }
+
+  // Field option
+  {
+    int path[]  = {FileDescriptorProto::kMessageTypeFieldNumber,
+                   0,
+                   DescriptorProto::kFieldFieldNumber,
+                   0,
+                   FieldDescriptorProto::kOptionsFieldNumber,
+                   FieldOptions::kDeprecatedFieldNumber};
+    int unint[] = {FileDescriptorProto::kMessageTypeFieldNumber,
+                   0,
+                   DescriptorProto::kFieldFieldNumber,
+                   0,
+                   FieldDescriptorProto::kOptionsFieldNumber,
+                   FieldOptions::kUninterpretedOptionFieldNumber,
+                   0};
+    std::vector<int> vpath(path, path + 6);
+    EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
+    EXPECT_EQ("6:25-6:42", PrintSourceLocation(loc));
+
+    std::vector<int> vunint(unint, unint + 7);
+    EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
+  }
+
+  // Nested message option
+  {
+    int path[]  = {FileDescriptorProto::kMessageTypeFieldNumber,
+                   0,
+                   DescriptorProto::kNestedTypeFieldNumber,
+                   0,
+                   DescriptorProto::kFieldFieldNumber,
+                   0,
+                   FieldDescriptorProto::kOptionsFieldNumber,
+                   kCustomOptionFieldNumber};
+    int unint[] = {FileDescriptorProto::kMessageTypeFieldNumber,
+                   0,
+                   DescriptorProto::kNestedTypeFieldNumber,
+                   0,
+                   DescriptorProto::kFieldFieldNumber,
+                   0,
+                   FieldDescriptorProto::kOptionsFieldNumber,
+                   FieldOptions::kUninterpretedOptionFieldNumber,
+                   0};
+    std::vector<int> vpath(path, path + 8);
+    EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
+    EXPECT_EQ("8:28-8:55", PrintSourceLocation(loc));
+
+    std::vector<int> vunint(unint, unint + 9);
+    EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
+  }
+
+  // One-of option
+  {
+    int path[]  = {FileDescriptorProto::kMessageTypeFieldNumber,
+                   0,
+                   DescriptorProto::kOneofDeclFieldNumber,
+                   0,
+                   OneofDescriptorProto::kOptionsFieldNumber,
+                   kCustomOptionFieldNumber};
+    int unint[] = {FileDescriptorProto::kMessageTypeFieldNumber,
+                   0,
+                   DescriptorProto::kOneofDeclFieldNumber,
+                   0,
+                   OneofDescriptorProto::kOptionsFieldNumber,
+                   OneofOptions::kUninterpretedOptionFieldNumber,
+                   0};
+    std::vector<int> vpath(path, path + 6);
+    EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
+    EXPECT_EQ("11:5-11:40", PrintSourceLocation(loc));
+
+    std::vector<int> vunint(unint, unint + 7);
+    EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
+  }
+
+  // Enum option, repeated options
+  {
+    int path[]  = {FileDescriptorProto::kEnumTypeFieldNumber,
+                   0,
+                   EnumDescriptorProto::kOptionsFieldNumber,
+                   kCustomOptionFieldNumber,
+                   0};
+    int unint[] = {FileDescriptorProto::kEnumTypeFieldNumber,
+                   0,
+                   EnumDescriptorProto::kOptionsFieldNumber,
+                   EnumOptions::kUninterpretedOptionFieldNumber,
+                   0};
+    std::vector<int> vpath(path, path + 5);
+    EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
+    EXPECT_EQ("18:3-18:31", PrintSourceLocation(loc));
+
+    std::vector<int> vunint(unint, unint + 5);
+    EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
+  }
+  {
+    int path[]  = {FileDescriptorProto::kEnumTypeFieldNumber,
+                   0,
+                   EnumDescriptorProto::kOptionsFieldNumber,
+                   kCustomOptionFieldNumber,
+                   1};
+    int unint[] = {FileDescriptorProto::kEnumTypeFieldNumber,
+                   0,
+                   EnumDescriptorProto::kOptionsFieldNumber,
+                   EnumOptions::kUninterpretedOptionFieldNumber,
+                   1};
+    std::vector<int> vpath(path, path + 5);
+    EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
+    EXPECT_EQ("19:3-19:31", PrintSourceLocation(loc));
+
+    std::vector<int> vunint(unint, unint + 5);
+    EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
+  }
+  {
+    int path[]  = {FileDescriptorProto::kEnumTypeFieldNumber,
+                   0,
+                   EnumDescriptorProto::kOptionsFieldNumber,
+                   kCustomOptionFieldNumber,
+                   2};
+    int unint[] = {FileDescriptorProto::kEnumTypeFieldNumber,
+                   0,
+                   EnumDescriptorProto::kOptionsFieldNumber,
+                   OneofOptions::kUninterpretedOptionFieldNumber,
+                   2};
+    std::vector<int> vpath(path, path + 5);
+    EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
+    EXPECT_EQ("20:3-20:31", PrintSourceLocation(loc));
+
+    std::vector<int> vunint(unint, unint + 5);
+    EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
+  }
+
+  // Enum value options
+  {
+    // option w/ message type that directly sets field
+    int path[]  = {FileDescriptorProto::kEnumTypeFieldNumber,
+                   0,
+                   EnumDescriptorProto::kValueFieldNumber,
+                   0,
+                   EnumValueDescriptorProto::kOptionsFieldNumber,
+                   kCustomOptionFieldNumber,
+                   kA_aFieldNumber};
+    int unint[] = {FileDescriptorProto::kEnumTypeFieldNumber,
+                   0,
+                   EnumDescriptorProto::kValueFieldNumber,
+                   0,
+                   EnumValueDescriptorProto::kOptionsFieldNumber,
+                   EnumValueOptions::kUninterpretedOptionFieldNumber,
+                   0};
+    std::vector<int> vpath(path, path + 7);
+    EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
+    EXPECT_EQ("21:14-21:40", PrintSourceLocation(loc));
+
+    std::vector<int> vunint(unint, unint + 7);
+    EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
+  }
+  {
+    int path[]  = {FileDescriptorProto::kEnumTypeFieldNumber,
+                   0,
+                   EnumDescriptorProto::kValueFieldNumber,
+                   1,
+                   EnumValueDescriptorProto::kOptionsFieldNumber,
+                   kCustomOptionFieldNumber};
+    int unint[] = {FileDescriptorProto::kEnumTypeFieldNumber,
+                   0,
+                   EnumDescriptorProto::kValueFieldNumber,
+                   1,
+                   EnumValueDescriptorProto::kOptionsFieldNumber,
+                   EnumValueOptions::kUninterpretedOptionFieldNumber,
+                   0};
+    std::vector<int> vpath(path, path + 6);
+    EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
+    EXPECT_EQ("22:14-22:42", PrintSourceLocation(loc));
+
+    std::vector<int> vunint(unint, unint + 7);
+    EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
+  }
+
+  // Service option, repeated options
+  {
+    int path[]  = {FileDescriptorProto::kServiceFieldNumber,
+                   0,
+                   ServiceDescriptorProto::kOptionsFieldNumber,
+                   kCustomOptionFieldNumber,
+                   0};
+    int unint[] = {FileDescriptorProto::kServiceFieldNumber,
+                   0,
+                   ServiceDescriptorProto::kOptionsFieldNumber,
+                   ServiceOptions::kUninterpretedOptionFieldNumber,
+                   0};
+    std::vector<int> vpath(path, path + 5);
+    EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
+    EXPECT_EQ("26:3-26:35", PrintSourceLocation(loc));
+
+    std::vector<int> vunint(unint, unint + 5);
+    EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
+  }
+  {
+    int path[]  = {FileDescriptorProto::kServiceFieldNumber,
+                   0,
+                   ServiceDescriptorProto::kOptionsFieldNumber,
+                   kCustomOptionFieldNumber,
+                   1};
+    int unint[] = {FileDescriptorProto::kServiceFieldNumber,
+                   0,
+                   ServiceDescriptorProto::kOptionsFieldNumber,
+                   ServiceOptions::kUninterpretedOptionFieldNumber,
+                   1};
+    std::vector<int> vpath(path, path + 5);
+    EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
+    EXPECT_EQ("27:3-27:35", PrintSourceLocation(loc));
+
+    std::vector<int> vunint(unint, unint + 5);
+    EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
+  }
+  {
+    int path[]  = {FileDescriptorProto::kServiceFieldNumber,
+                   0,
+                   ServiceDescriptorProto::kOptionsFieldNumber,
+                   kCustomOptionFieldNumber,
+                   2};
+    int unint[] = {FileDescriptorProto::kServiceFieldNumber,
+                   0,
+                   ServiceDescriptorProto::kOptionsFieldNumber,
+                   ServiceOptions::kUninterpretedOptionFieldNumber,
+                   2};
+    std::vector<int> vpath(path, path + 5);
+    EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
+    EXPECT_EQ("28:3-28:35", PrintSourceLocation(loc));
+
+    std::vector<int> vunint(unint, unint + 5);
+    EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
+  }
+
+  // Method options
+  {
+    int path[]  = {FileDescriptorProto::kServiceFieldNumber,
+                   0,
+                   ServiceDescriptorProto::kMethodFieldNumber,
+                   1,
+                   MethodDescriptorProto::kOptionsFieldNumber,
+                   MethodOptions::kDeprecatedFieldNumber};
+    int unint[] = {FileDescriptorProto::kServiceFieldNumber,
+                   0,
+                   ServiceDescriptorProto::kMethodFieldNumber,
+                   1,
+                   MethodDescriptorProto::kOptionsFieldNumber,
+                   MethodOptions::kUninterpretedOptionFieldNumber,
+                   0};
+    std::vector<int> vpath(path, path + 6);
+    EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
+    EXPECT_EQ("32:5-32:30", PrintSourceLocation(loc));
+
+    std::vector<int> vunint(unint, unint + 7);
+    EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
+  }
+  {
+    int path[]  = {FileDescriptorProto::kServiceFieldNumber,
+                   0,
+                   ServiceDescriptorProto::kMethodFieldNumber,
+                   1,
+                   MethodDescriptorProto::kOptionsFieldNumber,
+                   kCustomOptionFieldNumber};
+    int unint[] = {FileDescriptorProto::kServiceFieldNumber,
+                   0,
+                   ServiceDescriptorProto::kMethodFieldNumber,
+                   1,
+                   MethodDescriptorProto::kOptionsFieldNumber,
+                   MethodOptions::kUninterpretedOptionFieldNumber,
+                   1};
+    std::vector<int> vpath(path, path + 6);
+    EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
+    EXPECT_EQ("33:5-33:41", PrintSourceLocation(loc));
+
+    std::vector<int> vunint(unint, unint + 7);
+    EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
+  }
+
+  // Extension range options
+  {
+    int path[]  = {FileDescriptorProto::kMessageTypeFieldNumber,
+                   1,
+                   DescriptorProto::kExtensionRangeFieldNumber,
+                   0,
+                   DescriptorProto_ExtensionRange::kOptionsFieldNumber};
+    std::vector<int> vpath(path, path + 5);
+    EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
+    EXPECT_EQ("37:40-37:67", PrintSourceLocation(loc));
+  }
+  {
+    int path[]  = {FileDescriptorProto::kMessageTypeFieldNumber,
+                   1,
+                   DescriptorProto::kExtensionRangeFieldNumber,
+                   0,
+                   DescriptorProto_ExtensionRange::kOptionsFieldNumber,
+                   kCustomOptionFieldNumber};
+    int unint[] = {FileDescriptorProto::kMessageTypeFieldNumber,
+                   1,
+                   DescriptorProto::kExtensionRangeFieldNumber,
+                   0,
+                   DescriptorProto_ExtensionRange::kOptionsFieldNumber,
+                   ExtensionRangeOptions::kUninterpretedOptionFieldNumber,
+                   0};
+    std::vector<int> vpath(path, path + 6);
+    EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
+    EXPECT_EQ("37:41-37:66", PrintSourceLocation(loc));
+
+    std::vector<int> vunint(unint, unint + 7);
+    EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
+  }
+  {
+    int path[]  = {FileDescriptorProto::kMessageTypeFieldNumber,
+                   1,
+                   DescriptorProto::kExtensionRangeFieldNumber,
+                   1,
+                   DescriptorProto_ExtensionRange::kOptionsFieldNumber,
+                   kCustomOptionFieldNumber};
+    int unint[] = {FileDescriptorProto::kMessageTypeFieldNumber,
+                   1,
+                   DescriptorProto::kExtensionRangeFieldNumber,
+                   1,
+                   DescriptorProto_ExtensionRange::kOptionsFieldNumber,
+                   ExtensionRangeOptions::kUninterpretedOptionFieldNumber,
+                   0};
+    std::vector<int> vpath(path, path + 6);
+    EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
+    EXPECT_EQ("37:41-37:66", PrintSourceLocation(loc));
+
+    std::vector<int> vunint(unint, unint + 7);
+    EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
+  }
+
+  // Field option on extension
+  {
+    int path[]  = {FileDescriptorProto::kExtensionFieldNumber,
+                   0,
+                   FieldDescriptorProto::kOptionsFieldNumber,
+                   FieldOptions::kPackedFieldNumber};
+    int unint[] = {FileDescriptorProto::kExtensionFieldNumber,
+                   0,
+                   FieldDescriptorProto::kOptionsFieldNumber,
+                   FieldOptions::kUninterpretedOptionFieldNumber,
+                   0};
+    std::vector<int> vpath(path, path + 4);
+    EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
+    EXPECT_EQ("40:42-40:53", PrintSourceLocation(loc));
+
+    std::vector<int> vunint(unint, unint + 5);
+    EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
+  }
 }
 
 // Missing SourceCodeInfo doesn't cause crash: