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