-
Notifications
You must be signed in to change notification settings - Fork 124
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add new features to slang-tidy (#777)
This PR adds new features to slang-tidy: * slang-tidy can read its configuration from a .slang-tidy file, closely similar to what .clang-tidy offers. * Adds new checks to the linter. * Small refactor in the code of the checks. * Add option to skip linting checks from specific files.
- Loading branch information
Showing
32 changed files
with
2,048 additions
and
248 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
//------------------------------------------------------------------------------ | ||
//! @file TypePrinter.h | ||
//! @brief Utility function that let's you print the type of a variable | ||
// | ||
// SPDX-FileCopyrightText: Michael Popoloski | ||
// SPDX-License-Identifier: MIT | ||
//------------------------------------------------------------------------------ | ||
#pragma once | ||
|
||
// Courtesy of: | ||
// https://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c/64490578#64490578 | ||
|
||
#include <string_view> | ||
|
||
namespace slang { | ||
|
||
/// Allows you to print the demangled name of a C++ type. | ||
template<typename T> | ||
constexpr std::string_view typeName(); | ||
|
||
template<> | ||
constexpr std::string_view typeName<void>() { | ||
return "void"; | ||
} | ||
|
||
namespace type_printer_inner { | ||
|
||
using type_name_prober = void; | ||
|
||
template<typename T> | ||
constexpr std::string_view wrappedTypeName() { | ||
#ifdef __clang__ | ||
return __PRETTY_FUNCTION__; | ||
#elif defined(__GNUC__) | ||
return __PRETTY_FUNCTION__; | ||
#elif defined(_MSC_VER) | ||
return __FUNCSIG__; | ||
#else | ||
# error "Unsupported compiler" | ||
#endif | ||
} | ||
|
||
constexpr std::size_t wrappedTypeNamePrefixLength() { | ||
return wrappedTypeName<type_name_prober>().find(typeName<type_name_prober>()); | ||
} | ||
|
||
constexpr std::size_t wrappedTypeNameSuffixLength() { | ||
return wrappedTypeName<type_name_prober>().length() - wrappedTypeNamePrefixLength() - | ||
typeName<type_name_prober>().length(); | ||
} | ||
|
||
} // namespace type_printer_inner | ||
|
||
template<typename T> | ||
constexpr std::string_view typeName() { | ||
constexpr auto wrapped_name = type_printer_inner::wrappedTypeName<T>(); | ||
constexpr auto prefix_length = type_printer_inner::wrappedTypeNamePrefixLength(); | ||
constexpr auto suffix_length = type_printer_inner::wrappedTypeNameSuffixLength(); | ||
constexpr auto type_name_length = wrapped_name.length() - prefix_length - suffix_length; | ||
return wrapped_name.substr(prefix_length, type_name_length); | ||
} | ||
} // namespace slang |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# Slang Tidy | ||
|
||
A SystemVerilog linter | ||
|
||
## Configuration File | ||
|
||
Slang Tidy can be configured using a `.slang-tidy`. This file can be provided using the `--config-file` argument or the | ||
`slang-tidy` tool will search for it from the path where it has been called until the root directory. | ||
|
||
### Configuration file grammar | ||
|
||
The grammar of the config file is the following | ||
|
||
``` | ||
config ::= section+ | ||
section ::= checks_config_section | checks_section | ||
checks_section ::= "Checks:" [EOL] rule+ | ||
rule ::= ["-"] ((check_or_all ",")* | check_or_all) END | ||
check_or_all ::= "*" | check_group "-" check_name_or_all | ||
check_group ::= identifier | ||
check_name_or_all ::= "*" | identifier | ||
checks_config_section ::= "CheckConfigs:" [EOL] config+ | ||
config ::= ((config_tuple ",")* | config_tuple) END | ||
config_tuple ::= config_name ":" config_value | ||
config_name ::= identifier | ||
config_value ::= identifier | ||
identifier ::= {A-Za-z}+ | ||
EOL ::= '\n' | '\r' | '\r\n' | ||
END ::= EOL* | EOF | ||
``` | ||
|
||
### Configuration file example | ||
|
||
``` | ||
Checks: | ||
-synthesis-only-assigned-on-reset, | ||
-ports-enforce-port-suffix | ||
CheckConfigs: | ||
clkName: clk, | ||
resetIsActiveHigh: false, | ||
inputPortSuffix: _k, | ||
inputPortSuffix: _p | ||
``` | ||
|
||
## How to add a new check | ||
1. Create a new `cpp` file with the name of the check in CamelCase format inside the check kind folder. | ||
2. Inside the new `cpp` file create a class that inherits from `TidyChecks`. Use the `check` function to implement | ||
the code that will perform the check in the AST. | ||
3. Use the `REGISTER` macro to register the new check in the factory. | ||
4. Create the new tidy diagnostic in the `TidyDiags.h` file. | ||
5. Add the new check to the corresponding map in the `TidyConfig` constructor. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
//------------------------------------------------------------------------------ | ||
//! @file TidyConfig.h | ||
//! @brief Configuration of the tidy tool | ||
// | ||
// SPDX-FileCopyrightText: Michael Popoloski | ||
// SPDX-License-Identifier: MIT | ||
//------------------------------------------------------------------------------ | ||
#pragma once | ||
|
||
#include "TidyKind.h" | ||
#include <algorithm> | ||
#include <fmt/format.h> | ||
#include <slang/util/CppTypePrinter.h> | ||
#include <string> | ||
#include <unordered_map> | ||
#include <unordered_set> | ||
#include <vector> | ||
|
||
class TidyConfig { | ||
friend class TidyConfigParser; | ||
|
||
public: | ||
/// Configuration values of checks | ||
struct CheckConfigs { | ||
std::string clkName; | ||
std::string resetName; | ||
bool resetIsActiveHigh; | ||
std::string inputPortSuffix; | ||
std::string outputPortSuffix; | ||
std::string inoutPortSuffix; | ||
}; | ||
|
||
/// Default TidyConfig constructor which will set the default check's configuration values | ||
TidyConfig(); | ||
|
||
/// Returns whether a check is enabled or not | ||
[[nodiscard]] bool isCheckEnabled(slang::TidyKind kind, const std::string& checkName) const; | ||
|
||
/// Returns the check config object | ||
inline const CheckConfigs& getCheckConfigs() const { return checkConfigs; } | ||
|
||
// Returns a vector containing the file names that won't be checked by slang-tidy | ||
inline const std::vector<std::string>& getSkipFiles() const { return skipFiles; } | ||
|
||
// Adds a new file into the list of skipped files | ||
void addSkipFile(const std::string& path); | ||
void addSkipFile(std::vector<std::string> paths); | ||
|
||
private: | ||
CheckConfigs checkConfigs; | ||
|
||
/// Possible status of the checks | ||
enum class CheckStatus { ENABLED, DISABLED }; | ||
|
||
std::unordered_map<slang::TidyKind, std::unordered_map<std::string, CheckStatus>> checkKinds; | ||
|
||
// List of files that won't be checked by slang-tidy | ||
std::vector<std::string> skipFiles; | ||
|
||
/// Enables or disables all the implemented checks based on status | ||
void toggleAl(CheckStatus status); | ||
|
||
/// Enables or disables all the checks implemented in the TidyKind provided based on status | ||
void toggleGroup(slang::TidyKind kind, CheckStatus status); | ||
|
||
/// Disables or enables a particular check implemented in the TidyKind provided based on status. | ||
/// It will return false if the check do not exist. | ||
[[nodiscard]] bool toggleCheck(slang::TidyKind kind, const std::string& checkName, | ||
CheckStatus status); | ||
|
||
/// Sets the value of a check config. Will throw an invalid_argument exception if the type of | ||
/// value doesn't match the config type | ||
template<typename T> | ||
void setConfig(const std::string& configName, T value) { | ||
if (configName == "clkName") { | ||
innerSetConfig(checkConfigs.clkName, value); | ||
return; | ||
} | ||
else if (configName == "resetName") { | ||
innerSetConfig(checkConfigs.resetName, value); | ||
return; | ||
} | ||
else if (configName == "resetIsActiveHigh") { | ||
innerSetConfig(checkConfigs.resetIsActiveHigh, value); | ||
return; | ||
} | ||
else if (configName == "inputPortSuffix") { | ||
innerSetConfig(checkConfigs.inputPortSuffix, value); | ||
return; | ||
} | ||
else if (configName == "outputPortSuffix") { | ||
innerSetConfig(checkConfigs.outputPortSuffix, value); | ||
return; | ||
} | ||
else if (configName == "inoutPortSuffix") { | ||
innerSetConfig(checkConfigs.inoutPortSuffix, value); | ||
return; | ||
} | ||
|
||
SLANG_THROW(std::invalid_argument(fmt::format("The check: {} does not exist", configName))); | ||
} | ||
|
||
template<typename T, typename U> | ||
void innerSetConfig(T& config, U value) { | ||
if constexpr (std::is_same_v<T, U>) | ||
config = value; | ||
else | ||
SLANG_THROW( | ||
std::invalid_argument(fmt::format("check config expected a {} found {}", | ||
slang::typeName<T>(), slang::typeName<U>()))); | ||
} | ||
}; |
Oops, something went wrong.