blob: c171430707f01a6e860f30db050c2487b03c273c [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/disable_statement_rule.h"
#include <iterator>
#include <set>
#include "absl/strings/string_view.h"
#include "common/analysis/lint_rule_status.h"
#include "common/analysis/matcher/bound_symbol_manager.h"
#include "common/analysis/matcher/matcher.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::LintRuleStatus;
using verible::LintViolation;
using verible::SyntaxTreeContext;
using verible::matcher::Matcher;
VERILOG_REGISTER_LINT_RULE(DisableStatementNoLabelsRule);
static constexpr absl::string_view kMessage =
"Invalid usage of disable statement. Preferred construction is: disable "
"fork;";
static constexpr absl::string_view kMessageSeqBlock =
"Invalid usage of disable statement. Preferred construction is: disable "
"label_of_seq_block;";
const LintRuleDescriptor &DisableStatementNoLabelsRule::GetDescriptor() {
static const LintRuleDescriptor d{
.name = "disable-statement",
.topic = "disable-invalid-in-non-sequential",
.desc =
"Checks that there are no occurrences of `disable some_label` "
"if label is referring to a fork or other none sequential block "
"label. Use `disable fork` instead.",
};
return d;
}
static const Matcher &DisableMatcher() {
static const Matcher matcher(
NodekDisableStatement(DisableStatementHasLabel()));
return matcher;
}
void DisableStatementNoLabelsRule::HandleSymbol(
const verible::Symbol &symbol, const SyntaxTreeContext &context) {
verible::matcher::BoundSymbolManager manager;
if (!DisableMatcher().Matches(symbol, &manager)) {
return;
}
absl::string_view message_final = kMessage;
// if no kDisable label, return, nothing to be checked
const auto &disableLabels = FindAllSymbolIdentifierLeafs(symbol);
if (disableLabels.empty()) {
return;
}
// look for every kBegin node starting from kDisableLabel token
// the kDisableLabel can be nested in some kBegin nodes
// so we're looking for the kBegin node that direct parent
// is not one of the initial/final/always statements since such blocks are
// considered to be invalid. If the label for disable statements is not
// found, it means that there is no appropriate label or the label
// points to the illegal node such as forked label
const auto &rcontext = reversed_view(context);
for (auto rc = rcontext.begin(); rc != rcontext.end(); rc++) {
const auto &node = *rc;
if (node->Tag().tag != static_cast<int>(NodeEnum::kSeqBlock)) {
continue;
}
for (const auto &ch : node->children()) {
if (ch->Tag().tag != static_cast<int>(NodeEnum::kBegin)) {
continue;
}
const auto &beginLabels = FindAllSymbolIdentifierLeafs(*ch);
if (beginLabels.empty()) {
continue;
}
const auto &pnode = *std::next(rc);
const auto &ptag = pnode->Tag().tag;
if (ptag == static_cast<int>(NodeEnum::kInitialStatement) ||
ptag == static_cast<int>(NodeEnum::kFinalStatement) ||
ptag == static_cast<int>(NodeEnum::kAlwaysStatement)) {
message_final = kMessageSeqBlock;
break;
}
const auto &beginLabel = SymbolCastToLeaf(*beginLabels[0].match);
const auto &disableLabel = SymbolCastToLeaf(*disableLabels[0].match);
if (beginLabel.get().text() == disableLabel.get().text()) {
return;
}
}
}
violations_.insert(LintViolation(symbol, message_final, context));
}
LintRuleStatus DisableStatementNoLabelsRule::Report() const {
return LintRuleStatus(violations_, GetDescriptor());
}
} // namespace analysis
} // namespace verilog