diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index b570171d032ac..766916911bb66 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -1068,6 +1068,7 @@ struct AdditionalKeywords { kw_interface = &IdentTable.get("interface"); kw_native = &IdentTable.get("native"); kw_package = &IdentTable.get("package"); + kw_record = &IdentTable.get("record"); kw_synchronized = &IdentTable.get("synchronized"); kw_throws = &IdentTable.get("throws"); kw___except = &IdentTable.get("__except"); @@ -1417,6 +1418,7 @@ struct AdditionalKeywords { IdentifierInfo *kw_interface; IdentifierInfo *kw_native; IdentifierInfo *kw_package; + IdentifierInfo *kw_record; IdentifierInfo *kw_synchronized; IdentifierInfo *kw_throws; diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index ae33f4f6987ca..2e138ff7ef59c 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -1704,6 +1704,11 @@ void UnwrappedLineParser::parseStructuralElement( *HasLabel = true; return; } + if (Style.isJava() && FormatTok->is(Keywords.kw_record)) { + parseRecord(/*ParseAsExpr=*/false, /*IsJavaRecord=*/true); + addUnwrappedLine(); + return; + } // In all other cases, parse the declaration. break; default: @@ -3996,11 +4001,13 @@ void UnwrappedLineParser::parseJavaEnumBody() { addUnwrappedLine(); } -void UnwrappedLineParser::parseRecord(bool ParseAsExpr) { +void UnwrappedLineParser::parseRecord(bool ParseAsExpr, bool IsJavaRecord) { + assert(!IsJavaRecord || FormatTok->is(Keywords.kw_record)); const FormatToken &InitialToken = *FormatTok; nextToken(); - FormatToken *ClassName = nullptr; + FormatToken *ClassName = + IsJavaRecord && FormatTok->is(tok::identifier) ? FormatTok : nullptr; bool IsDerived = false; auto IsNonMacroIdentifier = [](const FormatToken *Tok) { return Tok->is(tok::identifier) && Tok->TokenText != Tok->TokenText.upper(); @@ -4035,7 +4042,7 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr) { switch (FormatTok->Tok.getKind()) { case tok::l_paren: // We can have macros in between 'class' and the class name. - if (!IsNonMacroIdentifier(Previous) || + if (IsJavaRecord || !IsNonMacroIdentifier(Previous) || // e.g. `struct macro(a) S { int i; };` Previous->Previous == &InitialToken) { parseParens(); diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h index 3c5d2c3e030e5..2d1492c1a6b8c 100644 --- a/clang/lib/Format/UnwrappedLineParser.h +++ b/clang/lib/Format/UnwrappedLineParser.h @@ -177,7 +177,7 @@ class UnwrappedLineParser { // Parses a record (aka class) as a top level element. If ParseAsExpr is true, // parses the record as a child block, i.e. if the class declaration is an // expression. - void parseRecord(bool ParseAsExpr = false); + void parseRecord(bool ParseAsExpr = false, bool IsJavaRecord = false); void parseObjCLightweightGenerics(); void parseObjCMethod(); void parseObjCProtocolList(); diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp index 7982ccb167b53..f39468424a393 100644 --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -3792,6 +3792,17 @@ TEST_F(TokenAnnotatorTest, SwitchExpression) { EXPECT_TOKEN(Tokens[20], tok::arrow, TT_CaseLabelArrow); } +TEST_F(TokenAnnotatorTest, JavaRecord) { + auto Tokens = annotate("public record MyRecord() {}", + getLLVMStyle(FormatStyle::LK_Java)); + ASSERT_EQ(Tokens.size(), 8u) << Tokens; + EXPECT_TOKEN(Tokens[2], tok::identifier, TT_ClassHeadName); + // Not TT_FunctionDeclarationLParen. + EXPECT_TOKEN(Tokens[3], tok::l_paren, TT_Unknown); + EXPECT_TOKEN(Tokens[5], tok::l_brace, TT_RecordLBrace); + EXPECT_TOKEN(Tokens[6], tok::r_brace, TT_RecordRBrace); +} + TEST_F(TokenAnnotatorTest, CppAltOperatorKeywords) { auto Tokens = annotate("a = b and c;"); ASSERT_EQ(Tokens.size(), 7u) << Tokens;