[clang] 99b5ec1 - [include-cleaner] Merge 2 parseIWYUPragma impls in libToolingInclusions

Sam McCall via cfe-commits cfe-commits at lists.llvm.org
Mon Nov 28 04:21:36 PST 2022


Author: Sam McCall
Date: 2022-11-28T13:20:09+01:00
New Revision: 99b5ec1fd1a70680a8483e5efb86807254e44e0e

URL: https://github.com/llvm/llvm-project/commit/99b5ec1fd1a70680a8483e5efb86807254e44e0e
DIFF: https://github.com/llvm/llvm-project/commit/99b5ec1fd1a70680a8483e5efb86807254e44e0e.diff

LOG: [include-cleaner] Merge 2 parseIWYUPragma impls in libToolingInclusions

Based on include-cleaner's version, but:

- remove assert that can fail for input `/\<newline>* */`
- assert was also checking the wrong condition: that the prefix *differed* from
  either `//` or from `/*`. Avoid use of strncmp where we can.
- add a comment that the brittleness of the text matching is intentional

Differential Revision: https://reviews.llvm.org/D138780

Added: 
    

Modified: 
    clang-tools-extra/clangd/Headers.cpp
    clang-tools-extra/clangd/Headers.h
    clang-tools-extra/clangd/unittests/HeadersTests.cpp
    clang-tools-extra/include-cleaner/lib/Record.cpp
    clang/include/clang/Tooling/Inclusions/HeaderAnalysis.h
    clang/lib/Tooling/Inclusions/HeaderAnalysis.cpp
    clang/unittests/Tooling/HeaderAnalysisTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/Headers.cpp b/clang-tools-extra/clangd/Headers.cpp
index f276f5bd8c6c1..c808c6f2bcc51 100644
--- a/clang-tools-extra/clangd/Headers.cpp
+++ b/clang-tools-extra/clangd/Headers.cpp
@@ -23,18 +23,6 @@
 namespace clang {
 namespace clangd {
 
-llvm::Optional<StringRef> parseIWYUPragma(const char *Text) {
-  // This gets called for every comment seen in the preamble, so it's quite hot.
-  constexpr llvm::StringLiteral IWYUPragma = "// IWYU pragma: ";
-  if (strncmp(Text, IWYUPragma.data(), IWYUPragma.size()))
-    return llvm::None;
-  Text += IWYUPragma.size();
-  const char *End = Text;
-  while (*End != 0 && *End != '\n')
-    ++End;
-  return StringRef(Text, End - Text);
-}
-
 class IncludeStructure::RecordHeaders : public PPCallbacks,
                                         public CommentHandler {
 public:
@@ -136,7 +124,8 @@ class IncludeStructure::RecordHeaders : public PPCallbacks,
   }
 
   bool HandleComment(Preprocessor &PP, SourceRange Range) override {
-    auto Pragma = parseIWYUPragma(SM.getCharacterData(Range.getBegin()));
+    auto Pragma =
+        tooling::parseIWYUPragma(SM.getCharacterData(Range.getBegin()));
     if (!Pragma)
       return false;
 

diff  --git a/clang-tools-extra/clangd/Headers.h b/clang-tools-extra/clangd/Headers.h
index ba72ad397bf8f..ff3f063168325 100644
--- a/clang-tools-extra/clangd/Headers.h
+++ b/clang-tools-extra/clangd/Headers.h
@@ -35,12 +35,6 @@ namespace clangd {
 /// Returns true if \p Include is literal include like "path" or <path>.
 bool isLiteralInclude(llvm::StringRef Include);
 
-/// If Text begins an Include-What-You-Use directive, returns it.
-/// Given "// IWYU pragma: keep", returns "keep".
-/// Input is a null-terminated char* as provided by SM.getCharacterData().
-/// (This should not be StringRef as we do *not* want to scan for its length).
-llvm::Optional<StringRef> parseIWYUPragma(const char *Text);
-
 /// Represents a header file to be #include'd.
 struct HeaderFile {
   std::string File;

diff  --git a/clang-tools-extra/clangd/unittests/HeadersTests.cpp b/clang-tools-extra/clangd/unittests/HeadersTests.cpp
index 324d4b58a1ef1..cad81d40ffc81 100644
--- a/clang-tools-extra/clangd/unittests/HeadersTests.cpp
+++ b/clang-tools-extra/clangd/unittests/HeadersTests.cpp
@@ -447,18 +447,6 @@ TEST_F(HeadersTest, HasIWYUPragmas) {
   EXPECT_FALSE(Includes.hasIWYUExport(getID("none.h", Includes)));
 }
 
-TEST(Headers, ParseIWYUPragma) {
-  EXPECT_THAT(parseIWYUPragma("// IWYU pragma: keep"), HasValue(Eq("keep")));
-  EXPECT_THAT(parseIWYUPragma("// IWYU pragma: keep\netc"),
-              HasValue(Eq("keep")));
-  EXPECT_EQ(parseIWYUPragma("/* IWYU pragma: keep"), llvm::None)
-      << "Only // comments supported!";
-  EXPECT_EQ(parseIWYUPragma("//  IWYU pragma: keep"), llvm::None)
-      << "Sensitive to whitespace";
-  EXPECT_EQ(parseIWYUPragma("// IWYU pragma:keep"), llvm::None)
-      << "Sensitive to whitespace";
-}
-
 } // namespace
 } // namespace clangd
 } // namespace clang

diff  --git a/clang-tools-extra/include-cleaner/lib/Record.cpp b/clang-tools-extra/include-cleaner/lib/Record.cpp
index e93bc14d4b46a..a0aa1ad6845d2 100644
--- a/clang-tools-extra/include-cleaner/lib/Record.cpp
+++ b/clang-tools-extra/include-cleaner/lib/Record.cpp
@@ -141,21 +141,6 @@ class PPRecorder : public PPCallbacks {
 
 } // namespace
 
-// FIXME: this is a mirror of clang::clangd::parseIWYUPragma, move to libTooling
-// to share the code?
-static llvm::Optional<StringRef> parseIWYUPragma(const char *Text) {
-  assert(strncmp(Text, "//", 2) || strncmp(Text, "/*", 2));
-  constexpr llvm::StringLiteral IWYUPragma = " IWYU pragma: ";
-  Text += 2; // Skip the comment start, // or /*.
-  if (strncmp(Text, IWYUPragma.data(), IWYUPragma.size()))
-    return llvm::None;
-  Text += IWYUPragma.size();
-  const char *End = Text;
-  while (*End != 0 && *End != '\n')
-    ++End;
-  return StringRef(Text, End - Text);
-}
-
 class PragmaIncludes::RecordPragma : public PPCallbacks, public CommentHandler {
 public:
   RecordPragma(const CompilerInstance &CI, PragmaIncludes *Out)
@@ -229,7 +214,8 @@ class PragmaIncludes::RecordPragma : public PPCallbacks, public CommentHandler {
 
   bool HandleComment(Preprocessor &PP, SourceRange Range) override {
     auto &SM = PP.getSourceManager();
-    auto Pragma = parseIWYUPragma(SM.getCharacterData(Range.getBegin()));
+    auto Pragma =
+        tooling::parseIWYUPragma(SM.getCharacterData(Range.getBegin()));
     if (!Pragma)
       return false;
 

diff  --git a/clang/include/clang/Tooling/Inclusions/HeaderAnalysis.h b/clang/include/clang/Tooling/Inclusions/HeaderAnalysis.h
index b0b4b1455c5df..3d47829296e91 100644
--- a/clang/include/clang/Tooling/Inclusions/HeaderAnalysis.h
+++ b/clang/include/clang/Tooling/Inclusions/HeaderAnalysis.h
@@ -9,6 +9,8 @@
 #ifndef LLVM_CLANG_TOOLING_INCLUSIONS_HEADER_ANALYSIS_H
 #define LLVM_CLANG_TOOLING_INCLUSIONS_HEADER_ANALYSIS_H
 
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
 namespace clang {
 class FileEntry;
 class SourceManager;
@@ -27,6 +29,13 @@ namespace tooling {
 bool isSelfContainedHeader(const FileEntry *FE, const SourceManager &SM,
                            HeaderSearch &HeaderInfo);
 
+/// If Text begins an Include-What-You-Use directive, returns it.
+/// Given "// IWYU pragma: keep", returns "keep".
+/// Input is a null-terminated char* as provided by SM.getCharacterData().
+/// (This should not be StringRef as we do *not* want to scan for its length).
+/// For multi-line comments, we return only the first line.
+llvm::Optional<llvm::StringRef> parseIWYUPragma(const char *Text);
+
 } // namespace tooling
 } // namespace clang
 

diff  --git a/clang/lib/Tooling/Inclusions/HeaderAnalysis.cpp b/clang/lib/Tooling/Inclusions/HeaderAnalysis.cpp
index 78b866eae0967..c0ed78134f1a1 100644
--- a/clang/lib/Tooling/Inclusions/HeaderAnalysis.cpp
+++ b/clang/lib/Tooling/Inclusions/HeaderAnalysis.cpp
@@ -64,4 +64,28 @@ bool isSelfContainedHeader(const FileEntry *FE, const SourceManager &SM,
       const_cast<SourceManager &>(SM).getMemoryBufferForFileOrNone(FE).value_or(
           llvm::MemoryBufferRef()));
 }
+
+llvm::Optional<StringRef> parseIWYUPragma(const char *Text) {
+  // Skip the comment start, // or /*.
+  if (Text[0] != '/' || (Text[1] != '/' && Text[1] != '*'))
+    return llvm::None;
+  bool BlockComment = Text[1] == '*';
+  Text += 2;
+
+  // Per spec, direcitves are whitespace- and case-sensitive.
+  constexpr llvm::StringLiteral IWYUPragma = " IWYU pragma: ";
+  if (strncmp(Text, IWYUPragma.data(), IWYUPragma.size()))
+    return llvm::None;
+  Text += IWYUPragma.size();
+  const char *End = Text;
+  while (*End != 0 && *End != '\n')
+    ++End;
+  StringRef Rest(Text, End - Text);
+  // Strip off whitespace and comment markers to avoid confusion. This isn't
+  // fully-compatible with IWYU, which splits into whitespace-delimited tokens.
+  if (BlockComment)
+    Rest.consume_back("*/");
+  return Rest.trim();
+}
+
 } // namespace clang::tooling

diff  --git a/clang/unittests/Tooling/HeaderAnalysisTest.cpp b/clang/unittests/Tooling/HeaderAnalysisTest.cpp
index 37a4c3ffb483e..c6ba625c0c062 100644
--- a/clang/unittests/Tooling/HeaderAnalysisTest.cpp
+++ b/clang/unittests/Tooling/HeaderAnalysisTest.cpp
@@ -9,11 +9,14 @@
 #include "clang/Tooling/Inclusions/HeaderAnalysis.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Testing/TestAST.h"
+#include "llvm/Testing/Support/SupportHelpers.h"
 #include "gtest/gtest.h"
 
 namespace clang {
 namespace tooling {
 namespace {
+using llvm::ValueIs;
+using testing::Eq;
 
 TEST(HeaderAnalysisTest, IsSelfContained) {
   TestInputs Inputs;
@@ -58,6 +61,19 @@ TEST(HeaderAnalysisTest, IsSelfContained) {
   EXPECT_FALSE(isSelfContainedHeader(FM.getFile("bad.h").get(), SM, HI));
 }
 
+TEST(HeaderAnalysisTest, ParseIWYUPragma) {
+  EXPECT_THAT(parseIWYUPragma("// IWYU pragma: keep"), ValueIs(Eq("keep")));
+  EXPECT_THAT(parseIWYUPragma("// IWYU pragma:   keep  me\netc"),
+              ValueIs(Eq("keep  me")));
+  EXPECT_THAT(parseIWYUPragma("/* IWYU pragma: keep */"), ValueIs(Eq("keep")));
+  EXPECT_EQ(parseIWYUPragma("//  IWYU pragma: keep"), llvm::None)
+      << "Prefix is sensitive to whitespace";
+  EXPECT_EQ(parseIWYUPragma("// IWYU pragma:keep"), llvm::None)
+      << "Prefix is sensitive to whitespace";
+  EXPECT_EQ(parseIWYUPragma("/\n* IWYU pragma: keep */"), llvm::None)
+      << "Must start with /* or //";
+}
+
 } // namespace
 } // namespace tooling
 } // namespace clang


        


More information about the cfe-commits mailing list