[llvm-branch-commits] [clang] 065d1ba - Revert "Revert "Make sanitizer special case list slash-agnostic" (#205399)"
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sat Jun 27 06:33:42 PDT 2026
Author: Vitaly Buka
Date: 2026-06-27T06:33:39-07:00
New Revision: 065d1ba46f6b6701042dd73ca308d590bde9f9ff
URL: https://github.com/llvm/llvm-project/commit/065d1ba46f6b6701042dd73ca308d590bde9f9ff
DIFF: https://github.com/llvm/llvm-project/commit/065d1ba46f6b6701042dd73ca308d590bde9f9ff.diff
LOG: Revert "Revert "Make sanitizer special case list slash-agnostic" (#205399)"
This reverts commit 0b5c006208cf7b0fee3709f468152e7de81607ef.
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/docs/SanitizerSpecialCaseList.rst
clang/unittests/Basic/DiagnosticTest.cpp
llvm/lib/Support/SpecialCaseList.cpp
llvm/unittests/Support/SpecialCaseListTest.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 93f770c10afae..d6b978ec91659 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -1103,6 +1103,11 @@ Sanitizers
warning for deprecated matches. Version 5 drops backward compatibility and
requires rules to match canonicalized paths (without leading ``./``).
+- Sanitizer Special Case Lists (``-fsanitize-ignorelist``) and warning
+ suppression mappings (``--warning-suppression-mappings``) now recognize version
+ 4 of the Special Case List format (indicated by ``#!special-case-list-v4``).
+ On Windows hosts, path matching is slash-agnostic (both forward slashes (``/``)
+ and backslashes (``\``) match either path separator in both patterns and paths).
Python Binding Changes
----------------------
diff --git a/clang/docs/SanitizerSpecialCaseList.rst b/clang/docs/SanitizerSpecialCaseList.rst
index 1de3555c5a8ce..918abf19f8f10 100644
--- a/clang/docs/SanitizerSpecialCaseList.rst
+++ b/clang/docs/SanitizerSpecialCaseList.rst
@@ -230,6 +230,18 @@ tool-specific docs.
[{cfi-vcall,cfi-icall}]
fun:*BadCfiCall
+
+.. note::
+
+ By default, ``src`` and ``mainfile`` are matched against the filename as seen
+ by LLVM. On Windows, this might involve a mix of forward and backslashes as
+ file separators, and writing patterns to match both variants can be
+ inconvenient.
+
+ Starting with version 4 (indicated by ``#!special-case-list-v4``), path matching
+ on Windows hosts is slash-agnostic: both forward slashes (``/``) and backslashes
+ (``\``) match either path separator in both patterns and paths.
+
.. note::
By default, path matching (for ``src`` and ``mainfile``) matches the query
diff --git a/clang/unittests/Basic/DiagnosticTest.cpp b/clang/unittests/Basic/DiagnosticTest.cpp
index 4d310d3ece23f..4ced52c8f715f 100644
--- a/clang/unittests/Basic/DiagnosticTest.cpp
+++ b/clang/unittests/Basic/DiagnosticTest.cpp
@@ -414,4 +414,39 @@ TEST_F(SuppressionMappingTest, ParsingRespectsOtherWarningOpts) {
clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
EXPECT_THAT(diags(), IsEmpty());
}
+
+#ifdef _WIN32
+TEST_F(SuppressionMappingTest, CanonicalizesSlashesOnWindows) {
+ llvm::StringLiteral SuppressionMappingFile = R"(#!special-case-list-v4
+ [unused]
+ src:*clang/*
+ src:*clang/lib/Sema/*=emit
+ src:*clang/lib\\Sema/foo*
+ fun:suppress/me)";
+ Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+ FS->addFile("foo.txt", /*ModificationTime=*/{},
+ llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
+ clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+ EXPECT_THAT(diags(), IsEmpty());
+
+ EXPECT_TRUE(Diags.isSuppressedViaMapping(
+ diag::warn_unused_function, locForFile(R"(clang/lib/Basic/bar.h)")));
+ EXPECT_TRUE(Diags.isSuppressedViaMapping(
+ diag::warn_unused_function, locForFile(R"(clang/lib/Basic\bar.h)")));
+ EXPECT_TRUE(Diags.isSuppressedViaMapping(
+ diag::warn_unused_function, locForFile(R"(clang\lib/Basic/bar.h)")));
+ EXPECT_FALSE(Diags.isSuppressedViaMapping(
+ diag::warn_unused_function, locForFile(R"(clang/lib/Sema/baz.h)")));
+ EXPECT_FALSE(Diags.isSuppressedViaMapping(
+ diag::warn_unused_function, locForFile(R"(clang/lib/Sema\baz.h)")));
+
+ // Under slash-agnostic matching, backslashes and forward slashes match each
+ // other, so we match the third pattern.
+ EXPECT_TRUE(Diags.isSuppressedViaMapping(
+ diag::warn_unused_function, locForFile(R"(clang\lib\Sema/foo.h)")));
+ EXPECT_TRUE(Diags.isSuppressedViaMapping(
+ diag::warn_unused_function, locForFile(R"(clang/lib/Sema/foo.h)")));
+}
+#endif
+
} // namespace
diff --git a/llvm/lib/Support/SpecialCaseList.cpp b/llvm/lib/Support/SpecialCaseList.cpp
index d72f7e7fd1d81..fe12039719059 100644
--- a/llvm/lib/Support/SpecialCaseList.cpp
+++ b/llvm/lib/Support/SpecialCaseList.cpp
@@ -24,6 +24,7 @@
#include "llvm/Support/GlobPattern.h"
#include "llvm/Support/LineIterator.h"
#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Support/WithColor.h"
@@ -44,7 +45,7 @@ namespace {
// Lagacy v1 matcher.
class RegexMatcher {
public:
- Error insert(StringRef Pattern, unsigned LineNumber);
+ Error insert(StringRef Pattern, unsigned LineNumber, bool SlashAgnostic);
unsigned match(StringRef Query) const;
StringRef findRule(unsigned LineNo) const;
@@ -62,7 +63,7 @@ class RegexMatcher {
class GlobMatcher {
public:
- Error insert(StringRef Pattern, unsigned LineNumber);
+ Error insert(StringRef Pattern, unsigned LineNumber, bool SlashAgnostic);
unsigned match(StringRef Query) const;
StringRef findRule(unsigned LineNo) const;
@@ -95,6 +96,7 @@ struct QueryOptions {
bool UseGlobs = true;
bool RemoveDotSlash = false;
bool WarnDotSlashMatch = false;
+ bool SlashAgnostic = false;
};
/// Represents a set of patterns and their line numbers
@@ -116,7 +118,8 @@ class Matcher {
mutable std::once_flag Warned;
};
-Error RegexMatcher::insert(StringRef Pattern, unsigned LineNumber) {
+Error RegexMatcher::insert(StringRef Pattern, unsigned LineNumber,
+ bool SlashAgnostic) {
if (Pattern.empty())
return createStringError(errc::invalid_argument,
"Supplied regex was blank");
@@ -155,11 +158,13 @@ StringRef RegexMatcher::findRule(unsigned LineNo) const {
return {};
}
-Error GlobMatcher::insert(StringRef Pattern, unsigned LineNumber) {
+Error GlobMatcher::insert(StringRef Pattern, unsigned LineNumber,
+ bool SlashAgnostic) {
if (Pattern.empty())
return createStringError(errc::invalid_argument, "Supplied glob was blank");
- auto Res = GlobPattern::create(Pattern, /*MaxSubPatterns=*/1024);
+ auto Res =
+ GlobPattern::create(Pattern, /*MaxSubPatterns=*/1024, SlashAgnostic);
if (auto Err = Res.takeError())
return Err;
Globs.emplace_back(Pattern, LineNumber, std::move(Res.get()));
@@ -257,7 +262,11 @@ Matcher::Matcher(QueryOptions QOpts) : Options(QOpts) {
}
Error Matcher::insert(StringRef Pattern, unsigned LineNumber) {
- return std::visit([&](auto &V) { return V.insert(Pattern, LineNumber); }, M);
+ return std::visit(
+ [&](auto &V) {
+ return V.insert(Pattern, LineNumber, Options.SlashAgnostic);
+ },
+ M);
}
/// Matches Query against the patterns. The behavior is controlled by
@@ -406,6 +415,15 @@ bool SpecialCaseList::parse(unsigned FileIdx, const MemoryBuffer *MB,
bool UseGlobs = MinVersion(2);
bool RemoveDotSlash = MinVersion(3);
bool WarnDotSlash = MinVersion(4) && !MinVersion(5);
+ // TODO: Improve efficiency on Windows.
+ // `SlashAgnostic` makes `GlobMatcher` lookup inefficient by reducing the part
+ // of the pattern handled by the RadixTree. This was already the case even
+ // before `SlashAgnostic` because `GlobMatcher` pessimizes on escape sequences
+ // needed to represent Windows backslashes. A possible, but not unique,
+ // solution is to assume (or convert Windows query) backslashes, and
+ // preprocess the Glob pattern to use
diff erent escape sequences.
+ bool SlashAgnostic = MinVersion(4) && llvm::sys::path::is_style_windows(
+ llvm::sys::path::Style::native);
auto ErrOrSection = addSection("*", FileIdx, 1, true);
if (auto Err = ErrOrSection.takeError()) {
@@ -457,6 +475,7 @@ bool SpecialCaseList::parse(unsigned FileIdx, const MemoryBuffer *MB,
if (llvm::is_contained(PathPrefixes, Prefix)) {
QOpts.RemoveDotSlash = RemoveDotSlash;
QOpts.WarnDotSlashMatch = WarnDotSlash;
+ QOpts.SlashAgnostic = SlashAgnostic;
}
auto [Pattern, Category] = Postfix.split("=");
diff --git a/llvm/unittests/Support/SpecialCaseListTest.cpp b/llvm/unittests/Support/SpecialCaseListTest.cpp
index 5bcd111f53059..d4d47c37993f2 100644
--- a/llvm/unittests/Support/SpecialCaseListTest.cpp
+++ b/llvm/unittests/Support/SpecialCaseListTest.cpp
@@ -464,4 +464,24 @@ TEST_F(SpecialCaseListTest, FileIdx) {
sys::fs::remove(Path);
}
+#ifdef _WIN32
+TEST_F(SpecialCaseListTest, SlashAgnosticPathsOnWindows) {
+ std::unique_ptr<SpecialCaseList> SCL =
+ makeSpecialCaseList("#!special-case-list-v4\n"
+ "\n"
+ "src:*foo/bar*\n"
+ "src:*foo\\\\baz\n"
+ "fun:hi\\\\bye=category\n");
+ EXPECT_TRUE(SCL->inSection("", "src", "foo/bar"));
+ EXPECT_TRUE(SCL->inSection("", "src", "foo\\bar"));
+ // The baz pattern matches because paths are matched slash-agnostically
+ EXPECT_TRUE(SCL->inSection("", "src", "foo/baz"));
+ EXPECT_TRUE(SCL->inSection("", "src", "foo\\baz"));
+ // Slash-agnostic matching only applies to files
+ EXPECT_TRUE(SCL->inSection("", "fun", "hi\\bye", "category"));
+ EXPECT_FALSE(SCL->inSection("", "fun", "hi/bye", "category"));
+}
+
+#endif
+
} // namespace
More information about the llvm-branch-commits
mailing list