blob: e4f5c9048193a608935e2281b5f2b50cd480ce3c [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 "verible/verilog/tools/kythe/scope-resolver.h"
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "absl/container/flat_hash_set.h"
#include "absl/strings/str_cat.h"
#include "verible/common/util/logging.h"
#include "verible/verilog/tools/kythe/kythe-facts.h"
namespace verilog {
namespace kythe {
void ScopeResolver::SetCurrentScope(const Signature &scope) {
if (current_scope_ == scope && !current_scope_digest_.rolling_hash.empty()) {
return;
}
current_scope_digest_ = scope.Digest();
current_scope_ = scope;
if (enable_debug_) {
scope_to_string_debug_.emplace(scope.Digest(), scope.ToString());
}
VLOG(2) << "Set scope to: " << ScopeDebug(scope.Digest());
}
void ScopeResolver::RemoveDefinitionFromCurrentScope(const VName &vname) {
std::string_view name = vname.signature.Names().back();
auto scopes = variable_to_scoped_vname_.find(name);
if (scopes == variable_to_scoped_vname_.end()) {
VLOG(1) << "No definition for '" << name << "'. Nothing to remove.";
return;
}
SignatureDigest current_scope_digest = CurrentScopeDigest();
VLOG(2) << "Remove " << name << " from " << ScopeDebug(current_scope_digest);
for (auto iter = scopes->second.begin(); iter != scopes->second.end();
++iter) {
if (iter->instantiation_scope == current_scope_digest) {
scopes->second.erase(iter);
break;
}
}
auto current_scope_names = scope_to_vnames_.find(current_scope_digest);
if (current_scope_names == scope_to_vnames_.end()) {
return;
}
auto &vnames = current_scope_names->second;
for (auto iter = vnames.begin(); iter != vnames.end(); ++iter) {
if (*iter == vname) {
vnames.erase(iter);
break;
}
}
}
void ScopeResolver::AppendScopeToCurrentScope(
const SignatureDigest &source_scope) {
AppendScopeToScope(source_scope, CurrentScopeDigest());
}
void ScopeResolver::AppendScopeToScope(
const SignatureDigest &source_scope,
const SignatureDigest &destination_scope) {
auto scope_vnames = scope_to_vnames_.find(source_scope);
if (scope_vnames == scope_to_vnames_.end()) {
VLOG(2) << "Can't find scope " << ScopeDebug(source_scope)
<< " to append it to the current scope";
return;
}
if (source_scope == destination_scope) {
// The source and destination scope are equal. Nothing to add.
return;
}
for (const auto &vn : scope_vnames->second) {
const std::optional<ScopedVname> vn_type =
FindScopeAndDefinition(vn.signature.Names().back(), source_scope);
if (!vn_type) {
continue;
}
variable_to_scoped_vname_[vn.signature.Names().back()].insert(
ScopedVname{.type_scope = vn_type->type_scope,
.instantiation_scope = destination_scope,
.vname = vn});
scope_to_vnames_[destination_scope].insert(vn);
}
}
void ScopeResolver::AddDefinitionToCurrentScope(const VName &new_member) {
AddDefinitionToCurrentScope(new_member, new_member.signature.Digest());
}
void ScopeResolver::AddDefinitionToCurrentScope(
const VName &new_member, const SignatureDigest &type_scope) {
// Remove the existing definition -- overwrite it with the new which has
// updated information about types.
RemoveDefinitionFromCurrentScope(new_member);
auto current_scope_digest = CurrentScopeDigest();
variable_to_scoped_vname_[new_member.signature.Names().back()].insert(
ScopedVname{.type_scope = type_scope,
.instantiation_scope = current_scope_digest,
.vname = new_member});
scope_to_vnames_[current_scope_digest].insert(new_member);
}
std::optional<ScopedVname> ScopeResolver::FindScopeAndDefinition(
std::string_view name, const SignatureDigest &scope_focus) {
VLOG(2) << "Find definition for '" << name << "' within scope "
<< ScopeDebug(scope_focus);
auto scope = variable_to_scoped_vname_.find(name);
if (scope == variable_to_scoped_vname_.end()) {
VLOG(2) << "Failed to find definition for '" << name << "' within scope "
<< ScopeDebug(scope_focus) << " (unregistered name)";
return {};
}
const ScopedVname *match = nullptr;
for (auto &scope_member : scope->second) {
SignatureDigest digest = scope_member.instantiation_scope;
if (scope_focus.rolling_hash.size() < digest.rolling_hash.size() ||
(match != nullptr &&
digest.rolling_hash.size() <
match->instantiation_scope.rolling_hash.size())) {
// Mismatch, or not interesting (worse match).
VLOG(2) << "Scope resolution mismatch for '" << name << "' at scope "
<< ScopeDebug(digest);
continue;
}
if (scope_focus.rolling_hash[digest.rolling_hash.size() - 1] ==
digest.Hash()) {
match = &scope_member;
}
}
if (match != nullptr) {
VLOG(2) << "Found definition for '" << name << "' within scope "
<< ScopeDebug(scope_focus);
return *match;
}
VLOG(2) << "Failed to find definition for '" << name << "' within scope "
<< ScopeDebug(scope_focus);
return {};
}
std::optional<ScopedVname> ScopeResolver::FindScopeAndDefinition(
std::string_view name) {
return FindScopeAndDefinition(name, CurrentScopeDigest());
}
const absl::flat_hash_set<VName> &ScopeResolver::ListScopeMembers(
const SignatureDigest &scope_digest) const {
const static absl::flat_hash_set<VName> kEmptyMemberList;
auto scope = scope_to_vnames_.find(scope_digest);
if (scope == scope_to_vnames_.end()) {
return kEmptyMemberList;
}
return scope->second;
}
std::string ScopeResolver::ScopeDebug(const SignatureDigest &scope) const {
if (!enable_debug_) {
return "UNKNOWN (debug off)";
}
const auto s = scope_to_string_debug_.find(scope);
if (s == scope_to_string_debug_.end()) {
return absl::StrCat("UNKNOWN ", scope.Hash());
}
return absl::StrCat(s->second, " H: ", scope.Hash());
}
} // namespace kythe
} // namespace verilog