From 24f4c3ebef63c7d2553132d7d9d75ea19338bcb7 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 2 Jul 2021 19:43:01 +0000 Subject: [PATCH] Lex: add a callback for `#pragma mark` Allow a preprocessor observer to be notified of mark pragmas. Although this does not impact code generation in any way, it is useful for other clients, such as clangd, to be able to identify any marked regions. Reviewed By: dgoldman Differential Revision: https://reviews.llvm.org/D105368 --- clang/include/clang/Lex/PPCallbacks.h | 4 +++ clang/include/clang/Lex/Preprocessor.h | 2 +- clang/lib/Lex/Pragma.cpp | 10 ++++-- clang/unittests/Lex/PPCallbacksTest.cpp | 62 +++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/Lex/PPCallbacks.h b/clang/include/clang/Lex/PPCallbacks.h index d57be19..bcf49c5 100644 --- a/clang/include/clang/Lex/PPCallbacks.h +++ b/clang/include/clang/Lex/PPCallbacks.h @@ -191,6 +191,10 @@ public: StringRef Str) { } + /// Callback invoked when a \#pragma mark comment is read. + virtual void PragmaMark(SourceLocation Loc, StringRef Trivia) { + } + /// Callback invoked when a \#pragma detect_mismatch directive is /// read. virtual void PragmaDetectMismatch(SourceLocation Loc, StringRef Name, diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index 16fbf5e..2d633547 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -2365,7 +2365,7 @@ private: public: void HandlePragmaOnce(Token &OnceTok); - void HandlePragmaMark(); + void HandlePragmaMark(Token &MarkTok); void HandlePragmaPoison(); void HandlePragmaSystemHeader(Token &SysHeaderTok); void HandlePragmaDependency(Token &DependencyTok); diff --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp index 081b92a..c89061b 100644 --- a/clang/lib/Lex/Pragma.cpp +++ b/clang/lib/Lex/Pragma.cpp @@ -412,9 +412,13 @@ void Preprocessor::HandlePragmaOnce(Token &OnceTok) { HeaderInfo.MarkFileIncludeOnce(getCurrentFileLexer()->getFileEntry()); } -void Preprocessor::HandlePragmaMark() { +void Preprocessor::HandlePragmaMark(Token &MarkTok) { assert(CurPPLexer && "No current lexer?"); - CurLexer->ReadToEndOfLine(); + + SmallString<64> Buffer; + CurLexer->ReadToEndOfLine(&Buffer); + if (Callbacks) + Callbacks->PragmaMark(MarkTok.getLocation(), Buffer); } /// HandlePragmaPoison - Handle \#pragma GCC poison. PoisonTok is the 'poison'. @@ -992,7 +996,7 @@ struct PragmaMarkHandler : public PragmaHandler { void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, Token &MarkTok) override { - PP.HandlePragmaMark(); + PP.HandlePragmaMark(MarkTok); } }; diff --git a/clang/unittests/Lex/PPCallbacksTest.cpp b/clang/unittests/Lex/PPCallbacksTest.cpp index 5581f9f..f92587a 100644 --- a/clang/unittests/Lex/PPCallbacksTest.cpp +++ b/clang/unittests/Lex/PPCallbacksTest.cpp @@ -112,6 +112,20 @@ public: unsigned State; }; +class PragmaMarkCallbacks : public PPCallbacks { +public: + struct Mark { + SourceLocation Location; + std::string Trivia; + }; + + std::vector Marks; + + void PragmaMark(SourceLocation Loc, StringRef Trivia) override { + Marks.emplace_back(Mark{Loc, Trivia.str()}); + } +}; + // PPCallbacks test fixture. class PPCallbacksTest : public ::testing::Test { protected: @@ -256,6 +270,36 @@ protected: return Callbacks->Results; } + std::vector + PragmaMarkCall(const char *SourceText) { + std::unique_ptr SourceBuf = + llvm::MemoryBuffer::getMemBuffer(SourceText, "test.c"); + SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(SourceBuf))); + + HeaderSearch HeaderInfo(std::make_shared(), SourceMgr, + Diags, LangOpts, Target.get()); + TrivialModuleLoader ModLoader; + + Preprocessor PP(std::make_shared(), Diags, LangOpts, + SourceMgr, HeaderInfo, ModLoader, /*IILookup=*/nullptr, + /*OwnsHeaderSearch=*/false); + PP.Initialize(*Target); + + auto *Callbacks = new PragmaMarkCallbacks; + PP.addPPCallbacks(std::unique_ptr(Callbacks)); + + // Lex source text. + PP.EnterMainSourceFile(); + while (true) { + Token Tok; + PP.Lex(Tok); + if (Tok.is(tok::eof)) + break; + } + + return Callbacks->Marks; + } + PragmaOpenCLExtensionCallbacks::CallbackParameters PragmaOpenCLExtensionCall(const char *SourceText) { LangOptions OpenCLLangOpts; @@ -424,6 +468,24 @@ TEST_F(PPCallbacksTest, OpenCLExtensionPragmaDisabled) { ASSERT_EQ(ExpectedState, Parameters.State); } +TEST_F(PPCallbacksTest, CollectMarks) { + const char *Source = + "#pragma mark\n" + "#pragma mark\r\n" + "#pragma mark - trivia\n" + "#pragma mark - trivia\r\n"; + + auto Marks = PragmaMarkCall(Source); + + ASSERT_EQ(4u, Marks.size()); + ASSERT_TRUE(Marks[0].Trivia.empty()); + ASSERT_TRUE(Marks[1].Trivia.empty()); + ASSERT_FALSE(Marks[2].Trivia.empty()); + ASSERT_FALSE(Marks[3].Trivia.empty()); + ASSERT_EQ(" - trivia", Marks[2].Trivia); + ASSERT_EQ(" - trivia", Marks[3].Trivia); +} + TEST_F(PPCallbacksTest, DirectiveExprRanges) { const auto &Results1 = DirectiveExprRange("#if FLUZZY_FLOOF\n#endif\n"); EXPECT_EQ(Results1.size(), 1U); -- 2.7.4