hokein created this revision. hokein added a reviewer: kadircet. Herald added a project: All. hokein requested review of this revision. Herald added a project: clang-tools-extra.
Based on https://reviews.llvm.org/D137697 Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D137698 Files: clang-tools-extra/include-cleaner/include/clang-include-cleaner/Record.h clang-tools-extra/include-cleaner/lib/Analysis.cpp clang-tools-extra/include-cleaner/lib/CMakeLists.txt clang-tools-extra/include-cleaner/lib/Record.cpp clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp
Index: clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp =================================================================== --- clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp +++ clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp @@ -68,6 +68,10 @@ } }; +std::string guard(const llvm::StringRef Code) { + return "#pragma once\n" + Code.str(); +} + TEST_F(WalkUsedTest, Basic) { llvm::Annotations Code(R"cpp( #include "header.h" @@ -78,14 +82,14 @@ } )cpp"); Inputs.Code = Code.code(); - Inputs.ExtraFiles["header.h"] = R"cpp( + Inputs.ExtraFiles["header.h"] = guard(R"cpp( void foo(); namespace std { class vector {}; } - )cpp"; - Inputs.ExtraFiles["private.h"] = R"cpp( - // IWYU pragma: private, include "path/public.h" - class Private {}; - )cpp"; + )cpp"); + Inputs.ExtraFiles["private.h"] = guard(R"cpp( + // IWYU pragma: private, include "path/public.h" + class Private {}; + )cpp"); TestAST AST(Inputs); auto HeaderFile = Header(AST.fileManager().getFile("header.h").get()); @@ -123,21 +127,26 @@ Inputs.Code = R"cpp( #include "header1.h" #include "header2.h" + #include "header3.h" )cpp"; - Inputs.ExtraFiles["header1.h"] = R"cpp( - // IWYU pragma: private, include "path/public.h" - )cpp"; - Inputs.ExtraFiles["header2.h"] = R"cpp( - #include "detail1.h" // IWYU pragma: export + Inputs.ExtraFiles["header1.h"] = guard(R"cpp( + // IWYU pragma: private, include "path/public.h" + )cpp"); + Inputs.ExtraFiles["header2.h"] = guard(R"cpp( + #include "detail1.h" // IWYU pragma: export - // IWYU pragma: begin_exports - #include "detail2.h" - // IWYU pragma: end_exports + // IWYU pragma: begin_exports + #include "detail2.h" + // IWYU pragma: end_exports - #include "normal.h" - )cpp"; + #include "normal.h" + )cpp"); + Inputs.ExtraFiles["header3.h"] = guard(R"cpp( + #include "imp.inc" + )cpp"); Inputs.ExtraFiles["normal.h"] = Inputs.ExtraFiles["detail1.h"] = - Inputs.ExtraFiles["detail2.h"] = ""; + Inputs.ExtraFiles["detail2.h"] = guard(""); + Inputs.ExtraFiles["imp.inc"] = ""; TestAST AST(Inputs); const auto &SM = AST.sourceManager(); auto &FM = SM.getFileManager(); @@ -156,6 +165,9 @@ EXPECT_THAT(findIncludeHeaders(SourceLocFromFile("detail2.h"), SM, PI), UnorderedElementsAre(Header(FM.getFile("header2.h").get()), Header(FM.getFile("detail2.h").get()))); + // header3.h is the first #includer for the non self-contained header imp.inc + EXPECT_THAT(findIncludeHeaders(SourceLocFromFile("imp.inc"), SM, PI), + UnorderedElementsAre(Header(FM.getFile("header3.h").get()))); EXPECT_THAT(findIncludeHeaders(SourceLocFromFile("normal.h"), SM, PI), UnorderedElementsAre(Header(FM.getFile("normal.h").get()))); Index: clang-tools-extra/include-cleaner/lib/Record.cpp =================================================================== --- clang-tools-extra/include-cleaner/lib/Record.cpp +++ clang-tools-extra/include-cleaner/lib/Record.cpp @@ -14,6 +14,7 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Tooling/Inclusions/Header.h" namespace clang::include_cleaner { @@ -35,12 +36,26 @@ class PragmaIncludes::RecordPragma : public PPCallbacks, public CommentHandler { public: RecordPragma(const CompilerInstance &CI, PragmaIncludes *Out) - : SM(CI.getSourceManager()), Out(Out) {} + : SM(CI.getSourceManager()), + HeaderInfo(CI.getPreprocessor().getHeaderSearchInfo()), Out(Out) {} void FileChanged(SourceLocation Loc, FileChangeReason Reason, SrcMgr::CharacteristicKind FileType, FileID PrevFID) override { InMainFile = SM.isWrittenInMainFile(Loc); + + if (Reason == PPCallbacks::ExitFile) { + // At file exit time HeaderSearchInfo is valid and can be used to + // determine whether the file was a self-contained header or not. + if (const FileEntry *FE = SM.getFileEntryForID(PrevFID)) { + if (tooling::isSelfContainedHeader(FE, SM, HeaderInfo)) + Out->NonSelfContainedFiles.erase( + SM.getFileEntryForID(PrevFID)->getUniqueID()); + else + Out->NonSelfContainedFiles.insert( + SM.getFileEntryForID(PrevFID)->getUniqueID()); + } + } } void EndOfMainFile() override { @@ -138,6 +153,7 @@ private: bool InMainFile = false; const SourceManager &SM; + HeaderSearch &HeaderInfo; PragmaIncludes *Out; // Track the last line "IWYU pragma: keep" was seen in the main file, 1-based. int LastPragmaKeepInMainFileLine = -1; @@ -184,6 +200,10 @@ return Results; } +bool PragmaIncludes::isSelfContained(const FileEntry *ID) const { + return !NonSelfContainedFiles.contains(ID->getUniqueID()); +} + std::unique_ptr<ASTConsumer> RecordedAST::record() { class Recorder : public ASTConsumer { RecordedAST *Out; Index: clang-tools-extra/include-cleaner/lib/CMakeLists.txt =================================================================== --- clang-tools-extra/include-cleaner/lib/CMakeLists.txt +++ clang-tools-extra/include-cleaner/lib/CMakeLists.txt @@ -11,5 +11,6 @@ clangBasic clangLex clangToolingInclusionsStdlib + clangToolingInclusions ) Index: clang-tools-extra/include-cleaner/lib/Analysis.cpp =================================================================== --- clang-tools-extra/include-cleaner/lib/Analysis.cpp +++ clang-tools-extra/include-cleaner/lib/Analysis.cpp @@ -38,14 +38,28 @@ // FIXME: Handle references of macros. } +// Find a responsible header for the given file FID. If the file is non +// self-contained, we find its first includer that is self-contained by walking +// up the include stack. +static const FileEntry *getResponsibleHeader(FileID FID, + const SourceManager &SM, + const PragmaIncludes &PI) { + const FileEntry *FE = SM.getFileEntryForID(FID); + for (; FID != SM.getMainFileID(); FE = SM.getFileEntryForID(FID)) { + if (!FE || PI.isSelfContained(FE)) + return FE; + + FID = SM.getFileID(SM.getIncludeLoc(FID)); + } + return FE; +} + llvm::SmallVector<Header> findIncludeHeaders(const SymbolLocation &SLoc, const SourceManager &SM, const PragmaIncludes &PI) { llvm::SmallVector<Header> Results; if (auto *Loc = std::get_if<SourceLocation>(&SLoc)) { - // FIXME: Handle non self-contained files. - FileID FID = SM.getFileID(*Loc); - const auto *FE = SM.getFileEntryForID(FID); + const auto *FE = getResponsibleHeader(SM.getFileID(*Loc), SM, PI); if (!FE) return {}; Index: clang-tools-extra/include-cleaner/include/clang-include-cleaner/Record.h =================================================================== --- clang-tools-extra/include-cleaner/include/clang-include-cleaner/Record.h +++ clang-tools-extra/include-cleaner/include/clang-include-cleaner/Record.h @@ -62,6 +62,9 @@ llvm::SmallVector<const FileEntry *> getExporters(const FileEntry *File, FileManager &FM) const; + /// Returns true if the given file is a self-contained file. + bool isSelfContained(const FileEntry *File) const; + private: class RecordPragma; /// 1-based Line numbers for the #include directives of the main file that @@ -85,8 +88,10 @@ llvm::DenseMap<llvm::sys::fs::UniqueID, std::string /*RealPath*/> RealPathNamesByUID; + /// Contains all non self-contained files during the parsing. + llvm::DenseSet<llvm::sys::fs::UniqueID> NonSelfContainedFiles; + // FIXME: add support for clang use_instead pragma - // FIXME: add selfcontained file. }; // Contains recorded parser events relevant to include-cleaner.
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits