]> granicus.if.org Git - clang/commitdiff
clang-format: distinguish ObjC call subexpressions after r355434
authorKrasimir Georgiev <krasimir@google.com>
Mon, 11 Mar 2019 16:02:52 +0000 (16:02 +0000)
committerKrasimir Georgiev <krasimir@google.com>
Mon, 11 Mar 2019 16:02:52 +0000 (16:02 +0000)
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

lib/Format/UnwrappedLineParser.cpp
unittests/Format/FormatTestObjC.cpp

index de7e646b17cf9b8db98983cff0b86713010f99fd..3abb6f40d7474275470dac2a6da662dcf3def7ea 100644 (file)
@@ -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:
index ef725a81a885569e704c3d23620e8c7baf88f430..e1a95dbb39541d33d7309dc97e83349dcf3a5ea4 100644 (file)
@@ -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