Index: clang/docs/ClangFormatStyleOptions.rst =================================================================== --- clang/docs/ClangFormatStyleOptions.rst +++ clang/docs/ClangFormatStyleOptions.rst @@ -3758,6 +3758,26 @@ } } +**RemoveSemiColons** (``Boolean``) :versionbadge:`clang-format 16` + Removes unnecessary semicolons from the function braces + + .. warning:: + + Setting this option to `true` could lead to incorrect code formatting due + to clang-format's lack of complete semantic information. As such, extra + care should be taken to review code changes made by this option. + NOTE: + Setting this to false will not add `;` where they were missing + + .. code-block:: c++ + + false: true: + + int max(int a, int b) int max(int a, int b) + { { + return a>b?a:b; return a>b?a:b; + }; }; + **RequiresClausePosition** (``RequiresClausePositionStyle``) :versionbadge:`clang-format 15` The position of the ``requires`` clause. Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -529,6 +529,7 @@ clang-format ------------ +- Add `RemoveSemiColons` option for removing unnecessary `;` after a function definition. clang-extdef-mapping -------------------- Index: clang/include/clang/Format/Format.h =================================================================== --- clang/include/clang/Format/Format.h +++ clang/include/clang/Format/Format.h @@ -3055,6 +3055,26 @@ /// \version 14 bool RemoveBracesLLVM; + /// Removes unnecessary semicolons from the function braces + /// \warning + /// Setting this option to `true` could lead to incorrect code formatting due + /// to clang-format's lack of complete semantic information. As such, extra + /// care should be taken to review code changes made by this option. + /// NOTE: + /// Setting this to false will not add `;` where they were missing + /// \endwarning + /// \code + /// false: true: + /// + /// int max(int a, int b) int max(int a, int b) + /// { { + /// return a>b?a:b; return a>b?a:b; + /// }; } + /// + /// \endcode + /// \version 16 + bool RemoveSemiColons; + /// \brief The possible positions for the requires clause. The /// ``IndentRequires`` option is only used if the ``requires`` is put on the /// start of a line. @@ -3969,6 +3989,7 @@ RawStringFormats == R.RawStringFormats && ReferenceAlignment == R.ReferenceAlignment && RemoveBracesLLVM == R.RemoveBracesLLVM && + RemoveSemiColons == R.RemoveSemiColons && RequiresClausePosition == R.RequiresClausePosition && SeparateDefinitionBlocks == R.SeparateDefinitionBlocks && ShortNamespaceLines == R.ShortNamespaceLines && Index: clang/lib/Format/Format.cpp =================================================================== --- clang/lib/Format/Format.cpp +++ clang/lib/Format/Format.cpp @@ -852,6 +852,7 @@ IO.mapOptional("ReferenceAlignment", Style.ReferenceAlignment); IO.mapOptional("ReflowComments", Style.ReflowComments); IO.mapOptional("RemoveBracesLLVM", Style.RemoveBracesLLVM); + IO.mapOptional("RemoveSemiColons", Style.RemoveSemiColons); IO.mapOptional("RequiresClausePosition", Style.RequiresClausePosition); IO.mapOptional("SeparateDefinitionBlocks", Style.SeparateDefinitionBlocks); IO.mapOptional("ShortNamespaceLines", Style.ShortNamespaceLines); @@ -1297,6 +1298,7 @@ LLVMStyle.UseTab = FormatStyle::UT_Never; LLVMStyle.ReflowComments = true; LLVMStyle.RemoveBracesLLVM = false; + LLVMStyle.RemoveSemiColons = false; LLVMStyle.SpacesInParentheses = false; LLVMStyle.SpacesInSquareBrackets = false; LLVMStyle.SpaceInEmptyBlock = false; @@ -1915,7 +1917,59 @@ Token = Token->Next) { if (!Token->Optional) continue; - assert(Token->isOneOf(tok::l_brace, tok::r_brace)); + if (!Token->isOneOf(tok::l_brace, tok::r_brace)) + continue; + auto Next = Token->Next; + assert(Next || Token == Line->Last); + if (!Next && NextLine) + Next = NextLine->First; + SourceLocation Start; + if (Next && Next->NewlinesBefore == 0 && Next->isNot(tok::eof)) { + Start = Token->Tok.getLocation(); + Next->WhitespaceRange = Token->WhitespaceRange; + } else { + Start = Token->WhitespaceRange.getBegin(); + } + const auto Range = + CharSourceRange::getCharRange(Start, Token->Tok.getEndLoc()); + cantFail(Result.add(tooling::Replacement(SourceMgr, Range, ""))); + } + } + } +}; + +class SemiRemover : public TokenAnalyzer { +public: + SemiRemover(const Environment &Env, const FormatStyle &Style) + : TokenAnalyzer(Env, Style) {} + + std::pair + analyze(TokenAnnotator &Annotator, + SmallVectorImpl &AnnotatedLines, + FormatTokenLexer &Tokens) override { + AffectedRangeMgr.computeAffectedLines(AnnotatedLines); + tooling::Replacements Result; + removeSemi(AnnotatedLines, Result); + return {Result, 0}; + } + +private: + void removeSemi(SmallVectorImpl &Lines, + tooling::Replacements &Result) { + const auto &SourceMgr = Env.getSourceManager(); + const auto End = Lines.end(); + for (auto I = Lines.begin(); I != End; ++I) { + const auto Line = *I; + removeSemi(Line->Children, Result); + if (!Line->Affected) + continue; + const auto NextLine = I + 1 == End ? nullptr : I[1]; + for (auto Token = Line->First; Token && !Token->Finalized; + Token = Token->Next) { + if (!Token->Optional) + continue; + if (!Token->is(tok::semi)) + continue; auto Next = Token->Next; assert(Next || Token == Line->Last); if (!Next && NextLine) @@ -3280,6 +3334,12 @@ }); } + if (Style.RemoveSemiColons) { + Passes.emplace_back([&](const Environment &Env) { + return SemiRemover(Env, Expanded).process(/*SkipAnnotation=*/true); + }); + } + if (Style.FixNamespaceComments) { Passes.emplace_back([&](const Environment &Env) { return NamespaceEndCommentsFixer(Env, Expanded).process(); Index: clang/lib/Format/UnwrappedLineParser.h =================================================================== --- clang/lib/Format/UnwrappedLineParser.h +++ clang/lib/Format/UnwrappedLineParser.h @@ -111,7 +111,8 @@ bool KeepBraces = true, IfStmtKind *IfKind = nullptr, bool UnindentWhitesmithsBraces = false, bool CanContainBracedList = true, - TokenType NextLBracesType = TT_Unknown); + TokenType NextLBracesType = TT_Unknown, + bool IsFunctionBlock = false); void parseChildBlock(bool CanContainBracedList = true, TokenType NextLBracesType = TT_Unknown); void parsePPDirective(); Index: clang/lib/Format/UnwrappedLineParser.cpp =================================================================== --- clang/lib/Format/UnwrappedLineParser.cpp +++ clang/lib/Format/UnwrappedLineParser.cpp @@ -839,7 +839,8 @@ FormatToken *UnwrappedLineParser::parseBlock( bool MustBeDeclaration, unsigned AddLevels, bool MunchSemi, bool KeepBraces, IfStmtKind *IfKind, bool UnindentWhitesmithsBraces, - bool CanContainBracedList, TokenType NextLBracesType) { + bool CanContainBracedList, TokenType NextLBracesType, + bool IsFunctionBlock) { auto HandleVerilogBlockLabel = [this]() { // ":" name if (Style.isVerilog() && FormatTok->is(tok::colon)) { @@ -978,8 +979,17 @@ parseStructuralElement(); } - if (MunchSemi && FormatTok->is(tok::semi)) + // When this is a function block and there is an unnecessary semicolon + // afterwards then mark it as optional (so the RemoveSemi pass can get rid of + // it later). + if (MunchSemi && FormatTok->is(tok::semi)) { + if (IsFunctionBlock) { + FormatToken *Previous = Tokens->getPreviousToken(); + if (Previous && Previous->is(tok::r_brace)) + FormatTok->Optional = true; + } nextToken(); + } if (PPStartHash == PPEndHash) { Line->MatchingOpeningBlockLineIndex = OpeningLineIndex; @@ -1897,7 +1907,11 @@ addUnwrappedLine(); } FormatTok->setFinalizedType(TT_FunctionLBrace); - parseBlock(); + parseBlock(/*MustBeDeclaration=*/false, 1u, /*MunchSemi=*/true, + /*KeepBraces=*/true, nullptr, + /*UnindentWhitesmithsBraces=*/false, + /*CanContainBracedList=*/true, TT_Unknown, + /*IsFunctionBlock=*/true); addUnwrappedLine(); return; } Index: clang/unittests/Format/FormatTest.cpp =================================================================== --- clang/unittests/Format/FormatTest.cpp +++ clang/unittests/Format/FormatTest.cpp @@ -20281,6 +20281,7 @@ CHECK_PARSE_BOOL(Cpp11BracedListStyle); CHECK_PARSE_BOOL(ReflowComments); CHECK_PARSE_BOOL(RemoveBracesLLVM); + CHECK_PARSE_BOOL(RemoveSemiColons); CHECK_PARSE_BOOL(SortUsingDeclarations); CHECK_PARSE_BOOL(SpacesInParentheses); CHECK_PARSE_BOOL(SpacesInSquareBrackets); @@ -26740,6 +26741,22 @@ "inline bool var = is_integral_v && is_signed_v;"); } +TEST_F(FormatTest, RemoveSemiColons) { + FormatStyle Style = getLLVMStyle(); + Style.InsertBraces = true; + + verifyFormat("int max(int a, int b) { return a > b ? a : b; }", + "int max(int a, int b) { return a > b ? a : b; };", Style); + + verifyFormat("class Foo {\n" + " int getSomething() const { return something; }\n" + "};", + "class Foo {\n" + " int getSomething() const { return something; };\n" + "};", + Style); +} + } // namespace } // namespace format } // namespace clang