Skip to content

Commit

Permalink
[clang-format] Break an unwrapped line at a K&R C parameter decl
Browse files Browse the repository at this point in the history
Break an unwrapped line before the first parameter declaration in a
K&R C function definition.

This fixes PR51074.

Differential Revision: https://reviews.llvm.org/D106112
  • Loading branch information
owenca committed Jul 19, 2021
1 parent 87039c0 commit 9da70ab
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 5 deletions.
43 changes: 41 additions & 2 deletions clang/lib/Format/UnwrappedLineParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ void UnwrappedLineParser::parseLevel(bool HasOpeningBrace) {
}
LLVM_FALLTHROUGH;
default:
parseStructuralElement();
parseStructuralElement(/*IsTopLevel=*/true);
break;
}
} while (!eof());
Expand Down Expand Up @@ -994,6 +994,33 @@ static bool isJSDeclOrStmt(const AdditionalKeywords &Keywords,
Keywords.kw_import, tok::kw_export);
}

// This function checks whether a token starts the first parameter declaration
// in a K&R C (aka C78) function definition, e.g.:
// int f(a, b)
// short a, b;
// {
// return a + b;
// }
static bool isC78ParameterDecl(const FormatToken *Tok) {
if (!Tok)
return false;

if (!Tok->isOneOf(tok::kw_int, tok::kw_char, tok::kw_float, tok::kw_double,
tok::kw_struct, tok::kw_union, tok::kw_long, tok::kw_short,
tok::kw_unsigned, tok::kw_register, tok::identifier))
return false;

Tok = Tok->Previous;
if (!Tok || Tok->isNot(tok::r_paren))
return false;

Tok = Tok->Previous;
if (!Tok || Tok->isNot(tok::identifier))
return false;

return Tok->Previous && Tok->Previous->isOneOf(tok::l_paren, tok::comma);
}

// readTokenWithJavaScriptASI reads the next token and terminates the current
// line if JavaScript Automatic Semicolon Insertion must
// happen between the current token and the next token.
Expand Down Expand Up @@ -1041,7 +1068,7 @@ void UnwrappedLineParser::readTokenWithJavaScriptASI() {
return addUnwrappedLine();
}

void UnwrappedLineParser::parseStructuralElement() {
void UnwrappedLineParser::parseStructuralElement(bool IsTopLevel) {
assert(!FormatTok->is(tok::l_brace));
if (Style.Language == FormatStyle::LK_TableGen &&
FormatTok->is(tok::pp_include)) {
Expand Down Expand Up @@ -1343,6 +1370,18 @@ void UnwrappedLineParser::parseStructuralElement() {
return;
case tok::l_paren:
parseParens();
// Break the unwrapped line if a K&R C function definition has a parameter
// declaration.
if (!IsTopLevel || !Style.isCpp())
break;
if (!Previous || Previous->isNot(tok::identifier))
break;
if (Previous->Previous && Previous->Previous->is(tok::at))
break;
if (isC78ParameterDecl(FormatTok)) {
addUnwrappedLine();
return;
}
break;
case tok::kw_operator:
nextToken();
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Format/UnwrappedLineParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class UnwrappedLineParser {
void parsePPEndIf();
void parsePPUnknown();
void readTokenWithJavaScriptASI();
void parseStructuralElement();
void parseStructuralElement(bool IsTopLevel = false);
bool tryToParseBracedList();
bool parseBracedList(bool ContinueOnSemicolons = false, bool IsEnum = false,
tok::TokenKind ClosingBraceKind = tok::r_brace);
Expand Down
13 changes: 11 additions & 2 deletions clang/unittests/Format/FormatTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8216,7 +8216,16 @@ TEST_F(FormatTest, ReturnTypeBreakingStyle) {
"f(i)\n"
"{\n"
" return i + 1;\n"
"}\n",
"}",
Style);
verifyFormat("int f(a, b, c);\n" // No break here.
"int\n" // Break here.
"f(a, b, c)\n" // Break here.
"short a, b;\n"
"float c;\n"
"{\n"
" return a + b < c;\n"
"}",
Style);

Style = getGNUStyle();
Expand Down Expand Up @@ -9423,7 +9432,7 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) {
verifyFormat("vector<A(uint64_t) * attr> v;", TypeMacros); // multiplication

FormatStyle CustomQualifier = getLLVMStyle();
// Add indentifers that should not be parsed as a qualifier by default.
// Add identifiers that should not be parsed as a qualifier by default.
CustomQualifier.AttributeMacros.push_back("__my_qualifier");
CustomQualifier.AttributeMacros.push_back("_My_qualifier");
CustomQualifier.AttributeMacros.push_back("my_other_qualifier");
Expand Down

0 comments on commit 9da70ab

Please sign in to comment.