kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/cxx/indexer/proto/proto_analyzer.cc (about)

     1  /*
     2   * Copyright 2018 The Kythe Authors. All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *   http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  #include "kythe/cxx/indexer/proto/proto_analyzer.h"
    18  
    19  #include "absl/container/flat_hash_map.h"
    20  #include "absl/log/log.h"
    21  #include "absl/strings/string_view.h"
    22  #include "kythe/cxx/indexer/proto/file_descriptor_walker.h"
    23  
    24  namespace kythe {
    25  namespace lang_proto {
    26  
    27  using ::google::protobuf::FileDescriptorProto;
    28  using ::kythe::proto::VName;
    29  
    30  // TODO: it seems very likely that a lot of the path-mangling
    31  // logic can be removed, though it doesn't seem to cause any harm...
    32  
    33  // It is apparently preferred to use the ProtoFileParser api rather than
    34  // google::protobuf::compiler::parser. We also use the DescriptorPool to fetch
    35  // filenames of resolved type vnames.
    36  ProtoAnalyzer::ProtoAnalyzer(
    37      const proto::CompilationUnit* unit,
    38      google::protobuf::DescriptorDatabase* descriptor_db,
    39      KytheGraphRecorder* recorder,
    40      absl::flat_hash_map<std::string, std::string>* path_substitution_cache)
    41      : unit_(unit),
    42        recorder_(recorder),
    43        path_substitution_cache_(path_substitution_cache),
    44        descriptor_db_(descriptor_db) {}
    45  
    46  bool ProtoAnalyzer::AnalyzeFile(const std::string& rel_path,
    47                                  const VName& v_name,
    48                                  const std::string& content) {
    49    google::protobuf::DescriptorPool pool(descriptor_db_);
    50    ProtoGraphBuilder builder(recorder_, [&](const std::string& path) {
    51      return VNameFromRelPath(path);
    52    });
    53  
    54    // We keep track of all visited files, effectively performing per-replica
    55    // claiming.  Formerly this helped avoid issues with cyclic dependencies, but
    56    // now only the reused proto2 infrastructure descends into dependencies and
    57    // thus only it is exposed to cyclic dependency risks.
    58    if (!visited_files_.insert(rel_path).second) {
    59      return true;
    60    }
    61  
    62    builder.SetText(v_name, content);
    63  
    64    // Note: It would appear to be cleaner to have these calls within
    65    // FileDescriptorWalker.  However, for files which fail to have a valid
    66    // FileDescriptor (which actually happens just processing the files in
    67    // devtools/grok/proto/, it turns out), we at least get the partial info
    68    // of having the file in our index and acknowledging the lexer results.
    69    builder.AddNode(v_name, NodeKindID::kFile);
    70  
    71    // TODO: If FileDescriptor surfaced the source code info, then we
    72    // wouldn't need to look up the proto as well.
    73    const google::protobuf::FileDescriptor* descriptor =
    74        pool.FindFileByName(rel_path);
    75    google::protobuf::FileDescriptorProto descriptor_proto;
    76    if ((descriptor == nullptr) ||
    77        !descriptor_db_->FindFileByName(rel_path, &descriptor_proto)) {
    78      // TODO: We should be associating any such "diagnostic" messages
    79      // with the file, so that we are aware of problems with files without
    80      // reanalyzing them.
    81      LOG(ERROR) << (descriptor == nullptr ? "File not found: "
    82                                           : "Error parsing: ")
    83                 << rel_path;
    84      // Undo the cache recording of visitation we performed at function entry in
    85      // case another context can successfully read this later.
    86      visited_files_.erase(rel_path);
    87      return false;
    88    }
    89  
    90    FileDescriptorWalker walker(descriptor, descriptor_proto.source_code_info(),
    91                                v_name, content, &builder, this);
    92    walker.PopulateCodeGraph();
    93    return true;
    94  }
    95  
    96  bool ProtoAnalyzer::Parse(const std::string& proto_file,
    97                            const std::string& content) {
    98    VLOG(1) << "FILE : " << proto_file << std::endl;
    99    return AnalyzeFile(proto_file, VNameFromFullPath(proto_file), content);
   100  }
   101  
   102  VName ProtoAnalyzer::VNameFromRelPath(
   103      const std::string& simplified_path) const {
   104    const std::string* full_path = &simplified_path;
   105    if (auto iter = path_substitution_cache_->find(simplified_path);
   106        iter != path_substitution_cache_->end()) {
   107      full_path = &iter->second;
   108    }
   109    return VNameFromFullPath(*full_path);
   110  }
   111  
   112  VName ProtoAnalyzer::VNameFromFullPath(const std::string& path) const {
   113    for (const auto& input : unit_->required_input()) {
   114      if (input.info().path() == path) {
   115        return input.v_name();
   116      }
   117    }
   118    return VName{};
   119  }
   120  
   121  }  // namespace lang_proto
   122  }  // namespace kythe