[PATCH] D125468: [clangd] Include Cleaner: ignore headers with IWYU export pragmas
This revision was landed with ongoing or failed builds. This revision was automatically updated to reflect the committed changes. Closed by commit rG40f361ace3e9: [clangd] Include Cleaner: ignore headers with IWYU export pragmas (authored by kbobyrev). Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D125468/new/ https://reviews.llvm.org/D125468 Files: clang-tools-extra/clangd/Headers.cpp clang-tools-extra/clangd/Headers.h clang-tools-extra/clangd/IncludeCleaner.cpp clang-tools-extra/clangd/unittests/HeadersTests.cpp clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp Index: clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp === --- clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp +++ clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp @@ -544,8 +544,7 @@ EXPECT_TRUE( ReferencedFiles.User.contains(AST.getSourceManager().getMainFileID())); EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); - auto Unused = computeUnusedIncludes(AST); - EXPECT_THAT(Unused, IsEmpty()); + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); } TEST(IncludeCleaner, RecursiveInclusion) { @@ -576,8 +575,34 @@ findReferencedLocations(AST), AST.getIncludeStructure(), AST.getCanonicalIncludes(), AST.getSourceManager()); EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); - auto Unused = computeUnusedIncludes(AST); - EXPECT_THAT(Unused, IsEmpty()); + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); +} + +TEST(IncludeCleaner, IWYUPragmaExport) { + TestTU TU; + TU.Code = R"cpp( +#include "foo.h" +)cpp"; + TU.AdditionalFiles["foo.h"] = R"cpp( +#ifndef FOO_H +#define FOO_H + +#include "bar.h" // IWYU pragma: export + +#endif + )cpp"; + TU.AdditionalFiles["bar.h"] = guard(R"cpp( +void bar() {} + )cpp"); + ParsedAST AST = TU.build(); + + auto ReferencedFiles = findReferencedFiles( + findReferencedLocations(AST), AST.getIncludeStructure(), + AST.getCanonicalIncludes(), AST.getSourceManager()); + EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); + // FIXME: This is not correct: foo.h is unused but is not diagnosed as such + // because we ignore headers with IWYU export pragmas for now. + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); } } // namespace Index: clang-tools-extra/clangd/unittests/HeadersTests.cpp === --- clang-tools-extra/clangd/unittests/HeadersTests.cpp +++ clang-tools-extra/clangd/unittests/HeadersTests.cpp @@ -406,7 +406,7 @@ #define RECURSIVE_H #include "recursive.h" - + #endif // RECURSIVE_H )cpp"; @@ -418,6 +418,33 @@ EXPECT_FALSE(Includes.isSelfContained(getID("pp_depend.h", Includes))); } +TEST_F(HeadersTest, HasIWYUPragmas) { + FS.Files[MainFile] = R"cpp( +#include "export.h" +#include "begin_exports.h" +#include "none.h" +)cpp"; + FS.Files["export.h"] = R"cpp( +#pragma once +#include "none.h" // IWYU pragma: export +)cpp"; + FS.Files["begin_exports.h"] = R"cpp( +#pragma once +// IWYU pragma: begin_exports +#include "none.h" +// IWYU pragma: end_exports +)cpp"; + FS.Files["none.h"] = R"cpp( +#pragma once +// Not a pragma. +)cpp"; + + auto Includes = collectIncludes(); + EXPECT_TRUE(Includes.hasIWYUExport(getID("export.h", Includes))); + EXPECT_TRUE(Includes.hasIWYUExport(getID("begin_exports.h", Includes))); + EXPECT_FALSE(Includes.hasIWYUExport(getID("none.h", Includes))); +} + } // namespace } // namespace clangd } // namespace clang Index: clang-tools-extra/clangd/IncludeCleaner.cpp === --- clang-tools-extra/clangd/IncludeCleaner.cpp +++ clang-tools-extra/clangd/IncludeCleaner.cpp @@ -268,13 +268,17 @@ return true; return false; } - // Headers without include guards have side effects and are not - // self-contained, skip them. assert(Inc.HeaderID); + auto HID = static_cast(*Inc.HeaderID); + // FIXME: Ignore the headers with IWYU export pragmas for now, remove this + // check when we have more precise tracking of exported headers. + if (AST.getIncludeStructure().hasIWYUExport(HID)) +return false; auto FE = AST.getSourceManager().getFileManager().getFileRef( - AST.getIncludeStructure().getRealPath( - static_cast(*Inc.HeaderID))); + AST.getIncludeStructure().getRealPath(HID)); assert(FE); + // Headers without include guards have side effects and are not + // self-contained, skip them. if (!AST.getPreprocessor().getHeaderSearchInfo().isFileMultipleIncludeGuarded( >getFileEntry())) { dlog("{0} doesn't have header guard and will not be considered unused", Index: clang-tools-extra/clangd/Headers.h === --- clang-tools-extra/clangd/Headers.h +++
[PATCH] D125468: [clangd] Include Cleaner: ignore headers with IWYU export pragmas
kbobyrev updated this revision to Diff 429637. kbobyrev marked 4 inline comments as done. kbobyrev added a comment. Address review comments. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D125468/new/ https://reviews.llvm.org/D125468 Files: clang-tools-extra/clangd/Headers.cpp clang-tools-extra/clangd/Headers.h clang-tools-extra/clangd/IncludeCleaner.cpp clang-tools-extra/clangd/unittests/HeadersTests.cpp clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp Index: clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp === --- clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp +++ clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp @@ -544,8 +544,7 @@ EXPECT_TRUE( ReferencedFiles.User.contains(AST.getSourceManager().getMainFileID())); EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); - auto Unused = computeUnusedIncludes(AST); - EXPECT_THAT(Unused, IsEmpty()); + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); } TEST(IncludeCleaner, RecursiveInclusion) { @@ -576,8 +575,34 @@ findReferencedLocations(AST), AST.getIncludeStructure(), AST.getCanonicalIncludes(), AST.getSourceManager()); EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); - auto Unused = computeUnusedIncludes(AST); - EXPECT_THAT(Unused, IsEmpty()); + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); +} + +TEST(IncludeCleaner, IWYUPragmaExport) { + TestTU TU; + TU.Code = R"cpp( +#include "foo.h" +)cpp"; + TU.AdditionalFiles["foo.h"] = R"cpp( +#ifndef FOO_H +#define FOO_H + +#include "bar.h" // IWYU pragma: export + +#endif + )cpp"; + TU.AdditionalFiles["bar.h"] = guard(R"cpp( +void bar() {} + )cpp"); + ParsedAST AST = TU.build(); + + auto ReferencedFiles = findReferencedFiles( + findReferencedLocations(AST), AST.getIncludeStructure(), + AST.getCanonicalIncludes(), AST.getSourceManager()); + EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); + // FIXME: This is not correct: foo.h is unused but is not diagnosed as such + // because we ignore headers with IWYU export pragmas for now. + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); } } // namespace Index: clang-tools-extra/clangd/unittests/HeadersTests.cpp === --- clang-tools-extra/clangd/unittests/HeadersTests.cpp +++ clang-tools-extra/clangd/unittests/HeadersTests.cpp @@ -406,7 +406,7 @@ #define RECURSIVE_H #include "recursive.h" - + #endif // RECURSIVE_H )cpp"; @@ -418,6 +418,33 @@ EXPECT_FALSE(Includes.isSelfContained(getID("pp_depend.h", Includes))); } +TEST_F(HeadersTest, HasIWYUPragmas) { + FS.Files[MainFile] = R"cpp( +#include "export.h" +#include "begin_exports.h" +#include "none.h" +)cpp"; + FS.Files["export.h"] = R"cpp( +#pragma once +#include "none.h" // IWYU pragma: export +)cpp"; + FS.Files["begin_exports.h"] = R"cpp( +#pragma once +// IWYU pragma: begin_exports +#include "none.h" +// IWYU pragma: end_exports +)cpp"; + FS.Files["none.h"] = R"cpp( +#pragma once +// Not a pragma. +)cpp"; + + auto Includes = collectIncludes(); + EXPECT_TRUE(Includes.hasIWYUExport(getID("export.h", Includes))); + EXPECT_TRUE(Includes.hasIWYUExport(getID("begin_exports.h", Includes))); + EXPECT_FALSE(Includes.hasIWYUExport(getID("none.h", Includes))); +} + } // namespace } // namespace clangd } // namespace clang Index: clang-tools-extra/clangd/IncludeCleaner.cpp === --- clang-tools-extra/clangd/IncludeCleaner.cpp +++ clang-tools-extra/clangd/IncludeCleaner.cpp @@ -268,13 +268,17 @@ return true; return false; } - // Headers without include guards have side effects and are not - // self-contained, skip them. assert(Inc.HeaderID); + auto HID = static_cast(*Inc.HeaderID); + // FIXME: Ignore the headers with IWYU export pragmas for now, remove this + // check when we have more precise tracking of exported headers. + if (AST.getIncludeStructure().hasIWYUExport(HID)) +return false; auto FE = AST.getSourceManager().getFileManager().getFileRef( - AST.getIncludeStructure().getRealPath( - static_cast(*Inc.HeaderID))); + AST.getIncludeStructure().getRealPath(HID)); assert(FE); + // Headers without include guards have side effects and are not + // self-contained, skip them. if (!AST.getPreprocessor().getHeaderSearchInfo().isFileMultipleIncludeGuarded( >getFileEntry())) { dlog("{0} doesn't have header guard and will not be considered unused", Index: clang-tools-extra/clangd/Headers.h === --- clang-tools-extra/clangd/Headers.h +++ clang-tools-extra/clangd/Headers.h @@ -145,6 +145,10 @@ return !NonSelfContained.contains(ID); } + bool
[PATCH] D125468: [clangd] Include Cleaner: ignore headers with IWYU export pragmas
sammccall accepted this revision. sammccall added a comment. This revision is now accepted and ready to land. Great! A few more nits Comment at: clang-tools-extra/clangd/Headers.cpp:151 + // will know that the next inclusion is behind the IWYU pragma. + if (!Text.consume_front(IWYUPragmaExport) && + !Text.consume_front(IWYUPragmaKeep)) you never use Text again, so consume_front is unneccesary and confusing here, we can just use starts_with Comment at: clang-tools-extra/clangd/Headers.cpp:151 + // will know that the next inclusion is behind the IWYU pragma. + if (!Text.consume_front(IWYUPragmaExport) && + !Text.consume_front(IWYUPragmaKeep)) sammccall wrote: > you never use Text again, so consume_front is unneccesary and confusing here, > we can just use starts_with add a FIXME that begin_export is not handled here (now that the other branch supports it) Comment at: clang-tools-extra/clangd/Headers.h:148 + bool hasIWYUPragmas(HeaderID ID) const { +return HasIWYUPragmas.contains(ID); hasIWYUExport? (if the file contains non-export pragmas we need to return false) Comment at: clang-tools-extra/clangd/IncludeCleaner.cpp:274 + // FIXME: Ignore the headers with IWYU export pragmas for now, remove this + // check when we have actual support for export pragmas. + if (AST.getIncludeStructure().hasIWYUPragmas(HID)) actual support for export pragmas => more precise tracking of exported headers? (current wording doesn't really say what's missing) Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D125468/new/ https://reviews.llvm.org/D125468 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D125468: [clangd] Include Cleaner: ignore headers with IWYU export pragmas
kbobyrev updated this revision to Diff 429619. kbobyrev added a comment. Also rebase on top of main. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D125468/new/ https://reviews.llvm.org/D125468 Files: clang-tools-extra/clangd/Headers.cpp clang-tools-extra/clangd/Headers.h clang-tools-extra/clangd/IncludeCleaner.cpp clang-tools-extra/clangd/unittests/HeadersTests.cpp clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp Index: clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp === --- clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp +++ clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp @@ -544,8 +544,7 @@ EXPECT_TRUE( ReferencedFiles.User.contains(AST.getSourceManager().getMainFileID())); EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); - auto Unused = computeUnusedIncludes(AST); - EXPECT_THAT(Unused, IsEmpty()); + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); } TEST(IncludeCleaner, RecursiveInclusion) { @@ -576,8 +575,34 @@ findReferencedLocations(AST), AST.getIncludeStructure(), AST.getCanonicalIncludes(), AST.getSourceManager()); EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); - auto Unused = computeUnusedIncludes(AST); - EXPECT_THAT(Unused, IsEmpty()); + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); +} + +TEST(IncludeCleaner, IWYUPragmaExport) { + TestTU TU; + TU.Code = R"cpp( +#include "foo.h" +)cpp"; + TU.AdditionalFiles["foo.h"] = R"cpp( +#ifndef FOO_H +#define FOO_H + +#include "bar.h" // IWYU pragma: export + +#endif + )cpp"; + TU.AdditionalFiles["bar.h"] = guard(R"cpp( +void bar() {} + )cpp"); + ParsedAST AST = TU.build(); + + auto ReferencedFiles = findReferencedFiles( + findReferencedLocations(AST), AST.getIncludeStructure(), + AST.getCanonicalIncludes(), AST.getSourceManager()); + EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); + // FIXME: This is not correct: foo.h is unused but is not diagnosed as such + // because we ignore headers with IWYU export pragmas for now. + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); } } // namespace Index: clang-tools-extra/clangd/unittests/HeadersTests.cpp === --- clang-tools-extra/clangd/unittests/HeadersTests.cpp +++ clang-tools-extra/clangd/unittests/HeadersTests.cpp @@ -406,7 +406,7 @@ #define RECURSIVE_H #include "recursive.h" - + #endif // RECURSIVE_H )cpp"; @@ -418,6 +418,33 @@ EXPECT_FALSE(Includes.isSelfContained(getID("pp_depend.h", Includes))); } +TEST_F(HeadersTest, HasIWYUPragmas) { + FS.Files[MainFile] = R"cpp( +#include "export.h" +#include "begin_exports.h" +#include "none.h" +)cpp"; + FS.Files["export.h"] = R"cpp( +#pragma once +#include "none.h" // IWYU pragma: export +)cpp"; + FS.Files["begin_exports.h"] = R"cpp( +#pragma once +// IWYU pragma: begin_exports +#include "none.h" +// IWYU pragma: end_exports +)cpp"; + FS.Files["none.h"] = R"cpp( +#pragma once +// Not a pragma. +)cpp"; + + auto Includes = collectIncludes(); + EXPECT_TRUE(Includes.hasIWYUPragmas(getID("export.h", Includes))); + EXPECT_TRUE(Includes.hasIWYUPragmas(getID("begin_exports.h", Includes))); + EXPECT_FALSE(Includes.hasIWYUPragmas(getID("none.h", Includes))); +} + } // namespace } // namespace clangd } // namespace clang Index: clang-tools-extra/clangd/IncludeCleaner.cpp === --- clang-tools-extra/clangd/IncludeCleaner.cpp +++ clang-tools-extra/clangd/IncludeCleaner.cpp @@ -268,13 +268,17 @@ return true; return false; } - // Headers without include guards have side effects and are not - // self-contained, skip them. assert(Inc.HeaderID); + auto HID = static_cast(*Inc.HeaderID); + // FIXME: Ignore the headers with IWYU export pragmas for now, remove this + // check when we have actual support for export pragmas. + if (AST.getIncludeStructure().hasIWYUPragmas(HID)) +return false; auto FE = AST.getSourceManager().getFileManager().getFileRef( - AST.getIncludeStructure().getRealPath( - static_cast(*Inc.HeaderID))); + AST.getIncludeStructure().getRealPath(HID)); assert(FE); + // Headers without include guards have side effects and are not + // self-contained, skip them. if (!AST.getPreprocessor().getHeaderSearchInfo().isFileMultipleIncludeGuarded( >getFileEntry())) { dlog("{0} doesn't have header guard and will not be considered unused", Index: clang-tools-extra/clangd/Headers.h === --- clang-tools-extra/clangd/Headers.h +++ clang-tools-extra/clangd/Headers.h @@ -145,6 +145,10 @@ return !NonSelfContained.contains(ID); } + bool hasIWYUPragmas(HeaderID ID) const { +return
[PATCH] D125468: [clangd] Include Cleaner: ignore headers with IWYU export pragmas
kbobyrev updated this revision to Diff 429618. kbobyrev marked 7 inline comments as done. kbobyrev added a comment. Address review comments: the structure is a bit different but the bug is now actually removed. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D125468/new/ https://reviews.llvm.org/D125468 Files: clang-tools-extra/clangd/Headers.cpp clang-tools-extra/clangd/Headers.h clang-tools-extra/clangd/IncludeCleaner.cpp clang-tools-extra/clangd/unittests/HeadersTests.cpp clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp Index: clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp === --- clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp +++ clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp @@ -544,8 +544,7 @@ EXPECT_TRUE( ReferencedFiles.User.contains(AST.getSourceManager().getMainFileID())); EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); - auto Unused = computeUnusedIncludes(AST); - EXPECT_THAT(Unused, IsEmpty()); + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); } TEST(IncludeCleaner, RecursiveInclusion) { @@ -576,8 +575,34 @@ findReferencedLocations(AST), AST.getIncludeStructure(), AST.getCanonicalIncludes(), AST.getSourceManager()); EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); - auto Unused = computeUnusedIncludes(AST); - EXPECT_THAT(Unused, IsEmpty()); + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); +} + +TEST(IncludeCleaner, IWYUPragmaExport) { + TestTU TU; + TU.Code = R"cpp( +#include "foo.h" +)cpp"; + TU.AdditionalFiles["foo.h"] = R"cpp( +#ifndef FOO_H +#define FOO_H + +#include "bar.h" // IWYU pragma: export + +#endif + )cpp"; + TU.AdditionalFiles["bar.h"] = guard(R"cpp( +void bar() {} + )cpp"); + ParsedAST AST = TU.build(); + + auto ReferencedFiles = findReferencedFiles( + findReferencedLocations(AST), AST.getIncludeStructure(), + AST.getCanonicalIncludes(), AST.getSourceManager()); + EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); + // FIXME: This is not correct: foo.h is unused but is not diagnosed as such + // because we ignore headers with IWYU export pragmas for now. + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); } } // namespace Index: clang-tools-extra/clangd/unittests/HeadersTests.cpp === --- clang-tools-extra/clangd/unittests/HeadersTests.cpp +++ clang-tools-extra/clangd/unittests/HeadersTests.cpp @@ -406,7 +406,7 @@ #define RECURSIVE_H #include "recursive.h" - + #endif // RECURSIVE_H )cpp"; @@ -418,6 +418,33 @@ EXPECT_FALSE(Includes.isSelfContained(getID("pp_depend.h", Includes))); } +TEST_F(HeadersTest, HasIWYUPragmas) { + FS.Files[MainFile] = R"cpp( +#include "export.h" +#include "begin_exports.h" +#include "none.h" +)cpp"; + FS.Files["export.h"] = R"cpp( +#pragma once +#include "none.h" // IWYU pragma: export +)cpp"; + FS.Files["begin_exports.h"] = R"cpp( +#pragma once +// IWYU pragma: begin_exports +#include "none.h" +// IWYU pragma: end_exports +)cpp"; + FS.Files["none.h"] = R"cpp( +#pragma once +// Not a pragma. +)cpp"; + + auto Includes = collectIncludes(); + EXPECT_TRUE(Includes.hasIWYUPragmas(getID("export.h", Includes))); + EXPECT_TRUE(Includes.hasIWYUPragmas(getID("begin_exports.h", Includes))); + EXPECT_FALSE(Includes.hasIWYUPragmas(getID("none.h", Includes))); +} + } // namespace } // namespace clangd } // namespace clang Index: clang-tools-extra/clangd/IncludeCleaner.cpp === --- clang-tools-extra/clangd/IncludeCleaner.cpp +++ clang-tools-extra/clangd/IncludeCleaner.cpp @@ -268,13 +268,17 @@ return true; return false; } - // Headers without include guards have side effects and are not - // self-contained, skip them. assert(Inc.HeaderID); + auto HID = static_cast(*Inc.HeaderID); + // FIXME: Ignore the headers with IWYU export pragmas for now, remove this + // check when we have actual support for export pragmas. + if (AST.getIncludeStructure().hasIWYUPragmas(HID)) +return false; auto FE = AST.getSourceManager().getFileManager().getFileRef( - AST.getIncludeStructure().getRealPath( - static_cast(*Inc.HeaderID))); + AST.getIncludeStructure().getRealPath(HID)); assert(FE); + // Headers without include guards have side effects and are not + // self-contained, skip them. if (!AST.getPreprocessor().getHeaderSearchInfo().isFileMultipleIncludeGuarded( >getFileEntry())) { dlog("{0} doesn't have header guard and will not be considered unused", Index: clang-tools-extra/clangd/Headers.h === --- clang-tools-extra/clangd/Headers.h +++ clang-tools-extra/clangd/Headers.h @@ -145,6 +145,10
[PATCH] D125468: [clangd] Include Cleaner: ignore headers with IWYU export pragmas
kadircet added inline comments. Comment at: clang-tools-extra/clangd/Headers.cpp:134 + return false; +return inMainFile() ? handleCommentInMainFile(PP, Range) +: handleCommentInHeaderFile(PP, Range); the split seems to be increasing code duplication (we've already copied a bug with it ), what about: ``` bool Err = false; llvm::StringRef Text = SM.getCharacterData(Range.getBegin(), ); if (!Err || (!Text.consume_front(IWYUPragmaKeep) && !Text.consume_front(IWYUPragmaExport))) return false; if(inMainFile()) { unsigned Offset = SM.getFileOffset(Range.getBegin()); LastPragmaKeepInMainFileLine = SM.getLineNumber(SM.getMainFileID(), Offset) - 1; } else { Out->HasIWYUPragmas.insert( *Out->getID(SM.getFileEntryForID(SM.getFileID(Range.getBegin(); } return false; ``` Comment at: clang-tools-extra/clangd/Headers.cpp:154 + bool handleCommentInMainFile(Preprocessor , SourceRange Range) { +assert(inMainFile()); +assert(!Range.getBegin().isMacroID()); i don't think the asserts carry their weight (here and also in the headerfile comment handler). we're not really performing anything dubious if the asserts don't hold. getCharacterData will return non-sense (but valid) buffers in case of a macroid, and we're already reading data whether we're in the main file or not. moreover these are private and controlled at the entry by the public interface (`HandleComment`). Comment at: clang-tools-extra/clangd/Headers.cpp:172 +llvm::StringRef Text = SM.getCharacterData(Range.getBegin(), ); +if (Err && !Text.consume_front(IWYUPragmaExport) && +!Text.consume_front(IWYUPragmaBeginExports)) shouldn't this be `if (Err || (!Text.consume ... && !Text.consume...)) return false;` ? same above for the main file comment handling Comment at: clang-tools-extra/clangd/IncludeCleaner.cpp:271 } // Headers without include guards have side effects and are not // self-contained, skip them. can you also move this comment below, next to the `isFileMultipleHeaderGuarded` check? Comment at: clang-tools-extra/clangd/unittests/HeadersTests.cpp:427 +)cpp"; + FS.Files[testPath("export.h")] = R"cpp( +#pragma once no need for `testPath`s here and below. Comment at: clang-tools-extra/clangd/unittests/HeadersTests.cpp:438 + FS.Files[testPath("none.h")] = R"cpp( +#pragma once +)cpp"; this is passing not because you don't have a IWYU pragma comment here, but because you don't have any comments at all. can you try inserting an arbitrary comment into the file? Comment at: clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp:603 + EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); + // FIXME: This is not correct: foo.h and bar.h are unused but are not + // diagnosed as such because we ignore headers with IWYU export pragmas for do you mean just `foo.h is unused` why are we talking about `bar.h` also being unused here? Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D125468/new/ https://reviews.llvm.org/D125468 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D125468: [clangd] Include Cleaner: ignore headers with IWYU export pragmas
kbobyrev updated this revision to Diff 428949. kbobyrev added a comment. Remove redundant comment. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D125468/new/ https://reviews.llvm.org/D125468 Files: clang-tools-extra/clangd/Headers.cpp clang-tools-extra/clangd/Headers.h clang-tools-extra/clangd/IncludeCleaner.cpp clang-tools-extra/clangd/unittests/HeadersTests.cpp clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp Index: clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp === --- clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp +++ clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp @@ -544,8 +544,7 @@ EXPECT_TRUE( ReferencedFiles.User.contains(AST.getSourceManager().getMainFileID())); EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); - auto Unused = computeUnusedIncludes(AST); - EXPECT_THAT(Unused, IsEmpty()); + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); } TEST(IncludeCleaner, RecursiveInclusion) { @@ -576,8 +575,35 @@ findReferencedLocations(AST), AST.getIncludeStructure(), AST.getCanonicalIncludes(), AST.getSourceManager()); EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); - auto Unused = computeUnusedIncludes(AST); - EXPECT_THAT(Unused, IsEmpty()); + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); +} + +TEST(IncludeCleaner, IWYUPragmaExport) { + TestTU TU; + TU.Code = R"cpp( +#include "foo.h" +)cpp"; + TU.AdditionalFiles["foo.h"] = R"cpp( +#ifndef FOO_H +#define FOO_H + +#include "bar.h" // IWYU pragma: export + +#endif + )cpp"; + TU.AdditionalFiles["bar.h"] = guard(R"cpp( +void bar() {} + )cpp"); + ParsedAST AST = TU.build(); + + auto ReferencedFiles = findReferencedFiles( + findReferencedLocations(AST), AST.getIncludeStructure(), + AST.getCanonicalIncludes(), AST.getSourceManager()); + EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); + // FIXME: This is not correct: foo.h and bar.h are unused but are not + // diagnosed as such because we ignore headers with IWYU export pragmas for + // now. + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); } } // namespace Index: clang-tools-extra/clangd/unittests/HeadersTests.cpp === --- clang-tools-extra/clangd/unittests/HeadersTests.cpp +++ clang-tools-extra/clangd/unittests/HeadersTests.cpp @@ -406,7 +406,7 @@ #define RECURSIVE_H #include "recursive.h" - + #endif // RECURSIVE_H )cpp"; @@ -418,6 +418,32 @@ EXPECT_FALSE(Includes.isSelfContained(getID("pp_depend.h", Includes))); } +TEST_F(HeadersTest, HasIWYUPragmas) { + FS.Files[MainFile] = R"cpp( +#include "export.h" +#include "begin_exports.h" +#include "none.h" +)cpp"; + FS.Files[testPath("export.h")] = R"cpp( +#pragma once +#include "none.h" // IWYU pragma: export +)cpp"; + FS.Files[testPath("begin_exports.h")] = R"cpp( +#pragma once +// IWYU pragma: begin_exports +#include "none.h" +// IWYU pragma: end_exports +)cpp"; + FS.Files[testPath("none.h")] = R"cpp( +#pragma once +)cpp"; + + auto Includes = collectIncludes(); + EXPECT_TRUE(Includes.hasIWYUPragmas(getID("export.h", Includes))); + EXPECT_TRUE(Includes.hasIWYUPragmas(getID("begin_exports.h", Includes))); + EXPECT_FALSE(Includes.hasIWYUPragmas(getID("none.h", Includes))); +} + } // namespace } // namespace clangd } // namespace clang Index: clang-tools-extra/clangd/IncludeCleaner.cpp === --- clang-tools-extra/clangd/IncludeCleaner.cpp +++ clang-tools-extra/clangd/IncludeCleaner.cpp @@ -271,9 +271,13 @@ // Headers without include guards have side effects and are not // self-contained, skip them. assert(Inc.HeaderID); + auto HID = static_cast(*Inc.HeaderID); + // FIXME: Ignore the headers with IWYU export pragmas for now, remove this + // check when we have actual support for export pragmas. + if (AST.getIncludeStructure().hasIWYUPragmas(HID)) +return false; auto FE = AST.getSourceManager().getFileManager().getFileRef( - AST.getIncludeStructure().getRealPath( - static_cast(*Inc.HeaderID))); + AST.getIncludeStructure().getRealPath(HID)); assert(FE); if (!AST.getPreprocessor().getHeaderSearchInfo().isFileMultipleIncludeGuarded( >getFileEntry())) { Index: clang-tools-extra/clangd/Headers.h === --- clang-tools-extra/clangd/Headers.h +++ clang-tools-extra/clangd/Headers.h @@ -145,6 +145,10 @@ return !NonSelfContained.contains(ID); } + bool hasIWYUPragmas(HeaderID ID) const { +return HasIWYUPragmas.contains(ID); + } + // Return all transitively reachable files. llvm::ArrayRef allHeaders() const { return RealPathNames; } @@ -185,6 +189,9 @@ // Contains HeaderIDs
[PATCH] D125468: [clangd] Include Cleaner: ignore headers with IWYU export pragmas
kbobyrev updated this revision to Diff 428947. kbobyrev added a comment. Remove unwanted formatting changes. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D125468/new/ https://reviews.llvm.org/D125468 Files: clang-tools-extra/clangd/Headers.cpp clang-tools-extra/clangd/Headers.h clang-tools-extra/clangd/IncludeCleaner.cpp clang-tools-extra/clangd/unittests/HeadersTests.cpp clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp Index: clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp === --- clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp +++ clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp @@ -544,8 +544,7 @@ EXPECT_TRUE( ReferencedFiles.User.contains(AST.getSourceManager().getMainFileID())); EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); - auto Unused = computeUnusedIncludes(AST); - EXPECT_THAT(Unused, IsEmpty()); + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); } TEST(IncludeCleaner, RecursiveInclusion) { @@ -576,8 +575,35 @@ findReferencedLocations(AST), AST.getIncludeStructure(), AST.getCanonicalIncludes(), AST.getSourceManager()); EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); - auto Unused = computeUnusedIncludes(AST); - EXPECT_THAT(Unused, IsEmpty()); + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); +} + +TEST(IncludeCleaner, IWYUPragmaExport) { + TestTU TU; + TU.Code = R"cpp( +#include "foo.h" +)cpp"; + TU.AdditionalFiles["foo.h"] = R"cpp( +#ifndef FOO_H +#define FOO_H + +#include "bar.h" // IWYU pragma: export + +#endif + )cpp"; + TU.AdditionalFiles["bar.h"] = guard(R"cpp( +void bar() {} + )cpp"); + ParsedAST AST = TU.build(); + + auto ReferencedFiles = findReferencedFiles( + findReferencedLocations(AST), AST.getIncludeStructure(), + AST.getCanonicalIncludes(), AST.getSourceManager()); + EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); + // FIXME: This is not correct: foo.h and bar.h are unused but are not + // diagnosed as such because we ignore headers with IWYU export pragmas for + // now. + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); } } // namespace Index: clang-tools-extra/clangd/unittests/HeadersTests.cpp === --- clang-tools-extra/clangd/unittests/HeadersTests.cpp +++ clang-tools-extra/clangd/unittests/HeadersTests.cpp @@ -406,7 +406,7 @@ #define RECURSIVE_H #include "recursive.h" - + #endif // RECURSIVE_H )cpp"; @@ -418,6 +418,32 @@ EXPECT_FALSE(Includes.isSelfContained(getID("pp_depend.h", Includes))); } +TEST_F(HeadersTest, HasIWYUPragmas) { + FS.Files[MainFile] = R"cpp( +#include "export.h" +#include "begin_exports.h" +#include "none.h" +)cpp"; + FS.Files[testPath("export.h")] = R"cpp( +#pragma once +#include "none.h" // IWYU pragma: export +)cpp"; + FS.Files[testPath("begin_exports.h")] = R"cpp( +#pragma once +// IWYU pragma: begin_exports +#include "none.h" +// IWYU pragma: end_exports +)cpp"; + FS.Files[testPath("none.h")] = R"cpp( +#pragma once +)cpp"; + + auto Includes = collectIncludes(); + EXPECT_TRUE(Includes.hasIWYUPragmas(getID("export.h", Includes))); + EXPECT_TRUE(Includes.hasIWYUPragmas(getID("begin_exports.h", Includes))); + EXPECT_FALSE(Includes.hasIWYUPragmas(getID("none.h", Includes))); +} + } // namespace } // namespace clangd } // namespace clang Index: clang-tools-extra/clangd/IncludeCleaner.cpp === --- clang-tools-extra/clangd/IncludeCleaner.cpp +++ clang-tools-extra/clangd/IncludeCleaner.cpp @@ -271,9 +271,13 @@ // Headers without include guards have side effects and are not // self-contained, skip them. assert(Inc.HeaderID); + auto HID = static_cast(*Inc.HeaderID); + // FIXME: Ignore the headers with IWYU export pragmas for now, remove this + // check when we have actual support for export pragmas. + if (AST.getIncludeStructure().hasIWYUPragmas(HID)) +return false; auto FE = AST.getSourceManager().getFileManager().getFileRef( - AST.getIncludeStructure().getRealPath( - static_cast(*Inc.HeaderID))); + AST.getIncludeStructure().getRealPath(HID)); assert(FE); if (!AST.getPreprocessor().getHeaderSearchInfo().isFileMultipleIncludeGuarded( >getFileEntry())) { Index: clang-tools-extra/clangd/Headers.h === --- clang-tools-extra/clangd/Headers.h +++ clang-tools-extra/clangd/Headers.h @@ -145,6 +145,10 @@ return !NonSelfContained.contains(ID); } + bool hasIWYUPragmas(HeaderID ID) const { +return HasIWYUPragmas.contains(ID); + } + // Return all transitively reachable files. llvm::ArrayRef allHeaders() const { return RealPathNames; } @@ -185,6 +189,9 @@ // Contains
[PATCH] D125468: [clangd] Include Cleaner: ignore headers with IWYU export pragmas
kbobyrev created this revision. kbobyrev added reviewers: sammccall, kadircet. Herald added subscribers: usaxena95, arphaman. Herald added a project: All. kbobyrev requested review of this revision. Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov. Herald added a project: clang-tools-extra. Disable the warnings with `IWYU pragma: export` or `begin_exports` + `end_exports` until we have support for these pragmas. There are too many false-positive warnings for the headers that have the correct pragmas for now and it makes the user experience very unpleasant. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D125468 Files: clang-tools-extra/clangd/Headers.cpp clang-tools-extra/clangd/Headers.h clang-tools-extra/clangd/IncludeCleaner.cpp clang-tools-extra/clangd/unittests/HeadersTests.cpp clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp Index: clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp === --- clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp +++ clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp @@ -544,8 +544,7 @@ EXPECT_TRUE( ReferencedFiles.User.contains(AST.getSourceManager().getMainFileID())); EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); - auto Unused = computeUnusedIncludes(AST); - EXPECT_THAT(Unused, IsEmpty()); + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); } TEST(IncludeCleaner, RecursiveInclusion) { @@ -576,8 +575,35 @@ findReferencedLocations(AST), AST.getIncludeStructure(), AST.getCanonicalIncludes(), AST.getSourceManager()); EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); - auto Unused = computeUnusedIncludes(AST); - EXPECT_THAT(Unused, IsEmpty()); + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); +} + +TEST(IncludeCleaner, IWYUPragmaExport) { + TestTU TU; + TU.Code = R"cpp( +#include "foo.h" +)cpp"; + TU.AdditionalFiles["foo.h"] = R"cpp( +#ifndef FOO_H +#define FOO_H + +#include "bar.h" // IWYU pragma: export + +#endif + )cpp"; + TU.AdditionalFiles["bar.h"] = guard(R"cpp( +void bar() {} + )cpp"); + ParsedAST AST = TU.build(); + + auto ReferencedFiles = findReferencedFiles( + findReferencedLocations(AST), AST.getIncludeStructure(), + AST.getCanonicalIncludes(), AST.getSourceManager()); + EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); + // FIXME: This is not correct: foo.h and bar.h are unused but are not + // diagnosed as such because we ignore headers with IWYU export pragmas for + // now. + EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); } } // namespace Index: clang-tools-extra/clangd/unittests/HeadersTests.cpp === --- clang-tools-extra/clangd/unittests/HeadersTests.cpp +++ clang-tools-extra/clangd/unittests/HeadersTests.cpp @@ -406,7 +406,7 @@ #define RECURSIVE_H #include "recursive.h" - + #endif // RECURSIVE_H )cpp"; @@ -418,6 +418,32 @@ EXPECT_FALSE(Includes.isSelfContained(getID("pp_depend.h", Includes))); } +TEST_F(HeadersTest, HasIWYUPragmas) { + FS.Files[MainFile] = R"cpp( +#include "export.h" +#include "begin_exports.h" +#include "none.h" +)cpp"; + FS.Files[testPath("export.h")] = R"cpp( +#pragma once +#include "none.h" // IWYU pragma: export +)cpp"; + FS.Files[testPath("begin_exports.h")] = R"cpp( +#pragma once +// IWYU pragma: begin_exports +#include "none.h" +// IWYU pragma: end_exports +)cpp"; + FS.Files[testPath("none.h")] = R"cpp( +#pragma once +)cpp"; + + auto Includes = collectIncludes(); + EXPECT_TRUE(Includes.hasIWYUPragmas(getID("export.h", Includes))); + EXPECT_TRUE(Includes.hasIWYUPragmas(getID("begin_exports.h", Includes))); + EXPECT_FALSE(Includes.hasIWYUPragmas(getID("none.h", Includes))); +} + } // namespace } // namespace clangd } // namespace clang Index: clang-tools-extra/clangd/IncludeCleaner.cpp === --- clang-tools-extra/clangd/IncludeCleaner.cpp +++ clang-tools-extra/clangd/IncludeCleaner.cpp @@ -82,7 +82,7 @@ // Using templateName case is handled by the override TraverseTemplateName. if (TST->getTemplateName().getKind() == TemplateName::UsingTemplate) return true; -add(TST->getAsCXXRecordDecl()); // Specialization +add(TST->getAsCXXRecordDecl()); // Specialization return true; } @@ -271,9 +271,13 @@ // Headers without include guards have side effects and are not // self-contained, skip them. assert(Inc.HeaderID); + auto HID = static_cast(*Inc.HeaderID); + // FIXME: Ignore the headers with IWYU export pragmas for now, remove this + // check when we have actual support for export pragmas. + if (AST.getIncludeStructure().hasIWYUPragmas(HID)) +return false; auto FE = AST.getSourceManager().getFileManager().getFileRef( -