rymiel updated this revision to Diff 485062. rymiel marked an inline comment as done. rymiel retitled this revision from "[WIP][clang-format] Properly handle the C11 _Generic keyword." to "[clang-format] Properly handle the C11 _Generic keyword.". rymiel added a comment. This revision is now accepted and ready to land.
Change indenting behaviour I'm still not sure what I'm doing regarding ContinuationIndenter, since I don't fully understand its mechanisms, so I just followed my usual method of "stick a very specific check in a random spot that makes the correct fields have the correct value", which feels like a hack here. There is probably a much better way to do this, perhaps something with fake parens or something, to actually treat the "controlling expression" and the actual selectors differently, but right now I've just put in a specific check to dedent the selectors to be relative to the _Generic keyword, and not aligned to the opening paren. It is also no surprise that I don't know how to write tests; I included organic examples of cases that are semi-realistic use cases of _Generic, but there are definitely edge cases I don't have listed. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D139211/new/ https://reviews.llvm.org/D139211 Files: clang/lib/Format/ContinuationIndenter.cpp clang/lib/Format/FormatToken.h clang/lib/Format/TokenAnnotator.cpp clang/unittests/Format/FormatTest.cpp clang/unittests/Format/TokenAnnotatorTest.cpp
Index: clang/unittests/Format/TokenAnnotatorTest.cpp =================================================================== --- clang/unittests/Format/TokenAnnotatorTest.cpp +++ clang/unittests/Format/TokenAnnotatorTest.cpp @@ -1136,6 +1136,14 @@ EXPECT_TOKEN(Tokens[1], tok::identifier, TT_FunctionDeclarationName); } +TEST_F(TokenAnnotatorTest, UnderstandsC11GenericSelection) { + auto Tokens = annotate("_Generic(x, int: 1, default: 0)"); + ASSERT_EQ(Tokens.size(), 13u) << Tokens; + EXPECT_TOKEN(Tokens[0], tok::kw__Generic, TT_Unknown); + EXPECT_TOKEN(Tokens[5], tok::colon, TT_GenericSelectionColon); + EXPECT_TOKEN(Tokens[9], tok::colon, TT_GenericSelectionColon); +} + TEST_F(TokenAnnotatorTest, UnderstandsVerilogOperators) { auto Annotate = [this](llvm::StringRef Code) { return annotate(Code, getLLVMStyle(FormatStyle::LK_Verilog)); Index: clang/unittests/Format/FormatTest.cpp =================================================================== --- clang/unittests/Format/FormatTest.cpp +++ clang/unittests/Format/FormatTest.cpp @@ -22996,6 +22996,41 @@ verifyFormat("x = (_Atomic( uint64_t ))&a;", Style); } +TEST_F(FormatTest, C11Generic) { + verifyFormat("_Generic(x, int: 1, default: 0)"); + verifyFormat("#define cbrt(X) _Generic((X), float: cbrtf, default: cbrt)(X)"); + verifyFormat("_Generic(x, const char *: 1, char *const: 16, int: 8);"); + verifyFormat("_Generic(x, int: f1, const int: f2)();"); + verifyFormat("_Generic(x, struct A: 1, void (*)(void): 2);"); + + verifyFormat("_Generic(x,\n" + " float: f,\n" + " default: d,\n" + " long double: ld,\n" + " float _Complex: fc,\n" + " double _Complex: dc,\n" + " long double _Complex: ldc)"); + + FormatStyle Style = getLLVMStyle(); + Style.ColumnLimit = 40; + verifyFormat("#define LIMIT_MAX(T) \\\n" + " _Generic(((T)0), \\\n" + " unsigned int: UINT_MAX, \\\n" + " unsigned long: ULONG_MAX, \\\n" + " unsigned long long: ULLONG_MAX)", + Style); + verifyFormat("_Generic(x,\n" + " struct A: 1,\n" + " void (*)(void): 2);", + Style); + + Style.ContinuationIndentWidth = 2; + verifyFormat("_Generic(x,\n" + " struct A: 1,\n" + " void (*)(void): 2);", + Style); +} + TEST_F(FormatTest, AmbersandInLamda) { // Test case reported in https://bugs.llvm.org/show_bug.cgi?id=41899 FormatStyle AlignStyle = getLLVMStyle(); Index: clang/lib/Format/TokenAnnotator.cpp =================================================================== --- clang/lib/Format/TokenAnnotator.cpp +++ clang/lib/Format/TokenAnnotator.cpp @@ -323,6 +323,10 @@ Contexts.back().IsExpression = false; } else if (OpeningParen.is(TT_RequiresExpressionLParen)) { Contexts.back().IsExpression = false; + } else if (OpeningParen.Previous && + OpeningParen.Previous->is(tok::kw__Generic)) { + Contexts.back().ContextType = Context::C11GenericSelection; + Contexts.back().IsExpression = true; } else if (Line.InPPDirective && (!OpeningParen.Previous || !OpeningParen.Previous->is(tok::identifier))) { @@ -1028,6 +1032,8 @@ } } else if (Contexts.back().ColonIsForRangeExpr) { Tok->setType(TT_RangeBasedForLoopColon); + } else if (Contexts.back().ContextType == Context::C11GenericSelection) { + Tok->setType(TT_GenericSelectionColon); } else if (CurrentToken && CurrentToken->is(tok::numeric_constant)) { Tok->setType(TT_BitFieldColon); } else if (Contexts.size() == 1 && @@ -1623,6 +1629,8 @@ StructArrayInitializer, // Like in `static_cast<int>`. TemplateArgument, + // C11 _Generic selection. + C11GenericSelection, } ContextType = Unknown; }; @@ -3251,7 +3259,8 @@ return 100; } if (Left.is(tok::l_paren) && Left.Previous && - (Left.Previous->is(tok::kw_for) || Left.Previous->isIf())) { + (Left.Previous->isOneOf(tok::kw_for, tok::kw__Generic) || + Left.Previous->isIf())) { return 1000; } if (Left.is(tok::equal) && InFunctionDecl) @@ -4159,6 +4168,8 @@ return false; if (Right.is(TT_CSharpNamedArgumentColon)) return false; + if (Right.is(TT_GenericSelectionColon)) + return false; if (Right.is(TT_BitFieldColon)) { return Style.BitFieldColonSpacing == FormatStyle::BFCS_Both || Style.BitFieldColonSpacing == FormatStyle::BFCS_Before; Index: clang/lib/Format/FormatToken.h =================================================================== --- clang/lib/Format/FormatToken.h +++ clang/lib/Format/FormatToken.h @@ -70,6 +70,8 @@ TYPE(FunctionLBrace) \ TYPE(FunctionLikeOrFreestandingMacro) \ TYPE(FunctionTypeLParen) \ + /* The colons as part of a C11 _Generic selection */ \ + TYPE(GenericSelectionColon) \ /* The colon at the end of a goto label or a case label. Currently only used \ * for Verilog. */ \ TYPE(GotoLabelColon) \ Index: clang/lib/Format/ContinuationIndenter.cpp =================================================================== --- clang/lib/Format/ContinuationIndenter.cpp +++ clang/lib/Format/ContinuationIndenter.cpp @@ -1538,6 +1538,12 @@ std::max(State.Column, NewParenState.Indent), CurrentState.LastSpace); } + // Special case for generic selection expressions, its comma-separated + // expressions are not aligned to the opening paren like regular calls, but + // rather continuation-indented relative to the _Generic keyword. + if (Previous && Previous->endsSequence(tok::l_paren, tok::kw__Generic)) + NewParenState.Indent = CurrentState.LastSpace; + if (Previous && (Previous->getPrecedence() == prec::Assignment || Previous->isOneOf(tok::kw_return, TT_RequiresClause) || @@ -1684,8 +1690,12 @@ (State.Line->Type != LT_ObjCDecl && Style.BinPackParameters) || (State.Line->Type == LT_ObjCDecl && ObjCBinPackProtocolList); + bool GenericSelection = + Current.getPreviousNonComment() && + Current.getPreviousNonComment()->is(tok::kw__Generic); + AvoidBinPacking = - (CurrentState.IsCSharpGenericTypeConstraint) || + (CurrentState.IsCSharpGenericTypeConstraint) || GenericSelection || (Style.isJavaScript() && EndsInComma) || (State.Line->MustBeDeclaration && !BinPackDeclaration) || (!State.Line->MustBeDeclaration && !Style.BinPackArguments) ||
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits