blob: a2edd8bcc497f2ff82c0ecb509164e4996285e75 [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/analysis/checkers/instance_shadow_rule.h"
#include <iterator>
#include <set>
#include <sstream>
#include "absl/strings/string_view.h"
#include "common/analysis/citation.h"
#include "common/analysis/lint_rule_status.h"
#include "common/analysis/matcher/bound_symbol_manager.h"
#include "common/analysis/matcher/matcher.h"
#include "common/analysis/syntax_tree_search.h"
#include "common/text/symbol.h"
#include "common/text/syntax_tree_context.h"
#include "common/text/tree_utils.h"
#include "common/util/iterator_adaptors.h"
#include "verilog/CST/identifier.h"
#include "verilog/CST/verilog_matchers.h" // IWYU pragma: keep
#include "verilog/CST/verilog_nonterminals.h"
#include "verilog/analysis/descriptions.h"
#include "verilog/analysis/lint_rule_registry.h"
namespace verilog {
namespace analysis {
using verible::GetStyleGuideCitation;
using verible::LintRuleStatus;
using verible::LintViolation;
using verible::SyntaxTreeContext;
using verible::matcher::Matcher;
// Register InstanceShadowRule
VERILOG_REGISTER_LINT_RULE(InstanceShadowRule);
absl::string_view InstanceShadowRule::Name() { return "instance-shadowing"; }
const char InstanceShadowRule::kTopic[] = "mark-shadowed-instances";
const char InstanceShadowRule::kMessage[] =
"Instance shadows the already declared variable";
const LintRuleDescriptor &InstanceShadowRule::GetDescriptor() {
static const LintRuleDescriptor d{
.name = "instance-shadowing",
.topic = "mark-shadowed-instances",
.desc =
"Warns if there are multiple declarations in the same scope "
"that shadow each other with the same name."};
return d;
}
static const Matcher &InstanceShadowMatcher() {
static const Matcher matcher(SymbolIdentifierLeaf());
return matcher;
}
static bool isInAllowedNode(const SyntaxTreeContext &ctx) {
return ctx.IsInside(NodeEnum::kSeqBlock) ||
ctx.IsInside(NodeEnum::kGenvarDeclaration) ||
ctx.IsInside(NodeEnum::kReference); // ||
// ctx.IsInside(NodeEnum::kModportSimplePort);
}
void InstanceShadowRule::HandleSymbol(const verible::Symbol &symbol,
const SyntaxTreeContext &context) {
verible::matcher::BoundSymbolManager manager;
bool omit_node = false;
if (!InstanceShadowMatcher().Matches(symbol, &manager)) {
return;
}
const auto labels = FindAllSymbolIdentifierLeafs(symbol);
if (labels.empty()) return;
// if the considered symbol name is not a declaration
if (context.IsInside(NodeEnum::kReference)) return;
// if the considered symbol name is in a modport, discard it
if (context.IsInside(NodeEnum::kModportSimplePort) ||
context.IsInside(NodeEnum::kModportClockingPortsDeclaration)) {
return;
}
// TODO: don't latch on to K&R-Style form in which the same symbol shows
// up twice.
const auto rcontext = reversed_view(context);
const auto *const rdirectParent = *std::next(rcontext.begin());
// we are looking for the potential labels that might overlap the considered
// declaration. We are searching all the labels within the visible scope
// until we find the node or we reach the top of the scope
for (const auto *node : rcontext) {
for (const verible::SymbolPtr &child : node->children()) {
if (child == nullptr) {
continue;
}
const auto overlappingLabels = FindAllSymbolIdentifierLeafs(*child);
for (const verible::TreeSearchMatch &omatch : overlappingLabels) {
const auto &overlappingLabel = SymbolCastToLeaf(*omatch.match);
// variable in different scopes or this is not a
// vulnerable declaration
if (isInAllowedNode(omatch.context)) {
omit_node = true;
break;
}
const auto &label = SymbolCastToLeaf(*labels[0].match);
// if found label has the same adress as considered label
// we found the same node so we don't
// want to look further
if (omatch.match == labels[0].match) {
omit_node = true;
break;
}
// if considered label is the last node
// this is the ending node with label
if (rdirectParent->back() == node->back()) {
return;
}
if (overlappingLabel.get().text() == label.get().text()) {
std::stringstream ss;
ss << "Symbol `" << overlappingLabel.get().text()
<< "` is shadowing symbol `" << label.get().text()
<< "` defined at @";
violations_.insert(LintViolation(symbol, ss.str(), context, {},
{overlappingLabel.get()}));
return;
}
}
if (omit_node) {
omit_node = false;
break;
}
}
}
}
LintRuleStatus InstanceShadowRule::Report() const {
return LintRuleStatus(violations_, Name(), GetStyleGuideCitation(kTopic));
}
} // namespace analysis
} // namespace verilog