https://github.com/owenca created https://github.com/llvm/llvm-project/pull/128122
Closes #128119 >From 507c54acac3e73826f63691c901ceba9c569869f Mon Sep 17 00:00:00 2001 From: Owen Pan <owenpi...@gmail.com> Date: Thu, 20 Feb 2025 20:02:44 -0800 Subject: [PATCH] [clang-format] Allow specifying the language for `.h` files Closes #128119 --- clang/docs/ClangFormatStyleOptions.rst | 8 ++++++- clang/docs/ReleaseNotes.rst | 3 +++ clang/include/clang/Format/Format.h | 7 +++++- clang/lib/Format/Format.cpp | 33 ++++++++++++++++++++++++++ clang/unittests/Format/FormatTest.cpp | 9 +++++++ 5 files changed, 58 insertions(+), 2 deletions(-) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index bf6dd9e13915f..2d4ead76cfef2 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -4782,7 +4782,13 @@ the configuration (without a prefix: ``Auto``). .. _Language: **Language** (``LanguageKind``) :versionbadge:`clang-format 3.5` :ref:`¶ <Language>` - Language, this format style is targeted at. + The language that this format style targets. + + .. note:: + + You can also specify the language (``Cpp`` or ``ObjC``) for ``.h`` files + by adding a ``// clang-format Language:`` line before the first + non-comment and non-empty line, e.g. ``// clang-format Language: ObjC``. Possible values: diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index e1c61992512b5..0e65f72623f28 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -269,6 +269,9 @@ clang-format - Adds ``BreakBeforeTemplateCloser`` option. - Adds ``BinPackLongBracedList`` option to override bin packing options in long (20 item or more) braced list initializer lists. +- Allow specifying the language (C++ or Objective-C) for a ``.h`` file by adding + a special comment (e.g. ``// clang-format Language: ObjC``) near the top of + the file. libclang -------- diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 16956b4e0fbd4..55709a0261b12 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -3353,7 +3353,12 @@ struct FormatStyle { } bool isTableGen() const { return Language == LK_TableGen; } - /// Language, this format style is targeted at. + /// The language that this format style targets. + /// \note + /// You can also specify the language (``Cpp`` or ``ObjC``) for ``.h`` files + /// by adding a ``// clang-format Language:`` line before the first + /// non-comment and non-empty line, e.g. ``// clang-format Language: ObjC``. + /// \endnote /// \version 3.5 LanguageKind Language; diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 0898b69528ebc..400f39ecc5483 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -4021,6 +4021,35 @@ static FormatStyle::LanguageKind getLanguageByFileName(StringRef FileName) { return FormatStyle::LK_Cpp; } +static FormatStyle::LanguageKind getLanguageByComment(const Environment &Env) { + const auto ID = Env.getFileID(); + const auto &SourceMgr = Env.getSourceManager(); + + LangOptions LangOpts; + LangOpts.CPlusPlus = 1; + LangOpts.LineComment = 1; + + Lexer Lex(ID, SourceMgr.getBufferOrFake(ID), SourceMgr, LangOpts); + Lex.SetCommentRetentionState(true); + + for (Token Tok; !Lex.LexFromRawLexer(Tok) && Tok.is(tok::comment);) { + auto Text = StringRef(SourceMgr.getCharacterData(Tok.getLocation()), + Tok.getLength()); + if (!Text.consume_front("// clang-format Language:")) + continue; + + Text = Text.trim(); + // if (Text == "C") + // return FormatStyle::LK_C; + if (Text == "Cpp") + return FormatStyle::LK_Cpp; + if (Text == "ObjC") + return FormatStyle::LK_ObjC; + } + + return FormatStyle::LK_None; +} + FormatStyle::LanguageKind guessLanguage(StringRef FileName, StringRef Code) { const auto GuessedLanguage = getLanguageByFileName(FileName); if (GuessedLanguage == FormatStyle::LK_Cpp) { @@ -4030,6 +4059,10 @@ FormatStyle::LanguageKind guessLanguage(StringRef FileName, StringRef Code) { if (!Code.empty() && (Extension.empty() || Extension == ".h")) { auto NonEmptyFileName = FileName.empty() ? "guess.h" : FileName; Environment Env(Code, NonEmptyFileName, /*Ranges=*/{}); + if (const auto Language = getLanguageByComment(Env); + Language != FormatStyle::LK_None) { + return Language; + } ObjCHeaderStyleGuesser Guesser(Env, getLLVMStyle()); Guesser.process(); if (Guesser.isObjC()) diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 132264486100d..05febf12c17ba 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -25136,6 +25136,15 @@ TEST_F(FormatTest, GuessLanguageWithChildLines) { guessLanguage("foo.h", "#define FOO ({ foo(); ({ NSString *s; }) })")); } +TEST_F(FormatTest, GetLanguageByComment) { + EXPECT_EQ(FormatStyle::LK_Cpp, + guessLanguage("foo.h", "// clang-format Language: Cpp\n" + "int DoStuff(CGRect rect);")); + EXPECT_EQ(FormatStyle::LK_ObjC, + guessLanguage("foo.h", "// clang-format Language: ObjC\n" + "int i;")); +} + TEST_F(FormatTest, TypenameMacros) { std::vector<std::string> TypenameMacros = {"STACK_OF", "LIST", "TAILQ_ENTRY"}; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits