Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions clang/docs/SanitizerSpecialCaseList.rst
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,23 @@ 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.

If the special case list file begins with ``#!canonical-paths``, then paths
will be canonicalized before patterns are matched against them. This involves
stripping any leading dots and slashes, and (on Windows only) converting all
backslashes to forward slashes.

If the file uses both ``#!special-case-list-v1`` and ``#!canonical-paths``,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's just do '#!special-case-list-v2' for current behavior,
and we will garbage collect v1, v2 in future.

#!special-case-list-v1 is just for transition, maybe it's already time to remove it.

If we do opt-in with #!canonical-paths, then we just delaying transition for later and it does not make it easier.

Copy link
Collaborator

@vitalybuka vitalybuka Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I sudgest to not add #!special-case-list-v1 and #!canonical-paths into this doc at all.

Users should update files ASAP

Good place for this is Beaking Changes in clang release notes. Something :
"Ingnore/suppression lists will need to use forward slashes from now, including Windows". Temporarily you can return to previous behavior with "#!special-case-list-v2"

And we should list in relnotest all flags which use special case list, and affected by the change

then they should occupy the first two lines, and ``#!canonical-paths`` must
appear on the second line.

``mainfile`` is similar to applying ``-fno-sanitize=`` to a set of files but
does not need plumbing into the build system. This works well for internal
linkage functions but has a caveat for C++ vague linkage functions.
Expand Down
16 changes: 12 additions & 4 deletions clang/lib/Basic/Diagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -612,10 +612,18 @@ bool WarningsSpecialCaseList::isDiagSuppressed(diag::kind DiagId,
SrcEntriesIt->getValue();
// We also use presumed locations here to improve reproducibility for
// preprocessed inputs.
if (PresumedLoc PLoc = SM.getPresumedLoc(DiagLoc); PLoc.isValid())
return globsMatches(
CategoriesToMatchers,
llvm::sys::path::remove_leading_dotslash(PLoc.getFilename()));
if (PresumedLoc PLoc = SM.getPresumedLoc(DiagLoc); PLoc.isValid()) {
if (CanonicalizePaths) {
return globsMatches(
CategoriesToMatchers,
llvm::sys::path::convert_to_slash(
llvm::sys::path::remove_leading_dotslash(PLoc.getFilename())));
} else {
return globsMatches(
CategoriesToMatchers,
llvm::sys::path::remove_leading_dotslash(PLoc.getFilename()));
}
}
return false;
}

Expand Down
34 changes: 34 additions & 0 deletions clang/unittests/Basic/DiagnosticTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,4 +360,38 @@ TEST_F(SuppressionMappingTest, ParsingRespectsOtherWarningOpts) {
clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
EXPECT_THAT(diags(), IsEmpty());
}

#ifdef _WIN32
TEST_F(SuppressionMappingTest, CanonicalizesSlashesOnWindows) {
llvm::StringLiteral SuppressionMappingFile = R"(#!canonical-paths
[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)")));

// The backslash gets canonicalized so we never match the third pattern
EXPECT_FALSE(Diags.isSuppressedViaMapping(
diag::warn_unused_function, locForFile(R"(clang\lib\Sema/foo.h)")));
EXPECT_FALSE(Diags.isSuppressedViaMapping(
diag::warn_unused_function, locForFile(R"(clang/lib/Sema/foo.h)")));
}
#endif

} // namespace
5 changes: 5 additions & 0 deletions llvm/docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,11 @@ Changes to BOLT
Changes to Sanitizers
---------------------

* (Sanitizer Special Case Lists)[https://clang.llvm.org/docs/SanitizerSpecialCaseList.html]
may now be prefixed with ``#!canonical-paths`` to specify that filename patterns
should be matched against canonicalized paths, without leading dots or slashes
and (on Windows only) without any backslashes.

Other Changes
-------------

Expand Down
3 changes: 2 additions & 1 deletion llvm/include/llvm/Support/SpecialCaseList.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ class SpecialCaseList {
class Matcher {
public:
LLVM_ABI Error insert(StringRef Pattern, unsigned LineNumber,
bool UseRegex);
bool UseGlobs);
// Returns the line number in the source file that this query matches to.
// Returns zero if no match is found.
LLVM_ABI unsigned match(StringRef Query) const;
Expand Down Expand Up @@ -154,6 +154,7 @@ class SpecialCaseList {
};

std::vector<Section> Sections;
bool CanonicalizePaths = false;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sannitizers support multiple files
So it should be a property of the section


LLVM_ABI Expected<Section *> addSection(StringRef SectionStr,
unsigned FileIdx, unsigned LineNo,
Expand Down
18 changes: 14 additions & 4 deletions llvm/lib/Support/SpecialCaseList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,17 @@ bool SpecialCaseList::parse(unsigned FileIdx, const MemoryBuffer *MB,
return false;
}

// Scan the start of the file for special comments. These don't appear when
// iterating below because comment lines are automatically skipped.
StringRef Buffer = MB->getBuffer();
// In https://reviews.llvm.org/D154014 we added glob support and planned to
// remove regex support in patterns. We temporarily support the original
// behavior using regexes if "#!special-case-list-v1" is the first line of the
// file. For more details, see
// behavior using regexes if "#!special-case-list-v1" is the first line of
// the file. For more details, see
// https://discourse.llvm.org/t/use-glob-instead-of-regex-for-specialcaselists/71666
bool UseGlobs = !MB->getBuffer().starts_with("#!special-case-list-v1\n");
bool UseGlobs = !Buffer.consume_front("#!special-case-list-v1\n");
// Specifies that patterns should be matched against canonicalized filepaths.
CanonicalizePaths = Buffer.consume_front("#!canonical-paths\n");

for (line_iterator LineIt(*MB, /*SkipBlanks=*/true, /*CommentMarker=*/'#');
!LineIt.is_at_eof(); LineIt++) {
Expand Down Expand Up @@ -237,7 +242,12 @@ unsigned SpecialCaseList::inSectionBlame(const SectionEntries &Entries,
if (II == I->second.end())
return 0;

return II->getValue().match(Query);
if (CanonicalizePaths && (Prefix == "src" || Prefix == "mainfile")) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually we don't need SpecialCaseList::NormalizePath

if we do src/mainfile it should be enough

return II->getValue().match(llvm::sys::path::convert_to_slash(
llvm::sys::path::remove_leading_dotslash(Query)));
} else {
return II->getValue().match(Query);
}
}

} // namespace llvm
18 changes: 18 additions & 0 deletions llvm/unittests/Support/SpecialCaseListTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,4 +372,22 @@ TEST_F(SpecialCaseListTest, FileIdx) {
sys::fs::remove(Path);
}

#ifdef _WIN32
TEST_F(SpecialCaseListTest, CanonicalizePathsOnWindows) {
std::unique_ptr<SpecialCaseList> SCL =
makeSpecialCaseList("#!canonical-paths\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 doesn't match because paths are canonicalized first
EXPECT_FALSE(SCL->inSection("", "src", "foo/baz"));
EXPECT_FALSE(SCL->inSection("", "src", "foo\\baz"));
// The canonicalization only applies to files
EXPECT_TRUE(SCL->inSection("", "fun", "hi\\bye", "category"));
}
#endif

} // namespace
Loading