[clang-tools-extra] 0b8866d - [clang-tidy] Add user-defined functions to `bugprone-unsafe-functions` check (#106350)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 26 05:52:17 PDT 2024
Author: Discookie
Date: 2024-09-26T12:52:13Z
New Revision: 0b8866d15ac5806a980d2ff2ea63240d8acfa778
URL: https://github.com/llvm/llvm-project/commit/0b8866d15ac5806a980d2ff2ea63240d8acfa778
DIFF: https://github.com/llvm/llvm-project/commit/0b8866d15ac5806a980d2ff2ea63240d8acfa778.diff
LOG: [clang-tidy] Add user-defined functions to `bugprone-unsafe-functions` check (#106350)
Adds the check option `bugprone-unsafe-functions.CustomFunctions` to be
able to match user-defined functions as part of the checker.
Adds the option `bugprone-unsafe-functions.ReportDefaultFunctions` to
disable reporting the default set of functions as well.
The functions names are matched using the same mechanism as the
`matchesAnyListedName` tidy matcher, documented in
`unsafe-functions.rst`.
Added:
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-functions-custom-regex.cpp
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-functions-custom.c
Modified:
clang-tools-extra/clang-tidy/bugprone/UnsafeFunctionsCheck.cpp
clang-tools-extra/clang-tidy/bugprone/UnsafeFunctionsCheck.h
clang-tools-extra/clang-tidy/utils/Matchers.h
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/bugprone/unsafe-functions.rst
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-functions.c
Removed:
################################################################################
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnsafeFunctionsCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UnsafeFunctionsCheck.cpp
index ea7eaa0b0ff811..604a7cac0e4903 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UnsafeFunctionsCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UnsafeFunctionsCheck.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "UnsafeFunctionsCheck.h"
+#include "../utils/OptionsUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/PPCallbacks.h"
@@ -18,6 +19,10 @@ using namespace llvm;
namespace clang::tidy::bugprone {
+static constexpr llvm::StringLiteral OptionNameCustomFunctions =
+ "CustomFunctions";
+static constexpr llvm::StringLiteral OptionNameReportDefaultFunctions =
+ "ReportDefaultFunctions";
static constexpr llvm::StringLiteral OptionNameReportMoreUnsafeFunctions =
"ReportMoreUnsafeFunctions";
@@ -26,6 +31,8 @@ static constexpr llvm::StringLiteral FunctionNamesWithAnnexKReplacementId =
static constexpr llvm::StringLiteral FunctionNamesId = "FunctionsNames";
static constexpr llvm::StringLiteral AdditionalFunctionNamesId =
"AdditionalFunctionsNames";
+static constexpr llvm::StringLiteral CustomFunctionNamesId =
+ "CustomFunctionNames";
static constexpr llvm::StringLiteral DeclRefId = "DRE";
static std::optional<std::string>
@@ -127,57 +134,128 @@ static bool isAnnexKAvailable(std::optional<bool> &CacheVar, Preprocessor *PP,
return CacheVar.value();
}
+static std::vector<UnsafeFunctionsCheck::CheckedFunction>
+parseCheckedFunctions(StringRef Option, ClangTidyContext *Context) {
+ const std::vector<StringRef> Functions =
+ utils::options::parseStringList(Option);
+ std::vector<UnsafeFunctionsCheck::CheckedFunction> Result;
+ Result.reserve(Functions.size());
+
+ for (StringRef Function : Functions) {
+ if (Function.empty())
+ continue;
+
+ const auto [Name, Rest] = Function.split(',');
+ const auto [Replacement, Reason] = Rest.split(',');
+
+ if (Name.trim().empty()) {
+ Context->configurationDiag("invalid configuration value for option '%0'; "
+ "expected the name of an unsafe function")
+ << OptionNameCustomFunctions;
+ continue;
+ }
+
+ Result.push_back(
+ {Name.trim().str(),
+ matchers::MatchesAnyListedNameMatcher::NameMatcher(Name.trim()),
+ Replacement.trim().str(), Reason.trim().str()});
+ }
+
+ return Result;
+}
+
+static std::string serializeCheckedFunctions(
+ const std::vector<UnsafeFunctionsCheck::CheckedFunction> &Functions) {
+ std::vector<std::string> Result;
+ Result.reserve(Functions.size());
+
+ for (const auto &Entry : Functions) {
+ if (Entry.Reason.empty())
+ Result.push_back(Entry.Name + "," + Entry.Replacement);
+ else
+ Result.push_back(Entry.Name + "," + Entry.Replacement + "," +
+ Entry.Reason);
+ }
+
+ return llvm::join(Result, ";");
+}
+
UnsafeFunctionsCheck::UnsafeFunctionsCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
+ CustomFunctions(parseCheckedFunctions(
+ Options.get(OptionNameCustomFunctions, ""), Context)),
+ ReportDefaultFunctions(
+ Options.get(OptionNameReportDefaultFunctions, true)),
ReportMoreUnsafeFunctions(
Options.get(OptionNameReportMoreUnsafeFunctions, true)) {}
void UnsafeFunctionsCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, OptionNameCustomFunctions,
+ serializeCheckedFunctions(CustomFunctions));
+ Options.store(Opts, OptionNameReportDefaultFunctions, ReportDefaultFunctions);
Options.store(Opts, OptionNameReportMoreUnsafeFunctions,
ReportMoreUnsafeFunctions);
}
void UnsafeFunctionsCheck::registerMatchers(MatchFinder *Finder) {
- if (getLangOpts().C11) {
- // Matching functions with safe replacements only in Annex K.
- auto FunctionNamesWithAnnexKReplacementMatcher = hasAnyName(
- "::bsearch", "::ctime", "::fopen", "::fprintf", "::freopen", "::fscanf",
- "::fwprintf", "::fwscanf", "::getenv", "::gmtime", "::localtime",
- "::mbsrtowcs", "::mbstowcs", "::memcpy", "::memmove", "::memset",
- "::printf", "::qsort", "::scanf", "::snprintf", "::sprintf", "::sscanf",
- "::strcat", "::strcpy", "::strerror", "::strlen", "::strncat",
- "::strncpy", "::strtok", "::swprintf", "::swscanf", "::vfprintf",
- "::vfscanf", "::vfwprintf", "::vfwscanf", "::vprintf", "::vscanf",
- "::vsnprintf", "::vsprintf", "::vsscanf", "::vswprintf", "::vswscanf",
- "::vwprintf", "::vwscanf", "::wcrtomb", "::wcscat", "::wcscpy",
- "::wcslen", "::wcsncat", "::wcsncpy", "::wcsrtombs", "::wcstok",
- "::wcstombs", "::wctomb", "::wmemcpy", "::wmemmove", "::wprintf",
- "::wscanf");
+ if (ReportDefaultFunctions) {
+ if (getLangOpts().C11) {
+ // Matching functions with safe replacements only in Annex K.
+ auto FunctionNamesWithAnnexKReplacementMatcher = hasAnyName(
+ "::bsearch", "::ctime", "::fopen", "::fprintf", "::freopen",
+ "::fscanf", "::fwprintf", "::fwscanf", "::getenv", "::gmtime",
+ "::localtime", "::mbsrtowcs", "::mbstowcs", "::memcpy", "::memmove",
+ "::memset", "::printf", "::qsort", "::scanf", "::snprintf",
+ "::sprintf", "::sscanf", "::strcat", "::strcpy", "::strerror",
+ "::strlen", "::strncat", "::strncpy", "::strtok", "::swprintf",
+ "::swscanf", "::vfprintf", "::vfscanf", "::vfwprintf", "::vfwscanf",
+ "::vprintf", "::vscanf", "::vsnprintf", "::vsprintf", "::vsscanf",
+ "::vswprintf", "::vswscanf", "::vwprintf", "::vwscanf", "::wcrtomb",
+ "::wcscat", "::wcscpy", "::wcslen", "::wcsncat", "::wcsncpy",
+ "::wcsrtombs", "::wcstok", "::wcstombs", "::wctomb", "::wmemcpy",
+ "::wmemmove", "::wprintf", "::wscanf");
+ Finder->addMatcher(
+ declRefExpr(to(functionDecl(FunctionNamesWithAnnexKReplacementMatcher)
+ .bind(FunctionNamesWithAnnexKReplacementId)))
+ .bind(DeclRefId),
+ this);
+ }
+
+ // Matching functions with replacements without Annex K.
+ auto FunctionNamesMatcher =
+ hasAnyName("::asctime", "asctime_r", "::gets", "::rewind", "::setbuf");
Finder->addMatcher(
- declRefExpr(to(functionDecl(FunctionNamesWithAnnexKReplacementMatcher)
- .bind(FunctionNamesWithAnnexKReplacementId)))
+ declRefExpr(
+ to(functionDecl(FunctionNamesMatcher).bind(FunctionNamesId)))
.bind(DeclRefId),
this);
+
+ if (ReportMoreUnsafeFunctions) {
+ // Matching functions with replacements without Annex K, at user request.
+ auto AdditionalFunctionNamesMatcher =
+ hasAnyName("::bcmp", "::bcopy", "::bzero", "::getpw", "::vfork");
+ Finder->addMatcher(
+ declRefExpr(to(functionDecl(AdditionalFunctionNamesMatcher)
+ .bind(AdditionalFunctionNamesId)))
+ .bind(DeclRefId),
+ this);
+ }
}
- // Matching functions with replacements without Annex K.
- auto FunctionNamesMatcher =
- hasAnyName("::asctime", "asctime_r", "::gets", "::rewind", "::setbuf");
- Finder->addMatcher(
- declRefExpr(to(functionDecl(FunctionNamesMatcher).bind(FunctionNamesId)))
- .bind(DeclRefId),
- this);
-
- if (ReportMoreUnsafeFunctions) {
- // Matching functions with replacements without Annex K, at user request.
- auto AdditionalFunctionNamesMatcher =
- hasAnyName("::bcmp", "::bcopy", "::bzero", "::getpw", "::vfork");
- Finder->addMatcher(
- declRefExpr(to(functionDecl(AdditionalFunctionNamesMatcher)
- .bind(AdditionalFunctionNamesId)))
- .bind(DeclRefId),
- this);
+ if (!CustomFunctions.empty()) {
+ std::vector<llvm::StringRef> FunctionNames;
+ FunctionNames.reserve(CustomFunctions.size());
+
+ for (const auto &Entry : CustomFunctions)
+ FunctionNames.push_back(Entry.Name);
+
+ auto CustomFunctionsMatcher = matchers::matchesAnyListedName(FunctionNames);
+
+ Finder->addMatcher(declRefExpr(to(functionDecl(CustomFunctionsMatcher)
+ .bind(CustomFunctionNamesId)))
+ .bind(DeclRefId),
+ this);
}
}
@@ -186,16 +264,46 @@ void UnsafeFunctionsCheck::check(const MatchFinder::MatchResult &Result) {
const auto *FuncDecl = cast<FunctionDecl>(DeclRef->getDecl());
assert(DeclRef && FuncDecl && "No valid matched node in check()");
+ // Only one of these are matched at a time.
const auto *AnnexK = Result.Nodes.getNodeAs<FunctionDecl>(
FunctionNamesWithAnnexKReplacementId);
const auto *Normal = Result.Nodes.getNodeAs<FunctionDecl>(FunctionNamesId);
const auto *Additional =
Result.Nodes.getNodeAs<FunctionDecl>(AdditionalFunctionNamesId);
- assert((AnnexK || Normal || Additional) && "No valid match category.");
+ const auto *Custom =
+ Result.Nodes.getNodeAs<FunctionDecl>(CustomFunctionNamesId);
+ assert((AnnexK || Normal || Additional || Custom) &&
+ "No valid match category.");
bool AnnexKIsAvailable =
isAnnexKAvailable(IsAnnexKAvailable, PP, getLangOpts());
StringRef FunctionName = FuncDecl->getName();
+
+ if (Custom) {
+ for (const auto &Entry : CustomFunctions) {
+ if (Entry.Pattern.match(*FuncDecl)) {
+ StringRef Reason =
+ Entry.Reason.empty() ? "is marked as unsafe" : Entry.Reason.c_str();
+
+ if (Entry.Replacement.empty()) {
+ diag(DeclRef->getExprLoc(), "function %0 %1; it should not be used")
+ << FuncDecl << Reason << Entry.Replacement
+ << DeclRef->getSourceRange();
+ } else {
+ diag(DeclRef->getExprLoc(),
+ "function %0 %1; '%2' should be used instead")
+ << FuncDecl << Reason << Entry.Replacement
+ << DeclRef->getSourceRange();
+ }
+
+ return;
+ }
+ }
+
+ llvm_unreachable("No custom function was matched.");
+ return;
+ }
+
const std::optional<std::string> ReplacementFunctionName =
[&]() -> std::optional<std::string> {
if (AnnexK) {
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnsafeFunctionsCheck.h b/clang-tools-extra/clang-tidy/bugprone/UnsafeFunctionsCheck.h
index 5adfee60d1a7de..63058c326ef291 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UnsafeFunctionsCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/UnsafeFunctionsCheck.h
@@ -10,6 +10,7 @@
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNSAFEFUNCTIONSCHECK_H
#include "../ClangTidyCheck.h"
+#include "../utils/Matchers.h"
#include <optional>
namespace clang::tidy::bugprone {
@@ -32,7 +33,18 @@ class UnsafeFunctionsCheck : public ClangTidyCheck {
Preprocessor *ModuleExpanderPP) override;
void onEndOfTranslationUnit() override;
+ struct CheckedFunction {
+ std::string Name;
+ matchers::MatchesAnyListedNameMatcher::NameMatcher Pattern;
+ std::string Replacement;
+ std::string Reason;
+ };
+
private:
+ const std::vector<CheckedFunction> CustomFunctions;
+
+ // If true, the default set of functions are reported.
+ const bool ReportDefaultFunctions;
/// If true, additional functions from widely used API-s (such as POSIX) are
/// added to the list of reported functions.
const bool ReportMoreUnsafeFunctions;
diff --git a/clang-tools-extra/clang-tidy/utils/Matchers.h b/clang-tools-extra/clang-tidy/utils/Matchers.h
index 5fd98db9678708..451c4ce92585b9 100644
--- a/clang-tools-extra/clang-tidy/utils/Matchers.h
+++ b/clang-tools-extra/clang-tidy/utils/Matchers.h
@@ -85,15 +85,7 @@ class MatchesAnyListedNameMatcher
NameList.begin(), NameList.end(), std::back_inserter(NameMatchers),
[](const llvm::StringRef Name) { return NameMatcher(Name); });
}
- bool matches(
- const NamedDecl &Node, ast_matchers::internal::ASTMatchFinder *Finder,
- ast_matchers::internal::BoundNodesTreeBuilder *Builder) const override {
- return llvm::any_of(NameMatchers, [&Node](const NameMatcher &NM) {
- return NM.match(Node);
- });
- }
-private:
class NameMatcher {
llvm::Regex Regex;
enum class MatchMode {
@@ -136,6 +128,15 @@ class MatchesAnyListedNameMatcher
}
};
+ bool matches(
+ const NamedDecl &Node, ast_matchers::internal::ASTMatchFinder *Finder,
+ ast_matchers::internal::BoundNodesTreeBuilder *Builder) const override {
+ return llvm::any_of(NameMatchers, [&Node](const NameMatcher &NM) {
+ return NM.match(Node);
+ });
+ }
+
+private:
std::vector<NameMatcher> NameMatchers;
};
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 9a130a23b6e896..bec768e30d64f4 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -135,6 +135,10 @@ Changes in existing checks
`bsl::optional` and `bdlb::NullableValue` from
<https://github.com/bloomberg/bde>_.
+- Improved :doc:`bugprone-unsafe-functions
+ <clang-tidy/checks/bugprone/unsafe-functions>` check to allow specifying
+ additional functions to match.
+
- Improved :doc:`cert-flp30-c <clang-tidy/checks/cert/flp30-c>` check to
fix false positive that floating point variable is only used in increment
expression.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/unsafe-functions.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/unsafe-functions.rst
index a0a267883b6fe9..fb070627e31b1d 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/unsafe-functions.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/unsafe-functions.rst
@@ -19,6 +19,8 @@ The check implements the following rules from the CERT C Coding Standard:
Unsafe functions
----------------
+The following functions are reported if :option:`ReportDefaultFunctions` is enabled.
+
If *Annex K.* is available, a replacement from *Annex K.* is suggested for the
following functions:
@@ -45,8 +47,7 @@ The following functions are always checked, regardless of *Annex K* availability
- ``rewind``, suggested replacement: ``fseek``
- ``setbuf``, suggested replacement: ``setvbuf``
-If `ReportMoreUnsafeFunctions
-<unsafe-functions.html#cmdoption-arg-ReportMoreUnsafeFunctions>`_ is enabled,
+If :option:`ReportMoreUnsafeFunctions` is enabled,
the following functions are also checked:
- ``bcmp``, suggested replacement: ``memcmp``
@@ -74,6 +75,44 @@ Both macros have to be defined to suggest replacement functions from *Annex K.*
``__STDC_WANT_LIB_EXT1__`` must be defined to ``1`` by the user **before**
including any system headers.
+.. _CustomFunctions:
+
+Custom functions
+----------------
+
+The option :option:`CustomFunctions` allows the user to define custom functions to be
+checked. The format is the following, without newlines:
+
+.. code::
+
+ bugprone-unsafe-functions.CustomFunctions="
+ functionRegex1[, replacement1[, reason1]];
+ functionRegex2[, replacement2[, reason2]];
+ ...
+ "
+
+The functions are matched using POSIX extended regular expressions.
+*(Note: The regular expressions do not support negative* ``(?!)`` *matches.)*
+
+The `reason` is optional and is used to provide additional information about the
+reasoning behind the replacement. The default reason is `is marked as unsafe`.
+
+If `replacement` is empty, the text `it should not be used` will be shown
+instead of the suggestion for a replacement.
+
+As an example, the configuration `^original$, replacement, is deprecated;`
+will produce the following diagnostic message.
+
+.. code:: c
+
+ original(); // warning: function 'original' is deprecated; 'replacement' should be used instead.
+ ::std::original(); // no-warning
+ original_function(); // no-warning
+
+If the regular expression contains the character `:`, it is matched against the
+qualified name (i.e. ``std::original``), otherwise the regex is matched against the unqualified name (``original``).
+If the regular expression starts with `::` (or `^::`), it is matched against the
+fully qualified name (``::std::original``).
Options
-------
@@ -86,6 +125,19 @@ Options
this option enables.
Default is `true`.
+.. option:: ReportDefaultFunctions
+
+ When `true`, the check reports the default set of functions.
+ Consider changing the setting to false if you only want to see custom
+ functions matched via :ref:`custom functions<CustomFunctions>`.
+ Default is `true`.
+
+.. option:: CustomFunctions
+
+ A semicolon-separated list of custom functions to be matched. A matched
+ function contains a regular expression, an optional name of the replacement
+ function, and an optional reason, separated by comma. For more information,
+ see :ref:`Custom functions<CustomFunctions>`.
Examples
--------
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-functions-custom-regex.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-functions-custom-regex.cpp
new file mode 100644
index 00000000000000..fc97d1bc93bc54
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-functions-custom-regex.cpp
@@ -0,0 +1,44 @@
+// RUN: %check_clang_tidy -check-suffix=NON-STRICT-REGEX %s bugprone-unsafe-functions %t --\
+// RUN: -config="{CheckOptions: {bugprone-unsafe-functions.CustomFunctions: '::name_match,replacement,is a qualname match;^::prefix_match,,is matched on qualname prefix'}}"
+// RUN: %check_clang_tidy -check-suffix=STRICT-REGEX %s bugprone-unsafe-functions %t --\
+// RUN: -config="{CheckOptions: {bugprone-unsafe-functions.CustomFunctions: '^name_match$,replacement,is matched on function name only;^::prefix_match$,,is a full qualname match'}}"
+
+void name_match();
+void prefix_match();
+
+namespace regex_test {
+void name_match();
+void prefix_match();
+}
+
+void name_match_regex();
+void prefix_match_regex();
+
+void f1() {
+ name_match();
+ // CHECK-MESSAGES-NON-STRICT-REGEX: :[[@LINE-1]]:3: warning: function 'name_match' is a qualname match; 'replacement' should be used instead
+ // CHECK-MESSAGES-STRICT-REGEX: :[[@LINE-2]]:3: warning: function 'name_match' is matched on function name only; 'replacement' should be used instead
+ prefix_match();
+ // CHECK-MESSAGES-NON-STRICT-REGEX: :[[@LINE-1]]:3: warning: function 'prefix_match' is matched on qualname prefix; it should not be used
+ // CHECK-MESSAGES-STRICT-REGEX: :[[@LINE-2]]:3: warning: function 'prefix_match' is a full qualname match; it should not be used
+
+ ::name_match();
+ // CHECK-MESSAGES-NON-STRICT-REGEX: :[[@LINE-1]]:3: warning: function 'name_match' is a qualname match; 'replacement' should be used instead
+ // CHECK-MESSAGES-STRICT-REGEX: :[[@LINE-2]]:3: warning: function 'name_match' is matched on function name only; 'replacement' should be used instead
+ regex_test::name_match();
+ // CHECK-MESSAGES-NON-STRICT-REGEX: :[[@LINE-1]]:3: warning: function 'name_match' is a qualname match; 'replacement' should be used instead
+ // CHECK-MESSAGES-STRICT-REGEX: :[[@LINE-2]]:3: warning: function 'name_match' is matched on function name only; 'replacement' should be used instead
+ name_match_regex();
+ // CHECK-MESSAGES-NON-STRICT-REGEX: :[[@LINE-1]]:3: warning: function 'name_match_regex' is a qualname match; 'replacement' should be used instead
+ // no-warning STRICT-REGEX
+
+ ::prefix_match();
+ // CHECK-MESSAGES-NON-STRICT-REGEX: :[[@LINE-1]]:3: warning: function 'prefix_match' is matched on qualname prefix; it should not be used
+ // CHECK-MESSAGES-STRICT-REGEX: :[[@LINE-2]]:3: warning: function 'prefix_match' is a full qualname match; it should not be used
+ regex_test::prefix_match();
+ // no-warning NON-STRICT-REGEX
+ // no-warning STRICT-REGEX
+ prefix_match_regex();
+ // CHECK-MESSAGES-NON-STRICT-REGEX: :[[@LINE-1]]:3: warning: function 'prefix_match_regex' is matched on qualname prefix; it should not be used
+ // no-warning STRICT-REGEX
+}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-functions-custom.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-functions-custom.c
new file mode 100644
index 00000000000000..7fd71ec2f2e7b0
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-functions-custom.c
@@ -0,0 +1,27 @@
+// RUN: %check_clang_tidy -check-suffix=NON-STRICT-REGEX %s bugprone-unsafe-functions %t --\
+// RUN: -config="{CheckOptions: {bugprone-unsafe-functions.CustomFunctions: '::name_match,replacement,is a qualname match;^::prefix_match,,is matched on qualname prefix'}}"
+// RUN: %check_clang_tidy -check-suffix=STRICT-REGEX %s bugprone-unsafe-functions %t --\
+// RUN: -config="{CheckOptions: {bugprone-unsafe-functions.CustomFunctions: '^name_match$,replacement,is matched on function name only;^::prefix_match$,,is a full qualname match'}}"
+
+void name_match();
+void prefix_match();
+
+void name_match_regex();
+void prefix_match_regex();
+
+void f1() {
+ name_match();
+ // CHECK-MESSAGES-NON-STRICT-REGEX: :[[@LINE-1]]:3: warning: function 'name_match' is a qualname match; 'replacement' should be used instead
+ // CHECK-MESSAGES-STRICT-REGEX: :[[@LINE-2]]:3: warning: function 'name_match' is matched on function name only; 'replacement' should be used instead
+ prefix_match();
+ // CHECK-MESSAGES-NON-STRICT-REGEX: :[[@LINE-1]]:3: warning: function 'prefix_match' is matched on qualname prefix; it should not be used
+ // CHECK-MESSAGES-STRICT-REGEX: :[[@LINE-2]]:3: warning: function 'prefix_match' is a full qualname match; it should not be used
+
+ name_match_regex();
+ // CHECK-MESSAGES-NON-STRICT-REGEX: :[[@LINE-1]]:3: warning: function 'name_match_regex' is a qualname match; 'replacement' should be used instead
+ // no-warning STRICT-REGEX
+
+ prefix_match_regex();
+ // CHECK-MESSAGES-NON-STRICT-REGEX: :[[@LINE-1]]:3: warning: function 'prefix_match_regex' is matched on qualname prefix; it should not be used
+ // no-warning STRICT-REGEX
+}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-functions.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-functions.c
index 4bc2bad996d706..0409dd6bfcaa3d 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-functions.c
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-functions.c
@@ -12,6 +12,12 @@
// RUN: %check_clang_tidy -check-suffix=WITH-ANNEX-K-CERT-ONLY %s bugprone-unsafe-functions %t -- \
// RUN: -config="{CheckOptions: {bugprone-unsafe-functions.ReportMoreUnsafeFunctions: false}}" \
// RUN: -- -D__STDC_LIB_EXT1__=1 -D__STDC_WANT_LIB_EXT1__=1
+// RUN: %check_clang_tidy -check-suffix=WITH-NONE-ENABLED %s bugprone-unsafe-functions %t --\
+// RUN: -config="{CheckOptions: {bugprone-unsafe-functions.ReportDefaultFunctions: false}}" \
+// RUN: -- -D__STDC_LIB_EXT1__=1 -D__STDC_WANT_LIB_EXT1__=1
+
+// CHECK-MESSAGES-WITH-NONE-ENABLED: 1 warning generated
+// CHECK-MESSAGES-WITH-NONE-ENABLED: Suppressed 1 warnings
typedef __SIZE_TYPE__ size_t;
typedef __WCHAR_TYPE__ wchar_t;
More information about the cfe-commits
mailing list