| // Copyright 2017-2020 The Verible Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "verible/verilog/tools/kythe/kythe-facts-extractor.h" |
| |
| #include <cstddef> |
| #include <cstdint> |
| #include <iostream> |
| #include <optional> |
| #include <string> |
| #include <string_view> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/container/flat_hash_set.h" |
| #include "absl/container/node_hash_set.h" |
| #include "absl/hash/hash.h" |
| #include "absl/log/die_if_null.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/time/clock.h" |
| #include "absl/time/time.h" |
| #include "verible/common/util/auto-pop-stack.h" |
| #include "verible/common/util/logging.h" |
| #include "verible/common/util/tree-operations.h" |
| #include "verible/verilog/analysis/verilog-project.h" |
| #include "verible/verilog/tools/kythe/indexing-facts-tree.h" |
| #include "verible/verilog/tools/kythe/kythe-facts.h" |
| #include "verible/verilog/tools/kythe/kythe-schema-constants.h" |
| #include "verible/verilog/tools/kythe/scope-resolver.h" |
| #include "verible/verilog/tools/kythe/verilog-extractor-indexing-fact-type.h" |
| |
| namespace verilog { |
| namespace kythe { |
| namespace { |
| |
| // Returns the file path of the file from the given indexing facts tree node |
| // tagged with kFile. |
| std::string_view GetFilePathFromRoot(const IndexingFactNode &root) { |
| CHECK_EQ(root.Value().GetIndexingFactType(), IndexingFactType::kFile); |
| return root.Value().Anchors()[0].Text(); |
| } |
| |
| } // namespace |
| |
| // KytheFactsExtractor processes indexing facts for a single file. |
| // Responsible for traversing IndexingFactsTree and processing its different |
| // nodes to produce kythe indexing facts. |
| // Iteratively extracts facts and keeps running until no new facts are found in |
| // the last iteration. |
| class KytheFactsExtractor { |
| public: |
| KytheFactsExtractor(std::string_view file_path, std::string_view corpus, |
| KytheOutput *facts_output, |
| ScopeResolver *previous_files_scopes) |
| : file_path_(file_path), |
| corpus_(corpus), |
| facts_output_(facts_output), |
| scope_resolver_(previous_files_scopes) {} |
| |
| private: |
| // Container with a stack of VNames to hold context of VNames during traversal |
| // of an IndexingFactsTree. |
| // This is used to generate to VNames inside the current scope. |
| // e.g. |
| // module foo(); |
| // wire x; ==> "foo#x" |
| // endmodule: foo |
| // |
| // module bar(); |
| // wire x; ==> "bar#x" |
| // endmodule: bar |
| class VNameContext : public verible::AutoPopStack<const VName *> { |
| public: |
| using base_type = verible::AutoPopStack<const VName *>; |
| |
| // member class to handle push and pop of stack safely |
| using AutoPop = base_type::AutoPop; |
| |
| // returns the top VName of the stack |
| // NOLINTNEXTLINE(bugprone-derived-method-shadowing-base-method) |
| const VName &top() const { return *ABSL_DIE_IF_NULL(base_type::top()); } |
| }; |
| |
| // Returns the full path of the current source file. |
| std::string_view FilePath() { return file_path_; } |
| |
| // Returns the corpus to which this file belongs. |
| std::string_view Corpus() const { return corpus_; } |
| |
| public: |
| // Extracts kythe facts from the given IndexingFactsTree root. The result is |
| // written to Kythe output. |
| void ExtractFile(const IndexingFactNode &); |
| |
| private: |
| // Resolves the tag of the given node and directs the flow to the appropriate |
| // function to extract kythe facts for that node. Returns true if any Kythe |
| // fact was created. |
| bool IndexingFactNodeTagResolver(const IndexingFactNode &); |
| |
| // Determines whether to create a scope for this node or not and visits the |
| // children. |
| void VisitAutoConstructScope(const IndexingFactNode &node, |
| const VName &vname); |
| |
| // Add the given VName to vnames_context (to be used in scope relative |
| // signatures) and visits the children of the given node creating a new scope |
| // for the given node. |
| void VisitUsingVName(const IndexingFactNode &node, const VName &); |
| |
| // Directs the flow to the children of the given node. |
| void Visit(const IndexingFactNode &node); |
| |
| // Determines whether or not to create a child of edge between the current |
| // node and the previous node. |
| void CreateChildOfEdge(IndexingFactType, const VName &); |
| |
| // Returns the scope of the parent's type. E.g. in case `my_class |
| // my_instance`, `my_instance` gets the definition scope from `my_class` so |
| // that `my_instance.method()` can be resolved as `method` exists in the |
| // `my_class`s scope. |
| std::optional<SignatureDigest> GetParentTypeScope( |
| const IndexingFactNode &node) const; |
| |
| //================================================================= |
| // Declare* methods create facts (some edges) and may introduce new scopes. |
| // Reference* methods only create edges, and may not modify scopes' contents. |
| |
| // Extracts kythe facts from file node and returns it VName. |
| VName DeclareFile(const IndexingFactNode &); |
| |
| // Extracts kythe facts for a reference to some user defined data type like |
| // class or module. |
| void ReferenceDataType(const IndexingFactNode &); |
| |
| // Extracts kythe facts for a constant like member in enums. |
| VName DeclareConstant(const IndexingFactNode &); |
| |
| // Extracts kythe facts for structs or unions. |
| VName DeclareStructOrUnion(const IndexingFactNode &); |
| |
| // Extracts kythe facts for a type declaration. |
| VName DeclareTypedef(const IndexingFactNode &); |
| |
| // Extracts kythe facts from interface node and returns it VName. |
| VName DeclareInterface(const IndexingFactNode &interface_fact_node); |
| |
| // Extracts kythe facts from program node and returns it VName. |
| VName DeclareProgram(const IndexingFactNode &program_fact_node); |
| |
| // Extracts kythe facts from module named port node e.g("m(.in1(a))"). |
| void ReferenceModuleNamedPort(const IndexingFactNode &); |
| |
| // Extracts kythe facts from named param |
| // e.g module_type #(.N(x)) extracts "N"; |
| void ReferenceNamedParam(const IndexingFactNode &); |
| |
| // Extracts kythe facts from module node and returns it VName. |
| VName DeclareModule(const IndexingFactNode &); |
| |
| // Extracts kythe facts from class node and returns it VName. |
| VName DeclareClass(const IndexingFactNode &); |
| |
| // Extracts kythe facts from class extends node. |
| void ReferenceExtendsInheritance(const IndexingFactNode &); |
| |
| // Extracts kythe facts from module instance, class instance, variable |
| // definition and param declaration nodes and returns its VName. |
| VName DeclareVariable(const IndexingFactNode &node); |
| |
| // Extracts kythe facts from a module port reference node. |
| void ReferenceVariable(const IndexingFactNode &node); |
| |
| // Creates a new anonymous scope for if conditions and loops. |
| VName DeclareAnonymousScope(const IndexingFactNode &temp_scope); |
| |
| // Extracts kythe facts from a function or task node and returns its VName. |
| VName DeclareFunctionOrTask(const IndexingFactNode &function_fact_node); |
| |
| // Extracts kythe facts from a function or task call node. |
| void ReferenceFunctionOrTaskCall( |
| const IndexingFactNode &function_call_fact_node); |
| |
| // Extracts kythe facts from a package declaration node and returns its VName. |
| VName DeclarePackage(const IndexingFactNode &node); |
| |
| // Extracts kythe facts from package import node. |
| void ReferencePackageImport(const IndexingFactNode &node); |
| |
| // Extracts kythe facts from a macro definition node and returns its VName. |
| VName DeclareMacroDefinition(const IndexingFactNode ¯o_definition_node); |
| |
| // Extracts kythe facts from a macro call node. |
| void ReferenceMacroCall(const IndexingFactNode ¯o_call_node); |
| |
| // Extracts kythe facts from a "`include" node. |
| void ReferenceIncludeFile(const IndexingFactNode &include_node); |
| |
| // Extracts kythe facts from member reference statement. |
| // e.g pkg::member or class::member or class.member |
| // The names are treated as anchors e.g: |
| // pkg::member => {Anchor(pkg), Anchor(member)} |
| // pkg::class_name::var => {Anchor(pkg), Anchor(class_name), Anchor(var)} |
| void ReferenceMember(const IndexingFactNode &member_reference_node); |
| |
| //============ end of Declare*, Reference* methods =================== |
| |
| // Create "ref" edges that point from the given anchor to the given |
| // definition. |
| void CreateAnchorReference(const Anchor &anchor, const VName &definition); |
| |
| // Generates an anchor VName for kythe. |
| VName CreateAnchor(const Anchor &); |
| |
| // Appends the signatures of previous containing scope vname to make |
| // signatures unique relative to scopes. |
| Signature CreateScopeRelativeSignature(std::string_view) const; |
| |
| // Generates fact strings for Kythe facts. |
| // Schema for this fact can be found here: |
| // https://kythe.io/docs/schema/writing-an-indexer.html |
| void CreateFact(const VName &vname, std::string_view name, |
| std::string_view value); |
| |
| // Generates edge strings for Kythe edges. |
| // Schema for this edge can be found here: |
| // https://kythe.io/docs/schema/writing-an-indexer.html |
| void CreateEdge(const VName &source, std::string_view name, |
| const VName &target); |
| |
| // Holds the hashes of the output Kythe facts and edges (for deduplication). |
| absl::flat_hash_set<int64_t> seen_kythe_hashes_; |
| |
| // The full path of the current source file. |
| std::string_view file_path_; |
| |
| // The corpus to which this file belongs. |
| std::string_view corpus_; |
| |
| // Output for produced Kythe facts. Not owned. |
| KytheOutput *const facts_output_; |
| |
| // Keeps track of VNames of ancestors as the visitor traverses the facts |
| // tree. |
| VNameContext vnames_context_; |
| |
| // Keeps track and saves the explored scopes. Used to resolve symbols to their |
| // definition. |
| ScopeResolver *const scope_resolver_; |
| |
| // Location signature backing store. |
| // Inside signatures, we record locations as string_views, |
| // this is the backing store for assembled strings as |
| // location markers such as "@123:456" |
| // Needs to be a node_hash_set to provide value stability. |
| absl::node_hash_set<std::string> signature_locations_; |
| }; |
| |
| void StreamKytheFactsEntries(KytheOutput *kythe_output, |
| const IndexingFactNode &file_list, |
| const VerilogProject &project) { |
| VLOG(1) << __FUNCTION__; |
| // TODO(fangism): re-implement root-level symbol lookup with a proper |
| // project-wide symbol table, for efficient lookup. |
| |
| // TODO(fangism): infer dependency ordering automatically based on |
| // the symbols defined in each file. |
| |
| // Process each file in the original listed order. |
| ScopeResolver scope_resolver(Signature("")); |
| for (const IndexingFactNode &root : file_list.Children()) { |
| scope_resolver.SetCurrentScope(Signature("")); |
| const absl::Time extraction_start = absl::Now(); |
| // 'root' corresponds to the fact tree for a particular file. |
| // 'file_path' is path-resolved. |
| const std::string_view file_path(GetFilePathFromRoot(root)); |
| VLOG(1) << "child file resolved path: " << file_path; |
| |
| // Create facts and edges. |
| KytheFactsExtractor kythe_extractor(file_path, project.Corpus(), |
| kythe_output, &scope_resolver); |
| |
| // Output facts and edges. |
| kythe_extractor.ExtractFile(root); |
| LOG(INFO) << "Extracted Kythe facts of " << file_path << " in " |
| << (absl::Now() - extraction_start); |
| } |
| |
| VLOG(1) << "end of " << __FUNCTION__; |
| } |
| |
| void KytheFactsExtractor::ExtractFile(const IndexingFactNode &root) { |
| // root corresponds to the indexing tree for a single file. |
| |
| // Fixed-point analysis: Repeat fact extraction until no new facts are found. |
| // This approach handles cases where symbols can be defined later in the file |
| // than their uses, e.g. class member declarations and references. |
| while (IndexingFactNodeTagResolver(root)) { |
| } |
| } |
| |
| std::optional<SignatureDigest> KytheFactsExtractor::GetParentTypeScope( |
| const IndexingFactNode &node) const { |
| std::string_view node_name = node.Value().Anchors()[0].Text(); |
| if (node.Parent() == nullptr) { |
| return std::nullopt; |
| } |
| const auto &parent_anchors = node.Parent()->Value().Anchors(); |
| if (parent_anchors.empty()) { |
| VLOG(2) << "GetParentTypeScope for " << node_name << " FAILED -- no parent"; |
| return std::nullopt; |
| } |
| |
| SignatureDigest focused_scope = scope_resolver_->CurrentScopeDigest(); |
| std::optional<ScopedVname> parent_type = std::nullopt; |
| for (const auto &parent_anchor : parent_anchors) { |
| parent_type = scope_resolver_->FindScopeAndDefinition(parent_anchor.Text(), |
| focused_scope); |
| if (!parent_type) { |
| VLOG(2) << "GetParentTypeScope for " << node_name |
| << " FAILED -- no parent type at " << parent_anchor.Text() |
| << " within scope " << scope_resolver_->ScopeDebug(focused_scope); |
| return std::nullopt; |
| } |
| focused_scope = parent_type->type_scope; |
| } |
| |
| if (!parent_type.has_value()) { |
| return std::nullopt; |
| } |
| VLOG(2) << "GetParentTypeScope for " << node_name << " succeeded. Parent: " |
| << scope_resolver_->ScopeDebug(parent_type->type_scope); |
| return parent_type->type_scope; |
| } |
| |
| bool KytheFactsExtractor::IndexingFactNodeTagResolver( |
| const IndexingFactNode &node) { |
| const size_t previously_extracted_facts_num = seen_kythe_hashes_.size(); |
| const auto tag = node.Value().GetIndexingFactType(); |
| |
| // Dispatch a node handler based on the node's tag. |
| // This VName is used to keep track of the new generated VName and it will be |
| // used in scopes, finding variable definitions and creating childof |
| // relations. |
| VName vname; |
| SignatureDigest current_scope = scope_resolver_->CurrentScopeDigest(); |
| switch (tag) { |
| case IndexingFactType::kFile: { |
| vname = DeclareFile(node); |
| scope_resolver_->AddDefinitionToCurrentScope(vname); |
| break; |
| } |
| case IndexingFactType::kModule: { |
| vname = DeclareModule(node); |
| scope_resolver_->AddDefinitionToCurrentScope(vname); |
| break; |
| } |
| case IndexingFactType::kInterface: { |
| vname = DeclareInterface(node); |
| scope_resolver_->AddDefinitionToCurrentScope(vname); |
| break; |
| } |
| case IndexingFactType::kProgram: { |
| vname = DeclareProgram(node); |
| scope_resolver_->AddDefinitionToCurrentScope(vname); |
| break; |
| } |
| case IndexingFactType::kClassInstance: |
| case IndexingFactType::kModuleInstance: |
| case IndexingFactType::kParamDeclaration: |
| case IndexingFactType::kVariableDefinition: { |
| vname = DeclareVariable(node); |
| const auto parent_type = GetParentTypeScope(node); |
| |
| if (parent_type) { |
| scope_resolver_->AddDefinitionToCurrentScope(vname, *parent_type); |
| } else { |
| scope_resolver_->AddDefinitionToCurrentScope(vname, |
| vname.signature.Digest()); |
| } |
| break; |
| } |
| case IndexingFactType::kConstant: { |
| vname = DeclareConstant(node); |
| scope_resolver_->AddDefinitionToCurrentScope(vname, current_scope); |
| break; |
| } |
| case IndexingFactType::kMacro: { |
| vname = DeclareMacroDefinition(node); |
| scope_resolver_->AddDefinitionToCurrentScope(vname, current_scope); |
| break; |
| } |
| case IndexingFactType::kClass: { |
| vname = DeclareClass(node); |
| scope_resolver_->AddDefinitionToCurrentScope(vname); |
| break; |
| } |
| case IndexingFactType::kFunctionOrTask: |
| case IndexingFactType::kFunctionOrTaskForwardDeclaration: |
| case IndexingFactType::kConstructor: { |
| vname = DeclareFunctionOrTask(node); |
| scope_resolver_->AddDefinitionToCurrentScope(vname); |
| break; |
| } |
| case IndexingFactType::kPackage: { |
| vname = DeclarePackage(node); |
| scope_resolver_->AddDefinitionToCurrentScope(vname); |
| break; |
| } |
| case IndexingFactType::kStructOrUnion: { |
| vname = DeclareStructOrUnion(node); |
| scope_resolver_->AddDefinitionToCurrentScope(vname); |
| break; |
| } |
| case IndexingFactType::kAnonymousScope: { |
| vname = DeclareAnonymousScope(node); |
| break; |
| } |
| case IndexingFactType::kTypeDeclaration: { |
| vname = DeclareTypedef(node); |
| scope_resolver_->AddDefinitionToCurrentScope(vname); |
| break; |
| } |
| // end of definition extraction cases. |
| |
| // The following cases extract references: |
| case IndexingFactType::kDataTypeReference: { |
| ReferenceDataType(node); |
| break; |
| } |
| case IndexingFactType::kModuleNamedPort: { |
| ReferenceModuleNamedPort(node); |
| break; |
| } |
| case IndexingFactType::kNamedParam: { |
| ReferenceNamedParam(node); |
| break; |
| } |
| case IndexingFactType::kExtends: { |
| ReferenceExtendsInheritance(node); |
| break; |
| } |
| case IndexingFactType::kVariableReference: { |
| ReferenceVariable(node); |
| break; |
| } |
| case IndexingFactType::kFunctionCall: { |
| ReferenceFunctionOrTaskCall(node); |
| break; |
| } |
| case IndexingFactType::kPackageImport: { |
| ReferencePackageImport(node); |
| break; |
| } |
| case IndexingFactType::kMacroCall: { |
| ReferenceMacroCall(node); |
| break; |
| } |
| case IndexingFactType::kMemberReference: { |
| ReferenceMember(node); |
| break; |
| } |
| case IndexingFactType::kInclude: { |
| ReferenceIncludeFile(node); |
| break; |
| } |
| // end of reference extraction cases. |
| default: { |
| break; |
| } |
| } |
| |
| CreateChildOfEdge(tag, vname); |
| VisitAutoConstructScope(node, vname); |
| |
| return seen_kythe_hashes_.size() > previously_extracted_facts_num; |
| } |
| |
| void KytheFactsExtractor::CreateChildOfEdge(IndexingFactType tag, |
| const VName &vname) { |
| // Determines whether to create a child of edge to the parent node or not. |
| switch (tag) { |
| case IndexingFactType::kFile: |
| case IndexingFactType::kPackageImport: |
| case IndexingFactType::kVariableReference: |
| case IndexingFactType::kDataTypeReference: |
| case IndexingFactType::kMacroCall: |
| case IndexingFactType::kFunctionCall: |
| case IndexingFactType::kMacro: |
| case IndexingFactType::kModuleNamedPort: |
| case IndexingFactType::kMemberReference: |
| case IndexingFactType::kInclude: |
| case IndexingFactType::kAnonymousScope: { |
| break; |
| } |
| default: { |
| if (!vnames_context_.empty()) { |
| CreateEdge(vname, kEdgeChildOf, vnames_context_.top()); |
| } |
| break; |
| } |
| } |
| } |
| |
| void KytheFactsExtractor::VisitAutoConstructScope(const IndexingFactNode &node, |
| const VName &vname) { |
| const auto tag = node.Value().GetIndexingFactType(); |
| |
| // Must be copied (as Visit() can change the current scope). |
| const Signature current_scope = scope_resolver_->CurrentScope(); |
| |
| // Determines whether to create a scope for this node or not. |
| switch (tag) { |
| case IndexingFactType::kAnonymousScope: |
| case IndexingFactType::kClass: |
| case IndexingFactType::kConstructor: |
| case IndexingFactType::kFile: |
| case IndexingFactType::kFunctionOrTask: |
| case IndexingFactType::kFunctionOrTaskForwardDeclaration: |
| case IndexingFactType::kInterface: |
| case IndexingFactType::kMacro: |
| case IndexingFactType::kModule: |
| case IndexingFactType::kPackage: |
| case IndexingFactType::kProgram: |
| case IndexingFactType::kParamDeclaration: |
| case IndexingFactType::kStructOrUnion: { |
| scope_resolver_->SetCurrentScope(vname.signature); |
| VisitUsingVName(node, vname); |
| break; |
| } |
| case IndexingFactType::kVariableDefinition: { |
| if (!node.Children().empty()) { |
| // Complex data type. Add it to the top of the signature. |
| VisitUsingVName(node, vname); |
| } else { |
| Visit(node); |
| } |
| break; |
| } |
| default: { |
| Visit(node); |
| } |
| } |
| scope_resolver_->SetCurrentScope(current_scope); |
| } |
| |
| void KytheFactsExtractor::VisitUsingVName(const IndexingFactNode &node, |
| const VName &vname) { |
| const VNameContext::AutoPop vnames_auto_pop(&vnames_context_, &vname); |
| // Must be copied (as Visit() can change the current scope). |
| const Signature current_scope = scope_resolver_->CurrentScope(); |
| Visit(node); |
| scope_resolver_->SetCurrentScope(current_scope); |
| } |
| |
| void KytheFactsExtractor::Visit(const IndexingFactNode &node) { |
| for (const IndexingFactNode &child : node.Children()) { |
| IndexingFactNodeTagResolver(child); |
| } |
| } |
| |
| VName KytheFactsExtractor::DeclareFile(const IndexingFactNode &file_fact_node) { |
| VName file_vname = {.path = FilePath(), |
| .root = "", |
| .signature = Signature(""), |
| .corpus = Corpus(), |
| .language = kEmptyKytheLanguage}; |
| const auto &anchors(file_fact_node.Value().Anchors()); |
| CHECK_GE(anchors.size(), 2); |
| const std::string_view code_text = file_fact_node.Value().Anchors()[1].Text(); |
| |
| CreateFact(file_vname, kFactNodeKind, kNodeFile); |
| CreateFact(file_vname, kFactText, code_text); |
| |
| // Update the signature of the file to be the global signature. |
| // Used in scopes and makes signatures unique. |
| file_vname.signature = Signature(FilePath()); |
| return file_vname; |
| } |
| |
| VName KytheFactsExtractor::DeclareModule( |
| const IndexingFactNode &module_fact_node) { |
| const auto &anchors = module_fact_node.Value().Anchors(); |
| const Anchor &module_name = anchors[0]; |
| VName module_vname = { |
| .path = FilePath(), |
| .root = "", |
| .signature = CreateScopeRelativeSignature(module_name.Text()), |
| .corpus = Corpus()}; |
| const VName module_name_anchor = CreateAnchor(module_name); |
| |
| CreateFact(module_vname, kFactNodeKind, kNodeRecord); |
| CreateFact(module_vname, kFactSubkind, kSubkindModule); |
| CreateFact(module_vname, kFactComplete, kCompleteDefinition); |
| CreateEdge(module_name_anchor, kEdgeDefinesBinding, module_vname); |
| |
| if (anchors.size() > 1) { |
| const Anchor &module_end_label = anchors[1]; |
| const VName module_end_label_anchor = CreateAnchor(module_end_label); |
| CreateEdge(module_end_label_anchor, kEdgeRef, module_vname); |
| } |
| |
| return module_vname; |
| } |
| |
| VName KytheFactsExtractor::DeclareProgram( |
| const IndexingFactNode &program_fact_node) { |
| const auto &anchors = program_fact_node.Value().Anchors(); |
| const Anchor &program_name = anchors[0]; |
| |
| VName program_vname = { |
| .path = FilePath(), |
| .root = "", |
| .signature = CreateScopeRelativeSignature(program_name.Text()), |
| .corpus = Corpus()}; |
| const VName program_name_anchor = CreateAnchor(program_name); |
| |
| CreateFact(program_vname, kFactNodeKind, kNodeRecord); |
| CreateFact(program_vname, kFactSubkind, kSubkindProgram); |
| CreateEdge(program_name_anchor, kEdgeDefinesBinding, program_vname); |
| |
| if (anchors.size() > 1) { |
| const Anchor &program_end_label = anchors[1]; |
| const VName program_end_label_anchor = CreateAnchor(program_end_label); |
| CreateEdge(program_end_label_anchor, kEdgeRef, program_vname); |
| } |
| |
| return program_vname; |
| } |
| |
| VName KytheFactsExtractor::DeclareInterface( |
| const IndexingFactNode &interface_fact_node) { |
| const auto &anchors = interface_fact_node.Value().Anchors(); |
| const Anchor &interface_name = anchors[0]; |
| |
| VName interface_vname = { |
| .path = FilePath(), |
| .root = "", |
| .signature = CreateScopeRelativeSignature(interface_name.Text()), |
| .corpus = Corpus()}; |
| const VName interface_name_anchor = CreateAnchor(interface_name); |
| |
| CreateFact(interface_vname, kFactNodeKind, kNodeInterface); |
| CreateEdge(interface_name_anchor, kEdgeDefinesBinding, interface_vname); |
| |
| if (anchors.size() > 1) { |
| const Anchor &interface_end_label = anchors[1]; |
| const VName interface_end_label_anchor = CreateAnchor(interface_end_label); |
| CreateEdge(interface_end_label_anchor, kEdgeRef, interface_vname); |
| } |
| |
| return interface_vname; |
| } |
| |
| void KytheFactsExtractor::ReferenceDataType( |
| const IndexingFactNode &data_type_reference) { |
| const auto &anchors = data_type_reference.Value().Anchors(); |
| |
| SignatureDigest focused_scope = scope_resolver_->CurrentScopeDigest(); |
| for (const auto &anchor : anchors) { |
| const std::optional<ScopedVname> type = |
| scope_resolver_->FindScopeAndDefinition(anchor.Text(), focused_scope); |
| if (!type) { |
| return; |
| } |
| CreateAnchorReference(anchor, type->vname); |
| focused_scope = type->type_scope; |
| } |
| } |
| |
| VName KytheFactsExtractor::DeclareTypedef( |
| const IndexingFactNode &type_declaration) { |
| const auto &anchor = type_declaration.Value().Anchors()[0]; |
| VName type_vname = {.path = FilePath(), |
| .root = "", |
| .signature = CreateScopeRelativeSignature(anchor.Text()), |
| .corpus = Corpus()}; |
| const VName type_vname_anchor = CreateAnchor(anchor); |
| |
| CreateFact(type_vname, kFactNodeKind, kNodeTAlias); |
| CreateEdge(type_vname_anchor, kEdgeDefinesBinding, type_vname); |
| |
| return type_vname; |
| } |
| |
| void KytheFactsExtractor::ReferenceNamedParam( |
| const IndexingFactNode &named_param_node) { |
| // Get the anchors. |
| const auto ¶m_name = named_param_node.Value().Anchors()[0]; |
| std::optional<ScopedVname> param_name_type = std::nullopt; |
| const auto parent_type = GetParentTypeScope(named_param_node); |
| if (parent_type) { |
| param_name_type = scope_resolver_->FindScopeAndDefinition(param_name.Text(), |
| *parent_type); |
| } else { |
| param_name_type = |
| scope_resolver_->FindScopeAndDefinition(param_name.Text()); |
| } |
| |
| if (!param_name_type) { |
| // No definition. Skip. |
| return; |
| } |
| |
| // Create the facts for this parameter reference. |
| const VName param_vname_anchor = CreateAnchor(param_name); |
| CreateEdge(param_vname_anchor, kEdgeRef, param_name_type->vname); |
| } |
| |
| void KytheFactsExtractor::ReferenceModuleNamedPort( |
| const IndexingFactNode &named_port_node) { |
| const auto &port_name = named_port_node.Value().Anchors()[0]; |
| |
| std::optional<ScopedVname> port_name_type; |
| const auto parent_type = GetParentTypeScope(named_port_node); |
| if (parent_type) { |
| port_name_type = |
| scope_resolver_->FindScopeAndDefinition(port_name.Text(), *parent_type); |
| } |
| if (!port_name_type) { |
| VLOG(2) << "Failed to find the port type"; |
| // No definition. Skip. |
| return; |
| } |
| |
| const VName port_vname_anchor = CreateAnchor(port_name); |
| CreateEdge(port_vname_anchor, kEdgeRef, port_name_type->vname); |
| |
| // The case where '.z(z)' is shortened to '.z' |
| if (is_leaf(named_port_node)) { |
| // Search in the current scope, not the type's scope. |
| const auto port_name_ref = |
| scope_resolver_->FindScopeAndDefinition(port_name.Text()); |
| if (port_name_ref) { |
| CreateEdge(port_vname_anchor, kEdgeRef, port_name_ref->vname); |
| } |
| } |
| } |
| |
| VName KytheFactsExtractor::DeclareVariable( |
| const IndexingFactNode &variable_definition_node) { |
| const auto &anchors = variable_definition_node.Value().Anchors(); |
| CHECK(!anchors.empty()); |
| const Anchor &anchor = anchors[0]; |
| VName variable_vname = { |
| .path = FilePath(), |
| .root = "", |
| .signature = CreateScopeRelativeSignature(anchor.Text()), |
| .corpus = Corpus()}; |
| const VName variable_vname_anchor = CreateAnchor(anchor); |
| |
| CreateFact(variable_vname, kFactNodeKind, kNodeVariable); |
| CreateFact(variable_vname, kFactComplete, kCompleteDefinition); |
| CreateEdge(variable_vname_anchor, kEdgeDefinesBinding, variable_vname); |
| |
| return variable_vname; |
| } |
| |
| void KytheFactsExtractor::ReferenceVariable( |
| const IndexingFactNode &variable_reference_node) { |
| const auto &anchors = variable_reference_node.Value().Anchors(); |
| |
| SignatureDigest focused_scope = scope_resolver_->CurrentScopeDigest(); |
| for (const auto &anchor : anchors) { |
| const auto type = |
| scope_resolver_->FindScopeAndDefinition(anchor.Text(), focused_scope); |
| if (!type) { |
| return; |
| } |
| CreateAnchorReference(anchor, type->vname); |
| focused_scope = type->type_scope; |
| } |
| } |
| |
| VName KytheFactsExtractor::DeclarePackage( |
| const IndexingFactNode &package_declaration_node) { |
| const auto &anchors = package_declaration_node.Value().Anchors(); |
| const Anchor &package_name = anchors[0]; |
| |
| VName package_vname = { |
| .path = FilePath(), |
| .root = "", |
| .signature = CreateScopeRelativeSignature(package_name.Text()), |
| .corpus = Corpus()}; |
| const VName package_name_anchor = CreateAnchor(package_name); |
| |
| CreateFact(package_vname, kFactNodeKind, kNodePackage); |
| CreateEdge(package_name_anchor, kEdgeDefinesBinding, package_vname); |
| |
| if (anchors.size() > 1) { |
| const Anchor &package_end_label = anchors[1]; |
| const VName package_end_label_anchor = CreateAnchor(package_end_label); |
| CreateEdge(package_end_label_anchor, kEdgeRef, package_vname); |
| } |
| |
| return package_vname; |
| } |
| |
| VName KytheFactsExtractor::DeclareMacroDefinition( |
| const IndexingFactNode ¯o_definition_node) { |
| const Anchor ¯o_name = macro_definition_node.Value().Anchors()[0]; |
| |
| // The signature is relative to the global scope so no relative signature |
| // created here. |
| VName macro_vname = {.path = FilePath(), |
| .root = "", |
| .signature = Signature(macro_name.Text()), |
| .corpus = Corpus()}; |
| const VName module_name_anchor = CreateAnchor(macro_name); |
| |
| CreateFact(macro_vname, kFactNodeKind, kNodeMacro); |
| CreateEdge(module_name_anchor, kEdgeDefinesBinding, macro_vname); |
| |
| return macro_vname; |
| } |
| |
| void KytheFactsExtractor::ReferenceMacroCall( |
| const IndexingFactNode ¯o_call_node) { |
| const Anchor ¯o_name = macro_call_node.Value().Anchors()[0]; |
| const VName macro_vname_anchor = CreateAnchor(macro_name); |
| |
| // The signature is relative to the global scope so no relative signature |
| // created here. |
| const VName variable_definition_vname = { |
| .path = FilePath(), |
| .root = "", |
| .signature = Signature(macro_name.Text()), |
| .corpus = Corpus()}; |
| |
| CreateEdge(macro_vname_anchor, kEdgeRefExpands, variable_definition_vname); |
| } |
| |
| VName KytheFactsExtractor::DeclareFunctionOrTask( |
| const IndexingFactNode &function_fact_node) { |
| // TODO(hzeller): null check added. Underlying issue |
| // needs more investigation; was encountered at |
| // https://chipsalliance.github.io/sv-tests-results/?v=veribleextractor+ivtest+regress-vlg_pr1628300_iv |
| if (function_fact_node.Value().Anchors().empty()) { |
| LOG(ERROR) << FilePath() << ": encountered empty function name"; |
| return VName(); |
| } |
| |
| const auto &function_name = function_fact_node.Value().Anchors()[0]; |
| |
| VName function_vname = { |
| .path = FilePath(), |
| .root = "", |
| .signature = CreateScopeRelativeSignature(function_name.Text()), |
| .corpus = Corpus()}; |
| |
| const VName function_vname_anchor = CreateAnchor(function_name); |
| |
| CreateFact(function_vname, kFactNodeKind, kNodeFunction); |
| CreateEdge(function_vname_anchor, kEdgeDefinesBinding, function_vname); |
| |
| auto tag = function_fact_node.Value().GetIndexingFactType(); |
| switch (tag) { |
| case IndexingFactType::kFunctionOrTask: { |
| CreateFact(function_vname, kFactComplete, kCompleteDefinition); |
| break; |
| } |
| case IndexingFactType::kFunctionOrTaskForwardDeclaration: { |
| CreateFact(function_vname, kFactComplete, kInComplete); |
| break; |
| } |
| case IndexingFactType::kConstructor: { |
| CreateFact(function_vname, kFactSubkind, kSubkindConstructor); |
| break; |
| } |
| default: { |
| break; |
| } |
| } |
| |
| // Check if there is a function with the same name in the current scope and if |
| // exists output "overrides" edge. |
| const auto function_type = |
| scope_resolver_->FindScopeAndDefinition(function_name.Text()); |
| // TODO(minatoma): add a check to output this edge only if the parent is class |
| // or interface. |
| // TODO(minatoma): add a function like SyntaxTreeNode::MatchesTagAnyOf to |
| // IndexingFactsTree. |
| if (function_type && scope_resolver_->CurrentScopeDigest() == |
| function_type->instantiation_scope) { |
| const VName &overridden_function_vname = function_type->vname; |
| CreateEdge(function_vname, kEdgeOverrides, overridden_function_vname); |
| |
| // Delete the overriden base class function from the current scope so that |
| // any reference would reference the current function and not the function |
| // in the base class. |
| scope_resolver_->RemoveDefinitionFromCurrentScope( |
| overridden_function_vname); |
| } |
| |
| return function_vname; |
| } |
| |
| void KytheFactsExtractor::ReferenceFunctionOrTaskCall( |
| const IndexingFactNode &function_call_fact_node) { |
| const auto &anchors = function_call_fact_node.Value().Anchors(); |
| |
| std::optional<ScopedVname> last_type = std::nullopt; |
| for (const auto &anchor : anchors) { |
| const std::optional<ScopedVname> type = |
| last_type ? scope_resolver_->FindScopeAndDefinition( |
| anchor.Text(), last_type->type_scope) |
| : scope_resolver_->FindScopeAndDefinition(anchor.Text()); |
| if (type) { |
| CreateAnchorReference(anchor, type->vname); |
| last_type = type; |
| } else { |
| // Failed to fully resolve the types. |
| return; |
| } |
| } |
| |
| if (last_type) { |
| // Create ref/call edge. |
| CreateEdge(CreateAnchor(anchors.back()), kEdgeRefCall, last_type->vname); |
| } |
| } |
| |
| VName KytheFactsExtractor::DeclareClass( |
| const IndexingFactNode &class_fact_node) { |
| const auto &anchors = class_fact_node.Value().Anchors(); |
| const Anchor &class_name = anchors[0]; |
| |
| VName class_vname = { |
| .path = FilePath(), |
| .root = "", |
| .signature = CreateScopeRelativeSignature(class_name.Text()), |
| .corpus = Corpus()}; |
| const VName class_name_anchor = CreateAnchor(class_name); |
| |
| CreateFact(class_vname, kFactNodeKind, kNodeRecord); |
| CreateFact(class_vname, kFactComplete, kCompleteDefinition); |
| CreateEdge(class_name_anchor, kEdgeDefinesBinding, class_vname); |
| |
| if (anchors.size() > 1) { |
| const Anchor &class_end_label = anchors[1]; |
| const VName class_end_label_anchor = CreateAnchor(class_end_label); |
| CreateEdge(class_end_label_anchor, kEdgeRef, class_vname); |
| } |
| |
| return class_vname; |
| } |
| |
| void KytheFactsExtractor::ReferenceExtendsInheritance( |
| const IndexingFactNode &extends_node) { |
| const auto &anchors = extends_node.Value().Anchors(); |
| |
| std::optional<ScopedVname> last_type = std::nullopt; |
| for (const auto &anchor : anchors) { |
| const auto type = scope_resolver_->FindScopeAndDefinition(anchor.Text()); |
| if (type) { |
| CreateAnchorReference(anchor, type->vname); |
| last_type = type; |
| } |
| } |
| |
| if (last_type) { |
| // Create kythe facts for extends. |
| const VName &derived_class_vname = vnames_context_.top(); |
| CreateEdge(derived_class_vname, kEdgeExtends, last_type->vname); |
| |
| // Append the members of the parent class as members of the current class's |
| // scope. |
| scope_resolver_->AppendScopeToCurrentScope(last_type->type_scope); |
| } |
| } |
| |
| void KytheFactsExtractor::ReferencePackageImport( |
| const IndexingFactNode &import_fact_node) { |
| // TODO(minatoma): remove the imported vnames before exporting the scope as |
| // imports aren't intended to be accessible from outside the enclosing parent. |
| // Alternatively, maintain separate sets: exported, non-exported, or provide |
| // an attribute to distinguish. |
| const auto &anchors = import_fact_node.Value().Anchors(); |
| const Anchor &package_name_anchor = anchors[0]; |
| |
| const std::optional<ScopedVname> package_name_anchor_type = |
| scope_resolver_->FindScopeAndDefinition(package_name_anchor.Text()); |
| if (!package_name_anchor_type) { |
| LOG(WARNING) << "Failed to find a definition of " |
| << package_name_anchor.Text() << " package."; |
| return; |
| } |
| CreateEdge(CreateAnchor(package_name_anchor), kEdgeRefImports, |
| package_name_anchor_type->vname); |
| |
| // case of import pkg::my_variable. |
| if (anchors.size() > 1) { |
| const Anchor &imported_item_name = anchors[1]; |
| const auto imported_item_name_type = |
| scope_resolver_->FindScopeAndDefinition( |
| imported_item_name.Text(), package_name_anchor_type->type_scope); |
| if (imported_item_name_type) { |
| CreateEdge(CreateAnchor(imported_item_name), kEdgeRef, |
| imported_item_name_type->vname); |
| |
| // Add the found definition to the current scope as if it was declared in |
| // our scope so that it can be captured without "::". |
| scope_resolver_->AddDefinitionToCurrentScope( |
| imported_item_name_type->vname, package_name_anchor_type->type_scope); |
| } |
| } else { |
| // case of import pkg::*. |
| |
| // Add all the definitions in that package to the current scope as if it was |
| // declared in our scope so that it can be captured without "::". |
| scope_resolver_->AppendScopeToCurrentScope( |
| package_name_anchor_type->type_scope); |
| } |
| } |
| |
| void KytheFactsExtractor::ReferenceMember( |
| const IndexingFactNode &member_reference_node) { |
| // Resolve pkg::class::member case. `pkg` must be in scope, but `class` is in |
| // `pkg`s scope, while `member` is in `class`es scope. |
| const auto &anchors = member_reference_node.Value().Anchors(); |
| if (anchors.empty()) { |
| return; |
| } |
| |
| SignatureDigest focused_scope = scope_resolver_->CurrentScopeDigest(); |
| for (const auto &anchor : anchors) { |
| const std::optional<ScopedVname> type = |
| scope_resolver_->FindScopeAndDefinition(anchor.Text(), focused_scope); |
| if (!type) { |
| // No need to look further. |
| return; |
| } |
| CreateAnchorReference(anchor, type->vname); |
| |
| focused_scope = type->type_scope; |
| } |
| } |
| |
| void KytheFactsExtractor::ReferenceIncludeFile( |
| const IndexingFactNode &include_node) { |
| const auto &anchors = include_node.Value().Anchors(); |
| CHECK_GE(anchors.size(), 2); |
| const Anchor &file_name = anchors[0]; |
| const Anchor &file_path = anchors[1]; |
| |
| const VName file_vname = {.path = file_path.Text(), |
| .root = "", |
| .signature = Signature(""), |
| .corpus = Corpus(), |
| .language = kEmptyKytheLanguage}; |
| const VName file_anchor = CreateAnchor(file_name); |
| |
| CreateEdge(file_anchor, kEdgeRefIncludes, file_vname); |
| |
| auto included_file_scope = scope_resolver_->FindScopeAndDefinition( |
| file_path.Text(), ScopeResolver::GlobalScope()); |
| if (!included_file_scope) { |
| LOG(INFO) << "File scope not found For file: " << file_path.Text(); |
| return; |
| } |
| |
| // Create child of edge between the parent and the member of the included |
| // file. |
| const auto &included_file_content = |
| scope_resolver_->ListScopeMembers(included_file_scope->type_scope); |
| for (const auto &ref : included_file_content) { |
| CreateEdge(ref, kEdgeChildOf, vnames_context_.top()); |
| } |
| } |
| |
| VName KytheFactsExtractor::DeclareAnonymousScope( |
| const IndexingFactNode &temp_scope) { |
| const auto &scope_id = temp_scope.Value().Anchors()[0]; |
| VName vname = {.path = FilePath(), |
| .root = "", |
| .signature = CreateScopeRelativeSignature(scope_id.Text()), |
| .corpus = Corpus()}; |
| return vname; |
| } |
| |
| VName KytheFactsExtractor::DeclareConstant(const IndexingFactNode &constant) { |
| const auto &anchor = constant.Value().Anchors()[0]; |
| VName constant_vname = { |
| .path = FilePath(), |
| .root = "", |
| .signature = CreateScopeRelativeSignature(anchor.Text()), |
| .corpus = Corpus()}; |
| const VName variable_vname_anchor = CreateAnchor(anchor); |
| |
| CreateFact(constant_vname, kFactNodeKind, kNodeConstant); |
| CreateEdge(variable_vname_anchor, kEdgeDefinesBinding, constant_vname); |
| |
| return constant_vname; |
| } |
| |
| VName KytheFactsExtractor::DeclareStructOrUnion( |
| const IndexingFactNode &struct_node) { |
| const auto &anchors = struct_node.Value().Anchors(); |
| const Anchor &struct_name = anchors[0]; |
| |
| VName struct_vname = { |
| .path = FilePath(), |
| .root = "", |
| .signature = CreateScopeRelativeSignature(struct_name.Text()), |
| .corpus = Corpus()}; |
| const VName struct_name_anchor = CreateAnchor(struct_name); |
| |
| CreateFact(struct_vname, kFactNodeKind, kNodeRecord); |
| CreateEdge(struct_name_anchor, kEdgeDefinesBinding, struct_vname); |
| |
| return struct_vname; |
| } |
| |
| void KytheFactsExtractor::CreateAnchorReference(const Anchor &anchor, |
| const VName &definition) { |
| CreateEdge(CreateAnchor(anchor), kEdgeRef, definition); |
| } |
| |
| VName KytheFactsExtractor::CreateAnchor(const Anchor &anchor) { |
| const auto &anchor_range = anchor.SourceTextRange(); |
| if (!anchor_range) { |
| LOG(ERROR) << "Anchor not set! This is a bug. Skipping this Anchor. File: " |
| << FilePath() << " Anchor text: " << anchor.Text(); |
| return VName(); |
| } |
| const int start_location = anchor_range->begin; |
| const int end_location = start_location + anchor_range->length; |
| if (start_location == end_location) { |
| LOG(ERROR) |
| << "Zero-sized Anchor! This is a bug. Skipping this Anchor. File: " |
| << FilePath() << " Anchor text: " << anchor.Text(); |
| return VName(); |
| } |
| const auto [location_str, _] = signature_locations_.emplace( |
| absl::StrCat("@", start_location, ":", end_location)); |
| VName anchor_vname = {.path = FilePath(), |
| .root = "", |
| .signature = Signature(*location_str), |
| .corpus = Corpus()}; |
| |
| CreateFact(anchor_vname, kFactNodeKind, kNodeAnchor); |
| // This is one of the only locations that passes a std::string&& to |
| // CreateFact, everywhere else is string_view. |
| CreateFact(anchor_vname, kFactAnchorStart, absl::StrCat(start_location)); |
| CreateFact(anchor_vname, kFactAnchorEnd, absl::StrCat(end_location)); |
| |
| return anchor_vname; |
| } |
| |
| Signature KytheFactsExtractor::CreateScopeRelativeSignature( |
| std::string_view signature) const { |
| // Append the given signature to the signature of the parent. |
| return Signature(vnames_context_.top().signature, signature); |
| } |
| |
| void KytheFactsExtractor::CreateFact(const VName &vname, |
| std::string_view fact_name, |
| std::string_view fact_value) { |
| Fact fact(vname, fact_name, fact_value); |
| auto hash = absl::HashOf(fact); |
| if (!seen_kythe_hashes_.contains(hash)) { |
| facts_output_->Emit(fact); |
| seen_kythe_hashes_.insert(hash); |
| } |
| } |
| |
| void KytheFactsExtractor::CreateEdge(const VName &source_node, |
| std::string_view edge_name, |
| const VName &target_node) { |
| Edge edge(source_node, edge_name, target_node); |
| auto hash = absl::HashOf(edge); |
| if (!seen_kythe_hashes_.contains(hash)) { |
| facts_output_->Emit(edge); |
| seen_kythe_hashes_.insert(hash); |
| } |
| } |
| |
| std::ostream &KytheFactsPrinter::PrintJsonStream(std::ostream &stream) const { |
| // TODO(fangism): Print function should not be doing extraction work. |
| class Printer final : public KytheOutput { |
| public: |
| explicit Printer(std::ostream &stream) : stream_(stream) {} |
| void Emit(const Fact &fact) final { |
| fact.FormatJSON(stream_, /*debug=*/false) << std::endl; |
| } |
| void Emit(const Edge &edge) final { |
| edge.FormatJSON(stream_, /*debug=*/false) << std::endl; |
| } |
| |
| private: |
| std::ostream &stream_; |
| } printer(stream); |
| |
| StreamKytheFactsEntries(&printer, file_list_facts_tree_, *project_); |
| |
| return stream; |
| } |
| |
| std::ostream &KytheFactsPrinter::PrintJson(std::ostream &stream) const { |
| // TODO(fangism): Print function should not be doing extraction work. |
| class Printer final : public KytheOutput { |
| public: |
| explicit Printer(std::ostream &stream) : stream_(stream) {} |
| void Emit(const Fact &fact) final { |
| if (add_comma_) stream_ << "," << std::endl; |
| fact.FormatJSON(stream_, /*debug=*/true) << std::endl; |
| add_comma_ = true; |
| } |
| void Emit(const Edge &edge) final { |
| if (add_comma_) stream_ << "," << std::endl; |
| edge.FormatJSON(stream_, /*debug=*/true) << std::endl; |
| add_comma_ = true; |
| } |
| |
| private: |
| std::ostream &stream_; |
| bool add_comma_ = false; |
| } printer(stream); |
| |
| stream << "["; |
| StreamKytheFactsEntries(&printer, file_list_facts_tree_, *project_); |
| stream << "]" << std::endl; |
| |
| return stream; |
| } |
| |
| std::ostream &operator<<(std::ostream &stream, |
| const KytheFactsPrinter &kythe_facts_printer) { |
| if (kythe_facts_printer.debug_) { |
| kythe_facts_printer.PrintJson(stream); |
| } else { |
| kythe_facts_printer.PrintJsonStream(stream); |
| } |
| return stream; |
| } |
| |
| } // namespace kythe |
| } // namespace verilog |