diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -113,8 +113,10 @@ Clang-format understands also special comments that switch formatting in a delimited range. The code between a comment ``// clang-format off`` or ``/* clang-format off */`` up to a comment ``// clang-format on`` or -``/* clang-format on */`` will not be formatted. The comments themselves -will be formatted (aligned) normally. +``/* clang-format on */`` will not be formatted. The comments themselves will be +formatted (aligned) normally. Also, a colon (``:``) and additional text may +follow ``// clang-format off`` or `` clang-format on`` to explain why +clang-format is turned off or back on. .. code-block:: c++ diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -4599,6 +4599,9 @@ } } +bool isClangFormatOn(StringRef Comment); +bool isClangFormatOff(StringRef Comment); + } // end namespace format } // end namespace clang diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -3002,13 +3002,10 @@ if (Trimmed.contains(RawStringTermination)) FormattingOff = false; - if (Trimmed == "// clang-format off" || - Trimmed == "/* clang-format off */") { + if (isClangFormatOff(Trimmed)) FormattingOff = true; - } else if (Trimmed == "// clang-format on" || - Trimmed == "/* clang-format on */") { + else if (isClangFormatOn(Trimmed)) FormattingOff = false; - } const bool EmptyLineSkipped = Trimmed.empty() && @@ -3185,9 +3182,9 @@ Code.substr(Prev, (Pos != StringRef::npos ? Pos : Code.size()) - Prev); StringRef Trimmed = Line.trim(); - if (Trimmed == "// clang-format off") + if (isClangFormatOff(Trimmed)) FormattingOff = true; - else if (Trimmed == "// clang-format on") + else if (isClangFormatOn(Trimmed)) FormattingOff = false; if (ImportRegex.match(Line, &Matches)) { @@ -3893,5 +3890,25 @@ return FallbackStyle; } +static bool isClangFormatOnOff(StringRef Comment, bool On) { + if (Comment == (On ? "/* clang-format on */" : "/* clang-format off */")) + return true; + + static const char ClangFormatOn[] = "// clang-format on"; + static const char ClangFormatOff[] = "// clang-format off"; + const unsigned Size = (On ? sizeof ClangFormatOn : sizeof ClangFormatOff) - 1; + + return Comment.startswith(On ? ClangFormatOn : ClangFormatOff) && + (Comment.size() == Size || Comment[Size] == ':'); +} + +bool isClangFormatOn(StringRef Comment) { + return isClangFormatOnOff(Comment, /*On=*/true); +} + +bool isClangFormatOff(StringRef Comment) { + return isClangFormatOnOff(Comment, /*On=*/false); +} + } // namespace format } // namespace clang diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp --- a/clang/lib/Format/FormatTokenLexer.cpp +++ b/clang/lib/Format/FormatTokenLexer.cpp @@ -1286,17 +1286,13 @@ Tok.Tok.setKind(tok::string_literal); } - if (Tok.is(tok::comment) && (Tok.TokenText == "// clang-format on" || - Tok.TokenText == "/* clang-format on */")) { + if (Tok.is(tok::comment) && isClangFormatOn(Tok.TokenText)) FormattingDisabled = false; - } Tok.Finalized = FormattingDisabled; - if (Tok.is(tok::comment) && (Tok.TokenText == "// clang-format off" || - Tok.TokenText == "/* clang-format off */")) { + if (Tok.is(tok::comment) && isClangFormatOff(Tok.TokenText)) FormattingDisabled = true; - } } void FormatTokenLexer::resetLexer(unsigned Offset) { diff --git a/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp b/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp --- a/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp +++ b/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp @@ -87,9 +87,9 @@ auto Location = Tok.getLocation(); auto Text = StringRef(SourceMgr.getCharacterData(Location), Length); if (Tok.is(tok::comment)) { - if (Text == "// clang-format off" || Text == "/* clang-format off */") + if (isClangFormatOff(Text)) Skip = true; - else if (Text == "// clang-format on" || Text == "/* clang-format on */") + else if (isClangFormatOn(Text)) Skip = false; continue; } diff --git a/clang/lib/Format/SortJavaScriptImports.cpp b/clang/lib/Format/SortJavaScriptImports.cpp --- a/clang/lib/Format/SortJavaScriptImports.cpp +++ b/clang/lib/Format/SortJavaScriptImports.cpp @@ -195,8 +195,7 @@ // Separate references from the main code body of the file. if (FirstNonImportLine && FirstNonImportLine->First->NewlinesBefore < 2 && !(FirstNonImportLine->First->is(tok::comment) && - FirstNonImportLine->First->TokenText.trim() == - "// clang-format on")) { + isClangFormatOn(FirstNonImportLine->First->TokenText.trim()))) { ReferencesText += "\n"; } @@ -376,9 +375,9 @@ // This is tracked in FormattingOff here and on JsModuleReference. while (Current && Current->is(tok::comment)) { StringRef CommentText = Current->TokenText.trim(); - if (CommentText == "// clang-format off") { + if (isClangFormatOff(CommentText)) { FormattingOff = true; - } else if (CommentText == "// clang-format on") { + } else if (isClangFormatOn(CommentText)) { FormattingOff = false; // Special case: consider a trailing "clang-format on" line to be part // of the module reference, so that it gets moved around together with diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -22620,6 +22620,28 @@ "/* clang-format off */\n" "/* long long long long long long line */\n", getLLVMStyleWithColumns(20))); + + verifyFormat("int *i;\n" + "// clang-format off:\n" + "int* j;\n" + "// clang-format on: 1\n" + "int *k;", + "int* i;\n" + "// clang-format off:\n" + "int* j;\n" + "// clang-format on: 1\n" + "int* k;"); + + verifyFormat("int *i;\n" + "// clang-format off:0\n" + "int* j;\n" + "// clang-format only\n" + "int* k;", + "int* i;\n" + "// clang-format off:0\n" + "int* j;\n" + "// clang-format only\n" + "int* k;"); } TEST_F(FormatTest, DoNotCrashOnInvalidInput) {