From: Nico Weber Date: Thu, 17 Jan 2013 17:17:19 +0000 (+0000) Subject: Formatter: Get bit tests in ifs right. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5096a44f2ec5ab9e5b801424e04919f4fcf29f1d;p=clang Formatter: Get bit tests in ifs right. It's generally not possible to know if 'a' '*' 'b' is a multiplication expression or a variable declaration with a purely lexer-based approach. The formatter currently uses a heuristic that classifies this token sequence as a multiplication in rhs contexts (after '=' or 'return') and as a declaration else. Because of this, it gets bit tests in ifs, such as "if (a & b)" wrong. However, declarations in ifs always have to be followed by '=', so this patch changes the formatter to classify '&' as an operator if it's at the start of an if statement. Before: if (a& b) if (int* b = f()) Now: if (a & b) if (int* b = f()) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@172731 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index f8e185086d..d1c3a4fa70 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -771,13 +771,28 @@ public: return false; } - bool parseParens() { + bool parseParens(bool LookForDecls = false) { if (CurrentToken == NULL) return false; AnnotatedToken *Left = CurrentToken->Parent; if (CurrentToken->is(tok::caret)) Left->Type = TT_ObjCBlockLParen; while (CurrentToken != NULL) { + // LookForDecls is set when "if (" has been seen. Check for + // 'identifier' '*' 'identifier' followed by not '=' -- this + // '*' has to be a binary operator but determineStarAmpUsage() will + // categorize it as an unary operator, so set the right type here. + if (LookForDecls && !CurrentToken->Children.empty()) { + AnnotatedToken &Prev = *CurrentToken->Parent; + AnnotatedToken &Next = CurrentToken->Children[0]; + if (Prev.Parent->is(tok::identifier) && + (Prev.is(tok::star) || Prev.is(tok::amp)) && + CurrentToken->is(tok::identifier) && Next.isNot(tok::equal)) { + Prev.Type = TT_BinaryOperator; + LookForDecls = false; + } + } + if (CurrentToken->is(tok::r_paren)) { Left->MatchingParen = CurrentToken; CurrentToken->MatchingParen = Left; @@ -896,6 +911,14 @@ public: if (ColonIsObjCMethodExpr) Tok->Type = TT_ObjCMethodExpr; break; + case tok::kw_if: + case tok::kw_while: + if (CurrentToken->is(tok::l_paren)) { + next(); + if (!parseParens(/*LookForDecls=*/true)) + return false; + } + break; case tok::l_paren: { if (!parseParens()) return false; diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 28d9fbe4e2..7b57e858a4 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -1235,6 +1235,14 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { " *A, // Operator detection might be confused by the '{'\n" " *BB // Operator detection might be confused by previous comment\n" "};"); + + verifyFormat("if (int *a = &b)"); + verifyFormat("if (int &a = *b)"); + verifyFormat("if (a & b[i])"); + verifyFormat("if (a::b::c::d & b[i])"); + verifyFormat("if (*b[i])"); + verifyFormat("if (int *a = (&b))"); + verifyFormat("while (int *a = &b)"); } TEST_F(FormatTest, FormatsCasts) {