blob: 1198279fb10a9116f601cd9bf43693cf691d92de [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 "common/text/tree_context_visitor.h"
#include "common/text/tree_utils.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/port.h"
#include "verilog/CST/tasks.h"
#include "verilog/CST/verilog_matchers.h"
#include "verilog/CST/verilog_nonterminals.h"
#include "verilog/analysis/verilog_analyzer.h"
namespace verilog {
namespace kythe {
namespace {
using verible::SyntaxTreeLeaf;
using verible::SyntaxTreeNode;
using verible::TreeSearchMatch;
} // namespace
IndexingFactNode ExtractOneFile(absl::string_view content,
absl::string_view filename, int& exit_status,
bool& parse_ok) {
const auto analyzer =
verilog::VerilogAnalyzer::AnalyzeAutomaticMode(content, filename);
const auto lex_status = ABSL_DIE_IF_NULL(analyzer)->LexStatus();
const auto parse_status = analyzer->ParseStatus();
if (!lex_status.ok() || !parse_status.ok()) {
const std::vector<std::string> syntax_error_messages(
analyzer->LinterTokenErrorMessages());
for (const auto& message : syntax_error_messages) {
std::cout << message << std::endl;
}
exit_status = 1;
}
parse_ok = parse_status.ok();
const auto& text_structure = analyzer->Data();
const auto& syntax_tree = text_structure.SyntaxTree();
return BuildIndexingFactsTree(syntax_tree, analyzer->Data().Contents(),
filename);
}
IndexingFactNode BuildIndexingFactsTree(
const verible::ConcreteSyntaxTree& syntax_tree, absl::string_view base,
absl::string_view file_name) {
IndexingFactsTreeExtractor visitor(base, file_name);
if (syntax_tree == nullptr) {
return visitor.GetRoot();
}
const SyntaxTreeNode& root = verible::SymbolCastToNode(*syntax_tree);
root.Accept(&visitor);
return visitor.GetRoot();
}
void IndexingFactsTreeExtractor::Visit(const SyntaxTreeNode& node) {
const auto tag = static_cast<verilog::NodeEnum>(node.Tag().tag);
switch (tag) {
case NodeEnum ::kDescriptionList: {
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&GetRoot());
TreeContextVisitor::Visit(node);
break;
}
case NodeEnum::kModuleDeclaration: {
ExtractModule(node);
break;
}
case NodeEnum::kDataDeclaration: {
// For module instantiations
const std::vector<TreeSearchMatch> gate_instances =
FindAllGateInstances(node);
if (!gate_instances.empty()) {
ExtractModuleInstantiation(node, gate_instances);
break;
}
// For bit, int and classes
const std::vector<TreeSearchMatch> register_variables =
FindAllRegisterVariables(node);
if (!register_variables.empty()) {
// for classes.
const std::vector<TreeSearchMatch> class_instances =
verible::SearchSyntaxTree(node, NodekClassNew());
if (!class_instances.empty()) {
ExtractClassInstances(node, register_variables);
break;
}
}
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::kFunctionCall: {
ExtractFunctionOrTaskCall(node);
break;
}
case NodeEnum::kClassDeclaration: {
ExtractClassDeclaration(node);
break;
}
case NodeEnum::kPackageImportItem: {
ExtractPackageImport(node);
break;
}
default: {
TreeContextVisitor::Visit(node);
}
}
}
void IndexingFactsTreeExtractor::Visit(const verible::SyntaxTreeLeaf& leaf) {
switch (leaf.get().token_enum()) {
case verilog_tokentype::MacroIdentifier: {
ExtractMacroReference(leaf);
break;
}
default:
break;
}
}
void IndexingFactsTreeExtractor::ExtractModule(
const SyntaxTreeNode& module_declaration_node) {
IndexingNodeData module_node_data(IndexingFactType::kModule);
IndexingFactNode module_node(module_node_data);
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&module_node);
ExtractModuleHeader(module_declaration_node);
ExtractModuleEnd(module_declaration_node);
const SyntaxTreeNode& module_item_list =
GetModuleItemList(module_declaration_node);
Visit(module_item_list);
}
facts_tree_context_.top().NewChild(module_node);
}
void IndexingFactsTreeExtractor::ExtractModuleHeader(
const SyntaxTreeNode& module_header_node) {
const verible::TokenInfo& module_name_token =
GetModuleNameToken(module_header_node);
const Anchor module_name_anchor(module_name_token, context_.base);
facts_tree_context_.top().Value().AppendAnchor(module_name_anchor);
// Extracting module ports e.g. (input a, input b).
// Ports are treated as children of the module.
const SyntaxTreeNode* port_list =
GetModulePortDeclarationList(module_header_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() == verible::SymbolKind::kLeaf) continue;
const SyntaxTreeNode& port_node = verible::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) {
ExtractModulePort(GetPortReferenceFromPort(port_node),
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 =
GetIdentifierFromModulePortDeclaration(module_port_node);
facts_tree_context_.top().NewChild(
IndexingNodeData({Anchor(leaf->get(), context_.base)},
IndexingFactType::kVariableDefinition));
} else if (tag == NodeEnum::kPortReference) {
// For extracting Non-ANSI style ports:
// module m(a, b);
const SyntaxTreeLeaf* leaf =
GetIdentifierFromPortReference(module_port_node);
facts_tree_context_.top().NewChild(IndexingNodeData(
{Anchor(leaf->get(), context_.base)},
has_propagated_type ? IndexingFactType::kVariableDefinition
: IndexingFactType::kVariableReference));
}
}
void IndexingFactsTreeExtractor::ExtractInputOutputDeclaration(
const SyntaxTreeNode& identifier_unpacked_dimension) {
const SyntaxTreeLeaf* port_name_leaf =
GetSymbolIdentifierFromIdentifierUnpackedDimensions(
identifier_unpacked_dimension);
facts_tree_context_.top().NewChild(
IndexingNodeData({Anchor(port_name_leaf->get(), context_.base)},
IndexingFactType::kVariableDefinition));
}
void IndexingFactsTreeExtractor::ExtractModuleEnd(
const SyntaxTreeNode& module_declaration_node) {
const verible::TokenInfo* module_name =
GetModuleEndLabel(module_declaration_node);
if (module_name != nullptr) {
const Anchor module_end_anchor(*module_name, context_.base);
facts_tree_context_.top().Value().AppendAnchor(module_end_anchor);
}
}
// TODO(minatoma): consider this case:
// foo_module foo_instance(id1[id2],id3[id4]); // where instance is
// "foo_instance(...)"
void IndexingFactsTreeExtractor::ExtractModuleInstantiation(
const SyntaxTreeNode& data_declaration_node,
const std::vector<TreeSearchMatch>& gate_instances) {
IndexingNodeData module_node_data(IndexingFactType::kDataTypeReference);
IndexingFactNode module_node(module_node_data);
const verible::TokenInfo& type =
GetTypeTokenInfoFromDataDeclaration(data_declaration_node);
const Anchor type_anchor(type, context_.base);
module_node.Value().AppendAnchor(type_anchor);
// 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) {
IndexingNodeData module_instance_node_data(
IndexingFactType::kModuleInstance);
const verible::TokenInfo& variable_name =
GetModuleInstanceNameTokenInfoFromGateInstance(*instance.match);
const Anchor variable_name_anchor(variable_name, context_.base);
module_instance_node_data.AppendAnchor(variable_name_anchor);
std::vector<TreeSearchMatch> port_names =
FindAllUnqualifiedIds(*instance.match);
// Module ports are treated as anchors in instantiations.
for (const TreeSearchMatch& port : port_names) {
const SyntaxTreeLeaf* leaf = GetIdentifier(*port.match);
const Anchor port_name_anchor(leaf->get(), context_.base);
module_instance_node_data.AppendAnchor(port_name_anchor);
}
module_node.NewChild(module_instance_node_data);
}
facts_tree_context_.top().NewChild(module_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 verible::TokenInfo*> identifiers =
GetIdentifiersFromNetDeclaration(net_declaration_node);
// Loop through each instance and associate each declared id with the same
// type.
for (const verible::TokenInfo* wire_token_info : identifiers) {
facts_tree_context_.top().NewChild(
IndexingNodeData({Anchor(*wire_token_info, context_.base)},
IndexingFactType::kVariableDefinition));
}
}
void IndexingFactsTreeExtractor::ExtractPackageDeclaration(
const SyntaxTreeNode& package_declaration_node) {
IndexingNodeData package_node_data(IndexingFactType::kPackage);
IndexingFactNode package_node(package_node_data);
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&package_node);
// Extract package name.
const SyntaxTreeLeaf& package_name_leaf =
GetPackageNameLeaf(package_declaration_node);
const Anchor class_name_anchor(package_name_leaf.get(), context_.base);
facts_tree_context_.top().Value().AppendAnchor(class_name_anchor);
// Extract package name after endpackage if exists.
const SyntaxTreeLeaf* package_end_name =
GetPackageNameEndLabel(package_declaration_node);
if (package_end_name != nullptr) {
const Anchor package_end_anchor(package_end_name->get(), context_.base);
facts_tree_context_.top().Value().AppendAnchor(package_end_anchor);
}
// Visit package body it exists.
const verible::Symbol* package_item_list =
GetPackageItemList(package_declaration_node);
if (package_item_list != nullptr) {
Visit(verible::SymbolCastToNode(*package_item_list));
}
}
facts_tree_context_.top().NewChild(package_node);
}
void IndexingFactsTreeExtractor::ExtractMacroDefinition(
const verible::SyntaxTreeNode& preprocessor_definition) {
const verible::SyntaxTreeLeaf& macro_name =
GetMacroName(preprocessor_definition);
IndexingFactNode macro_node(IndexingNodeData(
{Anchor(macro_name.get(), context_.base)}, IndexingFactType::kMacro));
const std::vector<verible::TreeSearchMatch> args =
FindAllMacroDefinitionsArgs(preprocessor_definition);
for (const verible::TreeSearchMatch& arg : args) {
const verible::SyntaxTreeLeaf& leaf = GetMacroArgName(*arg.match);
macro_node.NewChild(
IndexingNodeData({Anchor(leaf.get(), context_.base)},
IndexingFactType::kVariableDefinition));
}
facts_tree_context_.top().NewChild(macro_node);
}
void IndexingFactsTreeExtractor::ExtractMacroCall(
const verible::SyntaxTreeNode& macro_call) {
const verible::TokenInfo& macro_call_name_token = GetMacroCallId(macro_call);
IndexingFactNode macro_node(
IndexingNodeData({Anchor(macro_call_name_token, context_.base)},
IndexingFactType::kMacroCall));
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&macro_node);
const verible::SyntaxTreeNode& macro_call_args =
GetMacroCallArgs(macro_call);
Visit(macro_call_args);
}
facts_tree_context_.top().NewChild(macro_node);
}
void IndexingFactsTreeExtractor::ExtractMacroReference(
const verible::SyntaxTreeLeaf& macro_identifier) {
facts_tree_context_.top().NewChild(
IndexingNodeData({Anchor(macro_identifier.get(), context_.base)},
IndexingFactType::kMacroCall));
}
void IndexingFactsTreeExtractor::ExtractFunctionDeclaration(
const SyntaxTreeNode& function_declaration_node) {
IndexingNodeData function_node_data(IndexingFactType::kFunctionOrTask);
IndexingFactNode function_node(function_node_data);
// Extract function name.
const auto* function_name_leaf = GetFunctionName(function_declaration_node);
const Anchor function_name_anchor(function_name_leaf->get(), context_.base);
function_node.Value().AppendAnchor(function_name_anchor);
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&function_node);
// Extract function ports.
ExtractFunctionTaskPort(function_declaration_node);
// Extract function body.
const SyntaxTreeNode& function_body =
GetFunctionBlockStatementList(function_declaration_node);
Visit(function_body);
}
facts_tree_context_.top().NewChild(function_node);
}
void IndexingFactsTreeExtractor::ExtractTaskDeclaration(
const SyntaxTreeNode& task_declaration_node) {
IndexingNodeData task_node_data(IndexingFactType::kFunctionOrTask);
IndexingFactNode task_node(task_node_data);
// Extract task name.
const auto* task_name_leaf = GetTaskName(task_declaration_node);
const Anchor task_name_anchor(task_name_leaf->get(), context_.base);
task_node.Value().AppendAnchor(task_name_anchor);
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_, &task_node);
// Extract task ports.
ExtractFunctionTaskPort(task_declaration_node);
// Extract task body.
const SyntaxTreeNode& task_body =
GetTaskStatementList(task_declaration_node);
Visit(task_body);
}
facts_tree_context_.top().NewChild(task_node);
}
void IndexingFactsTreeExtractor::ExtractFunctionTaskPort(
const SyntaxTreeNode& function_declaration_node) {
const std::vector<TreeSearchMatch> ports =
FindAllTaskFunctionPortDeclarations(function_declaration_node);
for (const TreeSearchMatch& port : ports) {
const SyntaxTreeLeaf* leaf =
GetIdentifierFromTaskFunctionPortItem(*port.match);
// TODO(minatoma): Consider using kPorts or kParam for ports and params
// instead of variables (same goes for modules).
facts_tree_context_.top().NewChild(
IndexingNodeData({Anchor(leaf->get(), context_.base)},
IndexingFactType::kVariableDefinition));
}
}
void IndexingFactsTreeExtractor::ExtractFunctionOrTaskCall(
const SyntaxTreeNode& function_call_node) {
IndexingNodeData function_node_data(IndexingFactType::kFunctionCall);
IndexingFactNode function_node(function_node_data);
// Extract function or task name.
const auto* function_name_leaf = GetFunctionCallName(function_call_node);
const Anchor task_name_anchor(function_name_leaf->get(), context_.base);
function_node.Value().AppendAnchor(task_name_anchor);
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&function_node);
// Extract function or task parameters.
TreeContextVisitor::Visit(function_call_node);
}
facts_tree_context_.top().NewChild(function_node);
}
void IndexingFactsTreeExtractor::ExtractClassDeclaration(
const SyntaxTreeNode& class_declaration) {
IndexingNodeData class_node_data(IndexingFactType::kClass);
IndexingFactNode class_node(class_node_data);
{
const IndexingFactsTreeContext::AutoPop p(&facts_tree_context_,
&class_node);
// Extract class name.
const SyntaxTreeLeaf& class_name_leaf = GetClassName(class_declaration);
const Anchor class_name_anchor(class_name_leaf.get(), context_.base);
facts_tree_context_.top().Value().AppendAnchor(class_name_anchor);
// Extract class name after endclass.
const SyntaxTreeLeaf* class_end_name = GetClassEndLabel(class_declaration);
if (class_end_name != nullptr) {
const Anchor class_end_anchor(class_end_name->get(), context_.base);
facts_tree_context_.top().Value().AppendAnchor(class_end_anchor);
}
// Visit class body.
const SyntaxTreeNode& class_item_list = GetClassItemList(class_declaration);
Visit(class_item_list);
}
facts_tree_context_.top().NewChild(class_node);
}
void IndexingFactsTreeExtractor::ExtractClassInstances(
const SyntaxTreeNode& data_declaration_node,
const std::vector<TreeSearchMatch>& register_variables) {
IndexingNodeData class_node_data(IndexingFactType::kDataTypeReference);
IndexingFactNode class_node(class_node_data);
const verible::TokenInfo& type =
GetTypeTokenInfoFromDataDeclaration(data_declaration_node);
const Anchor type_anchor(type, context_.base);
class_node.Value().AppendAnchor(type_anchor);
// 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 : register_variables) {
IndexingNodeData indexing_node_data(IndexingFactType::kClassInstance);
const verible::TokenInfo& variable_name =
GetInstanceNameTokenInfoFromRegisterVariable(*instance.match);
const Anchor variable_name_anchor(variable_name, context_.base);
indexing_node_data.AppendAnchor(variable_name_anchor);
class_node.NewChild(indexing_node_data);
}
facts_tree_context_.top().NewChild(class_node);
}
void IndexingFactsTreeExtractor::ExtractPackageImport(
const SyntaxTreeNode& package_import_item) {
IndexingNodeData package_import_data(IndexingFactType::kPackageImport);
const SyntaxTreeLeaf& package_name =
GetImportedPackageName(package_import_item);
package_import_data.AppendAnchor(Anchor(package_name.get(), context_.base));
const SyntaxTreeLeaf* imported_item =
GeImportedItemNameFromPackageImportItem(package_import_item);
if (imported_item != nullptr) {
package_import_data.AppendAnchor(
Anchor(imported_item->get(), context_.base));
}
facts_tree_context_.top().NewChild(package_import_data);
}
} // namespace kythe
} // namespace verilog