[include-cleaner] Implement IWYU begin_keep/end_keep pragma support.

Implement support for begin_keep/end_keep pragmas.

Differential Revision: https://reviews.llvm.org/D138797
This commit is contained in:
Viktoriia Bakalova 2022-11-28 13:35:59 +00:00
parent 3d280b0375
commit 7452e053e5
2 changed files with 84 additions and 41 deletions

View File

@ -187,9 +187,7 @@ public:
FileID HashFID = SM.getFileID(HashLoc);
int HashLine = SM.getLineNumber(HashFID, SM.getFileOffset(HashLoc));
checkForExport(HashFID, HashLine, File ? &File->getFileEntry() : nullptr);
if (InMainFile && LastPragmaKeepInMainFileLine == HashLine)
Out->ShouldKeep.insert(HashLine);
checkForKeep(HashLine);
}
void checkForExport(FileID IncludingFile, int HashLine,
@ -213,6 +211,18 @@ public:
ExportStack.pop_back();
}
void checkForKeep(int HashLine) {
if (!InMainFile || KeepStack.empty())
return;
KeepPragma &Top = KeepStack.back();
// Check if the current include is covered by a keep pragma.
if ((Top.Block && HashLine > Top.SeenAtLine) || Top.SeenAtLine == HashLine)
Out->ShouldKeep.insert(HashLine);
if (!Top.Block)
KeepStack.pop_back(); // Pop immediately for single-line keep pragma.
}
bool HandleComment(Preprocessor &PP, SourceRange Range) override {
auto &SM = PP.getSourceManager();
auto Pragma =
@ -257,23 +267,14 @@ public:
}
if (InMainFile) {
if (!Pragma->startswith("keep"))
return false;
// Given:
//
// #include "foo.h"
// #include "bar.h" // IWYU pragma: keep
//
// The order in which the callbacks will be triggered:
//
// 1. InclusionDirective("foo.h")
// 2. handleCommentInMainFile("// IWYU pragma: keep")
// 3. InclusionDirective("bar.h")
//
// This code stores the last location of "IWYU pragma: keep" comment in
// the main file, so that when next InclusionDirective is called, it will
// know that the next inclusion is behind the IWYU pragma.
LastPragmaKeepInMainFileLine = CommentLine;
if (Pragma->startswith("keep")) {
KeepStack.push_back({CommentLine, false});
} else if (Pragma->starts_with("begin_keep")) {
KeepStack.push_back({CommentLine, true});
} else if (Pragma->starts_with("end_keep") && !KeepStack.empty()) {
assert(KeepStack.back().Block);
KeepStack.pop_back();
}
}
return false;
}
@ -288,8 +289,7 @@ private:
llvm::BumpPtrAllocator Arena;
/// Intern table for strings. Contents are on the arena.
llvm::StringSaver UniqueStrings;
// Track the last line "IWYU pragma: keep" was seen in the main file, 1-based.
int LastPragmaKeepInMainFileLine = -1;
struct ExportPragma {
// The line number where we saw the begin_exports or export pragma.
int SeenAtLine = 0; // 1-based line number.
@ -303,6 +303,16 @@ private:
};
// A stack for tracking all open begin_exports or single-line export.
std::vector<ExportPragma> ExportStack;
struct KeepPragma {
// The line number where we saw the begin_keep or keep pragma.
int SeenAtLine = 0; // 1-based line number.
// true if it is a block begin/end_keep pragma; false if it is a
// single-line keep pragma.
bool Block = false;
};
// A stack for tracking all open begin_keep pragmas or single-line keeps.
std::vector<KeepPragma> KeepStack;
};
void PragmaIncludes::record(const CompilerInstance &CI) {

View File

@ -311,35 +311,68 @@ protected:
};
TEST_F(PragmaIncludeTest, IWYUKeep) {
Inputs.Code = R"cpp(// Line 1
#include "keep1.h" // IWYU pragma: keep
#include "keep2.h" /* IWYU pragma: keep */
llvm::Annotations MainFile(R"cpp(
$keep1^#include "keep1.h" // IWYU pragma: keep
$keep2^#include "keep2.h" /* IWYU pragma: keep */
#include "export1.h" // IWYU pragma: export // line 5
// IWYU pragma: begin_exports
#include "export2.h" // Line 7
#include "export3.h"
// IWYU pragma: end_exports
$export1^#include "export1.h" // IWYU pragma: export
$begin_exports^// IWYU pragma: begin_exports
$export2^#include "export2.h"
$export3^#include "export3.h"
$end_exports^// IWYU pragma: end_exports
#include "normal.h" // Line 11
)cpp";
createEmptyFiles({"keep1.h", "keep2.h", "export1.h", "export2.h", "export3.h",
$normal^#include "normal.h"
$begin_keep^// IWYU pragma: begin_keep
$keep3^#include "keep3.h"
$end_keep^// IWYU pragma: end_keep
// IWYU pragma: begin_keep
$keep4^#include "keep4.h"
// IWYU pragma: begin_keep
$keep5^#include "keep5.h"
// IWYU pragma: end_keep
$keep6^#include "keep6.h"
// IWYU pragma: end_keep
)cpp");
auto OffsetToLineNum = [&MainFile](size_t Offset) {
int Count = MainFile.code().substr(0, Offset).count('\n');
return Count + 1;
};
Inputs.Code = MainFile.code();
createEmptyFiles({"keep1.h", "keep2.h", "keep3.h", "keep4.h", "keep5.h",
"keep6.h", "export1.h", "export2.h", "export3.h",
"normal.h"});
TestAST Processed = build();
EXPECT_FALSE(PI.shouldKeep(1));
// Keep
EXPECT_TRUE(PI.shouldKeep(2));
EXPECT_TRUE(PI.shouldKeep(3));
EXPECT_TRUE(PI.shouldKeep(OffsetToLineNum(MainFile.point("keep1"))));
EXPECT_TRUE(PI.shouldKeep(OffsetToLineNum(MainFile.point("keep2"))));
EXPECT_FALSE(PI.shouldKeep(
OffsetToLineNum(MainFile.point("begin_keep")))); // no # directive
EXPECT_TRUE(PI.shouldKeep(OffsetToLineNum(MainFile.point("keep3"))));
EXPECT_FALSE(PI.shouldKeep(
OffsetToLineNum(MainFile.point("end_keep")))); // no # directive
EXPECT_TRUE(PI.shouldKeep(OffsetToLineNum(MainFile.point("keep4"))));
EXPECT_TRUE(PI.shouldKeep(OffsetToLineNum(MainFile.point("keep5"))));
EXPECT_TRUE(PI.shouldKeep(OffsetToLineNum(MainFile.point("keep6"))));
// Exports
EXPECT_TRUE(PI.shouldKeep(5));
EXPECT_TRUE(PI.shouldKeep(7));
EXPECT_TRUE(PI.shouldKeep(8));
EXPECT_FALSE(PI.shouldKeep(6)); // no # directive
EXPECT_FALSE(PI.shouldKeep(9)); // no # directive
EXPECT_TRUE(PI.shouldKeep(OffsetToLineNum(MainFile.point("export1"))));
EXPECT_TRUE(PI.shouldKeep(OffsetToLineNum(MainFile.point("export2"))));
EXPECT_TRUE(PI.shouldKeep(OffsetToLineNum(MainFile.point("export3"))));
EXPECT_FALSE(PI.shouldKeep(
OffsetToLineNum(MainFile.point("begin_exports")))); // no # directive
EXPECT_FALSE(PI.shouldKeep(
OffsetToLineNum(MainFile.point("end_exports")))); // no # directive
EXPECT_FALSE(PI.shouldKeep(11));
EXPECT_FALSE(PI.shouldKeep(OffsetToLineNum(MainFile.point("normal"))));
}
TEST_F(PragmaIncludeTest, IWYUPrivate) {