Skip to content

Commit 25e2d0f

Browse files
committed
[clang-format] Support clang-format on/off line comments as prefix
Closes llvm#60264. Differential Revision: https://reviews.llvm.org/D142804
1 parent 4177e89 commit 25e2d0f

File tree

7 files changed

+60
-21
lines changed

7 files changed

+60
-21
lines changed

clang/docs/ClangFormatStyleOptions.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,10 @@ Disabling Formatting on a Piece of Code
113113
Clang-format understands also special comments that switch formatting in a
114114
delimited range. The code between a comment ``// clang-format off`` or
115115
``/* clang-format off */`` up to a comment ``// clang-format on`` or
116-
``/* clang-format on */`` will not be formatted. The comments themselves
117-
will be formatted (aligned) normally.
116+
``/* clang-format on */`` will not be formatted. The comments themselves will be
117+
formatted (aligned) normally. Also, a colon (``:``) and additional text may
118+
follow ``// clang-format off`` or `` clang-format on`` to explain why
119+
clang-format is turned off or back on.
118120

119121
.. code-block:: c++
120122

clang/include/clang/Format/Format.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4599,6 +4599,9 @@ inline StringRef getLanguageName(FormatStyle::LanguageKind Language) {
45994599
}
46004600
}
46014601

4602+
bool isClangFormatOn(StringRef Comment);
4603+
bool isClangFormatOff(StringRef Comment);
4604+
46024605
} // end namespace format
46034606
} // end namespace clang
46044607

clang/lib/Format/Format.cpp

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3002,13 +3002,10 @@ tooling::Replacements sortCppIncludes(const FormatStyle &Style, StringRef Code,
30023002
if (Trimmed.contains(RawStringTermination))
30033003
FormattingOff = false;
30043004

3005-
if (Trimmed == "// clang-format off" ||
3006-
Trimmed == "/* clang-format off */") {
3005+
if (isClangFormatOff(Trimmed))
30073006
FormattingOff = true;
3008-
} else if (Trimmed == "// clang-format on" ||
3009-
Trimmed == "/* clang-format on */") {
3007+
else if (isClangFormatOn(Trimmed))
30103008
FormattingOff = false;
3011-
}
30123009

30133010
const bool EmptyLineSkipped =
30143011
Trimmed.empty() &&
@@ -3185,9 +3182,9 @@ tooling::Replacements sortJavaImports(const FormatStyle &Style, StringRef Code,
31853182
Code.substr(Prev, (Pos != StringRef::npos ? Pos : Code.size()) - Prev);
31863183

31873184
StringRef Trimmed = Line.trim();
3188-
if (Trimmed == "// clang-format off")
3185+
if (isClangFormatOff(Trimmed))
31893186
FormattingOff = true;
3190-
else if (Trimmed == "// clang-format on")
3187+
else if (isClangFormatOn(Trimmed))
31913188
FormattingOff = false;
31923189

31933190
if (ImportRegex.match(Line, &Matches)) {
@@ -3893,5 +3890,25 @@ llvm::Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName,
38933890
return FallbackStyle;
38943891
}
38953892

3893+
static bool isClangFormatOnOff(StringRef Comment, bool On) {
3894+
if (Comment == (On ? "/* clang-format on */" : "/* clang-format off */"))
3895+
return true;
3896+
3897+
static const char ClangFormatOn[] = "// clang-format on";
3898+
static const char ClangFormatOff[] = "// clang-format off";
3899+
const unsigned Size = (On ? sizeof ClangFormatOn : sizeof ClangFormatOff) - 1;
3900+
3901+
return Comment.startswith(On ? ClangFormatOn : ClangFormatOff) &&
3902+
(Comment.size() == Size || Comment[Size] == ':');
3903+
}
3904+
3905+
bool isClangFormatOn(StringRef Comment) {
3906+
return isClangFormatOnOff(Comment, /*On=*/true);
3907+
}
3908+
3909+
bool isClangFormatOff(StringRef Comment) {
3910+
return isClangFormatOnOff(Comment, /*On=*/false);
3911+
}
3912+
38963913
} // namespace format
38973914
} // namespace clang

clang/lib/Format/FormatTokenLexer.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1286,17 +1286,13 @@ void FormatTokenLexer::readRawToken(FormatToken &Tok) {
12861286
Tok.Tok.setKind(tok::string_literal);
12871287
}
12881288

1289-
if (Tok.is(tok::comment) && (Tok.TokenText == "// clang-format on" ||
1290-
Tok.TokenText == "/* clang-format on */")) {
1289+
if (Tok.is(tok::comment) && isClangFormatOn(Tok.TokenText))
12911290
FormattingDisabled = false;
1292-
}
12931291

12941292
Tok.Finalized = FormattingDisabled;
12951293

1296-
if (Tok.is(tok::comment) && (Tok.TokenText == "// clang-format off" ||
1297-
Tok.TokenText == "/* clang-format off */")) {
1294+
if (Tok.is(tok::comment) && isClangFormatOff(Tok.TokenText))
12981295
FormattingDisabled = true;
1299-
}
13001296
}
13011297

13021298
void FormatTokenLexer::resetLexer(unsigned Offset) {

clang/lib/Format/IntegerLiteralSeparatorFixer.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ IntegerLiteralSeparatorFixer::process(const Environment &Env,
8787
auto Location = Tok.getLocation();
8888
auto Text = StringRef(SourceMgr.getCharacterData(Location), Length);
8989
if (Tok.is(tok::comment)) {
90-
if (Text == "// clang-format off" || Text == "/* clang-format off */")
90+
if (isClangFormatOff(Text))
9191
Skip = true;
92-
else if (Text == "// clang-format on" || Text == "/* clang-format on */")
92+
else if (isClangFormatOn(Text))
9393
Skip = false;
9494
continue;
9595
}

clang/lib/Format/SortJavaScriptImports.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,7 @@ class JavaScriptImportSorter : public TokenAnalyzer {
195195
// Separate references from the main code body of the file.
196196
if (FirstNonImportLine && FirstNonImportLine->First->NewlinesBefore < 2 &&
197197
!(FirstNonImportLine->First->is(tok::comment) &&
198-
FirstNonImportLine->First->TokenText.trim() ==
199-
"// clang-format on")) {
198+
isClangFormatOn(FirstNonImportLine->First->TokenText.trim()))) {
200199
ReferencesText += "\n";
201200
}
202201

@@ -376,9 +375,9 @@ class JavaScriptImportSorter : public TokenAnalyzer {
376375
// This is tracked in FormattingOff here and on JsModuleReference.
377376
while (Current && Current->is(tok::comment)) {
378377
StringRef CommentText = Current->TokenText.trim();
379-
if (CommentText == "// clang-format off") {
378+
if (isClangFormatOff(CommentText)) {
380379
FormattingOff = true;
381-
} else if (CommentText == "// clang-format on") {
380+
} else if (isClangFormatOn(CommentText)) {
382381
FormattingOff = false;
383382
// Special case: consider a trailing "clang-format on" line to be part
384383
// of the module reference, so that it gets moved around together with

clang/unittests/Format/FormatTest.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22620,6 +22620,28 @@ TEST_F(FormatTest, DisableRegions) {
2262022620
"/* clang-format off */\n"
2262122621
"/* long long long long long long line */\n",
2262222622
getLLVMStyleWithColumns(20)));
22623+
22624+
verifyFormat("int *i;\n"
22625+
"// clang-format off:\n"
22626+
"int* j;\n"
22627+
"// clang-format on: 1\n"
22628+
"int *k;",
22629+
"int* i;\n"
22630+
"// clang-format off:\n"
22631+
"int* j;\n"
22632+
"// clang-format on: 1\n"
22633+
"int* k;");
22634+
22635+
verifyFormat("int *i;\n"
22636+
"// clang-format off:0\n"
22637+
"int* j;\n"
22638+
"// clang-format only\n"
22639+
"int* k;",
22640+
"int* i;\n"
22641+
"// clang-format off:0\n"
22642+
"int* j;\n"
22643+
"// clang-format only\n"
22644+
"int* k;");
2262322645
}
2262422646

2262522647
TEST_F(FormatTest, DoNotCrashOnInvalidInput) {

0 commit comments

Comments
 (0)