blob: 9dfb64713d3f7d5b3975ba6045fa1c9033fb967e [file] [log] [blame]
// 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 "verilog/tools/kythe/indexing_facts_tree_extractor.h"
#include <iostream>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/strings/strip.h"
#include "common/text/concrete_syntax_tree.h"
#include "common/text/tree_context_visitor.h"
#include "common/text/tree_utils.h"
#include "common/util/file_util.h"
#include "common/util/logging.h"
#include "common/util/tree_operations.h"
#include "verilog/CST/class.h"
#include "verilog/CST/declaration.h"
#include "verilog/CST/functions.h"
#include "verilog/CST/identifier.h"
#include "verilog/CST/macro.h"
#include "verilog/CST/module.h"
#include "verilog/CST/net.h"
#include "verilog/CST/package.h"
#include "verilog/CST/parameters.h"
#include "verilog/CST/port.h"
#include "verilog/CST/statement.h"
#include "verilog/CST/tasks.h"
#include "verilog/CST/type.h"
#include "verilog/CST/verilog_matchers.h"
#include "verilog/CST/verilog_nonterminals.h"
#include "verilog/CST/verilog_tree_print.h"
#include "verilog/analysis/verilog_analyzer.h"
#include "verilog/analysis/verilog_project.h"
#include "verilog/tools/kythe/indexing_facts_tree.h"
#include "verilog/tools/kythe/indexing_facts_tree_context.h"
namespace verilog {
namespace kythe {
namespace {
using verible::Symbol;
using verible::SymbolKind;
using verible::SyntaxTreeLeaf;
using verible::SyntaxTreeNode;
using verible::TokenInfo;
using verible::TreeSearchMatch;
struct VerilogExtractionState {
// Multi-file tracker.
VerilogProject* const project;
// Keep track of which files (translation units, includes) have been
// extracted.
std::set<const VerilogSourceFile*> extracted_files;
};
// This class is used for traversing CST and extracting different indexing
// facts from CST nodes and constructs a tree of indexing facts.
class IndexingFactsTreeExtractor : public verible::TreeContextVisitor {
public:
IndexingFactsTreeExtractor(IndexingFactNode& file_list_facts_tree,
const VerilogSourceFile& source_file,
VerilogExtractionState* extraction_state,
std::vector<absl::Status>* errors)
: file_list_facts_tree_(file_list_facts_tree),
source_file_(source_file),
extraction_state_(extraction_state),
errors_(errors) {
const absl::string_view base = source_file_.GetTextStructure()->Contents();
root_.Value().AppendAnchor(
// Create the Anchor for file path node.
Anchor(source_file_.ResolvedPath()),
// Create the Anchor for text (code) node.
Anchor(base));
}
void Visit(const SyntaxTreeLeaf& leaf) final;
void Visit(const SyntaxTreeNode& node) final;
const IndexingFactNode& Root() const { return root_; }
IndexingFactNode TakeRoot() { return std::move(root_); }
private: // methods
// Extracts facts from module, intraface and program declarations.
void ExtractModuleOrInterfaceOrProgram(const SyntaxTreeNode& declaration_node,
IndexingFactType node_type);
// Extracts modules instantiations and creates its corresponding fact tree.
void ExtractModuleInstantiation(
const SyntaxTreeNode& data_declaration_node,
const std::vector<TreeSearchMatch>& gate_instances);
// Extracts endmodule, endinterface, endprogram and creates its corresponding
// fact tree.
void ExtractModuleOrInterfaceOrProgramEnd(
const SyntaxTreeNode& module_declaration_node);
// Extracts module, interface, program headers and creates its corresponding
// fact tree.
void ExtractModuleOrInterfaceOrProgramHeader(
const SyntaxTreeNode& module_declaration_node);
// Extracts modules ports and creates its corresponding fact tree.
// "has_propagated_type" determines if this port is "Non-ANSI" or not.
// e.g "module m(a, b, input c, d)" starting from "c" "has_propagated_type"
// will be true.
void ExtractModulePort(const SyntaxTreeNode& module_port_node,
bool has_propagated_type);
// Extracts "a" from "input a", "output a" and creates its corresponding fact
// tree.
void ExtractInputOutputDeclaration(
const SyntaxTreeNode& identifier_unpacked_dimensions);
// Extracts "a" from "wire a" and creates its corresponding fact tree.
void ExtractNetDeclaration(const SyntaxTreeNode& net_declaration_node);
// Extract package declarations and creates its corresponding facts tree.
void ExtractPackageDeclaration(
const SyntaxTreeNode& package_declaration_node);
// Extract macro definitions and explores its arguments and creates its
// corresponding facts tree.
void ExtractMacroDefinition(const SyntaxTreeNode& preprocessor_definition);
// Extract macro calls and explores its arguments and creates its
// corresponding facts tree.
void ExtractMacroCall(const SyntaxTreeNode& macro_call);
// Extract macro names from "kMacroIdentifiers" which are considered
// references to macros and creates its corresponding facts tree.
void ExtractMacroReference(const SyntaxTreeLeaf& macro_identifier);
// Extracts Include statements and creates its corresponding fact tree.
void ExtractInclude(const SyntaxTreeNode& preprocessor_include);
// Extracts function and creates its corresponding fact tree.
void ExtractFunctionDeclaration(
const SyntaxTreeNode& function_declaration_node);
// Extracts class constructor and creates its corresponding fact tree.
void ExtractClassConstructor(const SyntaxTreeNode& class_constructor);
// Extracts task and creates its corresponding fact tree.
void ExtractTaskDeclaration(const SyntaxTreeNode& task_declaration_node);
// Extracts function or task call and creates its corresponding fact tree.
void ExtractFunctionOrTaskCall(const SyntaxTreeNode& function_call_node);
// Extracts function or task call tagged with "kMethodCallExtension" (treated
// as kFunctionOrTaskCall in facts tree) and creates its corresponding fact
// tree.
void ExtractMethodCallExtension(const SyntaxTreeNode& call_extension_node);
// Extracts members tagged with "kHierarchyExtension" (treated as
// kMemberReference in facts tree) and creates its corresponding fact tree.
void ExtractMemberExtension(const SyntaxTreeNode& hierarchy_extension_node);
// Extracts function or task ports and parameters.
void ExtractFunctionOrTaskOrConstructorPort(
const SyntaxTreeNode& function_declaration_node);
// Extracts classes and creates its corresponding fact tree.
void ExtractClassDeclaration(const SyntaxTreeNode& class_declaration);
// Extracts class instances and creates its corresponding fact tree.
void ExtractClassInstances(
const SyntaxTreeNode& data_declaration,
const std::vector<TreeSearchMatch>& class_instances);
// Extracts primitive types declarations tagged with kRegisterVariable and
// creates its corresponding fact tree.
void ExtractRegisterVariable(const SyntaxTreeNode& register_variable);
// Extracts primitive types declarations tagged with
// kVariableDeclarationAssignment and creates its corresponding fact tree.
void ExtractVariableDeclarationAssignment(
const SyntaxTreeNode& variable_declaration_assignment);
// Extracts enum name and creates its corresponding fact tree.
void ExtractEnumName(const SyntaxTreeNode& enum_name);
// Extracts type declaration preceeded with "typedef" and creates its
// corresponding fact tree.
void ExtractTypeDeclaration(const SyntaxTreeNode& type_declaration);
// Extracts pure virtual functions and creates its corresponding fact tree.
void ExtractPureVirtualFunction(const SyntaxTreeNode& function_prototype);
// Extracts pure virtual tasks and creates its corresponding fact tree.
void ExtractPureVirtualTask(const SyntaxTreeNode& task_prototype);
// Extracts function header and creates its corresponding fact tree.
void ExtractFunctionHeader(const SyntaxTreeNode& function_header,
IndexingFactNode& function_node);
// Extracts task header and creates its corresponding fact tree.
void ExtractTaskHeader(const SyntaxTreeNode& task_header,
IndexingFactNode& task_node);
// Extracts enum type declaration preceeded with "typedef" and creates its
// corresponding fact tree.
void ExtractEnumTypeDeclaration(const SyntaxTreeNode& enum_type_declaration);
// Extracts struct type declaration preceeded with "typedef" and creates its
// corresponding fact tree.
void ExtractStructUnionTypeDeclaration(const SyntaxTreeNode& type_declaration,
const SyntaxTreeNode& struct_type);
// Extracts struct declaration and creates its corresponding fact tree.
void ExtractStructUnionDeclaration(
const SyntaxTreeNode& struct_type,
const std::vector<TreeSearchMatch>& variables_matched);
// Extracts struct and union members and creates its corresponding fact tree.
void ExtractDataTypeImplicitIdDimensions(
const SyntaxTreeNode& data_type_implicit_id_dimensions);
// Extracts variable definitions preceeded with some data type and creates its
// corresponding fact tree.
// e.g "some_type var1;"
void ExtractTypedVariableDefinition(
const Symbol& type_identifier,
const std::vector<TreeSearchMatch>& variables_matched);
// Extracts leaves tagged with SymbolIdentifier and creates its facts
// tree. This should only be reached in case of free variable references.
// e.g "assign out = in & in2."
// Other extraction functions should terminate in case the inner
// SymbolIdentifiers are extracted.
void ExtractSymbolIdentifier(const SyntaxTreeLeaf& symbol_identifier);
// Extracts nodes tagged with "kUnqualifiedId".
void ExtractUnqualifiedId(const SyntaxTreeNode& unqualified_id);
// Extracts parameter declarations and creates its corresponding fact tree.
void ExtractParamDeclaration(const SyntaxTreeNode& param_declaration);
// Extracts module instantiation named ports and creates its corresponding
// fact tree.
void ExtractModuleNamedPort(const SyntaxTreeNode& actual_named_port);
// Extracts package imports and creates its corresponding fact tree.
void ExtractPackageImport(const SyntaxTreeNode& package_import_item);
// Extracts qualified ids and creates its corresponding fact tree.
// e.g "pkg::member" or "class::member".
void ExtractQualifiedId(const SyntaxTreeNode& qualified_id);
// Extracts initializations in for loop and creates its corresponding fact
// tree. e.g from "for(int i = 0, j = k; ...)" extracts "i", "j" and "k".
void ExtractForInitialization(const SyntaxTreeNode& for_initialization);
// Extracts param references and the actual references names.
// e.g from "counter #(.N(r))" extracts "N".
void ExtractParamByName(const SyntaxTreeNode& param_by_name);
// Extracts new scope and assign unique id to it.
// specifically, intended for conditional/loop generate constructs.
void ExtractAnonymousScope(const SyntaxTreeNode& node);
// Determines how to deal with the given data declaration node as it may be
// module instance, class instance or primitive variable.
void ExtractDataDeclaration(const SyntaxTreeNode& data_declaration);
// Moves the anchors and children from the the last extracted node in
// "facts_tree_context_", adds them to the new_node and pops remove the last
// extracted node.
void MoveAndDeleteLastExtractedNode(IndexingFactNode& new_node);
absl::string_view FileContent() {
return source_file_.GetTextStructure()->Contents();
}
private: // data members
// The Root of the constructed facts tree.
IndexingFactNode root_{IndexingNodeData(IndexingFactType::kFile)};
// Keeps track of indexing facts tree ancestors as the visitor traverses CST.
IndexingFactsTreeContext facts_tree_context_;
// "IndexingFactNode" with tag kFileList which holds the extracted indexing
// facts trees of the files in the ordered file list. The extracted files will
// be children of this node and ordered as they are given in the ordered file
// list.
IndexingFactNode& file_list_facts_tree_;
// The current file being extracted.
const VerilogSourceFile& source_file_;
// The project configuration used to find included files.
VerilogExtractionState* const extraction_state_;
// Processing errors.
std::vector<absl::Status>* errors_;
// Counter used as an id for the anonymous scopes.
int next_anonymous_id = 0;
};
// Given a root to CST this function traverses the tree, extracts and constructs
// the indexing facts tree for one file.
IndexingFactNode BuildIndexingFactsTree(
IndexingFactNode& file_list_facts_tree,
const VerilogSourceFile& source_file,
VerilogExtractionState* extraction_state,
std::vector<absl::Status>* errors) {
VLOG(1) << __FUNCTION__ << ": file: " << source_file;
IndexingFactsTreeExtractor visitor(file_list_facts_tree, source_file,
extraction_state, errors);
if (source_file.Status().ok()) {
const auto& syntax_tree = source_file.GetTextStructure()->SyntaxTree();
if (syntax_tree != nullptr) {
VLOG(2) << "syntax:\n" << verible::RawTreePrinter(*syntax_tree);
syntax_tree->Accept(&visitor);
}
}
const PrintableIndexingFactNode debug_node(
visitor.Root(), source_file.GetTextStructure()->Contents());
VLOG(2) << "built facts tree: " << debug_node;
return visitor.TakeRoot();
}
} // namespace
IndexingFactNode ExtractFiles(absl::string_view file_list_path,
VerilogProject* project,
const std::vector<std::string>& file_names,
std::vector<absl::Status>* errors) {
VLOG(1) << __FUNCTION__;
// Open all of the translation units.
for (absl::string_view file_name : file_names) {
const auto status_or_file = project->OpenTranslationUnit(file_name);
if (!status_or_file.ok()) {
if (errors != nullptr) {
errors->push_back(status_or_file.status());
} else {
LOG(ERROR) << "Failed to open file " << file_name << ": "
<< status_or_file.status();
}
}
// For now, collect all diagnostics at the end.
// TODO(fangism): offer a mode to exit-early if there are file-not-found
// or read-permission issues (fail-fast, alert-user).
}
// Create a node to hold the path and root of the ordered file list, group
// all the files and acts as a ordered file list of these files.
IndexingFactNode file_list_facts_tree(
IndexingNodeData(IndexingFactType::kFileList, Anchor(file_list_path),
Anchor(project->TranslationUnitRoot())));
VerilogExtractionState project_extraction_state{project};
// pre-allocate file nodes with the number of translation units
file_list_facts_tree.Children().reserve(file_names.size());
for (absl::string_view file_name : file_names) {
auto* translation_unit = project->LookupRegisteredFile(file_name);
if (translation_unit == nullptr) continue;
const auto parse_status = translation_unit->Parse();
// status is also stored in translation_unit for later retrieval.
if (parse_status.ok()) {
file_list_facts_tree.Children().push_back(
BuildIndexingFactsTree(file_list_facts_tree, *translation_unit,
&project_extraction_state, errors));
} else {
if (errors != nullptr) {
errors->push_back(parse_status);
} else {
LOG(WARNING) << "Failed to parse file " << file_name << ": "
<< parse_status;
}
}
project->RemoveRegisteredFile(file_name);
}
VLOG(1) << "end of " << __FUNCTION__;
return file_list_facts_tree;
}
void IndexingFactsTreeExtractor::Visit(const SyntaxTreeNode& node) {
const auto tag = static_cast<verilog::NodeEnum>(node.Tag().tag);
VLOG(3) << __FUNCTION__ << ", tag: " << tag;
switch (tag) {
case NodeEnum ::kDescriptionList: {
// Adds the current root to facts tree context to keep track of the parent
// node so that it can be used to construct the tree and add children to
// it.
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_, &root_);
TreeContextVisitor::Visit(node);
break;
}
case NodeEnum::kInterfaceDeclaration: {
ExtractModuleOrInterfaceOrProgram(node, IndexingFactType::kInterface);
break;
}
case NodeEnum::kModuleDeclaration: {
ExtractModuleOrInterfaceOrProgram(node, IndexingFactType::kModule);
break;
}
case NodeEnum::kProgramDeclaration: {
ExtractModuleOrInterfaceOrProgram(node, IndexingFactType::kProgram);
break;
}
case NodeEnum::kDataDeclaration: {
ExtractDataDeclaration(node);
break;
}
case NodeEnum::kIdentifierUnpackedDimensions: {
ExtractInputOutputDeclaration(node);
break;
}
case NodeEnum ::kNetDeclaration: {
ExtractNetDeclaration(node);
break;
}
case NodeEnum::kPackageDeclaration: {
ExtractPackageDeclaration(node);
break;
}
case NodeEnum::kPreprocessorDefine: {
ExtractMacroDefinition(node);
break;
}
case NodeEnum::kMacroCall: {
ExtractMacroCall(node);
break;
}
case NodeEnum::kFunctionDeclaration: {
ExtractFunctionDeclaration(node);
break;
}
case NodeEnum::kTaskDeclaration: {
ExtractTaskDeclaration(node);
break;
}
case NodeEnum::kClassConstructor: {
ExtractClassConstructor(node);
break;
}
case NodeEnum::kFunctionCall: {
ExtractFunctionOrTaskCall(node);
break;
}
case NodeEnum::kMethodCallExtension: {
ExtractMethodCallExtension(node);
break;
}
case NodeEnum::kHierarchyExtension: {
ExtractMemberExtension(node);
break;
}
case NodeEnum::kClassDeclaration: {
ExtractClassDeclaration(node);
break;
}
case NodeEnum::kParamDeclaration: {
ExtractParamDeclaration(node);
break;
}
case NodeEnum::kActualNamedPort: {
ExtractModuleNamedPort(node);
break;
}
case NodeEnum::kPackageImportItem: {
ExtractPackageImport(node);
break;
}
case NodeEnum::kQualifiedId: {
ExtractQualifiedId(node);
break;
}
case NodeEnum::kForInitialization: {
ExtractForInitialization(node);
break;
}
case NodeEnum::kDataTypeImplicitIdDimensions: {
ExtractDataTypeImplicitIdDimensions(node);
break;
}
case NodeEnum::kParamByName: {
ExtractParamByName(node);
break;
}
case NodeEnum::kPreprocessorInclude: {
ExtractInclude(node);
break;
}
case NodeEnum::kRegisterVariable: {
ExtractRegisterVariable(node);
break;
}
case NodeEnum::kFunctionPrototype: {
ExtractPureVirtualFunction(node);
break;
}
case NodeEnum::kTaskPrototype: {
ExtractPureVirtualTask(node);
break;
}
case NodeEnum::kVariableDeclarationAssignment: {
ExtractVariableDeclarationAssignment(node);
break;
}
case NodeEnum::kEnumName: {
ExtractEnumName(node);
break;
}
case NodeEnum::kTypeDeclaration: {
ExtractTypeDeclaration(node);
break;
}
case NodeEnum::kLoopGenerateConstruct:
case NodeEnum::kIfClause:
case NodeEnum::kFinalStatement:
case NodeEnum::kInitialStatement:
case NodeEnum::kGenerateElseBody:
case NodeEnum::kElseClause:
case NodeEnum::kGenerateIfClause:
case NodeEnum::kForLoopStatement:
case NodeEnum::kDoWhileLoopStatement:
case NodeEnum::kWhileLoopStatement:
case NodeEnum::kForeachLoopStatement:
case NodeEnum::kRepeatLoopStatement:
case NodeEnum::kForeverLoopStatement: {
ExtractAnonymousScope(node);
break;
}
case NodeEnum::kUnqualifiedId: {
ExtractUnqualifiedId(node);
break;
}
default: {
TreeContextVisitor::Visit(node);
}
}
VLOG(3) << "end of " << __FUNCTION__ << ", tag: " << tag;
}
void IndexingFactsTreeExtractor::Visit(const SyntaxTreeLeaf& leaf) {
switch (leaf.get().token_enum()) {
case verilog_tokentype::SymbolIdentifier: {
ExtractSymbolIdentifier(leaf);
break;
}
default: {
break;
}
}
}
void IndexingFactsTreeExtractor::ExtractSymbolIdentifier(
const SyntaxTreeLeaf& symbol_identifier) {
facts_tree_context_.top().Children().emplace_back(
IndexingNodeData(IndexingFactType::kVariableReference,
Anchor(symbol_identifier.get(), FileContent())));
}
void IndexingFactsTreeExtractor::ExtractDataDeclaration(
const SyntaxTreeNode& data_declaration) {
// For module instantiations
const std::vector<TreeSearchMatch> gate_instances =
FindAllGateInstances(data_declaration);
if (!gate_instances.empty()) {
ExtractModuleInstantiation(data_declaration, gate_instances);
return;
}
// For bit, int and classes
const std::vector<TreeSearchMatch> register_variables =
FindAllRegisterVariables(data_declaration);
if (!register_variables.empty()) {
// for classes.
const std::vector<TreeSearchMatch> class_instances =
verible::SearchSyntaxTree(data_declaration, NodekClassNew());
if (!class_instances.empty()) {
ExtractClassInstances(data_declaration, register_variables);
return;
}
// for struct and union types.
const SyntaxTreeNode* type_node =
GetStructOrUnionOrEnumTypeFromDataDeclaration(data_declaration);
// Ignore if this isn't a struct or union type.
if (type_node != nullptr &&
NodeEnum(type_node->Tag().tag) != NodeEnum::kEnumType) {
ExtractStructUnionDeclaration(*type_node, register_variables);
return;
}
// In case "some_type var1".
const Symbol* type_identifier =
GetTypeIdentifierFromDataDeclaration(data_declaration);
if (type_identifier != nullptr) {
ExtractTypedVariableDefinition(*type_identifier, register_variables);
return;
}
// Traverse the children to extract inner nodes.
TreeContextVisitor::Visit(data_declaration);
return;
}
const std::vector<TreeSearchMatch> variable_declaration_assign =
FindAllVariableDeclarationAssignment(data_declaration);
if (!variable_declaration_assign.empty()) {
// for classes.
const std::vector<TreeSearchMatch> class_instances =
verible::SearchSyntaxTree(data_declaration, NodekClassNew());
if (!class_instances.empty()) {
ExtractClassInstances(data_declaration, variable_declaration_assign);
return;
}
// for struct and union types.
const SyntaxTreeNode* type_node =
GetStructOrUnionOrEnumTypeFromDataDeclaration(data_declaration);
// Ignore if this isn't a struct or union type.
if (type_node != nullptr &&
NodeEnum(type_node->Tag().tag) != NodeEnum::kEnumType) {
ExtractStructUnionDeclaration(*type_node, variable_declaration_assign);
return;
}
// In case "some_type var1".
const Symbol* type_identifier =
GetTypeIdentifierFromDataDeclaration(data_declaration);
if (type_identifier != nullptr) {
ExtractTypedVariableDefinition(*type_identifier,
variable_declaration_assign);
return;
}
// Traverse the children to extract inner nodes.
TreeContextVisitor::Visit(data_declaration);
return;
}
// Traverse the children to extract inner nodes.
TreeContextVisitor::Visit(data_declaration);
}
void IndexingFactsTreeExtractor::ExtractTypedVariableDefinition(
const Symbol& type_identifier,
const std::vector<TreeSearchMatch>& variables_matche) {
IndexingFactNode type_node(
IndexingNodeData{IndexingFactType::kDataTypeReference});
type_identifier.Accept(this);
MoveAndDeleteLastExtractedNode(type_node);
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_, &type_node);
for (const TreeSearchMatch& variable : variables_matche) {
variable.match->Accept(this);
}
}
facts_tree_context_.top().Children().push_back(std::move(type_node));
}
void IndexingFactsTreeExtractor::ExtractModuleOrInterfaceOrProgram(
const SyntaxTreeNode& declaration_node, IndexingFactType node_type) {
IndexingFactNode facts_node(IndexingNodeData{node_type});
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&facts_node);
ExtractModuleOrInterfaceOrProgramHeader(declaration_node);
ExtractModuleOrInterfaceOrProgramEnd(declaration_node);
const SyntaxTreeNode* item_list = GetModuleItemList(declaration_node);
if (item_list) Visit(*item_list);
}
facts_tree_context_.top().Children().push_back(std::move(facts_node));
}
void IndexingFactsTreeExtractor::ExtractModuleOrInterfaceOrProgramHeader(
const SyntaxTreeNode& module_declaration_node) {
// Extract module name e.g from "module my_module" extracts "my_module".
const SyntaxTreeLeaf* module_name_leaf =
GetModuleName(module_declaration_node);
if (!module_name_leaf) return;
facts_tree_context_.top().Value().AppendAnchor(
Anchor(module_name_leaf->get(), FileContent()));
// Extract parameters if exist.
const SyntaxTreeNode* param_declaration_list =
GetParamDeclarationListFromModuleDeclaration(module_declaration_node);
if (param_declaration_list != nullptr) {
Visit(*param_declaration_list);
}
// Extracting module ports e.g. (input a, input b).
// Ports are treated as children of the module.
const SyntaxTreeNode* port_list =
GetModulePortDeclarationList(module_declaration_node);
if (port_list == nullptr) {
return;
}
// This boolean is used to distinguish between ANSI and Non-ANSI module
// ports. e.g in this case: module m(a, b); has_propagated_type will be
// false as no type has been countered.
//
// in case like:
// module m(a, b, input x, y)
// for "a", "b" the boolean will be false but for "x", "y" the boolean will
// be true.
//
// The boolean is used to determine whether this the fact for this variable
// should be a reference or a defintiion.
bool has_propagated_type = false;
for (const auto& port : port_list->children()) {
if (port->Kind() == SymbolKind::kLeaf) continue;
const SyntaxTreeNode& port_node = SymbolCastToNode(*port);
const auto tag = static_cast<verilog::NodeEnum>(port_node.Tag().tag);
if (tag == NodeEnum::kPortDeclaration) {
has_propagated_type = true;
ExtractModulePort(port_node, has_propagated_type);
} else if (tag == NodeEnum::kPort) {
const SyntaxTreeNode* ref_port = GetPortReferenceFromPort(port_node);
if (ref_port) ExtractModulePort(*ref_port, has_propagated_type);
}
}
}
void IndexingFactsTreeExtractor::ExtractModulePort(
const SyntaxTreeNode& module_port_node, bool has_propagated_type) {
const auto tag = static_cast<verilog::NodeEnum>(module_port_node.Tag().tag);
// For extracting cases like:
// module m(input a, input b);
if (tag == NodeEnum::kPortDeclaration) {
const SyntaxTreeLeaf* leaf =
GetIdentifierFromPortDeclaration(module_port_node);
if (!leaf) return;
facts_tree_context_.top().Children().emplace_back(
IndexingNodeData(IndexingFactType::kVariableDefinition,
Anchor(leaf->get(), FileContent())));
} else if (tag == NodeEnum::kPortReference) {
// For extracting Non-ANSI style ports:
// module m(a, b);
const SyntaxTreeLeaf* leaf =
GetIdentifierFromPortReference(module_port_node);
if (!leaf) return;
if (has_propagated_type) {
// Check if the last type was not a primitive type.
// e.g module (interface_type x, y).
if (is_leaf(facts_tree_context_.top()) ||
facts_tree_context_.top()
.Children()
.back()
.Value()
.GetIndexingFactType() !=
IndexingFactType::kDataTypeReference) {
// Append this as a variable definition.
facts_tree_context_.top().Children().emplace_back(
IndexingNodeData(IndexingFactType::kVariableDefinition,
Anchor(leaf->get(), FileContent())));
} else {
// Append this as a child to previous kDataTypeReference.
facts_tree_context_.top().Children().back().Children().emplace_back(
IndexingNodeData(IndexingFactType::kVariableDefinition,
Anchor(leaf->get(), FileContent())));
}
} else {
// In case no preceeded data type.
facts_tree_context_.top().Children().emplace_back(
IndexingNodeData(IndexingFactType::kVariableReference,
Anchor(leaf->get(), FileContent())));
}
}
// Extract unpacked and packed dimensions.
for (const auto& child : module_port_node.children()) {
if (child == nullptr || child->Kind() == SymbolKind::kLeaf) {
continue;
}
const auto tag = static_cast<verilog::NodeEnum>(child->Tag().tag);
if (tag == NodeEnum::kUnqualifiedId) {
continue;
}
if (tag == NodeEnum::kDataType) {
const SyntaxTreeNode* data_type = GetTypeIdentifierFromDataType(*child);
// If not null this is a non primitive type and should create
// kDataTypeReference node for it.
// This data_type may be some class or interface type.
if (data_type != nullptr) {
// Create a node for this data type and append its anchor.
IndexingFactNode data_type_node(
IndexingNodeData{IndexingFactType::kDataTypeReference});
data_type->Accept(this);
MoveAndDeleteLastExtractedNode(data_type_node);
// Make the current port node child of this data type, remove it from
// the top node and push the kDataTypeRefernce Node.
data_type_node.Children().push_back(
std::move(facts_tree_context_.top().Children().back()));
facts_tree_context_.top().Children().back() = std::move(data_type_node);
continue;
}
}
child->Accept(this);
}
}
void IndexingFactsTreeExtractor::ExtractModuleNamedPort(
const SyntaxTreeNode& actual_named_port) {
const SyntaxTreeLeaf* named_port = GetActualNamedPortName(actual_named_port);
if (!named_port) return;
IndexingFactNode actual_port_node(
IndexingNodeData(IndexingFactType::kModuleNamedPort,
Anchor(named_port->get(), FileContent())));
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&actual_port_node);
const Symbol* paren_group = GetActualNamedPortParenGroup(actual_named_port);
if (paren_group != nullptr) {
paren_group->Accept(this);
}
}
facts_tree_context_.top().Children().emplace_back(
std::move(actual_port_node));
}
void IndexingFactsTreeExtractor::ExtractInputOutputDeclaration(
const SyntaxTreeNode& identifier_unpacked_dimension) {
const SyntaxTreeLeaf* port_name_leaf =
GetSymbolIdentifierFromIdentifierUnpackedDimensions(
identifier_unpacked_dimension);
if (port_name_leaf) {
facts_tree_context_.top().Children().emplace_back(
IndexingNodeData(IndexingFactType::kVariableDefinition,
Anchor(port_name_leaf->get(), FileContent())));
}
}
void IndexingFactsTreeExtractor::ExtractModuleOrInterfaceOrProgramEnd(
const SyntaxTreeNode& module_declaration_node) {
const SyntaxTreeLeaf* module_name =
GetModuleEndLabel(module_declaration_node);
if (module_name != nullptr) {
facts_tree_context_.top().Value().AppendAnchor(
Anchor(module_name->get(), FileContent()));
}
}
void IndexingFactsTreeExtractor::ExtractModuleInstantiation(
const SyntaxTreeNode& data_declaration_node,
const std::vector<TreeSearchMatch>& gate_instances) {
IndexingFactNode type_node(
IndexingNodeData{IndexingFactType::kDataTypeReference});
// Extract module type name.
const Symbol* type =
GetTypeIdentifierFromDataDeclaration(data_declaration_node);
if (type == nullptr) {
return;
}
// Extract module instance type and parameters.
type->Accept(this);
MoveAndDeleteLastExtractedNode(type_node);
// Module instantiations (data declarations) may declare multiple instances
// sharing the same type in a single statement e.g. bar b1(), b2().
//
// Loop through each instance and associate each declared id with the same
// type and create its corresponding facts tree node.
for (const TreeSearchMatch& instance : gate_instances) {
IndexingFactNode module_instance_node(
IndexingNodeData{IndexingFactType::kModuleInstance});
const TokenInfo* variable_name =
GetModuleInstanceNameTokenInfoFromGateInstance(*instance.match);
if (variable_name) {
module_instance_node.Value().AppendAnchor(
Anchor(*variable_name, FileContent()));
}
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&module_instance_node);
const SyntaxTreeNode* paren_group =
GetParenGroupFromModuleInstantiation(*instance.match);
if (paren_group) Visit(*paren_group);
}
type_node.Children().push_back(std::move(module_instance_node));
}
facts_tree_context_.top().Children().push_back(std::move(type_node));
}
void IndexingFactsTreeExtractor::ExtractNetDeclaration(
const SyntaxTreeNode& net_declaration_node) {
// Nets are treated as children of the enclosing parent.
// Net declarations may declare multiple instances sharing the same type in
// a single statement.
const std::vector<const TokenInfo*> identifiers =
GetIdentifiersFromNetDeclaration(net_declaration_node);
// Loop through each instance and associate each declared id with the same
// type.
for (const TokenInfo* wire_token_info : identifiers) {
facts_tree_context_.top().Children().emplace_back(
IndexingNodeData(IndexingFactType::kVariableDefinition,
Anchor(*wire_token_info, FileContent())));
}
}
void IndexingFactsTreeExtractor::ExtractPackageDeclaration(
const SyntaxTreeNode& package_declaration_node) {
IndexingFactNode package_node(IndexingNodeData{IndexingFactType::kPackage});
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&package_node);
// Extract package name.
const SyntaxTreeLeaf* pname = GetPackageNameLeaf(package_declaration_node);
if (pname) {
facts_tree_context_.top().Value().AppendAnchor(
Anchor(pname->get(), FileContent()));
}
// Extract package name after endpackage if exists.
const SyntaxTreeLeaf* package_end_name =
GetPackageNameEndLabel(package_declaration_node);
if (package_end_name != nullptr) {
facts_tree_context_.top().Value().AppendAnchor(
Anchor(package_end_name->get(), FileContent()));
}
// Visit package body it exists.
const Symbol* package_item_list =
GetPackageItemList(package_declaration_node);
if (package_item_list != nullptr) {
package_item_list->Accept(this);
}
}
facts_tree_context_.top().Children().push_back(std::move(package_node));
}
void IndexingFactsTreeExtractor::ExtractMacroDefinition(
const SyntaxTreeNode& preprocessor_definition) {
const SyntaxTreeLeaf* macro_name = GetMacroName(preprocessor_definition);
if (!macro_name) return;
IndexingFactNode macro_node(IndexingNodeData(
IndexingFactType::kMacro, Anchor(macro_name->get(), FileContent())));
// TODO(fangism): access directly, instead of searching.
const std::vector<TreeSearchMatch> args =
FindAllMacroDefinitionsArgs(preprocessor_definition);
for (const TreeSearchMatch& arg : args) {
const SyntaxTreeLeaf* macro_arg_name = GetMacroArgName(*arg.match);
if (macro_arg_name) {
macro_node.Children().emplace_back(
IndexingNodeData(IndexingFactType::kVariableDefinition,
Anchor(macro_arg_name->get(), FileContent())));
}
}
facts_tree_context_.top().Children().push_back(std::move(macro_node));
}
Anchor GetMacroAnchorFromTokenInfo(const TokenInfo& macro_token_info,
absl::string_view file_content) {
// Strip the prefix "`".
// e.g.
// `define TEN 0
// `TEN --> removes the `
const absl::string_view macro_name =
absl::StripPrefix(macro_token_info.text(), "`");
int begin = std::distance(file_content.begin(), macro_name.begin());
return Anchor(macro_name, begin, macro_name.size());
}
void IndexingFactsTreeExtractor::ExtractMacroCall(
const SyntaxTreeNode& macro_call) {
const TokenInfo* macro_call_name_token = GetMacroCallId(macro_call);
if (!macro_call_name_token) return;
IndexingFactNode macro_node(IndexingNodeData(
IndexingFactType::kMacroCall,
GetMacroAnchorFromTokenInfo(*macro_call_name_token, FileContent())));
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&macro_node);
const SyntaxTreeNode* macro_call_args = GetMacroCallArgs(macro_call);
if (macro_call_args) Visit(*macro_call_args);
}
facts_tree_context_.top().Children().push_back(std::move(macro_node));
}
void IndexingFactsTreeExtractor::ExtractMacroReference(
const SyntaxTreeLeaf& macro_identifier) {
facts_tree_context_.top().Children().emplace_back(IndexingNodeData(
IndexingFactType::kMacroCall,
GetMacroAnchorFromTokenInfo(macro_identifier.get(), FileContent())));
}
void IndexingFactsTreeExtractor::ExtractClassConstructor(
const SyntaxTreeNode& class_constructor) {
const SyntaxTreeLeaf* new_keyword =
GetNewKeywordFromClassConstructor(class_constructor);
if (!new_keyword) return;
IndexingFactNode constructor_node(
IndexingNodeData(IndexingFactType::kConstructor,
Anchor(new_keyword->get(), FileContent())));
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&constructor_node);
// Extract ports.
ExtractFunctionOrTaskOrConstructorPort(class_constructor);
// Extract constructor body.
const SyntaxTreeNode* constructor_body =
GetClassConstructorStatementList(class_constructor);
if (constructor_body) Visit(*constructor_body);
}
facts_tree_context_.top().Children().push_back(std::move(constructor_node));
}
void IndexingFactsTreeExtractor::ExtractPureVirtualFunction(
const SyntaxTreeNode& function_prototype) {
IndexingFactNode function_node(
IndexingNodeData{IndexingFactType::kFunctionOrTaskForwardDeclaration});
// Extract function header.
const SyntaxTreeNode* function_header =
GetFunctionPrototypeHeader(function_prototype);
if (function_header) ExtractFunctionHeader(*function_header, function_node);
facts_tree_context_.top().Children().push_back(std::move(function_node));
}
void IndexingFactsTreeExtractor::ExtractPureVirtualTask(
const SyntaxTreeNode& task_prototype) {
IndexingFactNode task_node(
IndexingNodeData{IndexingFactType::kFunctionOrTaskForwardDeclaration});
// Extract task header.
const SyntaxTreeNode* task_header = GetTaskPrototypeHeader(task_prototype);
if (task_header) ExtractTaskHeader(*task_header, task_node);
facts_tree_context_.top().Children().push_back(std::move(task_node));
}
void IndexingFactsTreeExtractor::ExtractFunctionDeclaration(
const SyntaxTreeNode& function_declaration_node) {
IndexingFactNode function_node(
IndexingNodeData{IndexingFactType::kFunctionOrTask});
// Extract function header.
const SyntaxTreeNode* function_header =
GetFunctionHeader(function_declaration_node);
if (function_header) ExtractFunctionHeader(*function_header, function_node);
{
// Extract function body.
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&function_node);
const SyntaxTreeNode* function_body =
GetFunctionBlockStatementList(function_declaration_node);
if (function_body) Visit(*function_body);
}
facts_tree_context_.top().Children().push_back(std::move(function_node));
}
void IndexingFactsTreeExtractor::ExtractTaskDeclaration(
const SyntaxTreeNode& task_declaration_node) {
IndexingFactNode task_node(
IndexingNodeData{IndexingFactType::kFunctionOrTask});
// Extract task header.
const SyntaxTreeNode* task_header = GetTaskHeader(task_declaration_node);
if (task_header) ExtractTaskHeader(*task_header, task_node);
{
// Extract task body.
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_, &task_node);
const SyntaxTreeNode* task_body =
GetTaskStatementList(task_declaration_node);
if (task_body) Visit(*task_body);
}
facts_tree_context_.top().Children().push_back(std::move(task_node));
}
void IndexingFactsTreeExtractor::ExtractFunctionHeader(
const SyntaxTreeNode& function_header, IndexingFactNode& function_node) {
// Extract function name.
const Symbol* function_name = GetFunctionHeaderId(function_header);
if (function_name == nullptr) {
return;
}
function_name->Accept(this);
MoveAndDeleteLastExtractedNode(function_node);
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&function_node);
// Extract function ports.
ExtractFunctionOrTaskOrConstructorPort(function_header);
}
}
void IndexingFactsTreeExtractor::ExtractTaskHeader(
const SyntaxTreeNode& task_header, IndexingFactNode& task_node) {
// Extract task name.
const Symbol* task_name = GetTaskHeaderId(task_header);
if (task_name == nullptr) {
return;
}
task_name->Accept(this);
MoveAndDeleteLastExtractedNode(task_node);
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_, &task_node);
// Extract task ports.
ExtractFunctionOrTaskOrConstructorPort(task_header);
}
}
void IndexingFactsTreeExtractor::ExtractFunctionOrTaskOrConstructorPort(
const SyntaxTreeNode& function_declaration_node) {
const std::vector<TreeSearchMatch> ports =
FindAllTaskFunctionPortDeclarations(function_declaration_node);
for (const TreeSearchMatch& port : ports) {
const Symbol* port_type = GetTypeOfTaskFunctionPortItem(*port.match);
if (port_type != nullptr) {
// port variable name.
const SyntaxTreeLeaf* port_identifier =
GetIdentifierFromTaskFunctionPortItem(*port.match);
if (!port_identifier) continue;
// variable identifier node.
IndexingFactNode variable_node(
IndexingNodeData(IndexingFactType::kVariableDefinition,
Anchor(port_identifier->get(), FileContent())));
// if this port has struct/union/enum data type.
const SyntaxTreeNode* struct_type =
GetStructOrUnionOrEnumTypeFromDataType(*port_type);
if (struct_type != nullptr) {
// Then this data type is struct/union/enum
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&variable_node);
struct_type->Accept(this);
}
facts_tree_context_.top().Children().push_back(
std::move(variable_node));
continue;
}
const SyntaxTreeNode* type_identifier =
GetTypeIdentifierFromDataType(*port_type);
if (type_identifier == nullptr) {
// Then this is a primitive data type.
// e.g "task f1(int x);"
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&variable_node);
const SyntaxTreeNode* packed_dim =
GetPackedDimensionFromDataType(*port_type);
if (packed_dim != nullptr) {
packed_dim->Accept(this);
}
const SyntaxTreeNode* unpacked_dimension =
GetUnpackedDimensionsFromTaskFunctionPortItem(*port.match);
if (unpacked_dimension) unpacked_dimension->Accept(this);
}
facts_tree_context_.top().Children().push_back(
std::move(variable_node));
continue;
}
// else this is a user defined type.
// e.g "task f1(some_class var1);".
IndexingFactNode type_node(
IndexingNodeData{IndexingFactType::kDataTypeReference});
type_identifier->Accept(this);
MoveAndDeleteLastExtractedNode(type_node);
type_node.Children().emplace_back(
IndexingNodeData(IndexingFactType::kVariableDefinition,
Anchor(port_identifier->get(), FileContent())));
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&type_node);
const SyntaxTreeNode* packed_dim =
GetPackedDimensionFromDataType(*port_type);
if (packed_dim != nullptr) {
packed_dim->Accept(this);
}
const SyntaxTreeNode* unpacked_dimension =
GetUnpackedDimensionsFromTaskFunctionPortItem(*port.match);
if (unpacked_dimension) unpacked_dimension->Accept(this);
}
facts_tree_context_.top().Children().push_back(std::move(type_node));
}
}
}
void IndexingFactsTreeExtractor::ExtractFunctionOrTaskCall(
const SyntaxTreeNode& function_call_node) {
IndexingFactNode function_node(
IndexingNodeData{IndexingFactType::kFunctionCall});
// Extract function or task name.
// It can be single or preceeded with a pkg or class names.
const SyntaxTreeNode* identifier =
GetIdentifiersFromFunctionCall(function_call_node);
if (identifier == nullptr) {
return;
}
Visit(*identifier);
// Move the data from the last extracted node to the current node and delete
// that last node.
MoveAndDeleteLastExtractedNode(function_node);
// Terminate if no function name is found.
// in case of built-in functions: "sin(x)";
if (function_node.Value().Anchors().empty()) {
return;
}
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&function_node);
const SyntaxTreeNode* arguments = GetParenGroupFromCall(function_call_node);
// Extract function or task parameters.
if (arguments) Visit(*arguments);
}
facts_tree_context_.top().Children().push_back(std::move(function_node));
}
void IndexingFactsTreeExtractor::ExtractMethodCallExtension(
const SyntaxTreeNode& call_extension_node) {
IndexingFactNode function_node(
IndexingNodeData{IndexingFactType::kFunctionCall});
// Move the data from the last extracted node to the current node and delete
// that last node.
MoveAndDeleteLastExtractedNode(function_node);
// Terminate if no function name is found.
// in case of built-in functions: "q.sort()";
if (function_node.Value().Anchors().empty()) {
return;
}
{
const SyntaxTreeLeaf* fun_call =
GetFunctionCallNameFromCallExtension(call_extension_node);
if (fun_call) {
function_node.Value().AppendAnchor(
Anchor(fun_call->get(), FileContent()));
}
}
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&function_node);
const SyntaxTreeNode* arguments =
GetParenGroupFromCallExtension(call_extension_node);
// parameters.
if (arguments) Visit(*arguments);
}
facts_tree_context_.top().Children().push_back(std::move(function_node));
}
void IndexingFactsTreeExtractor::ExtractMemberExtension(
const SyntaxTreeNode& hierarchy_extension_node) {
IndexingFactNode member_node(
IndexingNodeData{IndexingFactType::kMemberReference});
// Move the data from the last extracted node to the current node and delete
// that last node.
MoveAndDeleteLastExtractedNode(member_node);
{
const verible::SyntaxTreeLeaf* unqualified =
GetUnqualifiedIdFromHierarchyExtension(hierarchy_extension_node);
// member name
if (unqualified) {
member_node.Value().AppendAnchor(
Anchor(unqualified->get(), FileContent()));
}
}
facts_tree_context_.top().Children().push_back(std::move(member_node));
}
void IndexingFactsTreeExtractor::ExtractClassDeclaration(
const SyntaxTreeNode& class_declaration) {
IndexingFactNode class_node(IndexingNodeData{IndexingFactType::kClass});
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&class_node);
// Extract class name.
const SyntaxTreeLeaf* class_name = GetClassName(class_declaration);
if (class_name) {
facts_tree_context_.top().Value().AppendAnchor(
Anchor(class_name->get(), FileContent()));
}
// Extract class name after endclass.
const SyntaxTreeLeaf* class_end_name = GetClassEndLabel(class_declaration);
if (class_end_name != nullptr) {
facts_tree_context_.top().Value().AppendAnchor(
Anchor(class_end_name->get(), FileContent()));
}
const SyntaxTreeNode* param_list =
GetParamDeclarationListFromClassDeclaration(class_declaration);
if (param_list != nullptr) {
Visit(*param_list);
}
const SyntaxTreeNode* extended_class = GetExtendedClass(class_declaration);
if (extended_class != nullptr) {
IndexingFactNode extends_node(
IndexingNodeData{IndexingFactType::kExtends});
// In case of => class X extends Y.
if (NodeEnum(extended_class->Tag().tag) == NodeEnum::kUnqualifiedId) {
extends_node.Value().AppendAnchor(Anchor(
AutoUnwrapIdentifier(*extended_class)->get(), FileContent()));
} else {
// In case of => class X extends pkg1::Y.
ExtractQualifiedId(*extended_class);
// Construct extends node from the last node which is kMemberReference,
// remove kMemberReference node and append the new extends node.
MoveAndDeleteLastExtractedNode(extends_node);
}
// Add the extends node as a child of this class node.
class_node.Children().push_back(std::move(extends_node));
}
// Visit class body.
const SyntaxTreeNode* class_item_list = GetClassItemList(class_declaration);
if (class_item_list) Visit(*class_item_list);
}
facts_tree_context_.top().Children().push_back(std::move(class_node));
}
void IndexingFactsTreeExtractor::ExtractClassInstances(
const SyntaxTreeNode& data_declaration_node,
const std::vector<TreeSearchMatch>& class_instances) {
IndexingFactNode type_node(
IndexingNodeData{IndexingFactType::kDataTypeReference});
const Symbol* type =
GetTypeIdentifierFromDataDeclaration(data_declaration_node);
if (type == nullptr) {
return;
}
// Extract class type and parameters.
type->Accept(this);
MoveAndDeleteLastExtractedNode(type_node);
// Class instances may may appear as multiple instances sharing the same
// type in a single statement e.g. myClass b1 = new, b2 = new. LRM 8.8 Typed
// constructor calls
//
// Loop through each instance and associate each declared id with the same
// type and create its corresponding facts tree node.
for (const TreeSearchMatch& instance : class_instances) {
IndexingFactNode class_instance_node(
IndexingNodeData{IndexingFactType::kClassInstance});
// Re-use the kRegisterVariable and kVariableDeclarationAssignment tag
// resolver.
instance.match->Accept(this);
MoveAndDeleteLastExtractedNode(class_instance_node);
type_node.Children().push_back(std::move(class_instance_node));
}
facts_tree_context_.top().Children().push_back(std::move(type_node));
}
void IndexingFactsTreeExtractor::ExtractRegisterVariable(
const SyntaxTreeNode& register_variable) {
const TokenInfo* instance_name =
GetInstanceNameTokenInfoFromRegisterVariable(register_variable);
if (!instance_name) return;
IndexingFactNode variable_node(
IndexingNodeData(IndexingFactType::kVariableDefinition,
Anchor(*instance_name, FileContent())));
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&variable_node);
const SyntaxTreeNode* unpacked_dimension =
GetUnpackedDimensionFromRegisterVariable(register_variable);
if (unpacked_dimension) Visit(*unpacked_dimension);
const SyntaxTreeNode* expression =
GetTrailingExpressionFromRegisterVariable(register_variable);
if (expression != nullptr) {
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&variable_node);
// Visit Trailing Assignment Expression.
Visit(*expression);
}
}
facts_tree_context_.top().Children().push_back(std::move(variable_node));
}
void IndexingFactsTreeExtractor::ExtractVariableDeclarationAssignment(
const SyntaxTreeNode& variable_declaration_assignment) {
const SyntaxTreeLeaf* unqualified_id =
GetUnqualifiedIdFromVariableDeclarationAssignment(
variable_declaration_assignment);
if (!unqualified_id) return;
IndexingFactNode variable_node(
IndexingNodeData(IndexingFactType::kVariableDefinition,
Anchor(unqualified_id->get(), FileContent())));
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&variable_node);
const SyntaxTreeNode* unpacked_dimension =
GetUnpackedDimensionFromVariableDeclarationAssign(
variable_declaration_assignment);
if (unpacked_dimension) Visit(*unpacked_dimension);
const SyntaxTreeNode* expression =
GetTrailingExpressionFromVariableDeclarationAssign(
variable_declaration_assignment);
if (expression != nullptr) {
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&variable_node);
// Visit Trailing Assignment Expression.
Visit(*expression);
}
}
facts_tree_context_.top().Children().push_back(std::move(variable_node));
}
void IndexingFactsTreeExtractor::ExtractUnqualifiedId(
const SyntaxTreeNode& unqualified_id) {
const SyntaxTreeLeaf* identifier = AutoUnwrapIdentifier(unqualified_id);
if (identifier == nullptr) {
return;
}
switch (identifier->get().token_enum()) {
case verilog_tokentype::MacroIdentifier: {
ExtractMacroReference(*identifier);
break;
}
case verilog_tokentype::SymbolIdentifier: {
IndexingFactNode variable_reference(
IndexingNodeData{IndexingFactType::kVariableReference});
ExtractSymbolIdentifier(*identifier);
MoveAndDeleteLastExtractedNode(variable_reference);
const SyntaxTreeNode* param_list =
GetParamListFromUnqualifiedId(unqualified_id);
if (param_list != nullptr) {
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&variable_reference);
param_list->Accept(this);
}
facts_tree_context_.top().Children().push_back(
std::move(variable_reference));
break;
}
default: {
break;
}
}
}
void IndexingFactsTreeExtractor::ExtractParamDeclaration(
const SyntaxTreeNode& param_declaration) {
IndexingFactNode param_node(
IndexingNodeData{IndexingFactType::kParamDeclaration});
const SyntaxTreeNode* type_assignment =
GetTypeAssignmentFromParamDeclaration(param_declaration);
// Parameters can be in two cases:
// 1st => parameter type x;
if (type_assignment != nullptr) {
param_node.Value().AppendAnchor(Anchor(
ABSL_DIE_IF_NULL(GetIdentifierLeafFromTypeAssignment(*type_assignment))
->get(),
FileContent()));
const SyntaxTreeNode* expression =
GetExpressionFromTypeAssignment(*type_assignment);
if (expression != nullptr) {
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&param_node);
Visit(*expression);
}
} else {
// 2nd => parameter int x;
// Extract Param name.
const TokenInfo* parameter_name = GetParameterNameToken(param_declaration);
if (!parameter_name) return;
param_node.Value().AppendAnchor(Anchor(*parameter_name, FileContent()));
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&param_node);
const Symbol* assign_expression =
GetParamAssignExpression(param_declaration);
if (assign_expression != nullptr &&
assign_expression->Kind() == SymbolKind::kNode) {
// Extract trailing expression.
assign_expression->Accept(this);
}
}
}
facts_tree_context_.top().Children().push_back(std::move(param_node));
}
void IndexingFactsTreeExtractor::ExtractParamByName(
const SyntaxTreeNode& param_by_name) {
const verible::SyntaxTreeLeaf* named_param =
GetNamedParamFromActualParam(param_by_name);
if (!named_param) return;
IndexingFactNode named_param_node(
IndexingNodeData(IndexingFactType::kNamedParam,
Anchor(named_param->get(), FileContent())));
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&named_param_node);
const SyntaxTreeNode* paren_group =
GetParenGroupFromActualParam(param_by_name);
if (paren_group != nullptr) {
Visit(*paren_group);
}
}
facts_tree_context_.top().Children().push_back(std::move(named_param_node));
}
void IndexingFactsTreeExtractor::ExtractPackageImport(
const SyntaxTreeNode& package_import_item) {
const SyntaxTreeLeaf* package_name =
GetImportedPackageName(package_import_item);
if (!package_name) return;
IndexingNodeData package_import_data(
IndexingFactType::kPackageImport,
Anchor(package_name->get(), FileContent()));
// Get the name of the imported item (if exists).
// e.g pkg::var1 ==> return var1.
// will be nullptr in case of pkg::*.
const SyntaxTreeLeaf* imported_item =
GeImportedItemNameFromPackageImportItem(package_import_item);
if (imported_item != nullptr) {
package_import_data.AppendAnchor(
Anchor(imported_item->get(), FileContent()));
}
facts_tree_context_.top().Children().emplace_back(
std::move(package_import_data));
}
// This deep-copy works even if the IndexingNodeData's copy-constructor is
// deleted.
// Defining this here because the following function is the only place in the
// codebase that needs this workaround.
static IndexingNodeData CopyNodeData(const IndexingNodeData& src) {
IndexingNodeData copy(src.GetIndexingFactType());
for (const auto& anchor : src.Anchors()) {
copy.AppendAnchor(Anchor(anchor)); // copy
}
return copy;
}
void IndexingFactsTreeExtractor::ExtractQualifiedId(
const SyntaxTreeNode& qualified_id) {
IndexingNodeData member_reference_data(IndexingFactType::kMemberReference);
// Get all the variable names in the qualified id.
// e.g. split "A#(...)::B#(...)" into components "A#(...)" and "B#(...)"
for (const auto& child : qualified_id.children()) {
if (child == nullptr ||
NodeEnum(child->Tag().tag) != NodeEnum::kUnqualifiedId) {
continue;
}
member_reference_data.AppendAnchor(
Anchor(AutoUnwrapIdentifier(*child)->get(), FileContent()));
const SyntaxTreeNode* param_list = GetParamListFromUnqualifiedId(*child);
if (param_list != nullptr) {
// Create a copy from the current "member_reference" node to be used for
// this param reference.
// Copying inside this for loop costs O(N^2), where N is the
// depth of a reference (on "A::B::C::D", N=4).
// Downstream, the lookup for "A" is being done repeatedly.
// TODO(fangism): rewrite this and its consumer to eliminate the linear
// copy in a loop and avoid re-lookup.
IndexingFactNode param_member_reference(
CopyNodeData(member_reference_data));
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&param_member_reference);
Visit(*param_list);
}
facts_tree_context_.top().Children().push_back(
std::move(param_member_reference));
}
}
facts_tree_context_.top().Children().emplace_back(
std::move(member_reference_data));
}
void IndexingFactsTreeExtractor::ExtractForInitialization(
const SyntaxTreeNode& for_initialization) {
// Extracts the variable name from for initialization.
// e.g from "int i = 0"; ==> extracts "i".
const SyntaxTreeLeaf* variable_name =
GetVariableNameFromForInitialization(for_initialization);
if (variable_name) {
facts_tree_context_.top().Children().emplace_back(
IndexingNodeData(IndexingFactType::kVariableDefinition,
Anchor(variable_name->get(), FileContent())));
}
// Extracts the data the in case it contains packed or unpacked dimension.
// e.g bit [x : y] var [x : y].
const SyntaxTreeNode* data_type_node =
GetDataTypeFromForInitialization(for_initialization);
if (data_type_node != nullptr) {
Visit(*data_type_node);
}
// Extracts the RHS of the declaration.
// e.g int i = x; ==> extracts "x".
const SyntaxTreeNode* expression =
GetExpressionFromForInitialization(for_initialization);
if (expression) Visit(*expression);
}
// Returns string_view of `text` with outermost double-quotes removed.
// If `text` is not wrapped in quotes, return it as-is.
absl::string_view StripOuterQuotes(absl::string_view text) {
return absl::StripSuffix(absl::StripPrefix(text, "\""), "\"");
}
void IndexingFactsTreeExtractor::ExtractInclude(
const SyntaxTreeNode& preprocessor_include) {
VLOG(1) << __FUNCTION__;
const SyntaxTreeLeaf* included_filename =
GetFileFromPreprocessorInclude(preprocessor_include);
if (included_filename == nullptr) {
return;
}
const absl::string_view filename_text = included_filename->get().text();
// Remove the double quotes from the filesname.
const absl::string_view filename_unquoted = StripOuterQuotes(filename_text);
VLOG(1) << "got: `include \"" << filename_unquoted << "\"";
VerilogProject* const project = extraction_state_->project;
// Open this file (could be first time, or previously opened).
const auto status_or_file = project->OpenIncludedFile(filename_unquoted);
if (!status_or_file.ok()) {
if (errors_ != nullptr) {
errors_->push_back(status_or_file.status());
} else {
LOG(ERROR) << "Failed to open the include file " << filename_unquoted
<< ": " << status_or_file.status();
}
// Skip
return;
}
VerilogSourceFile* const included_file = *status_or_file;
if (included_file == nullptr) return;
VLOG(1) << "opened include file: " << included_file->ResolvedPath();
{
// Check whether or not this file was already extracted.
const auto p = extraction_state_->extracted_files.insert(included_file);
if (!p.second) {
// If already extracted, skip re-extraction.
VLOG(1) << "File was previously extracted.";
} else {
// Parse included file and extract.
const auto parse_status = included_file->Parse();
if (parse_status.ok()) {
file_list_facts_tree_.Children().push_back(BuildIndexingFactsTree(
file_list_facts_tree_, *included_file, extraction_state_, errors_));
} else {
if (errors_ != nullptr) {
errors_->push_back(parse_status);
} else {
LOG(WARNING) << "Failed to parse the include file "
<< filename_unquoted << ": " << parse_status;
}
}
}
}
// Create a node for include statement with two Anchors:
// 1st one holds the actual text in the include statement.
// 2nd one holds the path of the included file relative to the file list.
facts_tree_context_.top().Children().emplace_back(IndexingNodeData(
IndexingFactType::kInclude,
Anchor(filename_text, included_filename->get().left(FileContent()),
filename_text.size()),
Anchor(included_file->ResolvedPath())));
}
void IndexingFactsTreeExtractor::ExtractEnumName(
const SyntaxTreeNode& enum_name) {
const SyntaxTreeLeaf* symbol_id = GetSymbolIdentifierFromEnumName(enum_name);
if (!symbol_id) return;
IndexingFactNode enum_node(IndexingNodeData(
IndexingFactType::kConstant, Anchor(symbol_id->get(), FileContent())));
// Iterate over the children and traverse them to extract facts from inner
// nodes and ignore the leaves.
// e.g enum {RED[x] = 1, OLD=y} => explores "[x]", "=y".
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_, &enum_node);
for (const auto& child : enum_name.children()) {
if (child == nullptr || child->Kind() == SymbolKind::kLeaf) {
continue;
}
child->Accept(this);
}
}
facts_tree_context_.top().Children().push_back(std::move(enum_node));
}
void IndexingFactsTreeExtractor::ExtractEnumTypeDeclaration(
const SyntaxTreeNode& enum_type_declaration) {
// Extract enum type name.
const SyntaxTreeLeaf* enum_type_name =
GetIdentifierFromTypeDeclaration(enum_type_declaration);
if (!enum_type_name) return;
facts_tree_context_.top().Children().emplace_back(
IndexingNodeData(IndexingFactType::kVariableDefinition,
Anchor(enum_type_name->get(), FileContent())));
// Explore the children of this enum type to extract.
for (const auto& child : enum_type_declaration.children()) {
if (child == nullptr || child->Kind() == SymbolKind::kLeaf) {
continue;
}
child->Accept(this);
}
}
void IndexingFactsTreeExtractor::ExtractStructUnionTypeDeclaration(
const SyntaxTreeNode& type_declaration, const SyntaxTreeNode& struct_type) {
IndexingFactNode struct_type_node(IndexingNodeData(
IndexingFactType::kStructOrUnion,
Anchor(
ABSL_DIE_IF_NULL(GetIdentifierFromTypeDeclaration(type_declaration))
->get(),
FileContent())));
// Explore the children of this enum type to extract.
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&struct_type_node);
Visit(struct_type);
}
facts_tree_context_.top().Children().push_back(std::move(struct_type_node));
}
void IndexingFactsTreeExtractor::ExtractStructUnionDeclaration(
const SyntaxTreeNode& struct_type,
const std::vector<TreeSearchMatch>& variables_matched) {
VLOG(2) << __FUNCTION__;
// Dummy data type to hold the extracted struct members because there is no
// data type here. Its temporary children will be moved out before this
// returns.
IndexingFactNode struct_node(
IndexingNodeData{IndexingFactType::kStructOrUnion});
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&struct_node);
// Extract struct members.
Visit(struct_type);
}
for (const TreeSearchMatch& variable : variables_matched) {
// Extract this variable.
// This can be kRegisterVariable or kVariableDeclarationAssign.
variable.match->Accept(this);
CHECK(!facts_tree_context_.top().Children().empty());
IndexingFactNode& recent(facts_tree_context_.top().Children().back());
// Append the struct members to be a children of this variable.
// TODO(fangism): move instead of copying chidren
// However, std::move-ing each child in the loop crashes,
// and so does recent.AdoptSubtreesFrom(&struct_node).
recent.Children().reserve(struct_node.Children().size());
for (const auto& child : struct_node.Children()) {
recent.Children().push_back(child); // copy
}
}
VLOG(2) << "end of " << __FUNCTION__;
}
void IndexingFactsTreeExtractor::ExtractDataTypeImplicitIdDimensions(
const SyntaxTreeNode& data_type_implicit_id_dimensions) {
// This node has 2 cases:
// 1st case:
// typedef struct {
// data_type var_name;
// } my_struct;
// In this case this should be a kDataTypeReference with var_name as a
// child.
//
// 2nd case:
// typedef struct {
// struct {int xx;} var_name;
// } my_struct;
// In this case var_name should contain "xx" inside it.
std::pair<const SyntaxTreeLeaf*, int> variable_name =
GetSymbolIdentifierFromDataTypeImplicitIdDimensions(
data_type_implicit_id_dimensions);
if (!variable_name.first) return;
IndexingFactNode variable_node(
IndexingNodeData(IndexingFactType::kVariableDefinition,
Anchor(variable_name.first->get(), FileContent())));
if (variable_name.second == 1) {
const SyntaxTreeLeaf* type_identifier =
GetNonprimitiveTypeOfDataTypeImplicitDimensions(
data_type_implicit_id_dimensions);
if (type_identifier == nullptr) return;
IndexingFactNode type_node(
IndexingNodeData(IndexingFactType::kDataTypeReference,
Anchor(type_identifier->get(), FileContent())));
type_node.Children().push_back(std::move(variable_node));
facts_tree_context_.top().Children().push_back(std::move(type_node));
} else if (variable_name.second == 2) {
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&variable_node);
for (const auto& child : data_type_implicit_id_dimensions.children()) {
if (child == nullptr || child->Kind() == SymbolKind::kLeaf) {
continue;
}
child->Accept(this);
}
}
facts_tree_context_.top().Children().push_back(std::move(variable_node));
}
}
void IndexingFactsTreeExtractor::ExtractTypeDeclaration(
const SyntaxTreeNode& type_declaration) {
const SyntaxTreeNode* type =
GetReferencedTypeOfTypeDeclaration(type_declaration);
if (type == nullptr) return;
// Look for enum/struct/union in the referenced type.
const auto tag = static_cast<NodeEnum>(type->Tag().tag);
if (tag != NodeEnum::kDataType) return;
const SyntaxTreeNode* primitive =
GetStructOrUnionOrEnumTypeFromDataType(*type);
if (primitive == nullptr) {
// Then this is a user-defined type.
// Extract type name.
const SyntaxTreeLeaf* type_name =
GetIdentifierFromTypeDeclaration(type_declaration);
if (type_name == nullptr) return;
facts_tree_context_.top().Children().emplace_back(
IndexingNodeData(IndexingFactType::kTypeDeclaration,
Anchor(type_name->get(), FileContent())));
return;
}
switch (NodeEnum(primitive->Tag().tag)) {
case NodeEnum::kEnumType: {
ExtractEnumTypeDeclaration(type_declaration);
break;
}
case NodeEnum::kStructType: {
ExtractStructUnionTypeDeclaration(type_declaration, *type);
break;
}
case NodeEnum::kUnionType: {
ExtractStructUnionTypeDeclaration(type_declaration, *type);
break;
}
default: {
break;
}
}
}
void IndexingFactsTreeExtractor::ExtractAnonymousScope(
const SyntaxTreeNode& node) {
IndexingFactNode temp_scope_node(IndexingNodeData(
IndexingFactType::kAnonymousScope,
// Generate unique id for this scope.
Anchor(absl::StrCat("anonymous-scope-", next_anonymous_id++))));
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&temp_scope_node);
TreeContextVisitor::Visit(node);
}
facts_tree_context_.top().Children().push_back(std::move(temp_scope_node));
}
void IndexingFactsTreeExtractor::MoveAndDeleteLastExtractedNode(
IndexingFactNode& new_node) {
// Terminate if there is no parent or the parent has no children.
if (facts_tree_context_.empty() || is_leaf(facts_tree_context_.top())) {
return;
}
// Get The last extracted child.
IndexingFactNode& previous_node = facts_tree_context_.top().Children().back();
// Fill the anchors of the previous node to the current node.
new_node.Value().SwapAnchors(&previous_node.Value());
// Move the children of the previous node to this node.
AdoptSubtreesFrom(new_node, &previous_node);
// Remove the last extracted node.
facts_tree_context_.top().Children().pop_back();
}
} // namespace kythe
} // namespace verilog