https://github.com/dmaclach created https://github.com/llvm/llvm-project/pull/212582
This change updates HeaderIncludes to recognize #import directives in addition to #include when parsing existing headers. It also fixes up an existing issue parsing include/import statements that may have contained tabs. >From e7e3f9cac7cd180ac8ce8a826603a507ff01e439 Mon Sep 17 00:00:00 2001 From: Dave MacLachlan <[email protected]> Date: Tue, 28 Jul 2026 11:47:07 -0700 Subject: [PATCH] [include-cleaner]Support #import directives in HeaderIncludes This change updates HeaderIncludes to recognize #import directives in addition to #include when parsing existing headers. It also fixes up an existing issue parsing include/import statements that may have contained tabs. --- .../lib/Tooling/Inclusions/HeaderIncludes.cpp | 8 ++-- .../unittests/Tooling/HeaderIncludesTest.cpp | 41 +++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp b/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp index 34f21faa8e342..62752122db2ac 100644 --- a/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp +++ b/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp @@ -154,7 +154,7 @@ unsigned getMinHeaderInsertionOffset(StringRef FileName, StringRef Code, } // Check if a sequence of tokens is like -// "#include ("header.h" | <header.h>)". +// "#(include | import) ("header.h" | <header.h>)". // If it is, \p Tok will be the token after this directive; otherwise, it can be // any token after the given \p Tok (including \p Tok). bool checkAndConsumeInclusiveDirective(Lexer &Lex, Token &Tok) { @@ -163,7 +163,9 @@ bool checkAndConsumeInclusiveDirective(Lexer &Lex, Token &Tok) { return true; }; if (Tok.is(tok::hash) && !Lex.LexFromRawLexer(Tok) && - Tok.is(tok::raw_identifier) && Tok.getRawIdentifier() == "include") { + Tok.is(tok::raw_identifier) && + (Tok.getRawIdentifier() == "include" || + Tok.getRawIdentifier() == "import")) { if (Lex.LexFromRawLexer(Tok)) return false; if (Tok.is(tok::string_literal)) @@ -246,7 +248,7 @@ inline StringRef trimInclude(StringRef IncludeName) { } const char IncludeRegexPattern[] = - R"(^[\t\ ]*#[\t\ ]*(import|include)[^"<]*(["<][^">]*[">]))"; + "^[\t ]*#[\t ]*(import|include)[^\"<]*([\"<][^\">]*[\">])"; // The filename of Path excluding extension. // Used to match implementation with headers, this differs from sys::path::stem: diff --git a/clang/unittests/Tooling/HeaderIncludesTest.cpp b/clang/unittests/Tooling/HeaderIncludesTest.cpp index bc2ac3a97e97d..4c1848252df56 100644 --- a/clang/unittests/Tooling/HeaderIncludesTest.cpp +++ b/clang/unittests/Tooling/HeaderIncludesTest.cpp @@ -82,6 +82,47 @@ int x;)cpp"; EXPECT_EQ("\nint x;", remove(Code, "<abc.h>")); } +TEST_F(HeaderIncludesTest, DeleteMixedImportAndIncludeQuoted) { + std::string Code = R"cpp( +#include "a.h" +#import "a.h" +int x;)cpp"; + EXPECT_EQ("\nint x;", remove(Code, "\"a.h\"")); +} + +TEST_F(HeaderIncludesTest, ImportWithSpacesAndTabs) { + std::string Code = "int x;\n"; + // The parser should detect these as existing imports if we had them, + // but here we are testing insertion/detection integration. + // Let's verify that a file with weird spacing is parsed correctly. + std::string CodeWithSpaces = + "# import \"a.h\"\n#\tinclude\t\"b.h\"\nint x;\n"; + + // Try inserting "a.h" again as import - should be blocked by the existing one + // if the regex captures it correctly. + EXPECT_EQ(CodeWithSpaces, + insert(CodeWithSpaces, "\"a.h\"", IncludeDirective::Import)); + + // Try inserting "b.h" again as include - should be blocked. + EXPECT_EQ(CodeWithSpaces, + insert(CodeWithSpaces, "\"b.h\"", IncludeDirective::Include)); +} + +TEST_F(HeaderIncludesTest, InsertIncludeWhenImportExists) { + std::string Code = "#import \"a.h\"\n"; + std::string Expected = Code + "#include \"a.h\"\n"; + // Currently, the logic allows inserting #include even if #import exists + // because the Directive differs. This test verifies this current behavior. + EXPECT_EQ(Expected, insert(Code, "\"a.h\"", IncludeDirective::Include)); +} + +TEST_F(HeaderIncludesTest, InsertImportWhenIncludeExists) { + std::string Code = "#include \"a.h\"\n"; + std::string Expected = Code + "#import \"a.h\"\n"; + // Similarly, allows inserting #import even if #include exists. + EXPECT_EQ(Expected, insert(Code, "\"a.h\"", IncludeDirective::Import)); +} + TEST_F(HeaderIncludesTest, NoExistingIncludeWithDefine) { std::string Code = "#ifndef A_H\n" "#define A_H\n" _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
