[SourceKit] Introduce "source.lang.swift.expr.tuple" (SR-5977) (#12089)

diff --git a/include/swift/IDE/SyntaxModel.h b/include/swift/IDE/SyntaxModel.h
index 138ce92..6e223c4 100644
--- a/include/swift/IDE/SyntaxModel.h
+++ b/include/swift/IDE/SyntaxModel.h
@@ -111,6 +111,7 @@
   ArrayExpression,
   DictionaryExpression,
   ObjectLiteralExpression,
+  TupleExpression
 };
 
 enum class SyntaxStructureElementKind : uint8_t {
diff --git a/lib/IDE/SyntaxModel.cpp b/lib/IDE/SyntaxModel.cpp
index d6465b0..d43c0bc 100644
--- a/lib/IDE/SyntaxModel.cpp
+++ b/lib/IDE/SyntaxModel.cpp
@@ -507,10 +507,24 @@
     SN.BodyRange = innerCharSourceRangeFromSourceRange(SM, E->getSourceRange());
     pushStructureNode(SN, E);
   } else if (auto *Tup = dyn_cast<TupleExpr>(E)) {
-    for (unsigned I = 0; I < Tup->getNumElements(); ++ I) {
-      SourceLoc NameLoc = Tup->getElementNameLoc(I);
-      if (NameLoc.isValid())
-        passTokenNodesUntil(NameLoc, PassNodesBehavior::ExcludeNodeAtLocation);
+    if (isCurrentCallArgExpr(Tup)) {
+      for (unsigned I = 0; I < Tup->getNumElements(); ++ I) {
+        SourceLoc NameLoc = Tup->getElementNameLoc(I);
+        if (NameLoc.isValid())
+          passTokenNodesUntil(NameLoc, PassNodesBehavior::ExcludeNodeAtLocation);
+      }
+    } else {
+      SyntaxStructureNode SN;
+      SN.Kind = SyntaxStructureKind::TupleExpression;
+      SN.Range = charSourceRangeFromSourceRange(SM, Tup->getSourceRange());
+      SN.BodyRange = innerCharSourceRangeFromSourceRange(SM,
+                                                         Tup->getSourceRange());
+
+      for (auto *Elem : Tup->getElements()) {
+        addExprElem(Elem, SN);
+      }
+
+      pushStructureNode(SN, Tup);
     }
   }
 
diff --git a/test/IDE/structure.swift b/test/IDE/structure.swift
index 336610d..4274fd2 100644
--- a/test/IDE/structure.swift
+++ b/test/IDE/structure.swift
@@ -232,3 +232,21 @@
 
 a.b(c: d?.e?.f, g: h)
 // CHECK: <call><name>a.b</name>(<arg><name>c</name>: d?.e?.f</arg>, <arg><name>g</name>: h</arg>)</call>
+
+struct Tuples {
+  var foo: (Int, String) {
+    return (1, "test")
+    // CHECK: <tuple>(<elem-expr>1</elem-expr>, <elem-expr>"test"</elem-expr>)</tuple>
+  }
+  
+  func foo2() {
+    foo3(x: (1, 20))
+    // CHECK: <call><name>foo3</name>(<arg><name>x</name>: <tuple>(<elem-expr>1</elem-expr>, <elem-expr>20</elem-expr>)</tuple></arg>)</call>
+    let y = (x, foo4(a: 0))
+    // CHECK: <lvar>let <name>y</name> = <tuple>(<elem-expr>x</elem-expr>, <elem-expr><call><name>foo4</name>(<arg><name>a</name>: 0</arg>)</call></elem-expr>)</tuple></lvar>
+    
+    let z = (name1: 1, name2: 2)
+    // CHECK: <lvar>let <name>z</name> = <tuple>(name1: <elem-expr>1</elem-expr>, name2: <elem-expr>2</elem-expr>)</tuple></lvar>
+  }
+}
+
diff --git a/tools/SourceKit/include/SourceKit/Core/ProtocolUIDs.def b/tools/SourceKit/include/SourceKit/Core/ProtocolUIDs.def
index 8f019c7..b332d41 100644
--- a/tools/SourceKit/include/SourceKit/Core/ProtocolUIDs.def
+++ b/tools/SourceKit/include/SourceKit/Core/ProtocolUIDs.def
@@ -270,6 +270,7 @@
 KIND(ExprArray, "source.lang.swift.expr.array")
 KIND(ExprDictionary, "source.lang.swift.expr.dictionary")
 KIND(ExprObjectLiteral, "source.lang.swift.expr.object_literal")
+KIND(ExprTuple, "source.lang.swift.expr.tuple")
 KIND(StructureElemId, "source.lang.swift.structure.elem.id")
 KIND(StructureElemExpr, "source.lang.swift.structure.elem.expr")
 KIND(StructureElemInitExpr, "source.lang.swift.structure.elem.init_expr")
diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp
index 3e7ad81..dfb75c6 100644
--- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp
+++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp
@@ -421,6 +421,8 @@
       return KindExprDictionary;
     case SyntaxStructureKind::ObjectLiteralExpression:
       return KindExprObjectLiteral;
+    case SyntaxStructureKind::TupleExpression:
+      return KindExprTuple;
     case SyntaxStructureKind::Argument:
       return KindExprArg;
   }
diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp
index e6f4d81..e2566ce 100644
--- a/tools/swift-ide-test/swift-ide-test.cpp
+++ b/tools/swift-ide-test/swift-ide-test.cpp
@@ -1090,6 +1090,7 @@
       case SyntaxStructureKind::DictionaryExpression: return "dictionary";
       case SyntaxStructureKind::ObjectLiteralExpression:
         return "object-literal-expression";
+      case SyntaxStructureKind::TupleExpression: return "tuple";
     }
     llvm_unreachable("unhandled tag?");
   }