diff --git a/clang-tools-extra/include-cleaner/lib/Analysis.cpp b/clang-tools-extra/include-cleaner/lib/Analysis.cpp --- a/clang-tools-extra/include-cleaner/lib/Analysis.cpp +++ b/clang-tools-extra/include-cleaner/lib/Analysis.cpp @@ -28,10 +28,29 @@ #include "llvm/ADT/StringSet.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" +#include #include namespace clang::include_cleaner { +namespace { +bool shouldIgnoreMacroReference(const Macro &M) { + static const auto *MacroNamesToIgnore = new llvm::StringSet<>{ + // C standard says these are implementation defined macros, hence most of + // the standard library providers implement it by defining these as macros + // that resolve to themselves. + // This results in surprising behavior from users point of view (we + // generate a usage of stdio.h, in places unrelated to standard library). + // FIXME: Also eliminate the false negatives by treating declarations + // resulting from these expansions as used. + "stdin", + "stdout", + "stderr", + }; + return MacroNamesToIgnore->contains(M.Name->getName()); +} +} // namespace + void walkUsed(llvm::ArrayRef ASTRoots, llvm::ArrayRef MacroRefs, const PragmaIncludes *PI, const SourceManager &SM, @@ -51,7 +70,8 @@ } for (const SymbolReference &MacroRef : MacroRefs) { assert(MacroRef.Target.kind() == Symbol::Macro); - if (!SM.isWrittenInMainFile(SM.getSpellingLoc(MacroRef.RefLocation))) + if (!SM.isWrittenInMainFile(SM.getSpellingLoc(MacroRef.RefLocation)) || + shouldIgnoreMacroReference(MacroRef.Target.macro())) continue; CB(MacroRef, headersForSymbol(MacroRef.Target, SM, PI)); } diff --git a/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp b/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp --- a/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp +++ b/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp @@ -483,5 +483,29 @@ Pair(Code.point("partial"), UnorderedElementsAre(Partial))))); } +TEST_F(WalkUsedTest, IgnoresIdentityMacros) { + llvm::Annotations Code(R"cpp( + #include "header.h" + void $bar^bar() { + $stdin^stdin(); + } + )cpp"); + Inputs.Code = Code.code(); + Inputs.ExtraFiles["header.h"] = guard(R"cpp( + #include "inner.h" + void stdin(); + )cpp"); + Inputs.ExtraFiles["inner.h"] = guard(R"cpp( + #define stdin stdin + )cpp"); + + TestAST AST(Inputs); + auto &SM = AST.sourceManager(); + auto MainFile = Header(SM.getFileEntryForID(SM.getMainFileID())); + EXPECT_THAT(offsetToProviders(AST, SM), + UnorderedElementsAre( + // FIXME: we should have a reference from stdin to header.h + Pair(Code.point("bar"), UnorderedElementsAre(MainFile)))); +} } // namespace } // namespace clang::include_cleaner