From 1a298a3be75eda91310520a413ecdcd708843ed3 Mon Sep 17 00:00:00 2001 From: Shiyu Date: Thu, 23 Jan 2025 00:10:59 +0800 Subject: [PATCH] Add pragma region/endregion to `FoldingRange`. (#49) --- include/Compiler/Directive.h | 26 +++++++- src/Compiler/Directive.cpp | 100 +++++++++++++---------------- src/Feature/FoldingRange.cpp | 43 +++++++++++-- unittests/Compiler/Directive.cpp | 43 +++++++++---- unittests/Feature/FoldingRange.cpp | 44 ++++++++++--- 5 files changed, 174 insertions(+), 82 deletions(-) diff --git a/include/Compiler/Directive.h b/include/Compiler/Directive.h index a86767ba..d71b0622 100644 --- a/include/Compiler/Directive.h +++ b/include/Compiler/Directive.h @@ -81,8 +81,30 @@ struct MacroRef { clang::SourceLocation loc; }; -/// Do we need to store pragma information? -struct Pragma {}; +/// Information about `#pragma` directive. +struct Pragma { + enum class Kind : uint8_t { + Region, + EndRegion, + + // Other unused cases in clice, For example: `#pragma once`. + Other, + }; + + using enum Kind; + + /// The pragma text in that line, for example: + /// "#pragma region" + /// "#pragma once" + /// "#pragma GCC error" + llvm::StringRef stmt; + + /// Kind of the pragma. + Kind kind; + + /// Location of the pragma token. + clang::SourceLocation loc; +}; struct Directive { std::vector includes; diff --git a/src/Compiler/Directive.cpp b/src/Compiler/Directive.cpp index a116d74d..12600b9c 100644 --- a/src/Compiler/Directive.cpp +++ b/src/Compiler/Directive.cpp @@ -17,16 +17,13 @@ struct PPCallback : public clang::PPCallbacks { PPCallback(clang::Preprocessor& PP, llvm::DenseMap& directives) : PP(PP), SM(PP.getSourceManager()), directives(directives) {} - void addCondition(clang::SourceLocation loc, - Condition::BranchKind kind, - Condition::ConditionValue value, - clang::SourceRange conditionRange) { + void addCondition(clang::SourceLocation loc, Condition::BranchKind kind, + Condition::ConditionValue value, clang::SourceRange conditionRange) { auto& directive = directives[PP.getSourceManager().getFileID(loc)]; directive.conditions.emplace_back(Condition{kind, value, loc, conditionRange}); } - void addCondition(clang::SourceLocation loc, - Condition::BranchKind kind, + void addCondition(clang::SourceLocation loc, Condition::BranchKind kind, clang::PPCallbacks::ConditionValueKind value, clang::SourceRange conditionRange) { Condition::ConditionValue condValue = Condition::None; @@ -47,10 +44,8 @@ struct PPCallback : public clang::PPCallbacks { addCondition(loc, kind, condValue, conditionRange); } - void addCondition(clang::SourceLocation loc, - Condition::BranchKind kind, - const clang::Token& name, - const clang::MacroDefinition& definition) { + void addCondition(clang::SourceLocation loc, Condition::BranchKind kind, + const clang::Token& name, const clang::MacroDefinition& definition) { if(auto def = definition.getMacroInfo()) { addMacro(def, MacroRef::Ref, name.getLocation()); addCondition(loc, kind, Condition::True, name.getLocation()); @@ -73,10 +68,8 @@ struct PPCallback : public clang::PPCallbacks { /// Rewritten Preprocessor Callbacks /// ============================================================================ - void LexedFileChanged(clang::FileID currFID, - LexedFileChangeReason reason, - clang::SrcMgr::CharacteristicKind, - clang::FileID prevFID, + void LexedFileChanged(clang::FileID currFID, LexedFileChangeReason reason, + clang::SrcMgr::CharacteristicKind, clang::FileID prevFID, clang::SourceLocation) override { if(reason == LexedFileChangeReason::EnterFile && currFID.isValid() && prevFID.isValid() && this->prevFID.isValid() && prevFID == this->prevFID) { @@ -84,16 +77,10 @@ struct PPCallback : public clang::PPCallbacks { } } - void InclusionDirective(clang::SourceLocation hashLoc, - const clang::Token& includeTok, - llvm::StringRef, - bool, - clang::CharSourceRange, - clang::OptionalFileEntryRef, - llvm::StringRef, - llvm::StringRef, - const clang::Module*, - bool, + void InclusionDirective(clang::SourceLocation hashLoc, const clang::Token& includeTok, + llvm::StringRef, bool, clang::CharSourceRange, + clang::OptionalFileEntryRef, llvm::StringRef, llvm::StringRef, + const clang::Module*, bool, clang::SrcMgr::CharacteristicKind) override { prevFID = SM.getFileID(hashLoc); directives[prevFID].includes.emplace_back(Include{ @@ -102,11 +89,8 @@ struct PPCallback : public clang::PPCallbacks { }); } - void HasInclude(clang::SourceLocation location, - llvm::StringRef, - bool, - clang::OptionalFileEntryRef file, - clang::SrcMgr::CharacteristicKind) override { + void HasInclude(clang::SourceLocation location, llvm::StringRef, bool, + clang::OptionalFileEntryRef file, clang::SrcMgr::CharacteristicKind) override { directives[SM.getFileID(location)].hasIncludes.emplace_back(clice::HasInclude{ file ? file->getName() : "", location, @@ -114,59 +98,70 @@ struct PPCallback : public clang::PPCallbacks { } void PragmaDirective(clang::SourceLocation Loc, - clang::PragmaIntroducerKind Introducer) override {} + clang::PragmaIntroducerKind Introducer) override { + // Ignore other cases except starts with `#pragma`. + if(Introducer != clang::PragmaIntroducerKind::PIK_HashPragma) + return; - void If(clang::SourceLocation loc, - clang::SourceRange conditionRange, + clang::FileID fid = SM.getFileID(Loc); + + llvm::StringRef textToEnd = SM.getBufferData(fid).substr(SM.getFileOffset(Loc)); + llvm::StringRef thatLine = textToEnd.take_until([](char ch) { return ch == '\n'; }); + + Pragma::Kind kind = thatLine.contains("endregion") ? Pragma::EndRegion + : thatLine.contains("region") ? Pragma::Region + : Pragma::Other; + + auto& directive = directives[fid]; + directive.pragmas.emplace_back(Pragma{ + thatLine, + kind, + Loc, + }); + } + + void If(clang::SourceLocation loc, clang::SourceRange conditionRange, clang::PPCallbacks::ConditionValueKind value) override { addCondition(loc, Condition::If, value, conditionRange); } - void Elif(clang::SourceLocation loc, - clang::SourceRange conditionRange, - clang::PPCallbacks::ConditionValueKind value, - clang::SourceLocation) override { + void Elif(clang::SourceLocation loc, clang::SourceRange conditionRange, + clang::PPCallbacks::ConditionValueKind value, clang::SourceLocation) override { addCondition(loc, Condition::Elif, value, conditionRange); } - void Ifdef(clang::SourceLocation loc, - const clang::Token& name, + void Ifdef(clang::SourceLocation loc, const clang::Token& name, const clang::MacroDefinition& definition) override { addCondition(loc, Condition::Ifdef, name, definition); } /// Invoke when #elifdef branch is taken. - void Elifdef(clang::SourceLocation loc, - const clang::Token& name, + void Elifdef(clang::SourceLocation loc, const clang::Token& name, const clang::MacroDefinition& definition) override { addCondition(loc, Condition::Elifdef, name, definition); } /// Invoke when #elif is skipped. - void Elifdef(clang::SourceLocation loc, - clang::SourceRange conditionRange, + void Elifdef(clang::SourceLocation loc, clang::SourceRange conditionRange, clang::SourceLocation) override { /// FIXME: should we try to evaluate the condition to compute the macro reference? addCondition(loc, Condition::Elifdef, Condition::Skipped, conditionRange); } /// Invoke when #ifndef is taken. - void Ifndef(clang::SourceLocation loc, - const clang::Token& name, + void Ifndef(clang::SourceLocation loc, const clang::Token& name, const clang::MacroDefinition& definition) override { addCondition(loc, Condition::Ifndef, name, definition); } // Invoke when #elifndef is taken. - void Elifndef(clang::SourceLocation loc, - const clang::Token& name, + void Elifndef(clang::SourceLocation loc, const clang::Token& name, const clang::MacroDefinition& definition) override { addCondition(loc, Condition::Elifndef, name, definition); } // Invoke when #elifndef is skipped. - void Elifndef(clang::SourceLocation loc, - clang::SourceRange conditionRange, + void Elifndef(clang::SourceLocation loc, clang::SourceRange conditionRange, clang::SourceLocation) override { addCondition(loc, Condition::Elifndef, Condition::Skipped, conditionRange); } @@ -185,17 +180,14 @@ struct PPCallback : public clang::PPCallbacks { } } - void MacroExpands(const clang::Token& name, - const clang::MacroDefinition& definition, - clang::SourceRange range, - const clang::MacroArgs* args) override { + void MacroExpands(const clang::Token& name, const clang::MacroDefinition& definition, + clang::SourceRange range, const clang::MacroArgs* args) override { if(auto def = definition.getMacroInfo()) { addMacro(def, MacroRef::Ref, name.getLocation()); } } - void MacroUndefined(const clang::Token& name, - const clang::MacroDefinition& MD, + void MacroUndefined(const clang::Token& name, const clang::MacroDefinition& MD, const clang::MacroDirective* undef) override { if(auto def = MD.getMacroInfo()) { addMacro(def, MacroRef::Undef, name.getLocation()); diff --git a/src/Feature/FoldingRange.cpp b/src/Feature/FoldingRange.cpp index 3ac6d21d..203abf28 100644 --- a/src/Feature/FoldingRange.cpp +++ b/src/Feature/FoldingRange.cpp @@ -302,6 +302,7 @@ struct FoldingRangeCollector : public clang::RecursiveASTVisitor stack = {}; + llvm::SmallVector stack = {}; for(auto& cond: conds) { switch(cond.kind) { @@ -322,24 +323,24 @@ struct FoldingRangeCollector : public clang::RecursiveASTVisitorloc, prevLineLastColOf(cond.loc)}); } - stack.push_back(cond); + stack.push_back(&cond); break; } case Condition::BranchKind::EndIf: { if(!stack.empty()) { auto last = stack.pop_back_val(); - collect({last.loc, prevLineLastColOf(cond.loc)}); + collect({last->loc, prevLineLastColOf(cond.loc)}); } break; } @@ -348,6 +349,38 @@ struct FoldingRangeCollector : public clang::RecursiveASTVisitor& pragmas) { + auto lastLocOfLine = [this](clang::SourceLocation loc) { + return src.translateLineCol(src.getMainFileID(), + src.getPresumedLineNumber(loc), + std::numeric_limits::max()); + }; + + llvm::SmallVector stack = {}; + for(auto& pragma: pragmas) { + switch(pragma.kind) { + case Pragma::Kind::Region: stack.push_back(&pragma); break; + case Pragma::Kind::EndRegion: + if(!stack.empty()) { + auto last = stack.pop_back_val(); + collect({lastLocOfLine(last->loc), prevLineLastColOf(pragma.loc)}); + } + break; + default: break; + } + } + + // If there is some region without end pragma, use the end of file as the end region. + if(!stack.empty()) { + auto eof = src.getLocForEndOfFile(src.getMainFileID()); + while(!stack.empty()) { + auto last = stack.pop_back_val(); + collect({lastLocOfLine(last->loc), eof}); + } + } + } }; } // namespace diff --git a/unittests/Compiler/Directive.cpp b/unittests/Compiler/Directive.cpp index a6eafb7e..1ef3ef47 100644 --- a/unittests/Compiler/Directive.cpp +++ b/unittests/Compiler/Directive.cpp @@ -11,6 +11,7 @@ struct Directive : ::testing::Test, Tester { llvm::ArrayRef hasIncludes; llvm::ArrayRef conditions; llvm::ArrayRef macros; + llvm::ArrayRef pragmas; void run(const char* standard = "-std=c++20") { Tester::run("-std=c++23"); @@ -20,11 +21,10 @@ struct Directive : ::testing::Test, Tester { hasIncludes = info->directives()[fid].hasIncludes; conditions = info->directives()[fid].conditions; macros = info->directives()[fid].macros; + pragmas = info->directives()[fid].pragmas; } - void EXPECT_INCLUDE(std::size_t index, - llvm::StringRef position, - llvm::StringRef path, + void EXPECT_INCLUDE(std::size_t index, llvm::StringRef position, llvm::StringRef path, std::source_location current = std::source_location::current()) { auto& include = includes[index]; auto entry = SM->getFileEntryRefForID(include.fid); @@ -32,32 +32,35 @@ struct Directive : ::testing::Test, Tester { EXPECT_EQ(entry ? entry->getName() : "", path, current); } - void EXPECT_HAS_INCLUDE(std::size_t index, - llvm::StringRef position, - llvm::StringRef path, + void EXPECT_HAS_INCLUDE(std::size_t index, llvm::StringRef position, llvm::StringRef path, std::source_location current = std::source_location::current()) { auto& hasInclude = hasIncludes[index]; EXPECT_EQ(SourceConverter().toPosition(hasInclude.location, *SM), pos(position), current); EXPECT_EQ(hasInclude.path, path, current); } - void EXPECT_CON(std::size_t index, - Condition::BranchKind kind, - llvm::StringRef position, + void EXPECT_CON(std::size_t index, Condition::BranchKind kind, llvm::StringRef position, std::source_location current = std::source_location::current()) { auto& condition = conditions[index]; EXPECT_EQ(condition.kind, kind, current); EXPECT_EQ(SourceConverter().toPosition(condition.loc, *SM), pos(position), current); } - void EXPECT_MACRO(std::size_t index, - MacroRef::Kind kind, - llvm::StringRef position, + void EXPECT_MACRO(std::size_t index, MacroRef::Kind kind, llvm::StringRef position, std::source_location current = std::source_location::current()) { auto& macro = macros[index]; EXPECT_EQ(macro.kind, kind, current); EXPECT_EQ(SourceConverter().toPosition(macro.loc, *SM), pos(position), current); } + + void EXPECT_PRAGMA(std::size_t index, Pragma::Kind kind, llvm::StringRef position, + llvm::StringRef text, + std::source_location current = std::source_location::current()) { + auto& pragma = pragmas[index]; + EXPECT_EQ(pragma.kind, kind, current); + EXPECT_EQ(pragma.stmt, text, current); + EXPECT_EQ(SourceConverter().toPosition(pragma.loc, *SM), pos(position), current); + } }; TEST_F(Directive, Include) { @@ -194,6 +197,22 @@ int y = $(6)expr($(7)expr(1)); EXPECT_MACRO(8, MacroRef::Kind::Undef, "8"); } +TEST_F(Directive, Pragma) { + const char* code = R"cpp( +$(0)#pragma GCC poison printf sprintf fprintf +$(1)#pragma region +$(2)#pragma endregion +)cpp"; + + addMain("main.cpp", code); + run(); + + EXPECT_EQ(3, pragmas.size()); + EXPECT_PRAGMA(0, Pragma::Kind::Other, "0", "#pragma GCC poison printf sprintf fprintf"); + EXPECT_PRAGMA(1, Pragma::Kind::Region, "1", "#pragma region"); + EXPECT_PRAGMA(2, Pragma::Kind::EndRegion, "2", "#pragma endregion"); +} + } // namespace } // namespace clice::testing diff --git a/unittests/Feature/FoldingRange.cpp b/unittests/Feature/FoldingRange.cpp index 3287b55e..f205f6dc 100644 --- a/unittests/Feature/FoldingRange.cpp +++ b/unittests/Feature/FoldingRange.cpp @@ -19,17 +19,14 @@ struct FoldingRange : public ::testing::Test { result = feature::foldingRange(param, *info, converter); } - void EXPECT_RANGE(std::size_t index, - llvm::StringRef begin, - llvm::StringRef end, + void EXPECT_RANGE(std::size_t index, llvm::StringRef begin, llvm::StringRef end, std::source_location current = std::source_location::current()) { auto& folding = result[index]; - EXPECT_EQ(tester->pos(begin), - proto::Position{folding.startLine, folding.startCharacter}, - current); - EXPECT_EQ(tester->pos(end), - proto::Position{folding.endLine, folding.endCharacter}, - current); + + auto beginPos = tester->pos(begin); + EXPECT_EQ(beginPos, proto::Position{folding.startLine, folding.startCharacter}, current); + auto endPos = tester->pos(end); + EXPECT_EQ(endPos, proto::Position{folding.endLine, folding.endCharacter}, current); } }; @@ -372,6 +369,35 @@ $(2) EXPECT_RANGE(2, "3", "4"); } +TEST_F(FoldingRange, PragmaRegion) { + run(R"cpp( +#pragma region level1 $(1) + #pragma region level2 $(2) + #pragma region level3 $(3) + + //$(4) + #pragma endregion level3 + + //$(5) + #pragma endregion level2 + +//$(6) +#pragma endregion level1 + +#pragma endregion // mismatch region, skipeed + +// broken region, use the end of file as endregion +#pragma region $(7) + +$(eof))cpp"); + + EXPECT_EQ(result.size(), 4); + EXPECT_RANGE(0, "3", "4"); + EXPECT_RANGE(1, "2", "5"); + EXPECT_RANGE(2, "1", "6"); + EXPECT_RANGE(3, "7", "eof"); +} + } // namespace } // namespace clice::testing