Skip to content

Commit 44f5a35

Browse files
committed
Add lint rule aliasing
1 parent cb60b63 commit 44f5a35

8 files changed

+187
-10
lines changed

verilog/analysis/checkers/undersized_binary_literal_rule.cc

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ VERILOG_REGISTER_LINT_RULE(UndersizedBinaryLiteralRule);
5353

5454
const LintRuleDescriptor& UndersizedBinaryLiteralRule::GetDescriptor() {
5555
static const LintRuleDescriptor d{
56-
.name = "undersized-binary-literal",
56+
.name = "undersized-numeric-literal",
5757
.topic = "number-literals",
5858
.desc =
5959
"Checks that the digits of binary literals for the configured "
@@ -73,6 +73,20 @@ const LintRuleDescriptor& UndersizedBinaryLiteralRule::GetDescriptor() {
7373
return d;
7474
}
7575

76+
const std::vector<LintRuleAliasDescriptor>&
77+
UndersizedBinaryLiteralRule::GetAliasDescriptors() {
78+
static const std::vector<LintRuleAliasDescriptor> d{
79+
{
80+
.name = "undersized-binary-literal",
81+
.param_defaults =
82+
{
83+
{"bin", "true"},
84+
},
85+
},
86+
};
87+
return d;
88+
}
89+
7690
// Broadly, start by matching all number nodes with a
7791
// constant width and based literal.
7892

verilog/analysis/checkers/undersized_binary_literal_rule.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ class UndersizedBinaryLiteralRule : public verible::SyntaxTreeLintRule {
3838

3939
static const LintRuleDescriptor& GetDescriptor();
4040

41+
static const std::vector<LintRuleAliasDescriptor>& GetAliasDescriptors();
42+
4143
void HandleSymbol(const verible::Symbol& symbol,
4244
const verible::SyntaxTreeContext& context) final;
4345

verilog/analysis/default_rules.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ constexpr const char* kDefaultRuleSet[] = {
4444
"no-tabs",
4545
"posix-eof",
4646
"line-length",
47-
"undersized-binary-literal",
47+
"undersized-numeric-literal",
4848
"explicit-function-lifetime",
4949
"explicit-function-task-parameter-type",
5050
"explicit-task-lifetime",

verilog/analysis/descriptions.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,23 @@ struct LintConfigParameterDescriptor {
3636
};
3737

3838
struct LintRuleDescriptor {
39+
// TODO(wsip) use this data in help messages. Add/remove fields if needed
3940
LintRuleId name; // ID/name of the rule.
4041
absl::string_view topic; // section in style-guide
4142
absl::string_view dv_topic; // section in design verification style-guide
4243
std::string desc; // Detailed description.
4344
std::vector<LintConfigParameterDescriptor> param;
4445
};
4546

47+
struct LintRuleAliasDescriptor {
48+
LintRuleId name; // ID/name of the alias.
49+
absl::string_view topic; // section in style-guide
50+
absl::string_view dv_topic; // section in design verification style-guide
51+
std::string desc; // Detailed description
52+
std::vector<std::pair<absl::string_view, absl::string_view>>
53+
param_defaults; // List of param values set by the alias
54+
};
55+
4656
} // namespace analysis
4757
} // namespace verilog
4858

verilog/analysis/lint_rule_registry.cc

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include <memory>
1818
#include <string>
19+
#include <utility>
1920
#include <vector>
2021

2122
#include "absl/container/node_hash_map.h"
@@ -38,6 +39,21 @@ using verible::TokenStreamLintRule;
3839
using verible::container::FindOrNull;
3940

4041
namespace {
42+
43+
absl::node_hash_map<LintRuleId, LintRuleId>* GetLintRuleAliases() {
44+
// maps aliases to the original names of rules
45+
static auto* aliases = new absl::node_hash_map<LintRuleId, LintRuleId>();
46+
return aliases;
47+
}
48+
49+
absl::node_hash_map<LintRuleId, LintAliasDescriptionsFun>*
50+
GetLintRuleAliasDescriptors() {
51+
// maps rule name to a function that returns descriptors of its aliases
52+
static auto* desc =
53+
new absl::node_hash_map<LintRuleId, LintAliasDescriptionsFun>();
54+
return desc;
55+
}
56+
4157
// Used to export function local static pointer to avoid global variables
4258
template <typename RuleType>
4359
absl::node_hash_map<LintRuleId, LintRuleInfo<RuleType>>* GetLintRuleRegistry() {
@@ -112,11 +128,55 @@ class LintRuleRegistry {
112128

113129
} // namespace
114130

131+
std::set<LintRuleId> GetLintRuleAliases(LintRuleId rule_name) {
132+
std::set<LintRuleId> result;
133+
134+
for (auto const& alias : *GetLintRuleAliases()) {
135+
if (alias.second == rule_name) result.insert(alias.first);
136+
}
137+
return result;
138+
}
139+
140+
LintRuleAliasDescriptor GetLintRuleAliasDescriptor(LintRuleId rule_name,
141+
LintRuleId alias) {
142+
const auto* descriptors = GetLintRuleAliasDescriptors();
143+
const auto target = descriptors->find(rule_name);
144+
145+
CHECK(target != descriptors->end());
146+
LintAliasDescriptionsFun desc_fun = target->second;
147+
// desc_fun() returns a reference to a vector of descriptors of aliases
148+
std::vector<LintRuleAliasDescriptor> alias_descriptors = desc_fun();
149+
size_t i = 0;
150+
for (; i != alias_descriptors.size(); i++) {
151+
if (alias_descriptors[i].name == alias) {
152+
return alias_descriptors[i];
153+
}
154+
}
155+
LOG(FATAL) << "caller of " << __FUNCTION__
156+
<< "shall make sure that the alias belongs to the rule";
157+
abort();
158+
}
159+
115160
template <typename RuleType>
116161
LintRuleRegisterer<RuleType>::LintRuleRegisterer(
117162
const LintDescriptionFun& descriptor,
118-
const LintRuleGeneratorFun<RuleType>& creator) {
163+
const LintRuleGeneratorFun<RuleType>& creator,
164+
const LintAliasDescriptionsFun alias_descriptors) {
119165
LintRuleRegistry<RuleType>::Register(descriptor, creator);
166+
167+
if (!alias_descriptors) return;
168+
169+
// map rule name with the function that returns a vector of alias descriptions
170+
GetLintRuleAliasDescriptors()->insert(
171+
std::pair<LintRuleId, LintAliasDescriptionsFun>(descriptor().name,
172+
alias_descriptors));
173+
174+
const std::vector<LintRuleAliasDescriptor> descrs = alias_descriptors();
175+
for (auto const& descr : descrs) {
176+
// map every alias of this rule to the name of the rule
177+
GetLintRuleAliases()->insert(
178+
std::pair<LintRuleId, LintRuleId>(descr.name, descriptor().name));
179+
}
120180
}
121181

122182
bool IsRegisteredLintRule(const LintRuleId& rule_name) {
@@ -180,6 +240,16 @@ std::set<LintRuleId> GetAllRegisteredLintRuleNames() {
180240
return result;
181241
}
182242

243+
LintRuleId TranslateAliasIfExists(const LintRuleId alias) {
244+
const auto* aliases = GetLintRuleAliases();
245+
const auto target = aliases->find(alias);
246+
if (target != aliases->end()) {
247+
return target->second;
248+
} else {
249+
return alias;
250+
}
251+
}
252+
183253
LintRuleDescriptionsMap GetAllRuleDescriptions() {
184254
// Map that will hold the information to print about each rule.
185255
LintRuleDescriptionsMap res;

verilog/analysis/lint_rule_registry.h

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ class LintRuleRegisterer;
5252
template <typename RuleType>
5353
using LintRuleGeneratorFun = std::function<std::unique_ptr<RuleType>()>;
5454
using LintDescriptionFun = std::function<LintRuleDescriptor()>;
55+
using LintAliasDescriptionsFun =
56+
const std::vector<LintRuleAliasDescriptor>& (*)();
5557

5658
template <typename RuleType>
5759
struct LintRuleInfo {
@@ -67,6 +69,28 @@ struct LintRuleDefaultConfig {
6769
using LintRuleDescriptionsMap =
6870
std::map<LintRuleId, LintRuleDefaultConfig, verible::StringViewCompare>;
6971

72+
// Class that uses SFINAE to conditionally get T::GetAliasDescriptors function.
73+
template <class T>
74+
class GetAliasDescriptorsFuncOrNullptr {
75+
template <typename U>
76+
static constexpr LintAliasDescriptionsFun get(
77+
decltype(U::GetAliasDescriptors)*) {
78+
return U::GetAliasDescriptors;
79+
}
80+
template <typename U>
81+
static constexpr LintAliasDescriptionsFun get(...) {
82+
return nullptr;
83+
}
84+
85+
public:
86+
// Pointer to T::GetAliasDescriptors if it's implemented, nullptr otherwise.
87+
static constexpr LintAliasDescriptionsFun value = get<T>(nullptr);
88+
};
89+
90+
template <class T>
91+
inline constexpr LintAliasDescriptionsFun GetAliasDescriptorsFunc =
92+
GetAliasDescriptorsFuncOrNullptr<T>::value;
93+
7094
// Helper macro to register a LintRule with LintRuleRegistry. In order to have
7195
// a global registry, some static initialization is needed. This macros
7296
// centralizes this unsafe code into one place in order to prevent mistakes.
@@ -98,11 +122,14 @@ using LintRuleDescriptionsMap =
98122
//
99123
// TODO(hzeller): once the class does not contain a state, extract the name
100124
// and description from the instance to avoid weird static initialization.
101-
#define VERILOG_REGISTER_LINT_RULE(class_name) \
102-
static verilog::analysis::LintRuleRegisterer<class_name::rule_type> \
103-
__##class_name##__registerer(class_name::GetDescriptor, []() { \
104-
return std::unique_ptr<class_name::rule_type>(new class_name()); \
105-
});
125+
#define VERILOG_REGISTER_LINT_RULE(class_name) \
126+
static verilog::analysis::LintRuleRegisterer<class_name::rule_type> \
127+
__##class_name##__registerer( \
128+
class_name::GetDescriptor, \
129+
[]() { \
130+
return std::unique_ptr<class_name::rule_type>(new class_name()); \
131+
}, \
132+
verilog::analysis::GetAliasDescriptorsFunc<class_name>);
106133

107134
// Static objects of type LintRuleRegisterer are used to register concrete
108135
// parsers in LintRuleRegistry. Users are expected to create these objects
@@ -111,7 +138,8 @@ template <typename RuleType>
111138
class LintRuleRegisterer {
112139
public:
113140
LintRuleRegisterer(const LintDescriptionFun& descriptor,
114-
const LintRuleGeneratorFun<RuleType>& creator);
141+
const LintRuleGeneratorFun<RuleType>& creator,
142+
LintAliasDescriptionsFun alias_descriptors);
115143
};
116144

117145
// Returns true if rule_name refers to a known lint rule.
@@ -150,6 +178,18 @@ std::unique_ptr<verible::TextStructureLintRule> CreateTextStructureLintRule(
150178
// this set, because their lifetime is guaranteed by the registration process.
151179
std::set<LintRuleId> GetAllRegisteredLintRuleNames();
152180

181+
// Returns a set of aliases that were registered for given rule name.
182+
std::set<LintRuleId> GetLintRuleAliases(LintRuleId rule_name);
183+
184+
// Returns a descriptor of a specific alias of a rule.
185+
// The caller shall check that the alias belongs to this rule.
186+
LintRuleAliasDescriptor GetLintRuleAliasDescriptor(LintRuleId rule_name,
187+
LintRuleId alias);
188+
189+
// Returns a translated name of lint rule using a registered alias.
190+
// If such an alias doesn't exist, returns the input alias.
191+
LintRuleId TranslateAliasIfExists(LintRuleId alias);
192+
153193
// Returns a map mapping each rule to a struct of information about the rule to
154194
// print.
155195
LintRuleDescriptionsMap GetAllRuleDescriptions();

verilog/analysis/verilog_linter.cc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,7 @@ absl::Status PrintRuleInfo(std::ostream* os,
553553
constexpr int kParamIndent = kRuleWidth + 4;
554554
constexpr char kFill = ' ';
555555

556+
rule_name = analysis::TranslateAliasIfExists(rule_name);
556557
const auto it = rule_map.find(rule_name);
557558
if (it == rule_map.end())
558559
return absl::NotFoundError(absl::StrCat(
@@ -574,6 +575,21 @@ absl::Status PrintRuleInfo(std::ostream* os,
574575
}
575576
}
576577

578+
const auto aliases = analysis::GetLintRuleAliases(rule_name);
579+
if (!aliases.empty()) {
580+
*os << std::left << std::setw(kRuleWidth) << std::setfill(kFill) << " "
581+
<< "Alias" << (aliases.size() > 1 ? "es" : "") << ":\n";
582+
for (const auto& alias : aliases) {
583+
const auto descr = analysis::GetLintRuleAliasDescriptor(rule_name, alias);
584+
*os << std::left << std::setw(kParamIndent) << std::setfill(kFill) << " "
585+
<< "* `" << alias << "` sets parameters:";
586+
for (const auto& param : descr.param_defaults) {
587+
*os << " " << param.first << ":" << param.second << ";";
588+
}
589+
*os << "\n";
590+
}
591+
}
592+
577593
// Print default enabled.
578594
*os << std::left << std::setw(kRuleWidth) << std::setfill(kFill) << " "
579595
<< "Enabled by default: " << std::boolalpha << it->second.default_enabled

verilog/analysis/verilog_linter_configuration.cc

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ bool RuleBundle::ParseConfiguration(absl::string_view text, char separator,
130130
const auto config = rule_name_with_config.substr(equals_pos + 1);
131131
setting.configuration.assign(config.data(), config.size());
132132
}
133-
const auto rule_name = rule_name_with_config.substr(0, equals_pos);
133+
const auto raw_name = rule_name_with_config.substr(0, equals_pos);
134+
const auto rule_name = analysis::TranslateAliasIfExists(raw_name);
134135
const auto rule_name_set = analysis::GetAllRegisteredLintRuleNames();
135136
const auto rule_iter = rule_name_set.find(rule_name);
136137

@@ -139,6 +140,30 @@ bool RuleBundle::ParseConfiguration(absl::string_view text, char separator,
139140
*error = absl::StrCat("invalid flag \"", rule_name, "\"");
140141
return false;
141142
} else {
143+
// check if it's an alias
144+
if (raw_name != rule_name) {
145+
auto alias_descriptor =
146+
analysis::GetLintRuleAliasDescriptor(rule_name, raw_name);
147+
VLOG(2) << raw_name << " is an alias of " << rule_name;
148+
149+
if (setting.enabled) {
150+
// apply alias defaults, only if we're enabling the rule
151+
std::vector<std::string> params;
152+
for (auto const& param : alias_descriptor.param_defaults) {
153+
// get the default parameters from the alias descriptor
154+
params.push_back(absl::StrCat(param.first, ":", param.second));
155+
}
156+
std::string defaults = absl::StrJoin(params, ";");
157+
VLOG(2) << "configuration from alias defaults: " << defaults;
158+
159+
// use the defaults first, then the commandline arguments
160+
// join them with "," only if both have contents
161+
setting.configuration = absl::StrJoin(
162+
{defaults, setting.configuration},
163+
(defaults.empty() || setting.configuration.empty()) ? "" : ";");
164+
}
165+
}
166+
142167
// Map keys must use canonical registered string_views for guaranteed
143168
// lifetime, not just any string-equivalent copy.
144169
rules[*rule_iter] = setting;

0 commit comments

Comments
 (0)