Author: Owen Pan Date: 2025-02-21T20:46:43-08:00 New Revision: ffc61dc393e4224899c2496871f3ec3b3a1babf1
URL: https://github.com/llvm/llvm-project/commit/ffc61dc393e4224899c2496871f3ec3b3a1babf1 DIFF: https://github.com/llvm/llvm-project/commit/ffc61dc393e4224899c2496871f3ec3b3a1babf1.diff LOG: [clang-format] Allow specifying the language for `.h` files (#128122) Closes #128119 Added: Modified: clang/docs/ClangFormatStyleOptions.rst clang/docs/ReleaseNotes.rst clang/include/clang/Format/Format.h clang/lib/Format/Format.cpp clang/unittests/Format/FormatTest.cpp Removed: ################################################################################ diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index bf6dd9e13915f..ba50eb2c7e89b 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: Cpp``. Possible values: diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 3792a235538fa..ec9cab2eb657f 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -271,6 +271,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..2dc95c3c06d29 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: Cpp``. + /// \endnote /// \version 3.5 LanguageKind Language; diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 0898b69528ebc..b063843078251 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -4021,6 +4021,33 @@ 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 == "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 +4057,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