blob: ab5d03414bb7bfb381f4c209b4a1ee564172964c [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.
#ifndef VERIBLE_VERILOG_TOOLS_KYTHE_SCOPE_RESOLVER_H_
#define VERIBLE_VERILOG_TOOLS_KYTHE_SCOPE_RESOLVER_H_
#include <map>
#include <set>
#include <utility>
#include <vector>
#include "absl/container/node_hash_map.h"
#include "absl/strings/string_view.h"
#include "common/util/auto_pop_stack.h"
#include "common/util/iterator_range.h"
#include "verilog/tools/kythe/kythe_facts.h"
namespace verilog {
namespace kythe {
// Used to wrap whatever needs to be recorded in a scope item.
// Right now, this is just a VName, if it will be more, make this a struct.
using ScopeMemberItem = VName;
// Represents some scope in SystemVerilog code like class or module.
class Scope {
public:
Scope() = default;
explicit Scope(const Signature& signature) : signature_(signature) {}
Scope(const Scope&) = default;
Scope(Scope&&) = default;
Scope& operator=(const Scope&) = default;
Scope& operator=(Scope&&) = default;
// Appends the given scope item to the members of this scope.
void AddMemberItem(const ScopeMemberItem& member_item);
// Appends the member of the given scope to the current scope.
void AppendScope(const Scope& scope);
using MemberMap = absl::node_hash_map<absl::string_view, ScopeMemberItem>;
const MemberMap& Members() const { return members_; }
const Signature& GetSignature() const { return signature_; }
// Searches for the given reference_name in the current scope and returns its
// VName or nullptr if not found.
const VName* SearchForDefinition(absl::string_view name) const;
// Removes the given VName from the members.
void RemoveMember(const ScopeMemberItem& member);
private:
// Signature of the owner of this scope.
Signature signature_;
// list of the members inside this scope.
MemberMap members_;
};
// Container with a stack of Scopes to hold the accessible scopes during
// traversing an Indexing Facts Tree.
// This is used to get the definitions of some reference.
//
// This is modified during tree traversal because in case of entering new
// scope the new scope is resolved first and after that it's added to the
// containing scope and the next scope is being analyzed.
// e.g
// package pkg;
// int x;
// function int my_fun();
// return x;
// endfunction
// endpackage
//
// Scope stack when extracting my_fun:
// The Global Scope
// {
// "pkg"
// }
// Pkg Scope
// {
// pkg#x
// }
// when trying to find a definition for "x" in "return x" ScopeContext
// try to find the first matching definition starting from the innermost/nearest
// scope or returns a nullptr if not found.
class ScopeContext : public verible::AutoPopStack<Scope*> {
public:
typedef verible::AutoPopStack<Scope*> base_type;
ScopeContext() = default;
ScopeContext(const ScopeContext&) = delete;
ScopeContext(ScopeContext&&) = delete;
ScopeContext& operator=(const ScopeContext&) = delete;
ScopeContext& operator=(ScopeContext&&) = delete;
// member class to handle push and pop of stack safely
using AutoPop = base_type::AutoPop;
// returns the top VName of the stack
Scope& top() { return *ABSL_DIE_IF_NULL(base_type::top()); }
const Scope& top() const { return *ABSL_DIE_IF_NULL(base_type::top()); }
// TODO(minatoma): improve performance and memory for this function.
//
// This function uses string matching to find the definition of some
// variable in reverse order of the current scopes.
//
// Improvement can be replacing the string matching to comparison based on
// integers or enums.
//
// Search function to get the VName of a definitions of some reference.
// It loops over the scopes in reverse order and loops over members of every
// scope in reverse order to find a definition for the variable with given
// prefix signature.
// e.g
// {
// bar#module,
// foo#module,
// }
// {
// other scope,
// }
// Given bar#module it return the whole VName of that definition.
// And if more than one match is found the first would be returned.
const VName* SearchForDefinition(absl::string_view name) const;
};
// Keeps track and saves the explored scopes with a <key, value> and maps every
// signature to its scope.
//
// ScopeResolver saves the scopes generated while traversing the
// IndexingFactsTree so that they can be use to find some definition.
// Here the scopes are saved in a flattened manner instead of tree like
// hierarchy (in a structure which looks like symbol tables).
// e.g
// class m;
// int x;
// function int my_fun();
// int x;
// return x;
// endfunction
// endclass
//
// The generated scope would be:
// {
// "m": {
// m#x,
// m#my_fun
// },
// "my_fun": {
// m#my_fun#x
// }
// }
// this way definitions can be found using the signatures.
class ScopeResolver {
public:
ScopeResolver(const Signature& global_scope_signature,
const ScopeResolver* previous_file_scope_resolver)
: previous_file_scope_resolver_(previous_file_scope_resolver),
global_scope_signature_(global_scope_signature) {}
ScopeResolver(const ScopeResolver&) = delete;
ScopeResolver(ScopeResolver&&) = delete;
ScopeResolver& operator=(const ScopeResolver&) = delete;
ScopeResolver& operator=(ScopeResolver&&) = delete;
// Searches for the definitions of the given references' names.
std::vector<std::pair<const VName*, const Scope*>> SearchForDefinitions(
const std::vector<absl::string_view>& names) const;
// Searches for definition of the given reference's name in the current
// scope (the top of scope_context).
const VName* SearchForDefinitionInCurrentScope(absl::string_view name) const;
// Removes the given VName from the current scope (the top of scope_context).
void RemoveDefinitionFromCurrentScope(const VName& vname);
// Adds the members of the given scope to the current scope (the top of
// scope_context).
void AppendScopeToCurrentScope(const Scope& scope);
// Adds a ScopeMemberItem to the current scope (the top of scope_context).
void AddDefinitionToCurrentScope(const ScopeMemberItem& new_member);
// Searches for a scope with the given signature in the scopes.
const Scope* SearchForScope(const Signature& signature) const;
// Maps the given signature to the given scope.
void MapSignatureToScope(const Signature& signature, const Scope& scope);
ScopeContext& GetMutableScopeContext() { return scope_context_; }
private:
// Searches for a definition with the given name in the scope context (returns
// nullptr if a definitions is not found).
const VName* SearchForDefinitionInScopeContext(absl::string_view name) const;
// Searches for a definition with the given name in the global scope of this
// ScopeResolver.
const VName* SearchForDefinitionInGlobalScope(absl::string_view name) const;
// Searches for a definition with the given name in the scope with the given
// signature.
const VName* SearchForDefinitionInScope(const Signature& signature,
absl::string_view name) const;
// Keeps track of scopes and definitions inside the scopes of ancestors as
// the visitor traverses the facts tree.
ScopeContext scope_context_;
// Saves signatures alongside with their inner members (scope).
// This is used for resolving references to some variables after using
// import pkg::*. or other member access like class_Type::my_var.
// e.g
// package pkg1;
// function my_fun(); endfunction
// class my_class; endclass
// endpackage
//
// package pkg2;
// function my_fun(); endfunction
// class my_class; endclass
// endpackage
//
// Creates the following:
// {
// "pkg1": ["my_fun", "my_class"],
// "pkg2": ["my_fun", "my_class"]
// }
// NB: we internally work with pointers of scopes stored in the
// value, so we can't use flat_hash_map that moves around Scope.
absl::node_hash_map<Signature, Scope> scopes_;
// Pointer to the previous file's discovered scopes (if a previous file
// exists). This is used for definition finding in cross-file referencing.
// This forms a null-terminated singly-linked list across files.
const ScopeResolver* previous_file_scope_resolver_;
// The signature of the global scope of this ScopeResolver.
const Signature global_scope_signature_;
};
} // namespace kythe
} // namespace verilog
#endif // VERIBLE_VERILOG_TOOLS_KYTHE_SCOPE_RESOLVER_H_