From: Krasimir Georgiev Date: Mon, 11 Mar 2019 16:02:52 +0000 (+0000) Subject: clang-format: distinguish ObjC call subexpressions after r355434 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=cf1bcf5fdc1669bc01f35dba7fe793c6bb60588b;p=clang clang-format: distinguish ObjC call subexpressions after r355434 Summary: The revision r355434 had the unfortunate side-effect that it started to recognize certain ObjC expressions with a call subexpression followed by a `a->b` subexpression as C++ lambda expressions. This patch adds a bit of logic to handle these cases and documents them in tests. The commented-out test cases in the new test suite are ones that were problematic before r355434. Reviewers: MyDeveloperDay, gribozavr Reviewed By: MyDeveloperDay, gribozavr Subscribers: MyDeveloperDay, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D59210 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@355831 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Format/UnwrappedLineParser.cpp b/lib/Format/UnwrappedLineParser.cpp index de7e646b17..3abb6f40d7 100644 --- a/lib/Format/UnwrappedLineParser.cpp +++ b/lib/Format/UnwrappedLineParser.cpp @@ -1401,6 +1401,8 @@ bool UnwrappedLineParser::tryToParseLambda() { if (!tryToParseLambdaIntroducer()) return false; + bool SeenArrow = false; + while (FormatTok->isNot(tok::l_brace)) { if (FormatTok->isSimpleTypeSpecifier()) { nextToken(); @@ -1423,8 +1425,19 @@ bool UnwrappedLineParser::tryToParseLambda() { case tok::coloncolon: case tok::kw_mutable: case tok::kw_noexcept: + nextToken(); + break; // Specialization of a template with an integer parameter can contain // arithmetic, logical, comparison and ternary operators. + // + // FIXME: This also accepts sequences of operators that are not in the scope + // of a template argument list. + // + // In a C++ lambda a template type can only occur after an arrow. We use + // this as an heuristic to distinguish between Objective-C expressions + // followed by an `a->b` expression, such as: + // ([obj func:arg] + a->b) + // Otherwise the code below would parse as a lambda. case tok::plus: case tok::minus: case tok::exclaim: @@ -1444,13 +1457,17 @@ bool UnwrappedLineParser::tryToParseLambda() { case tok::colon: case tok::kw_true: case tok::kw_false: - nextToken(); - break; + if (SeenArrow) { + nextToken(); + break; + } + return true; case tok::arrow: // This might or might not actually be a lambda arrow (this could be an // ObjC method invocation followed by a dereferencing arrow). We might // reset this back to TT_Unknown in TokenAnnotator. FormatTok->Type = TT_LambdaArrow; + SeenArrow = true; nextToken(); break; default: diff --git a/unittests/Format/FormatTestObjC.cpp b/unittests/Format/FormatTestObjC.cpp index ef725a81a8..e1a95dbb39 100644 --- a/unittests/Format/FormatTestObjC.cpp +++ b/unittests/Format/FormatTestObjC.cpp @@ -1329,6 +1329,34 @@ TEST_F(FormatTestObjC, AlwaysBreakBeforeMultilineStrings) { " @\"fffff\"];"); } +TEST_F(FormatTestObjC, DisambiguatesCallsFromCppLambdas) { + verifyFormat("x = ([a foo:bar] && b->c == 'd');"); + verifyFormat("x = ([a foo:bar] + b->c == 'd');"); + verifyFormat("x = ([a foo:bar] + !b->c == 'd');"); + verifyFormat("x = ([a foo:bar] + ~b->c == 'd');"); + verifyFormat("x = ([a foo:bar] - b->c == 'd');"); + verifyFormat("x = ([a foo:bar] / b->c == 'd');"); + verifyFormat("x = ([a foo:bar] % b->c == 'd');"); + verifyFormat("x = ([a foo:bar] | b->c == 'd');"); + verifyFormat("x = ([a foo:bar] || b->c == 'd');"); + verifyFormat("x = ([a foo:bar] && b->c == 'd');"); + verifyFormat("x = ([a foo:bar] == b->c == 'd');"); + verifyFormat("x = ([a foo:bar] != b->c == 'd');"); + verifyFormat("x = ([a foo:bar] <= b->c == 'd');"); + verifyFormat("x = ([a foo:bar] >= b->c == 'd');"); + verifyFormat("x = ([a foo:bar] << b->c == 'd');"); + verifyFormat("x = ([a foo:bar] ? b->c == 'd' : 'e');"); + // FIXME: The following are wrongly classified as C++ lambda expressions. + // For example this code: + // x = ([a foo:bar] & b->c == 'd'); + // is formatted as: + // x = ([a foo:bar] & b -> c == 'd'); + // verifyFormat("x = ([a foo:bar] & b->c == 'd');"); + // verifyFormat("x = ([a foo:bar] > b->c == 'd');"); + // verifyFormat("x = ([a foo:bar] < b->c == 'd');"); + // verifyFormat("x = ([a foo:bar] >> b->c == 'd');"); +} + } // end namespace } // end namespace format } // end namespace clang