[include-cleaner] Report refs from macro-concat'd tokens as ambigious (#175532)

Previously we completely ignored these references as we couldn't detect
whether some pieces of concat'd token originated from main file and we
wanted to prevent false positives. Unfortunately these are resulting in
false negatives in certain cases and are breaking builds.

After this change, include-cleaner will treat such references as
ambigious to prevent deletion of likely-used headers (if they're already
directly included), while still giving user the opportunity to
explicitly delete them.
This commit is contained in:
kadir çetinkaya 2026-01-12 14:55:25 +01:00 committed by GitHub
parent 8380b57b7e
commit 51ee583b1a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 39 additions and 1 deletions

View File

@ -58,7 +58,19 @@ void walkUsed(llvm::ArrayRef<Decl *> ASTRoots,
tooling::stdlib::Recognizer Recognizer;
for (auto *Root : ASTRoots) {
walkAST(*Root, [&](SourceLocation Loc, NamedDecl &ND, RefType RT) {
auto FID = SM.getFileID(SM.getSpellingLoc(Loc));
auto SpellLoc = SM.getSpellingLoc(Loc);
// Tokens resulting from macro concatenation ends up in scratch space and
// clang currently doesn't have a good/simple APIs for tracking where
// pieces of a concataned token originated from.
// So we use the macro expansion location instead, and downgrade reference
// type to ambigious to prevent false negatives.
if (SM.isWrittenInScratchSpace(SpellLoc)) {
Loc = SM.getExpansionLoc(Loc);
if (RT == RefType::Explicit)
RT = RefType::Ambiguous;
SpellLoc = SM.getSpellingLoc(Loc);
}
auto FID = SM.getFileID(SpellLoc);
if (FID != SM.getMainFileID() && FID != SM.getPreambleFileID())
return;
// FIXME: Most of the work done here is repetitive. It might be useful to

View File

@ -680,5 +680,31 @@ TEST_F(WalkUsedTest, IgnoresIdentityMacros) {
// FIXME: we should have a reference from stdin to header.h
Pair(Code.point("bar"), UnorderedElementsAre(MainFile))));
}
TEST_F(WalkUsedTest, MacroConcat) {
llvm::Annotations Code(R"cpp(
#include "header.h"
void f() {
$xyz^FOO(xyz) = 1;
$bar^BAR = 2;
}
)cpp");
Inputs.Code = Code.code();
Inputs.ExtraFiles["header.h"] = guard(R"cpp(
#define FOO(x) FLAGS_##x
#define BAR FOO(bb)
int FLAGS_xyz;
int FLAGS_bb;
)cpp");
TestAST AST(Inputs);
auto &SM = AST.sourceManager();
auto Header = *SM.getFileManager().getOptionalFileRef("header.h");
EXPECT_THAT(
offsetToProviders(AST),
AllOf(Contains(Pair(Code.point("bar"), UnorderedElementsAre(Header))),
Contains(Pair(Code.point("xyz"), UnorderedElementsAre(Header)))));
}
} // namespace
} // namespace clang::include_cleaner