https://github.com/furtib updated https://github.com/llvm/llvm-project/pull/207407
>From f52244e33254f5564ce7104d61d96e0bc09f07b4 Mon Sep 17 00:00:00 2001 From: "F.Tibor" <[email protected]> Date: Mon, 8 Jun 2026 23:43:38 +0200 Subject: [PATCH 01/15] [clang-tidy] Add llvm-regex check --- .../clang-tidy/llvm/CMakeLists.txt | 1 + .../clang-tidy/llvm/LLVMTidyModule.cpp | 2 + .../clang-tidy/llvm/RegexCheck.cpp | 88 +++++++++++ .../clang-tidy/llvm/RegexCheck.h | 33 +++++ clang-tools-extra/docs/ReleaseNotes.md | 5 + .../docs/clang-tidy/checks/list.md | 1 + .../docs/clang-tidy/checks/llvm/regex.rst | 34 +++++ .../test/clang-tidy/checkers/llvm/regex.cpp | 140 ++++++++++++++++++ 8 files changed, 304 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/llvm/RegexCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/llvm/RegexCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/llvm/regex.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp diff --git a/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt b/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt index bec3ba50c81c51..2f5d136108191f 100644 --- a/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt @@ -12,6 +12,7 @@ add_clang_library(clangTidyLLVMModule STATIC PreferRegisterOverUnsignedCheck.cpp PreferStaticOverAnonymousNamespaceCheck.cpp RedundantCastingCheck.cpp + RegexCheck.cpp TwineLocalCheck.cpp TypeSwitchCaseTypesCheck.cpp UseNewMLIROpBuilderCheck.cpp diff --git a/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp b/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp index 918af88c979e0a..16409770f04679 100644 --- a/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp @@ -18,6 +18,7 @@ #include "PreferRegisterOverUnsignedCheck.h" #include "PreferStaticOverAnonymousNamespaceCheck.h" #include "RedundantCastingCheck.h" +#include "RegexCheck.h" #include "TwineLocalCheck.h" #include "TypeSwitchCaseTypesCheck.h" #include "UseNewMLIROpBuilderCheck.h" @@ -48,6 +49,7 @@ class LLVMModule : public ClangTidyModule { "llvm-qualified-auto"); CheckFactories.registerCheck<RedundantCastingCheck>( "llvm-redundant-casting"); + CheckFactories.registerCheck<RegexCheck>("llvm-regex"); CheckFactories.registerCheck<TwineLocalCheck>("llvm-twine-local"); CheckFactories.registerCheck<TypeSwitchCaseTypesCheck>( "llvm-type-switch-case-types"); diff --git a/clang-tools-extra/clang-tidy/llvm/RegexCheck.cpp b/clang-tools-extra/clang-tidy/llvm/RegexCheck.cpp new file mode 100644 index 00000000000000..f9f2b58e424f70 --- /dev/null +++ b/clang-tools-extra/clang-tidy/llvm/RegexCheck.cpp @@ -0,0 +1,88 @@ +//===--- RegexCheck.cpp - clang-tidy --------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RegexCheck.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "llvm/Support/Regex.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::llvm_check { + +void RegexCheck::registerMatchers(MatchFinder *Finder) { + // main matcher + auto IsConstllvmStringRef = qualType( + isConstQualified(), hasUnqualifiedDesugaredType(recordType(hasDeclaration( + cxxRecordDecl(hasName("::llvm::StringRef")))))); + auto IsConstStdString = qualType( + isConstQualified(), hasUnqualifiedDesugaredType(recordType(hasDeclaration( + cxxRecordDecl(hasName("::std::basic_string")))))); + auto GetStringLit = ignoringImplicit(stringLiteral().bind("stringLiteral")); + auto GetStringLiteralFromObject = + ignoringImplicit(cxxConstructExpr(hasAnyArgument(GetStringLit))); + auto IsConstCharPtr = pointerType(pointee(builtinType(), isConstQualified())); + auto IsStdStringView = qualType(hasUnqualifiedDesugaredType(recordType( + hasDeclaration(cxxRecordDecl(hasName("::std::basic_string_view")))))); + auto AnyCastedToStringRef = ignoringImplicit(anyOf( + stringLiteral().bind("stringLiteral"), + declRefExpr(to(varDecl(hasType(IsConstStdString), + hasInitializer(GetStringLiteralFromObject)))), + declRefExpr(to(varDecl(hasType(IsConstllvmStringRef), + hasInitializer(GetStringLiteralFromObject)))), + declRefExpr( + to(varDecl(hasType(IsConstCharPtr), hasInitializer(GetStringLit)))), + declRefExpr(to(varDecl(hasType(IsStdStringView), + hasInitializer(GetStringLiteralFromObject)))), + memberExpr( + member(fieldDecl(hasType(IsConstStdString), + hasInClassInitializer(GetStringLiteralFromObject)))), + memberExpr(member(fieldDecl(hasType(IsConstCharPtr), + hasInClassInitializer(GetStringLit)))), + memberExpr( + member(fieldDecl(hasType(IsConstllvmStringRef), + hasInClassInitializer(GetStringLiteralFromObject)))), + memberExpr(member( + fieldDecl(hasType(IsStdStringView), + hasInClassInitializer(GetStringLiteralFromObject)))))); + + auto IsRegexFlagsType = ignoringParenImpCasts( + anyOf(integerLiteral().bind("regexFlagsInt"), + declRefExpr(to(enumConstantDecl().bind("regexFlagEnum"))))); + Finder->addMatcher( + cxxConstructExpr( + hasDeclaration(cxxConstructorDecl(ofClass(hasName("llvm::Regex")))), + hasArgument(0, ignoringImplicit(cxxConstructExpr( + hasDeclaration(cxxConstructorDecl( + ofClass(hasName("::llvm::StringRef")))), + hasArgument(0, AnyCastedToStringRef)))), + optionally(hasArgument(1, IsRegexFlagsType))), + this); +} + +void RegexCheck::check(const MatchFinder::MatchResult &Result) { + const auto *DetectedPattern = + Result.Nodes.getNodeAs<StringLiteral>("stringLiteral"); + if (DetectedPattern) { + const auto *FlagInt = + Result.Nodes.getNodeAs<IntegerLiteral>("regexFlagsInt"); + const auto *FlagEnum = + Result.Nodes.getNodeAs<EnumConstantDecl>("regexFlagEnum"); + uint Flag = llvm::Regex::RegexFlags::NoFlags; + if (FlagInt) + Flag = FlagInt->getValue().getZExtValue(); + if (FlagEnum) + Flag = FlagEnum->getInitVal().getZExtValue(); + const llvm::Regex TestRegex(DetectedPattern->getString(), Flag); + std::string RegexError; + if (!TestRegex.isValid(RegexError)) + diag(DetectedPattern->getBeginLoc(), "invalid regex pattern: %0") + << RegexError << DetectedPattern->getSourceRange(); + } +} + +} // namespace clang::tidy::llvm_check diff --git a/clang-tools-extra/clang-tidy/llvm/RegexCheck.h b/clang-tools-extra/clang-tidy/llvm/RegexCheck.h new file mode 100644 index 00000000000000..17891713e62eeb --- /dev/null +++ b/clang-tools-extra/clang-tidy/llvm/RegexCheck.h @@ -0,0 +1,33 @@ +//===--- RegexCheck.h - clang-tidy ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_REGEXCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_REGEXCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::llvm_check { + +/// Detects malformed regex patterns used in ``llvm::Regex``. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/llvm/regex.html +class RegexCheck : public ClangTidyCheck { +public: + RegexCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } +}; + +} // namespace clang::tidy::llvm_check + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_REGEXCHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.md b/clang-tools-extra/docs/ReleaseNotes.md index 28da9da42d8ce2..a84d311ddd7889 100644 --- a/clang-tools-extra/docs/ReleaseNotes.md +++ b/clang-tools-extra/docs/ReleaseNotes.md @@ -98,6 +98,11 @@ infrastructure are described first, followed by tool-specific sections. Finds calls to `value_or` (and alternative spellings `valueOr`, `ValueOr`) on optional types where the return type is expensive to copy. +- New :doc:`llvm-regex + <clang-tidy/checks/llvm/regex>` check. + + Detects malformed regex patterns used in `llvm::Regex`. + #### New check aliases #### Changes in existing checks diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.md b/clang-tools-extra/docs/clang-tidy/checks/list.md index 12d8a48ee8d865..39c42a32a2258c 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.md +++ b/clang-tools-extra/docs/clang-tidy/checks/list.md @@ -251,6 +251,7 @@ zircon/* | {doc}`llvm-prefer-register-over-unsigned <llvm/prefer-register-over-unsigned>` | Yes | | {doc}`llvm-prefer-static-over-anonymous-namespace <llvm/prefer-static-over-anonymous-namespace>` | | | {doc}`llvm-redundant-casting <llvm/redundant-casting>` | Yes | +| {doc}`llvm-regex <llvm/regex>` | No | | {doc}`llvm-twine-local <llvm/twine-local>` | Yes | | {doc}`llvm-type-switch-case-types <llvm/type-switch-case-types>` | Yes | | {doc}`llvm-use-new-mlir-op-builder <llvm/use-new-mlir-op-builder>` | Yes | diff --git a/clang-tools-extra/docs/clang-tidy/checks/llvm/regex.rst b/clang-tools-extra/docs/clang-tidy/checks/llvm/regex.rst new file mode 100644 index 00000000000000..837181a8ea4a65 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/llvm/regex.rst @@ -0,0 +1,34 @@ +.. title:: clang-tidy - llvm-regex + +llvm-regex +========== + +The checker detects malformed regex patterns defined in a single string literal +to catch mistakes at compile time. +It detects these string literals if they are defined in the regex constructor +with a string literal, or stored in one of these non mutable container: + +- ``const std::string`` +- ``const char*`` +- ``const llvm::StringRef`` +- ``std::string_view`` + +In the event that the patterns are stored as a class member, the check checks +the initialization value, if defined, even if its overwritten by the constructor. + +Example of detection: + +.. code-block:: c++ + const std::string my_regex_pattern("[0-8"); // invalid regex pattern + llvm::Regex my_regex(my_regex_pattern); + +Example of member data being overwritten: + +.. code-block:: c++ + class foo{ + public: + foo(llvm::StringRef regex_ptrn) : regex_prtn(regex_prtn){}; + const llvm_StringRef regex_prtn = "("; // invalid regex pattern + }; + foo bar("[0-9]"); + llvm::Regex my_regex(bar.regex_prtn); diff --git a/clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp b/clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp new file mode 100644 index 00000000000000..229b35f2eb204e --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp @@ -0,0 +1,140 @@ +// RUN: %check_clang_tidy -std=c++17 %s llvm-regex %t + +namespace std { + template <typename T> + class basic_string { + public: + basic_string(const char*); + }; + using string = basic_string<char>; + + template <typename T> + class basic_string_view { + public: + basic_string_view(const char*); + const char* begin() const; + }; + using string_view = basic_string_view<char>; +} // namespace std + +namespace llvm { + class StringRef { + public: + StringRef(const char*); + StringRef(const std::string&); + StringRef(const std::string_view&); + }; + + class Regex { + public: + Regex(StringRef, unsigned int i = 0); + enum RegexFlags : unsigned { + NoFlags = 0, + IgnoreCase = 1, + Newline = 2, + BasicRegex = 4, + }; + }; +} // namespace llvm + +void test_detected_faulty_patterns(){ + llvm::Regex re1("("); + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: invalid regex pattern: parentheses not balanced + + const std::string badStdString("("); + // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: invalid regex pattern: parentheses not balanced + llvm::Regex re2(badStdString); + + const char* badCharPtr = "[]"; + // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: invalid regex pattern: brackets ([ ]) not balanced + llvm::Regex re3(badCharPtr); + + std::string_view badStrView("+"); + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: invalid regex pattern: repetition-operator operand invalid + llvm::Regex re4(badStrView); + + const llvm::StringRef badStrRef = "a*?"; + // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: invalid regex pattern: repetition-operator operand invalid + llvm::Regex re5(badStrRef); + + struct RegexPatterns { + const char* badMemberChar = ""; + // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: invalid regex pattern: empty (sub)expression + + const std::string badMemberStr = "("; + // CHECK-MESSAGES: :[[@LINE-1]]:38: warning: invalid regex pattern: parentheses not balanced + + std::string_view badMemberStrView = "+"; + // CHECK-MESSAGES: :[[@LINE-1]]:41: warning: invalid regex pattern: repetition-operator operand invalid + + const llvm::StringRef badMemberStrRef = "a*?"; + // CHECK-MESSAGES: :[[@LINE-1]]:45: warning: invalid regex pattern: repetition-operator operand invalid + }; + + RegexPatterns Pats; + + llvm::Regex re6(Pats.badMemberChar); + llvm::Regex re7(Pats.badMemberStr); + llvm::Regex re8(Pats.badMemberStrView); + llvm::Regex re9(Pats.badMemberStrRef); +} + +void test_no_detection_on_mutable(){ + std::string badMutStdString("("); + llvm::Regex re10(badMutStdString); + + char* badMutCharPtr = "["; + llvm::Regex re11(badMutCharPtr); + + llvm::StringRef badMutStrRef = "a*?"; + llvm::Regex re12(badMutStrRef); + + struct RegexMutPatterns { + char* badMutMemberChar = ""; + std::string badMutMemberStr = "("; + llvm::StringRef badMutMemberStrRef = "a*?"; + }; + + RegexMutPatterns mutPats; + + llvm::Regex re13(mutPats.badMutMemberChar); + llvm::Regex re14(mutPats.badMutMemberStr); + llvm::Regex re15(mutPats.badMutMemberStrRef); +} + +void test_no_report_on_correct_patterns(){ + llvm::Regex re16("[0-9]"); + + const std::string goodStdString("test"); + llvm::Regex re17(goodStdString); + + const char* goodCharPtr = "\\[test\\]"; + llvm::Regex re18(goodCharPtr); + + std::string_view goodStrView("testi+ng"); + llvm::Regex re19(goodStrView); + + const llvm::StringRef goodStrRef = "a*b?"; + llvm::Regex re20(goodStrRef); + + struct GoodRegexPatterns { + const char* goodMemberChar = "[0-9]"; + const std::string goodMemberStr = "test"; + std::string_view goodMemberStrView = "\[test\]"; + const llvm::StringRef goodMemberStrRef = "a*b"; + }; + + GoodRegexPatterns goodPats; + + llvm::Regex re21(goodPats.goodMemberChar); + llvm::Regex re22(goodPats.goodMemberStr); + llvm::Regex re23(goodPats.goodMemberStrView); + llvm::Regex re24(goodPats.goodMemberStrRef); +} + +void test_grammar_flags(){ + llvm::Regex re1_noflag("(", 0); + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: invalid regex pattern: parentheses not balanced + llvm::Regex re1_basic("(", 4U); + llvm::Regex re2_basic("(", llvm::Regex::RegexFlags::BasicRegex); +} >From 52d15ebc61e54aeadb16b8b9422fcb4f5ba4d757 Mon Sep 17 00:00:00 2001 From: "F.Tibor" <[email protected]> Date: Sat, 4 Jul 2026 10:57:53 +0200 Subject: [PATCH 02/15] Apply suggestions from code review Co-authored-by: EugeneZelenko <[email protected]> Co-authored-by: Zeyi Xu <[email protected]> --- clang-tools-extra/clang-tidy/llvm/RegexCheck.cpp | 2 +- clang-tools-extra/clang-tidy/llvm/RegexCheck.h | 2 +- clang-tools-extra/docs/clang-tidy/checks/list.md | 2 +- clang-tools-extra/docs/clang-tidy/checks/llvm/regex.rst | 2 +- clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clang-tools-extra/clang-tidy/llvm/RegexCheck.cpp b/clang-tools-extra/clang-tidy/llvm/RegexCheck.cpp index f9f2b58e424f70..57f6fa9588adf6 100644 --- a/clang-tools-extra/clang-tidy/llvm/RegexCheck.cpp +++ b/clang-tools-extra/clang-tidy/llvm/RegexCheck.cpp @@ -1,4 +1,4 @@ -//===--- RegexCheck.cpp - clang-tidy --------------------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/clang-tools-extra/clang-tidy/llvm/RegexCheck.h b/clang-tools-extra/clang-tidy/llvm/RegexCheck.h index 17891713e62eeb..e18bda161ae434 100644 --- a/clang-tools-extra/clang-tidy/llvm/RegexCheck.h +++ b/clang-tools-extra/clang-tidy/llvm/RegexCheck.h @@ -1,4 +1,4 @@ -//===--- RegexCheck.h - clang-tidy ------------------------------*- C++ -*-===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.md b/clang-tools-extra/docs/clang-tidy/checks/list.md index 39c42a32a2258c..2c17452e7c2f9d 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.md +++ b/clang-tools-extra/docs/clang-tidy/checks/list.md @@ -251,7 +251,7 @@ zircon/* | {doc}`llvm-prefer-register-over-unsigned <llvm/prefer-register-over-unsigned>` | Yes | | {doc}`llvm-prefer-static-over-anonymous-namespace <llvm/prefer-static-over-anonymous-namespace>` | | | {doc}`llvm-redundant-casting <llvm/redundant-casting>` | Yes | -| {doc}`llvm-regex <llvm/regex>` | No | +| {doc}`llvm-regex <llvm/regex>` | | | {doc}`llvm-twine-local <llvm/twine-local>` | Yes | | {doc}`llvm-type-switch-case-types <llvm/type-switch-case-types>` | Yes | | {doc}`llvm-use-new-mlir-op-builder <llvm/use-new-mlir-op-builder>` | Yes | diff --git a/clang-tools-extra/docs/clang-tidy/checks/llvm/regex.rst b/clang-tools-extra/docs/clang-tidy/checks/llvm/regex.rst index 837181a8ea4a65..f570fac85c8674 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/llvm/regex.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/llvm/regex.rst @@ -3,7 +3,7 @@ llvm-regex ========== -The checker detects malformed regex patterns defined in a single string literal +Detects malformed regex patterns defined in a single string literal to catch mistakes at compile time. It detects these string literals if they are defined in the regex constructor with a string literal, or stored in one of these non mutable container: diff --git a/clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp b/clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp index 229b35f2eb204e..c83fa9a85c5019 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy -std=c++17 %s llvm-regex %t +// RUN: %check_clang_tidy -std=c++17-or-later %s llvm-regex %t namespace std { template <typename T> >From ded741a8bb4eddfb4532ff4c8ac3f18e97168c1c Mon Sep 17 00:00:00 2001 From: "F.Tibor" <[email protected]> Date: Sat, 4 Jul 2026 11:20:06 +0200 Subject: [PATCH 03/15] Documentation fixes --- clang-tools-extra/docs/ReleaseNotes.md | 5 +++-- .../docs/clang-tidy/checks/llvm/regex.rst | 18 ++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/clang-tools-extra/docs/ReleaseNotes.md b/clang-tools-extra/docs/ReleaseNotes.md index a84d311ddd7889..41db9a1910c5d6 100644 --- a/clang-tools-extra/docs/ReleaseNotes.md +++ b/clang-tools-extra/docs/ReleaseNotes.md @@ -98,10 +98,11 @@ infrastructure are described first, followed by tool-specific sections. Finds calls to `value_or` (and alternative spellings `valueOr`, `ValueOr`) on optional types where the return type is expensive to copy. -- New :doc:`llvm-regex +- New {doc}`llvm-regex <clang-tidy/checks/llvm/regex>` check. - Detects malformed regex patterns used in `llvm::Regex`. + Detects malformed regex patterns defined in a single string literal + to catch mistakes at compile time. #### New check aliases diff --git a/clang-tools-extra/docs/clang-tidy/checks/llvm/regex.rst b/clang-tools-extra/docs/clang-tidy/checks/llvm/regex.rst index f570fac85c8674..604520f3d979af 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/llvm/regex.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/llvm/regex.rst @@ -13,22 +13,24 @@ with a string literal, or stored in one of these non mutable container: - ``const llvm::StringRef`` - ``std::string_view`` -In the event that the patterns are stored as a class member, the check checks +In the event that the patterns are stored as a class member, the check verifies the initialization value, if defined, even if its overwritten by the constructor. Example of detection: .. code-block:: c++ + const std::string my_regex_pattern("[0-8"); // invalid regex pattern llvm::Regex my_regex(my_regex_pattern); Example of member data being overwritten: .. code-block:: c++ - class foo{ - public: - foo(llvm::StringRef regex_ptrn) : regex_prtn(regex_prtn){}; - const llvm_StringRef regex_prtn = "("; // invalid regex pattern - }; - foo bar("[0-9]"); - llvm::Regex my_regex(bar.regex_prtn); + + class foo{ + public: + foo(llvm::StringRef regex_ptrn) : regex_prtn(regex_prtn){}; + const llvm_StringRef regex_prtn = "("; // invalid regex pattern + }; + foo bar("[0-9]"); + llvm::Regex my_regex(bar.regex_prtn); >From c0760c2e725d50581aee34d92cf3de957250638a Mon Sep 17 00:00:00 2001 From: "F.Tibor" <[email protected]> Date: Sat, 4 Jul 2026 11:20:32 +0200 Subject: [PATCH 04/15] Use include <string> in test file --- .../test/clang-tidy/checkers/llvm/regex.cpp | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp b/clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp index c83fa9a85c5019..88de3539da5831 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp @@ -1,21 +1,6 @@ // RUN: %check_clang_tidy -std=c++17-or-later %s llvm-regex %t -namespace std { - template <typename T> - class basic_string { - public: - basic_string(const char*); - }; - using string = basic_string<char>; - - template <typename T> - class basic_string_view { - public: - basic_string_view(const char*); - const char* begin() const; - }; - using string_view = basic_string_view<char>; -} // namespace std +#include <string> namespace llvm { class StringRef { >From d5c64b9ea8603b5561eaff18587e5d2e95c69286 Mon Sep 17 00:00:00 2001 From: "F.Tibor" <[email protected]> Date: Sat, 4 Jul 2026 11:36:39 +0200 Subject: [PATCH 05/15] Rename checker to llvm-invalid-regex-pattern --- clang-tools-extra/clang-tidy/llvm/CMakeLists.txt | 2 +- .../llvm/{RegexCheck.cpp => InvalidRegexPatternCheck.cpp} | 6 +++--- .../llvm/{RegexCheck.h => InvalidRegexPatternCheck.h} | 6 +++--- clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp | 4 ++-- clang-tools-extra/docs/ReleaseNotes.md | 4 ++-- clang-tools-extra/docs/clang-tidy/checks/list.md | 2 +- .../checks/llvm/{regex.rst => invalid-regex-pattern.rst} | 6 +++--- clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) rename clang-tools-extra/clang-tidy/llvm/{RegexCheck.cpp => InvalidRegexPatternCheck.cpp} (95%) rename clang-tools-extra/clang-tidy/llvm/{RegexCheck.h => InvalidRegexPatternCheck.h} (84%) rename clang-tools-extra/docs/clang-tidy/checks/llvm/{regex.rst => invalid-regex-pattern.rst} (90%) diff --git a/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt b/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt index 2f5d136108191f..563c05d14232a0 100644 --- a/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt @@ -12,7 +12,7 @@ add_clang_library(clangTidyLLVMModule STATIC PreferRegisterOverUnsignedCheck.cpp PreferStaticOverAnonymousNamespaceCheck.cpp RedundantCastingCheck.cpp - RegexCheck.cpp + InvalidRegexPatternCheck.cpp TwineLocalCheck.cpp TypeSwitchCaseTypesCheck.cpp UseNewMLIROpBuilderCheck.cpp diff --git a/clang-tools-extra/clang-tidy/llvm/RegexCheck.cpp b/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp similarity index 95% rename from clang-tools-extra/clang-tidy/llvm/RegexCheck.cpp rename to clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp index 57f6fa9588adf6..ca23dcc8c96500 100644 --- a/clang-tools-extra/clang-tidy/llvm/RegexCheck.cpp +++ b/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "RegexCheck.h" +#include "InvalidRegexPatternCheck.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "llvm/Support/Regex.h" @@ -14,7 +14,7 @@ using namespace clang::ast_matchers; namespace clang::tidy::llvm_check { -void RegexCheck::registerMatchers(MatchFinder *Finder) { +void InvalidRegexPatternCheck::registerMatchers(MatchFinder *Finder) { // main matcher auto IsConstllvmStringRef = qualType( isConstQualified(), hasUnqualifiedDesugaredType(recordType(hasDeclaration( @@ -64,7 +64,7 @@ void RegexCheck::registerMatchers(MatchFinder *Finder) { this); } -void RegexCheck::check(const MatchFinder::MatchResult &Result) { +void InvalidRegexPatternCheck::check(const MatchFinder::MatchResult &Result) { const auto *DetectedPattern = Result.Nodes.getNodeAs<StringLiteral>("stringLiteral"); if (DetectedPattern) { diff --git a/clang-tools-extra/clang-tidy/llvm/RegexCheck.h b/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.h similarity index 84% rename from clang-tools-extra/clang-tidy/llvm/RegexCheck.h rename to clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.h index e18bda161ae434..177a47b668dc16 100644 --- a/clang-tools-extra/clang-tidy/llvm/RegexCheck.h +++ b/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.h @@ -17,9 +17,9 @@ namespace clang::tidy::llvm_check { /// /// For the user-facing documentation see: /// http://clang.llvm.org/extra/clang-tidy/checks/llvm/regex.html -class RegexCheck : public ClangTidyCheck { +class InvalidRegexPatternCheck : public ClangTidyCheck { public: - RegexCheck(StringRef Name, ClangTidyContext *Context) + InvalidRegexPatternCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; @@ -30,4 +30,4 @@ class RegexCheck : public ClangTidyCheck { } // namespace clang::tidy::llvm_check -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_REGEXCHECK_H +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_InvalidRegexPatternCheck_H diff --git a/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp b/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp index 16409770f04679..549867beec25d4 100644 --- a/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp @@ -18,7 +18,7 @@ #include "PreferRegisterOverUnsignedCheck.h" #include "PreferStaticOverAnonymousNamespaceCheck.h" #include "RedundantCastingCheck.h" -#include "RegexCheck.h" +#include "InvalidRegexPatternCheck.h" #include "TwineLocalCheck.h" #include "TypeSwitchCaseTypesCheck.h" #include "UseNewMLIROpBuilderCheck.h" @@ -49,7 +49,7 @@ class LLVMModule : public ClangTidyModule { "llvm-qualified-auto"); CheckFactories.registerCheck<RedundantCastingCheck>( "llvm-redundant-casting"); - CheckFactories.registerCheck<RegexCheck>("llvm-regex"); + CheckFactories.registerCheck<InvalidRegexPatternCheck>("llvm-invalid-regex-pattern"); CheckFactories.registerCheck<TwineLocalCheck>("llvm-twine-local"); CheckFactories.registerCheck<TypeSwitchCaseTypesCheck>( "llvm-type-switch-case-types"); diff --git a/clang-tools-extra/docs/ReleaseNotes.md b/clang-tools-extra/docs/ReleaseNotes.md index 41db9a1910c5d6..ba0db92414488e 100644 --- a/clang-tools-extra/docs/ReleaseNotes.md +++ b/clang-tools-extra/docs/ReleaseNotes.md @@ -98,8 +98,8 @@ infrastructure are described first, followed by tool-specific sections. Finds calls to `value_or` (and alternative spellings `valueOr`, `ValueOr`) on optional types where the return type is expensive to copy. -- New {doc}`llvm-regex - <clang-tidy/checks/llvm/regex>` check. +- New {doc}`llvm-invalid-regex-pattern + <clang-tidy/checks/llvm/invalid-regex-pattern>` check. Detects malformed regex patterns defined in a single string literal to catch mistakes at compile time. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.md b/clang-tools-extra/docs/clang-tidy/checks/list.md index 2c17452e7c2f9d..a5fb11cfc6de01 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.md +++ b/clang-tools-extra/docs/clang-tidy/checks/list.md @@ -246,12 +246,12 @@ zircon/* | {doc}`llvm-formatv-string <llvm/formatv-string>` | | | {doc}`llvm-header-guard <llvm/header-guard>` | | | {doc}`llvm-include-order <llvm/include-order>` | Yes | +| {doc}`llvm-invalid-regex-pattern <llvm/invalid-regex-pattern>` | | | {doc}`llvm-namespace-comment <llvm/namespace-comment>` | | | {doc}`llvm-prefer-isa-or-dyn-cast-in-conditionals <llvm/prefer-isa-or-dyn-cast-in-conditionals>` | Yes | | {doc}`llvm-prefer-register-over-unsigned <llvm/prefer-register-over-unsigned>` | Yes | | {doc}`llvm-prefer-static-over-anonymous-namespace <llvm/prefer-static-over-anonymous-namespace>` | | | {doc}`llvm-redundant-casting <llvm/redundant-casting>` | Yes | -| {doc}`llvm-regex <llvm/regex>` | | | {doc}`llvm-twine-local <llvm/twine-local>` | Yes | | {doc}`llvm-type-switch-case-types <llvm/type-switch-case-types>` | Yes | | {doc}`llvm-use-new-mlir-op-builder <llvm/use-new-mlir-op-builder>` | Yes | diff --git a/clang-tools-extra/docs/clang-tidy/checks/llvm/regex.rst b/clang-tools-extra/docs/clang-tidy/checks/llvm/invalid-regex-pattern.rst similarity index 90% rename from clang-tools-extra/docs/clang-tidy/checks/llvm/regex.rst rename to clang-tools-extra/docs/clang-tidy/checks/llvm/invalid-regex-pattern.rst index 604520f3d979af..2cba3766608e78 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/llvm/regex.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/llvm/invalid-regex-pattern.rst @@ -1,7 +1,7 @@ -.. title:: clang-tidy - llvm-regex +.. title:: clang-tidy - llvm-invalid-regex-pattern -llvm-regex -========== +llvm-invalid-regex-pattern +========================== Detects malformed regex patterns defined in a single string literal to catch mistakes at compile time. diff --git a/clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp b/clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp index 88de3539da5831..440c59eea50f01 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy -std=c++17-or-later %s llvm-regex %t +// RUN: %check_clang_tidy -std=c++17-or-later %s llvm-invalid-regex-pattern %t #include <string> >From b69553bca020fe7cf1eb939d21e781c90cc827aa Mon Sep 17 00:00:00 2001 From: "F.Tibor" <[email protected]> Date: Sat, 4 Jul 2026 11:44:52 +0200 Subject: [PATCH 06/15] Missed a couple of things during renameing --- .../clang-tidy/llvm/InvalidRegexPatternCheck.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.h b/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.h index 177a47b668dc16..7adb7df285084d 100644 --- a/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.h +++ b/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.h @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_REGEXCHECK_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_REGEXCHECK_H +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_INVALIDREGEXPATTERNCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_INVALIDREGEXPATTERNCHECK_H #include "../ClangTidyCheck.h" @@ -30,4 +30,4 @@ class InvalidRegexPatternCheck : public ClangTidyCheck { } // namespace clang::tidy::llvm_check -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_InvalidRegexPatternCheck_H +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_INVALIDREGEXPATTERNCHECK_H >From 193f29a69aac74f123eba230cc88513c38946f59 Mon Sep 17 00:00:00 2001 From: "F.Tibor" <[email protected]> Date: Sat, 4 Jul 2026 11:55:01 +0200 Subject: [PATCH 07/15] Use unsigned int instead of uint --- clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp b/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp index ca23dcc8c96500..3a357093ffd183 100644 --- a/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp +++ b/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp @@ -72,7 +72,7 @@ void InvalidRegexPatternCheck::check(const MatchFinder::MatchResult &Result) { Result.Nodes.getNodeAs<IntegerLiteral>("regexFlagsInt"); const auto *FlagEnum = Result.Nodes.getNodeAs<EnumConstantDecl>("regexFlagEnum"); - uint Flag = llvm::Regex::RegexFlags::NoFlags; + unsigned int Flag = llvm::Regex::RegexFlags::NoFlags; if (FlagInt) Flag = FlagInt->getValue().getZExtValue(); if (FlagEnum) >From 5c5324758339dc857f97f0784b1b4f6cd2850abd Mon Sep 17 00:00:00 2001 From: "F.Tibor" <[email protected]> Date: Sat, 4 Jul 2026 14:40:35 +0200 Subject: [PATCH 08/15] Clang-format --- clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp b/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp index 549867beec25d4..ed8d04f22a9e23 100644 --- a/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp @@ -14,11 +14,11 @@ #include "FormatvStringCheck.h" #include "HeaderGuardCheck.h" #include "IncludeOrderCheck.h" +#include "InvalidRegexPatternCheck.h" #include "PreferIsaOrDynCastInConditionalsCheck.h" #include "PreferRegisterOverUnsignedCheck.h" #include "PreferStaticOverAnonymousNamespaceCheck.h" #include "RedundantCastingCheck.h" -#include "InvalidRegexPatternCheck.h" #include "TwineLocalCheck.h" #include "TypeSwitchCaseTypesCheck.h" #include "UseNewMLIROpBuilderCheck.h" @@ -49,7 +49,8 @@ class LLVMModule : public ClangTidyModule { "llvm-qualified-auto"); CheckFactories.registerCheck<RedundantCastingCheck>( "llvm-redundant-casting"); - CheckFactories.registerCheck<InvalidRegexPatternCheck>("llvm-invalid-regex-pattern"); + CheckFactories.registerCheck<InvalidRegexPatternCheck>( + "llvm-invalid-regex-pattern"); CheckFactories.registerCheck<TwineLocalCheck>("llvm-twine-local"); CheckFactories.registerCheck<TypeSwitchCaseTypesCheck>( "llvm-type-switch-case-types"); >From 37e4c956b96d9541bf1382798b2e43eb59da0143 Mon Sep 17 00:00:00 2001 From: "F.Tibor" <[email protected]> Date: Wed, 5 Aug 2026 13:24:02 +0200 Subject: [PATCH 09/15] Update clang-tools-extra/docs/clang-tidy/checks/llvm/invalid-regex-pattern.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Donát Nagy <[email protected]> --- .../docs/clang-tidy/checks/llvm/invalid-regex-pattern.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang-tools-extra/docs/clang-tidy/checks/llvm/invalid-regex-pattern.rst b/clang-tools-extra/docs/clang-tidy/checks/llvm/invalid-regex-pattern.rst index 2cba3766608e78..3ce072c193c9c6 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/llvm/invalid-regex-pattern.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/llvm/invalid-regex-pattern.rst @@ -29,8 +29,8 @@ Example of member data being overwritten: class foo{ public: - foo(llvm::StringRef regex_ptrn) : regex_prtn(regex_prtn){}; - const llvm_StringRef regex_prtn = "("; // invalid regex pattern + foo(llvm::StringRef regex_pat) : regex_p(regex_pat){}; + const llvm_StringRef regex_pat = "("; // invalid regex pattern }; foo bar("[0-9]"); - llvm::Regex my_regex(bar.regex_prtn); + llvm::Regex my_regex(bar.regex_pat); >From 37a01e226e97e094d1880309e80a6203a0a855c1 Mon Sep 17 00:00:00 2001 From: "F.Tibor" <[email protected]> Date: Thu, 6 Aug 2026 13:34:17 +0200 Subject: [PATCH 10/15] Reduce code duplication using anyOf --- .../llvm/InvalidRegexPatternCheck.cpp | 28 ++++++------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp b/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp index 3a357093ffd183..048a9c5328c0ef 100644 --- a/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp +++ b/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp @@ -30,25 +30,15 @@ void InvalidRegexPatternCheck::registerMatchers(MatchFinder *Finder) { hasDeclaration(cxxRecordDecl(hasName("::std::basic_string_view")))))); auto AnyCastedToStringRef = ignoringImplicit(anyOf( stringLiteral().bind("stringLiteral"), - declRefExpr(to(varDecl(hasType(IsConstStdString), - hasInitializer(GetStringLiteralFromObject)))), - declRefExpr(to(varDecl(hasType(IsConstllvmStringRef), - hasInitializer(GetStringLiteralFromObject)))), - declRefExpr( - to(varDecl(hasType(IsConstCharPtr), hasInitializer(GetStringLit)))), - declRefExpr(to(varDecl(hasType(IsStdStringView), - hasInitializer(GetStringLiteralFromObject)))), - memberExpr( - member(fieldDecl(hasType(IsConstStdString), - hasInClassInitializer(GetStringLiteralFromObject)))), - memberExpr(member(fieldDecl(hasType(IsConstCharPtr), - hasInClassInitializer(GetStringLit)))), - memberExpr( - member(fieldDecl(hasType(IsConstllvmStringRef), - hasInClassInitializer(GetStringLiteralFromObject)))), - memberExpr(member( - fieldDecl(hasType(IsStdStringView), - hasInClassInitializer(GetStringLiteralFromObject)))))); + declRefExpr(to(varDecl( + hasType(qualType(anyOf(IsConstStdString, IsConstllvmStringRef, + IsStdStringView, IsConstCharPtr))), + hasInitializer(anyOf(GetStringLiteralFromObject, GetStringLit))))), + memberExpr(member(fieldDecl( + hasType(qualType(anyOf(IsConstStdString, IsConstllvmStringRef, + IsStdStringView, IsConstCharPtr))), + hasInClassInitializer( + anyOf(GetStringLiteralFromObject, GetStringLit))))))); auto IsRegexFlagsType = ignoringParenImpCasts( anyOf(integerLiteral().bind("regexFlagsInt"), >From 22b511015b1a5b7582b125e04314bac8bac22b8c Mon Sep 17 00:00:00 2001 From: "F.Tibor" <[email protected]> Date: Thu, 6 Aug 2026 13:52:36 +0200 Subject: [PATCH 11/15] Change supported languages to c++17 since std::string_view is only available since then --- clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.h b/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.h index 7adb7df285084d..cb6223a668344e 100644 --- a/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.h +++ b/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.h @@ -24,7 +24,7 @@ class InvalidRegexPatternCheck : public ClangTidyCheck { void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { - return LangOpts.CPlusPlus; + return LangOpts.CPlusPlus17; } }; >From bd86583bcd9fa84b49900061a000260dfdecfc83 Mon Sep 17 00:00:00 2001 From: "F.Tibor" <[email protected]> Date: Fri, 7 Aug 2026 12:36:36 +0200 Subject: [PATCH 12/15] Detect char arrays too (normal, static, const) --- .../llvm/InvalidRegexPatternCheck.cpp | 7 ++++-- .../checks/llvm/invalid-regex-pattern.rst | 3 +++ .../test/clang-tidy/checkers/llvm/regex.cpp | 24 +++++++++++++++++-- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp b/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp index 048a9c5328c0ef..4fd1d86daf4dde 100644 --- a/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp +++ b/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp @@ -26,17 +26,20 @@ void InvalidRegexPatternCheck::registerMatchers(MatchFinder *Finder) { auto GetStringLiteralFromObject = ignoringImplicit(cxxConstructExpr(hasAnyArgument(GetStringLit))); auto IsConstCharPtr = pointerType(pointee(builtinType(), isConstQualified())); + auto IsCharArray = qualType( + hasUnqualifiedDesugaredType(arrayType(hasElementType(builtinType())))); auto IsStdStringView = qualType(hasUnqualifiedDesugaredType(recordType( hasDeclaration(cxxRecordDecl(hasName("::std::basic_string_view")))))); auto AnyCastedToStringRef = ignoringImplicit(anyOf( stringLiteral().bind("stringLiteral"), declRefExpr(to(varDecl( hasType(qualType(anyOf(IsConstStdString, IsConstllvmStringRef, - IsStdStringView, IsConstCharPtr))), + IsStdStringView, IsConstCharPtr, + IsCharArray))), hasInitializer(anyOf(GetStringLiteralFromObject, GetStringLit))))), memberExpr(member(fieldDecl( hasType(qualType(anyOf(IsConstStdString, IsConstllvmStringRef, - IsStdStringView, IsConstCharPtr))), + IsStdStringView, IsConstCharPtr, IsCharArray))), hasInClassInitializer( anyOf(GetStringLiteralFromObject, GetStringLit))))))); diff --git a/clang-tools-extra/docs/clang-tidy/checks/llvm/invalid-regex-pattern.rst b/clang-tools-extra/docs/clang-tidy/checks/llvm/invalid-regex-pattern.rst index 3ce072c193c9c6..aaec7c456c74ed 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/llvm/invalid-regex-pattern.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/llvm/invalid-regex-pattern.rst @@ -12,6 +12,9 @@ with a string literal, or stored in one of these non mutable container: - ``const char*`` - ``const llvm::StringRef`` - ``std::string_view`` +- ``char[]`` +- ``static char[]`` +- ``const char[]`` In the event that the patterns are stored as a class member, the check verifies the initialization value, if defined, even if its overwritten by the constructor. diff --git a/clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp b/clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp index 440c59eea50f01..2d36fe5d1c5c30 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/llvm/regex.cpp @@ -41,6 +41,18 @@ void test_detected_faulty_patterns(){ const llvm::StringRef badStrRef = "a*?"; // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: invalid regex pattern: repetition-operator operand invalid llvm::Regex re5(badStrRef); + + static const char badConstStaticChar[] = ""; + // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: invalid regex pattern: empty (sub)expression + llvm::Regex regex_badConstStaticChar(badConstStaticChar); + + static char badStaticChar[] = ""; + // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: invalid regex pattern: empty (sub)expression + llvm::Regex regex_badStaticChar(badStaticChar); + + char badCharArray[] = ""; + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: invalid regex pattern: empty (sub)expression + llvm::Regex regex_badCharArray(badCharArray); struct RegexPatterns { const char* badMemberChar = ""; @@ -54,14 +66,22 @@ void test_detected_faulty_patterns(){ const llvm::StringRef badMemberStrRef = "a*?"; // CHECK-MESSAGES: :[[@LINE-1]]:45: warning: invalid regex pattern: repetition-operator operand invalid - }; + const char badMemberConstChar[1] = ""; + // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: invalid regex pattern: empty (sub)expression + + char badMemberCharArray[1] = ""; + // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: invalid regex pattern: empty (sub)expression + }; + RegexPatterns Pats; - + llvm::Regex re6(Pats.badMemberChar); llvm::Regex re7(Pats.badMemberStr); llvm::Regex re8(Pats.badMemberStrView); llvm::Regex re9(Pats.badMemberStrRef); + llvm::Regex regex_badMemberConstStaticChar(Pats.badMemberConstChar); + llvm::Regex regex_badMemberCharArray(Pats.badMemberCharArray); } void test_no_detection_on_mutable(){ >From 2df860e922bbdca30a61d909c6494f30986527a5 Mon Sep 17 00:00:00 2001 From: "F.Tibor" <[email protected]> Date: Fri, 7 Aug 2026 12:40:33 +0200 Subject: [PATCH 13/15] Convert documentation to the markdown format --- .../checks/llvm/invalid-regex-pattern.md | 37 ++++++++++++++++++ .../checks/llvm/invalid-regex-pattern.rst | 39 ------------------- 2 files changed, 37 insertions(+), 39 deletions(-) create mode 100644 clang-tools-extra/docs/clang-tidy/checks/llvm/invalid-regex-pattern.md delete mode 100644 clang-tools-extra/docs/clang-tidy/checks/llvm/invalid-regex-pattern.rst diff --git a/clang-tools-extra/docs/clang-tidy/checks/llvm/invalid-regex-pattern.md b/clang-tools-extra/docs/clang-tidy/checks/llvm/invalid-regex-pattern.md new file mode 100644 index 00000000000000..ea6af2a1bb27a5 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/llvm/invalid-regex-pattern.md @@ -0,0 +1,37 @@ +# clang-tidy - llvm-invalid-regex-pattern + +## llvm-invalid-regex-pattern + +Detects malformed regex patterns defined in a single string literal +to catch mistakes at compile time. +It detects these string literals if they are defined in the regex constructor +with a string literal, or stored in one of these non mutable containers: + +- `const std::string` +- `const char*` +- `const char[]` +- `static const char[]` +- `const llvm::StringRef` +- `std::string_view` + +In the event that the patterns are stored as a class member, the check verifies +the initialization value, if defined, even if its overwritten by the constructor. + +Example of detection: + +```cpp +const std::string my_regex_pattern("[0-8"); // invalid regex pattern +llvm::Regex my_regex(my_regex_pattern); +``` + +Example of member data being overwritten: + +```cpp +class foo{ +public: + foo(llvm::StringRef regex_pat) : regex_p(regex_pat){}; + const llvm::StringRef regex_pat = "("; // invalid regex pattern +}; +foo bar("[0-9]"); // pattern not checked +llvm::Regex my_regex(bar.regex_pat); +``` diff --git a/clang-tools-extra/docs/clang-tidy/checks/llvm/invalid-regex-pattern.rst b/clang-tools-extra/docs/clang-tidy/checks/llvm/invalid-regex-pattern.rst deleted file mode 100644 index aaec7c456c74ed..00000000000000 --- a/clang-tools-extra/docs/clang-tidy/checks/llvm/invalid-regex-pattern.rst +++ /dev/null @@ -1,39 +0,0 @@ -.. title:: clang-tidy - llvm-invalid-regex-pattern - -llvm-invalid-regex-pattern -========================== - -Detects malformed regex patterns defined in a single string literal -to catch mistakes at compile time. -It detects these string literals if they are defined in the regex constructor -with a string literal, or stored in one of these non mutable container: - -- ``const std::string`` -- ``const char*`` -- ``const llvm::StringRef`` -- ``std::string_view`` -- ``char[]`` -- ``static char[]`` -- ``const char[]`` - -In the event that the patterns are stored as a class member, the check verifies -the initialization value, if defined, even if its overwritten by the constructor. - -Example of detection: - -.. code-block:: c++ - - const std::string my_regex_pattern("[0-8"); // invalid regex pattern - llvm::Regex my_regex(my_regex_pattern); - -Example of member data being overwritten: - -.. code-block:: c++ - - class foo{ - public: - foo(llvm::StringRef regex_pat) : regex_p(regex_pat){}; - const llvm_StringRef regex_pat = "("; // invalid regex pattern - }; - foo bar("[0-9]"); - llvm::Regex my_regex(bar.regex_pat); >From f617e588cd91244613a9cbed8eb20c5bfbb041a7 Mon Sep 17 00:00:00 2001 From: "F.Tibor" <[email protected]> Date: Fri, 7 Aug 2026 14:12:17 +0200 Subject: [PATCH 14/15] Use assert --- .../clang-tidy/llvm/InvalidRegexPatternCheck.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp b/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp index 4fd1d86daf4dde..ca74528332c30b 100644 --- a/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp +++ b/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp @@ -60,7 +60,8 @@ void InvalidRegexPatternCheck::registerMatchers(MatchFinder *Finder) { void InvalidRegexPatternCheck::check(const MatchFinder::MatchResult &Result) { const auto *DetectedPattern = Result.Nodes.getNodeAs<StringLiteral>("stringLiteral"); - if (DetectedPattern) { + assert(DetectedPattern && "stringLiteral must be bound in matcher"); + const auto *FlagInt = Result.Nodes.getNodeAs<IntegerLiteral>("regexFlagsInt"); const auto *FlagEnum = @@ -75,7 +76,6 @@ void InvalidRegexPatternCheck::check(const MatchFinder::MatchResult &Result) { if (!TestRegex.isValid(RegexError)) diag(DetectedPattern->getBeginLoc(), "invalid regex pattern: %0") << RegexError << DetectedPattern->getSourceRange(); - } } } // namespace clang::tidy::llvm_check >From 27ab432b4f1366b711e50a77e5e6f29f085a7679 Mon Sep 17 00:00:00 2001 From: "F.Tibor" <[email protected]> Date: Fri, 7 Aug 2026 14:15:00 +0200 Subject: [PATCH 15/15] Clang format --- .../llvm/InvalidRegexPatternCheck.cpp | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp b/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp index ca74528332c30b..3d582ab54d0c4d 100644 --- a/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp +++ b/clang-tools-extra/clang-tidy/llvm/InvalidRegexPatternCheck.cpp @@ -33,15 +33,16 @@ void InvalidRegexPatternCheck::registerMatchers(MatchFinder *Finder) { auto AnyCastedToStringRef = ignoringImplicit(anyOf( stringLiteral().bind("stringLiteral"), declRefExpr(to(varDecl( - hasType(qualType(anyOf(IsConstStdString, IsConstllvmStringRef, - IsStdStringView, IsConstCharPtr, - IsCharArray))), + hasType( + qualType(anyOf(IsConstStdString, IsConstllvmStringRef, + IsStdStringView, IsConstCharPtr, IsCharArray))), hasInitializer(anyOf(GetStringLiteralFromObject, GetStringLit))))), - memberExpr(member(fieldDecl( - hasType(qualType(anyOf(IsConstStdString, IsConstllvmStringRef, - IsStdStringView, IsConstCharPtr, IsCharArray))), - hasInClassInitializer( - anyOf(GetStringLiteralFromObject, GetStringLit))))))); + memberExpr(member( + fieldDecl(hasType(qualType( + anyOf(IsConstStdString, IsConstllvmStringRef, + IsStdStringView, IsConstCharPtr, IsCharArray))), + hasInClassInitializer( + anyOf(GetStringLiteralFromObject, GetStringLit))))))); auto IsRegexFlagsType = ignoringParenImpCasts( anyOf(integerLiteral().bind("regexFlagsInt"), @@ -60,22 +61,21 @@ void InvalidRegexPatternCheck::registerMatchers(MatchFinder *Finder) { void InvalidRegexPatternCheck::check(const MatchFinder::MatchResult &Result) { const auto *DetectedPattern = Result.Nodes.getNodeAs<StringLiteral>("stringLiteral"); - assert(DetectedPattern && "stringLiteral must be bound in matcher"); + assert(DetectedPattern && "stringLiteral must be bound in matcher"); - const auto *FlagInt = - Result.Nodes.getNodeAs<IntegerLiteral>("regexFlagsInt"); - const auto *FlagEnum = - Result.Nodes.getNodeAs<EnumConstantDecl>("regexFlagEnum"); - unsigned int Flag = llvm::Regex::RegexFlags::NoFlags; - if (FlagInt) - Flag = FlagInt->getValue().getZExtValue(); - if (FlagEnum) - Flag = FlagEnum->getInitVal().getZExtValue(); - const llvm::Regex TestRegex(DetectedPattern->getString(), Flag); - std::string RegexError; - if (!TestRegex.isValid(RegexError)) - diag(DetectedPattern->getBeginLoc(), "invalid regex pattern: %0") - << RegexError << DetectedPattern->getSourceRange(); + const auto *FlagInt = Result.Nodes.getNodeAs<IntegerLiteral>("regexFlagsInt"); + const auto *FlagEnum = + Result.Nodes.getNodeAs<EnumConstantDecl>("regexFlagEnum"); + unsigned int Flag = llvm::Regex::RegexFlags::NoFlags; + if (FlagInt) + Flag = FlagInt->getValue().getZExtValue(); + if (FlagEnum) + Flag = FlagEnum->getInitVal().getZExtValue(); + const llvm::Regex TestRegex(DetectedPattern->getString(), Flag); + std::string RegexError; + if (!TestRegex.isValid(RegexError)) + diag(DetectedPattern->getBeginLoc(), "invalid regex pattern: %0") + << RegexError << DetectedPattern->getSourceRange(); } } // namespace clang::tidy::llvm_check _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
