From 57fae474a57c5a09403fc1e8ee98a28afad260f5 Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Mon, 19 Jun 2017 07:40:49 +0000 Subject: clang-format: Handle "if constexpr". c++1z adds the following constructions to the language: if constexpr (cond) statement1; else if constexpr (cond) statement2; else if constexpr (cond) statement3; else statement4; A first version of this was proposed in reviews.llvm.org/D26953 by Francis Visoiu Mistrih, but never commited. This patch additionally fixes the behavior when allowing short if statements on a single line and was authored by Jacob Bandes-Storch. Thank you to both authors. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@305666 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/ContinuationIndenter.cpp | 3 +- lib/Format/TokenAnnotator.cpp | 7 +++- lib/Format/UnwrappedLineParser.cpp | 2 ++ unittests/Format/FormatTest.cpp | 68 +++++++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 2 deletions(-) diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp index 387923031f..bc8fe54995 100644 --- a/lib/Format/ContinuationIndenter.cpp +++ b/lib/Format/ContinuationIndenter.cpp @@ -453,7 +453,8 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun, State.Column += Spaces; if (Current.isNot(tok::comment) && Previous.is(tok::l_paren) && Previous.Previous && - Previous.Previous->isOneOf(tok::kw_if, tok::kw_for)) { + (Previous.Previous->isOneOf(tok::kw_if, tok::kw_for) || + Previous.Previous->endsSequence(tok::kw_constexpr, tok::kw_if))) { // Treat the condition inside an if as if it was a second function // parameter, i.e. let nested calls have a continuation indent. State.Stack.back().LastSpace = State.Column; diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index 7ce699cf14..5e50268e3a 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -145,6 +145,7 @@ private: (Left->Previous->isOneOf(tok::kw_static_assert, tok::kw_decltype, tok::kw_if, tok::kw_while, tok::l_paren, tok::comma) || + Left->Previous->endsSequence(tok::kw_constexpr, tok::kw_if) || Left->Previous->is(TT_BinaryOperator))) { // static_assert, if and while usually contain expressions. Contexts.back().IsExpression = true; @@ -572,6 +573,8 @@ private: break; case tok::kw_if: case tok::kw_while: + if (Tok->is(tok::kw_if) && CurrentToken && CurrentToken->is(tok::kw_constexpr)) + next(); if (CurrentToken && CurrentToken->is(tok::l_paren)) { next(); if (!parseParens(/*LookForDecls=*/true)) @@ -2060,7 +2063,8 @@ unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line, Style.AlignAfterOpenBracket != FormatStyle::BAS_DontAlign) return 100; if (Left.is(tok::l_paren) && Left.Previous && - Left.Previous->isOneOf(tok::kw_if, tok::kw_for)) + (Left.Previous->isOneOf(tok::kw_if, tok::kw_for) + || Left.Previous->endsSequence(tok::kw_constexpr, tok::kw_if))) return 1000; if (Left.is(tok::equal) && InFunctionDecl) return 110; @@ -2211,6 +2215,7 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, (Left.isOneOf(tok::kw_if, tok::pp_elif, tok::kw_for, tok::kw_while, tok::kw_switch, tok::kw_case, TT_ForEachMacro, TT_ObjCForIn) || + Left.endsSequence(tok::kw_constexpr, tok::kw_if) || (Left.isOneOf(tok::kw_try, Keywords.kw___except, tok::kw_catch, tok::kw_new, tok::kw_delete) && (!Left.Previous || Left.Previous->isNot(tok::period))))) || diff --git a/lib/Format/UnwrappedLineParser.cpp b/lib/Format/UnwrappedLineParser.cpp index 27436dda67..f7678bb6b2 100644 --- a/lib/Format/UnwrappedLineParser.cpp +++ b/lib/Format/UnwrappedLineParser.cpp @@ -1516,6 +1516,8 @@ void UnwrappedLineParser::parseSquare() { void UnwrappedLineParser::parseIfThenElse() { assert(FormatTok->Tok.is(tok::kw_if) && "'if' expected"); nextToken(); + if (FormatTok->Tok.is(tok::kw_constexpr)) + nextToken(); if (FormatTok->Tok.is(tok::l_paren)) parseParens(); bool NeedsUnwrappedLine = false; diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index f493717103..e6cd617abd 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -341,6 +341,18 @@ TEST_F(FormatTest, FormatIfWithoutCompoundStatement) { verifyFormat("if (true)\n f();\ng();"); verifyFormat("if (a)\n if (b)\n if (c)\n g();\nh();"); verifyFormat("if (a)\n if (b) {\n f();\n }\ng();"); + verifyFormat("if constexpr (true)\n" + " f();\ng();"); + verifyFormat("if constexpr (a)\n" + " if constexpr (b)\n" + " if constexpr (c)\n" + " g();\n" + "h();"); + verifyFormat("if constexpr (a)\n" + " if constexpr (b) {\n" + " f();\n" + " }\n" + "g();"); FormatStyle AllowsMergedIf = getLLVMStyle(); AllowsMergedIf.AlignEscapedNewlines = FormatStyle::ENAS_Left; @@ -423,9 +435,11 @@ TEST_F(FormatTest, FormatShortBracedStatements) { AllowSimpleBracedStatements.AllowShortLoopsOnASingleLine = true; verifyFormat("if (true) {}", AllowSimpleBracedStatements); + verifyFormat("if constexpr (true) {}", AllowSimpleBracedStatements); verifyFormat("while (true) {}", AllowSimpleBracedStatements); verifyFormat("for (;;) {}", AllowSimpleBracedStatements); verifyFormat("if (true) { f(); }", AllowSimpleBracedStatements); + verifyFormat("if constexpr (true) { f(); }", AllowSimpleBracedStatements); verifyFormat("while (true) { f(); }", AllowSimpleBracedStatements); verifyFormat("for (;;) { f(); }", AllowSimpleBracedStatements); verifyFormat("if (true) { //\n" @@ -496,6 +510,19 @@ TEST_F(FormatTest, ParseIfElse) { "else {\n" " i();\n" "}"); + verifyFormat("if (true)\n" + " if constexpr (true)\n" + " if (true) {\n" + " if constexpr (true)\n" + " f();\n" + " } else {\n" + " g();\n" + " }\n" + " else\n" + " h();\n" + "else {\n" + " i();\n" + "}"); verifyFormat("void f() {\n" " if (a) {\n" " } else {\n" @@ -511,6 +538,12 @@ TEST_F(FormatTest, ElseIf) { " g();\n" "else\n" " h();"); + verifyFormat("if constexpr (a)\n" + " f();\n" + "else if constexpr (b)\n" + " g();\n" + "else\n" + " h();"); verifyFormat("if (a) {\n" " f();\n" "}\n" @@ -528,6 +561,11 @@ TEST_F(FormatTest, ElseIf) { " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) {\n" "}", getLLVMStyleWithColumns(62)); + verifyFormat("if (a) {\n" + "} else if constexpr (\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) {\n" + "}", + getLLVMStyleWithColumns(62)); } TEST_F(FormatTest, FormatsForLoop) { @@ -2506,6 +2544,9 @@ TEST_F(FormatTest, LineBreakingInBinaryExpressions) { verifyFormat("if ((aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ||\n" " bbbbbbbbbbbbbbbbbb) && // aaaaaaaaaaaaaaaa\n" " cccccc) {\n}"); + verifyFormat("if constexpr ((aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ||\n" + " bbbbbbbbbbbbbbbbbb) && // aaaaaaaaaaa\n" + " cccccc) {\n}"); verifyFormat("b = a &&\n" " // Comment\n" " b.c && d;"); @@ -6627,6 +6668,10 @@ TEST_F(FormatTest, MergeHandlingInTheFaceOfPreprocessorDirectives) { " if (true) continue;\n" "}", ShortMergedIf); + ShortMergedIf.ColumnLimit = 33; + verifyFormat("#define A \\\n" + " if constexpr (true) return 42;", + ShortMergedIf); ShortMergedIf.ColumnLimit = 29; verifyFormat("#define A \\\n" " if (aaaaaaaaaa) return 1; \\\n" @@ -6638,6 +6683,11 @@ TEST_F(FormatTest, MergeHandlingInTheFaceOfPreprocessorDirectives) { " return 1; \\\n" " return 2;", ShortMergedIf); + verifyFormat("#define A \\\n" + " if constexpr (aaaaaaa) \\\n" + " return 1; \\\n" + " return 2;", + ShortMergedIf); } TEST_F(FormatTest, FormatStarDependingOnContext) { @@ -8845,11 +8895,24 @@ TEST_F(FormatTest, AllmanBraceBreaking) { " }\n" "}\n", BreakBeforeBraceShortIfs); + verifyFormat("void f(bool b)\n" + "{\n" + " if constexpr (b)\n" + " {\n" + " return;\n" + " }\n" + "}\n", + BreakBeforeBraceShortIfs); verifyFormat("void f(bool b)\n" "{\n" " if (b) return;\n" "}\n", BreakBeforeBraceShortIfs); + verifyFormat("void f(bool b)\n" + "{\n" + " if constexpr (b) return;\n" + "}\n", + BreakBeforeBraceShortIfs); verifyFormat("void f(bool b)\n" "{\n" " while (b)\n" @@ -10098,6 +10161,11 @@ TEST_F(FormatTest, FormatsLambdas) { " doo_dah();\n" " })) {\n" "}"); + verifyFormat("if constexpr (blah_blah(whatever, whatever, [] {\n" + " doo_dah();\n" + " doo_dah();\n" + " })) {\n" + "}"); verifyFormat("auto lambda = []() {\n" " int a = 2\n" "#if A\n" -- cgit v1.2.3