[clang] ffc61dc - [clang-format] Allow specifying the language for `.h` files (#128122)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 21 20:46:47 PST 2025
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"};
More information about the cfe-commits
mailing list