Skip to content

Commit ffc61dc

Browse files
authored
[clang-format] Allow specifying the language for .h files (#128122)
Closes #128119
1 parent 434d86a commit ffc61dc

File tree

5 files changed

+56
-2
lines changed

5 files changed

+56
-2
lines changed

clang/docs/ClangFormatStyleOptions.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4782,7 +4782,13 @@ the configuration (without a prefix: ``Auto``).
47824782
.. _Language:
47834783

47844784
**Language** (``LanguageKind``) :versionbadge:`clang-format 3.5` :ref:`<Language>`
4785-
Language, this format style is targeted at.
4785+
The language that this format style targets.
4786+
4787+
.. note::
4788+
4789+
You can also specify the language (``Cpp`` or ``ObjC``) for ``.h`` files
4790+
by adding a ``// clang-format Language:`` line before the first
4791+
non-comment (and non-empty) line, e.g. ``// clang-format Language: Cpp``.
47864792

47874793
Possible values:
47884794

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,9 @@ clang-format
271271
- Adds ``BreakBeforeTemplateCloser`` option.
272272
- Adds ``BinPackLongBracedList`` option to override bin packing options in
273273
long (20 item or more) braced list initializer lists.
274+
- Allow specifying the language (C++ or Objective-C) for a ``.h`` file by adding
275+
a special comment (e.g. ``// clang-format Language: ObjC``) near the top of
276+
the file.
274277

275278
libclang
276279
--------

clang/include/clang/Format/Format.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3353,7 +3353,12 @@ struct FormatStyle {
33533353
}
33543354
bool isTableGen() const { return Language == LK_TableGen; }
33553355

3356-
/// Language, this format style is targeted at.
3356+
/// The language that this format style targets.
3357+
/// \note
3358+
/// You can also specify the language (``Cpp`` or ``ObjC``) for ``.h`` files
3359+
/// by adding a ``// clang-format Language:`` line before the first
3360+
/// non-comment (and non-empty) line, e.g. ``// clang-format Language: Cpp``.
3361+
/// \endnote
33573362
/// \version 3.5
33583363
LanguageKind Language;
33593364

clang/lib/Format/Format.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4021,6 +4021,33 @@ static FormatStyle::LanguageKind getLanguageByFileName(StringRef FileName) {
40214021
return FormatStyle::LK_Cpp;
40224022
}
40234023

4024+
static FormatStyle::LanguageKind getLanguageByComment(const Environment &Env) {
4025+
const auto ID = Env.getFileID();
4026+
const auto &SourceMgr = Env.getSourceManager();
4027+
4028+
LangOptions LangOpts;
4029+
LangOpts.CPlusPlus = 1;
4030+
LangOpts.LineComment = 1;
4031+
4032+
Lexer Lex(ID, SourceMgr.getBufferOrFake(ID), SourceMgr, LangOpts);
4033+
Lex.SetCommentRetentionState(true);
4034+
4035+
for (Token Tok; !Lex.LexFromRawLexer(Tok) && Tok.is(tok::comment);) {
4036+
auto Text = StringRef(SourceMgr.getCharacterData(Tok.getLocation()),
4037+
Tok.getLength());
4038+
if (!Text.consume_front("// clang-format Language:"))
4039+
continue;
4040+
4041+
Text = Text.trim();
4042+
if (Text == "Cpp")
4043+
return FormatStyle::LK_Cpp;
4044+
if (Text == "ObjC")
4045+
return FormatStyle::LK_ObjC;
4046+
}
4047+
4048+
return FormatStyle::LK_None;
4049+
}
4050+
40244051
FormatStyle::LanguageKind guessLanguage(StringRef FileName, StringRef Code) {
40254052
const auto GuessedLanguage = getLanguageByFileName(FileName);
40264053
if (GuessedLanguage == FormatStyle::LK_Cpp) {
@@ -4030,6 +4057,10 @@ FormatStyle::LanguageKind guessLanguage(StringRef FileName, StringRef Code) {
40304057
if (!Code.empty() && (Extension.empty() || Extension == ".h")) {
40314058
auto NonEmptyFileName = FileName.empty() ? "guess.h" : FileName;
40324059
Environment Env(Code, NonEmptyFileName, /*Ranges=*/{});
4060+
if (const auto Language = getLanguageByComment(Env);
4061+
Language != FormatStyle::LK_None) {
4062+
return Language;
4063+
}
40334064
ObjCHeaderStyleGuesser Guesser(Env, getLLVMStyle());
40344065
Guesser.process();
40354066
if (Guesser.isObjC())

clang/unittests/Format/FormatTest.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25136,6 +25136,15 @@ TEST_F(FormatTest, GuessLanguageWithChildLines) {
2513625136
guessLanguage("foo.h", "#define FOO ({ foo(); ({ NSString *s; }) })"));
2513725137
}
2513825138

25139+
TEST_F(FormatTest, GetLanguageByComment) {
25140+
EXPECT_EQ(FormatStyle::LK_Cpp,
25141+
guessLanguage("foo.h", "// clang-format Language: Cpp\n"
25142+
"int DoStuff(CGRect rect);"));
25143+
EXPECT_EQ(FormatStyle::LK_ObjC,
25144+
guessLanguage("foo.h", "// clang-format Language: ObjC\n"
25145+
"int i;"));
25146+
}
25147+
2513925148
TEST_F(FormatTest, TypenameMacros) {
2514025149
std::vector<std::string> TypenameMacros = {"STACK_OF", "LIST", "TAILQ_ENTRY"};
2514125150

0 commit comments

Comments
 (0)