kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/cxx/indexer/proto/proto_graph_builder.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_graph_builder.h"
    18  
    19  #include <functional>
    20  #include <utility>
    21  
    22  #include "absl/log/log.h"
    23  #include "absl/strings/str_cat.h"
    24  #include "kythe/cxx/common/indexing/KytheGraphRecorder.h"
    25  #include "kythe/cxx/common/vname_ordering.h"
    26  #include "kythe/cxx/indexer/proto/comments.h"
    27  
    28  namespace kythe {
    29  
    30  using ::kythe::proto::VName;
    31  
    32  namespace {
    33  // Pretty-prints a VName.
    34  std::string StringifyNode(const VName& v_name) {
    35    return absl::StrCat(v_name.path(), ":", v_name.signature());
    36  }
    37  
    38  // Pretty-prints a NodeKindID.
    39  std::string StringifyKind(NodeKindID kind) {
    40    return std::string(spelling_of(kind));
    41  }
    42  
    43  // Pretty-prints an EdgeKindID.
    44  std::string StringifyKind(EdgeKindID kind) {
    45    return std::string(spelling_of(kind));
    46  }
    47  }  // anonymous namespace
    48  
    49  ProtoGraphBuilder::ProtoGraphBuilder(
    50      KytheGraphRecorder* recorder,
    51      std::function<proto::VName(const std::string&)> vname_for_rel_path)
    52      : recorder_(recorder), vname_for_rel_path_(std::move(vname_for_rel_path)) {
    53    builtin_rpc_type_constructor_.set_language(kLanguageName);
    54    builtin_rpc_type_constructor_.set_signature("rpc#builtin");
    55  }
    56  
    57  void ProtoGraphBuilder::SetText(const VName& node_name,
    58                                  const std::string& content) {
    59    VLOG(1) << "Setting text (length = " << content.length()
    60            << ") for: " << StringifyNode(node_name);
    61    recorder_->AddProperty(VNameRef(node_name), kythe::PropertyID::kText,
    62                           content);
    63    current_file_contents_ = content;
    64  }
    65  
    66  void ProtoGraphBuilder::MaybeAddEdgeFromMetadata(const Location& location,
    67                                                   const VName& target) {
    68    if (meta_ == nullptr) {
    69      return;
    70    }
    71    auto rules = meta_->rules().equal_range(location.begin);
    72    for (auto rule = rules.first; rule != rules.second; ++rule) {
    73      if (location.end == rule->second.end) {
    74        EdgeKindID edge_kind;
    75        if (of_spelling(rule->second.edge_out, &edge_kind)) {
    76          VName source(rule->second.vname);
    77          source.set_corpus(target.corpus());
    78          if (rule->second.reverse_edge) {
    79            AddEdge(source, target, edge_kind);
    80          } else {
    81            AddEdge(target, source, edge_kind);
    82          }
    83        }
    84      }
    85    }
    86  }
    87  
    88  void ProtoGraphBuilder::MaybeAddMetadataFileRules(const VName& file) {
    89    if (meta_ == nullptr) {
    90      return;
    91    }
    92    for (const auto& rule : meta_->file_scope_rules()) {
    93      EdgeKindID edge_kind;
    94      if (of_spelling(rule.edge_out, &edge_kind)) {
    95        VName source(rule.vname);
    96        source.set_corpus(file.corpus());
    97        if (rule.reverse_edge) {
    98          AddEdge(source, file, edge_kind);
    99        } else {
   100          AddEdge(file, source, edge_kind);
   101        }
   102      }
   103    }
   104  }
   105  
   106  void ProtoGraphBuilder::AddNode(const VName& node_name, NodeKindID node_kind) {
   107    VLOG(1) << "Writing node: " << StringifyNode(node_name) << "["
   108            << StringifyKind(node_kind) << "]";
   109    recorder_->AddProperty(VNameRef(node_name), node_kind);
   110  }
   111  
   112  void ProtoGraphBuilder::AddEdge(const VName& start, const VName& end,
   113                                  EdgeKindID start_to_end_kind) {
   114    VLOG(1) << "Writing edge: " << StringifyNode(start) << " >-->--["
   115            << StringifyKind(start_to_end_kind) << "]-->--> "
   116            << StringifyNode(end);
   117    recorder_->AddEdge(VNameRef(start), start_to_end_kind, VNameRef(end));
   118  }
   119  
   120  void ProtoGraphBuilder::AddEdge(const VName& start, const VName& end,
   121                                  EdgeKindID start_to_end_kind, int ordinal) {
   122    VLOG(1) << "Writing edge: " << StringifyNode(start) << " >-->--["
   123            << StringifyKind(start_to_end_kind) << "]-->--> "
   124            << StringifyNode(end) << " : " << ordinal;
   125    recorder_->AddEdge(VNameRef(start), start_to_end_kind, VNameRef(end),
   126                       ordinal);
   127  }
   128  
   129  VName ProtoGraphBuilder::CreateAndAddAnchorNode(const Location& location) {
   130    VName anchor = location.file;
   131    anchor.set_language(kLanguageName);
   132  
   133    auto* const signature = anchor.mutable_signature();
   134    absl::StrAppend(signature, "@", location.begin, ":", location.end);
   135  
   136    AddNode(anchor, NodeKindID::kAnchor);
   137    recorder_->AddProperty(VNameRef(anchor), PropertyID::kLocationStartOffset,
   138                           location.begin);
   139    recorder_->AddProperty(VNameRef(anchor), PropertyID::kLocationEndOffset,
   140                           location.end);
   141  
   142    return anchor;
   143  }
   144  
   145  VName ProtoGraphBuilder::CreateAndAddDocNode(const Location& location,
   146                                               const VName& element) {
   147    VName doc = location.file;
   148    doc.set_language(kLanguageName);
   149    doc.set_signature(
   150        absl::StrCat("doc-", location.begin, "-", element.signature()));
   151  
   152    // Adjust the text to splice out comment markers, as per
   153    // http://www.kythe.io/docs/schema/#doc
   154    AddNode(doc, NodeKindID::kDoc);
   155    std::string comment = StripCommentMarkers(current_file_contents_.substr(
   156        location.begin, location.end - location.begin));
   157    recorder_->AddProperty(VNameRef(doc), PropertyID::kText, comment);
   158    return doc;
   159  }
   160  
   161  void ProtoGraphBuilder::AddImport(const std::string& import,
   162                                    const Location& location) {
   163    VName dep = vname_for_rel_path_(import);
   164    LOG(INFO) << "DEPENDENCY : " << URI(dep).ToString();
   165    VName anchor = CreateAndAddAnchorNode(location);
   166    AddEdge(anchor, dep, EdgeKindID::kRefIncludes);
   167  }
   168  
   169  void ProtoGraphBuilder::AddNamespace(const VName& package,
   170                                       const Location& location) {
   171    VName anchor = CreateAndAddAnchorNode(location);
   172    AddNode(package, NodeKindID::kPackage);
   173    AddEdge(anchor, package, EdgeKindID::kRef);
   174  }
   175  
   176  void ProtoGraphBuilder::AddValueToEnum(const VName& enum_type,
   177                                         const VName& value,
   178                                         const Location& location) {
   179    VName anchor = CreateAndAddAnchorNode(location);
   180    AddNode(value, NodeKindID::kVariable);
   181    AddEdge(anchor, value, EdgeKindID::kDefinesBinding);
   182    MaybeAddEdgeFromMetadata(location, value);
   183    AddEdge(value, enum_type, EdgeKindID::kChildOf);
   184  }
   185  
   186  void ProtoGraphBuilder::AddFieldToMessage(const VName* parent,
   187                                            const VName& message,
   188                                            const VName* oneof,
   189                                            const VName& field,
   190                                            const Location& location) {
   191    VName anchor = CreateAndAddAnchorNode(location);
   192    AddNode(field, NodeKindID::kVariable);
   193    recorder_->AddProperty(VNameRef(field), PropertyID::kSubkind, "field");
   194    AddEdge(anchor, field, EdgeKindID::kDefinesBinding);
   195    MaybeAddEdgeFromMetadata(location, field);
   196    if (parent != nullptr) {
   197      AddEdge(field, *parent, EdgeKindID::kChildOf);
   198    }
   199    if (parent == nullptr || !VNameEquals(message, *parent)) {
   200      // Extension; add an edge to the message being extended.
   201      AddEdge(field, message, EdgeKindID::kExtends);
   202    }
   203    if (oneof != nullptr) {
   204      AddEdge(field, *oneof, EdgeKindID::kChildOf);
   205    }
   206  }
   207  
   208  void ProtoGraphBuilder::AddOneofToMessage(const VName& message,
   209                                            const VName& oneof,
   210                                            const Location& location) {
   211    VName anchor = CreateAndAddAnchorNode(location);
   212    AddNode(oneof, NodeKindID::kSum);
   213    AddEdge(anchor, oneof, EdgeKindID::kDefinesBinding);
   214    MaybeAddEdgeFromMetadata(location, oneof);
   215    AddEdge(oneof, message, EdgeKindID::kChildOf);
   216  }
   217  
   218  void ProtoGraphBuilder::AddMethodToService(const VName& service,
   219                                             const VName& method,
   220                                             const Location& location) {
   221    VName anchor = CreateAndAddAnchorNode(location);
   222    AddNode(method, NodeKindID::kFunction);
   223    AddEdge(anchor, method, EdgeKindID::kDefinesBinding);
   224    MaybeAddEdgeFromMetadata(location, method);
   225    AddEdge(method, service, EdgeKindID::kChildOf);
   226  }
   227  
   228  void ProtoGraphBuilder::AddMethodType(const VName& method, const VName& input,
   229                                        const VName& output) {
   230    if (!builtin_rpc_type_emitted_) {
   231      AddNode(builtin_rpc_type_constructor_, NodeKindID::kTBuiltin);
   232      builtin_rpc_type_emitted_ = true;
   233    }
   234  
   235    VName method_type = method;
   236    method_type.set_signature(absl::StrCat("type::", method.signature(),
   237                                           "::", output.signature(),
   238                                           "::", input.signature()));
   239    AddNode(method_type, NodeKindID::kTApp);
   240    AddEdge(method_type, builtin_rpc_type_constructor_, EdgeKindID::kParam, 0);
   241    AddEdge(method_type, output, EdgeKindID::kParam, 1);
   242    AddEdge(method_type, input, EdgeKindID::kParam, 2);
   243  
   244    AddEdge(method, method_type, EdgeKindID::kHasType);
   245  }
   246  
   247  void ProtoGraphBuilder::AddEnumType(const VName* parent, const VName& enum_type,
   248                                      const Location& location) {
   249    VName anchor = CreateAndAddAnchorNode(location);
   250    AddNode(enum_type, NodeKindID::kSum);
   251    AddEdge(anchor, enum_type, EdgeKindID::kDefinesBinding);
   252    MaybeAddEdgeFromMetadata(location, enum_type);
   253    if (parent != nullptr) {
   254      AddEdge(enum_type, *parent, EdgeKindID::kChildOf);
   255    }
   256  }
   257  
   258  void ProtoGraphBuilder::AddMessageType(const VName* parent,
   259                                         const VName& message,
   260                                         const Location& location) {
   261    VName anchor = CreateAndAddAnchorNode(location);
   262    AddNode(message, NodeKindID::kRecord);
   263    AddEdge(anchor, message, EdgeKindID::kDefinesBinding);
   264    MaybeAddEdgeFromMetadata(location, message);
   265    if (parent != nullptr) {
   266      AddEdge(message, *parent, EdgeKindID::kChildOf);
   267    }
   268  }
   269  
   270  void ProtoGraphBuilder::AddReference(const VName& referent,
   271                                       const Location& location) {
   272    VName anchor = CreateAndAddAnchorNode(location);
   273    AddEdge(anchor, referent, EdgeKindID::kRef);
   274  }
   275  
   276  void ProtoGraphBuilder::AddTyping(const VName& term, const VName& type) {
   277    AddEdge(term, type, EdgeKindID::kHasType);
   278  }
   279  
   280  void ProtoGraphBuilder::AddService(const VName* parent, const VName& service,
   281                                     const Location& location) {
   282    VName anchor = CreateAndAddAnchorNode(location);
   283    AddNode(service, NodeKindID::kInterface);
   284    AddEdge(anchor, service, EdgeKindID::kDefinesBinding);
   285    MaybeAddEdgeFromMetadata(location, service);
   286    if (parent != nullptr) {
   287      AddEdge(service, *parent, EdgeKindID::kChildOf);
   288    }
   289  }
   290  
   291  void ProtoGraphBuilder::AddDocComment(const VName& element,
   292                                        const Location& location) {
   293    VName doc = CreateAndAddDocNode(location, element);
   294    AddEdge(doc, element, EdgeKindID::kDocuments);
   295  }
   296  
   297  void ProtoGraphBuilder::AddCodeFact(const VName& element,
   298                                      const MarkedSource& code) {
   299    recorder_->AddMarkedSource(VNameRef(element), code);
   300  }
   301  
   302  void ProtoGraphBuilder::SetDeprecated(const VName& node_name) {
   303    VLOG(1) << "Adding deprecation tag for " << StringifyNode(node_name);
   304    recorder_->AddProperty(VNameRef(node_name), kythe::PropertyID::kTagDeprecated,
   305                           "");
   306  }
   307  
   308  }  // namespace kythe