Merge pull request #5656 from Gankro/xfail

diff --git a/include/swift/Basic/Version.h b/include/swift/Basic/Version.h
index f577923..a8e0add 100644
--- a/include/swift/Basic/Version.h
+++ b/include/swift/Basic/Version.h
@@ -149,6 +149,10 @@
 std::string getSwiftFullVersion(Version effectiveLanguageVersion =
                                 Version::getCurrentLanguageVersion());
 
+/// Retrieves the repository revision number (or identifer) from which                               
+/// this Swift was built.
+std::string getSwiftRevision();
+
 } // end namespace version
 } // end namespace swift
 
diff --git a/include/swift/IDE/Utils.h b/include/swift/IDE/Utils.h
index 25d3139..5b318c9 100644
--- a/include/swift/IDE/Utils.h
+++ b/include/swift/IDE/Utils.h
@@ -198,8 +198,11 @@
 
 enum class RangeKind : int8_t{
   Invalid = -1,
-  Expression,
+  SingleExpression,
   SingleStatement,
+  SingleDecl,
+
+  MultiStatement,
 };
 
 struct ResolvedRangeInfo {
diff --git a/lib/Basic/Version.cpp b/lib/Basic/Version.cpp
index 87a99ca..379c571 100644
--- a/lib/Basic/Version.cpp
+++ b/lib/Basic/Version.cpp
@@ -406,6 +406,14 @@
   return OS.str();
 }
 
+std::string getSwiftRevision() {
+#ifdef SWIFT_REVISION
+  return SWIFT_REVISION;
+#else
+  return "";
+#endif
+}
+
 } // end namespace version
 } // end namespace swift
 
diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp
index 8d17f21..b08eba8 100644
--- a/lib/ClangImporter/ImportName.cpp
+++ b/lib/ClangImporter/ImportName.cpp
@@ -793,9 +793,10 @@
   return baseName;
 }
 
-EffectiveClangContext NameImporter::determineEffectiveContext(
-    const clang::NamedDecl *decl, const clang::DeclContext *dc,
-    ImportNameOptions options, clang::Sema &clangSema) {
+EffectiveClangContext
+NameImporter::determineEffectiveContext(const clang::NamedDecl *decl,
+                                        const clang::DeclContext *dc,
+                                        ImportNameOptions options) {
   EffectiveClangContext res;
 
   // Enumerators can end up within their enclosing enum or in the global
@@ -841,8 +842,7 @@
 
 bool NameImporter::hasNamingConflict(const clang::NamedDecl *decl,
                                      const clang::IdentifierInfo *proposedName,
-                                     const clang::TypedefNameDecl *cfTypedef,
-                                     clang::Sema &clangSema) {
+                                     const clang::TypedefNameDecl *cfTypedef) {
   // Test to see if there is a value with the same name as 'proposedName'
   // in the same module as the decl
   // FIXME: This will miss macros.
@@ -1101,7 +1101,7 @@
 
   // Compute the effective context.
   auto dc = const_cast<clang::DeclContext *>(D->getDeclContext());
-  auto effectiveCtx = determineEffectiveContext(D, dc, options, clangSema);
+  auto effectiveCtx = determineEffectiveContext(D, dc, options);
   if (!effectiveCtx)
     return {};
   result.EffectiveContext = effectiveCtx;
@@ -1455,8 +1455,7 @@
   SmallString<16> baseNameWithProtocolSuffix;
   if (auto objcProto = dyn_cast<clang::ObjCProtocolDecl>(D)) {
     if (objcProto->hasDefinition()) {
-      if (hasNamingConflict(D, objcProto->getIdentifier(), nullptr,
-                            clangSema)) {
+      if (hasNamingConflict(D, objcProto->getIdentifier(), nullptr)) {
         baseNameWithProtocolSuffix = baseName;
         baseNameWithProtocolSuffix += SWIFT_PROTOCOL_SUFFIX;
         baseName = baseNameWithProtocolSuffix;
@@ -1472,7 +1471,7 @@
       auto swiftName = getCFTypeName(typedefNameDecl);
       if (!swiftName.empty() &&
           !hasNamingConflict(D, &clangCtx.Idents.get(swiftName),
-                             typedefNameDecl, clangSema)) {
+                             typedefNameDecl)) {
         // Adopt the requested name.
         baseName = swiftName;
       }
diff --git a/lib/ClangImporter/ImportName.h b/lib/ClangImporter/ImportName.h
index 901771a..7a2e82c 100644
--- a/lib/ClangImporter/ImportName.h
+++ b/lib/ClangImporter/ImportName.h
@@ -226,8 +226,7 @@
   /// the same module as the decl
   bool hasNamingConflict(const clang::NamedDecl *decl,
                          const clang::IdentifierInfo *proposedName,
-                         const clang::TypedefNameDecl *cfTypedef,
-                         clang::Sema &clangSema);
+                         const clang::TypedefNameDecl *cfTypedef);
 
   Optional<ImportedErrorInfo>
   considerErrorImport(const clang::ObjCMethodDecl *clangDecl,
@@ -241,8 +240,7 @@
 
   EffectiveClangContext determineEffectiveContext(const clang::NamedDecl *,
                                                   const clang::DeclContext *,
-                                                  ImportNameOptions options,
-                                                  clang::Sema &clangSema);
+                                                  ImportNameOptions options);
 
   ImportedName importNameImpl(const clang::NamedDecl *,
                               ImportNameOptions options);
diff --git a/lib/IDE/SwiftSourceDocInfo.cpp b/lib/IDE/SwiftSourceDocInfo.cpp
index 29fc717..961a246 100644
--- a/lib/IDE/SwiftSourceDocInfo.cpp
+++ b/lib/IDE/SwiftSourceDocInfo.cpp
@@ -176,35 +176,102 @@
 
 struct RangeResolver::Implementation {
   SourceFile &File;
+private:
+  enum class RangeMatchKind : int8_t {
+    NoneMatch,
+    StartMatch,
+    EndMatch,
+    RangeMatch,
+  };
+
+  struct ContextInfo {
+    ASTNode Parent;
+    std::vector<ASTNode> StartMatches;
+    std::vector<ASTNode> EndMatches;
+    ContextInfo(ASTNode Parent) : Parent(Parent) {}
+  };
+
   SourceLoc Start;
   SourceLoc End;
+  StringRef Content;
   Optional<ResolvedRangeInfo> Result;
-  Implementation(SourceFile &File, SourceLoc Start, SourceLoc End) :
-    File(File), Start(Start), End(End) {}
-
-  bool isRangeMatch(SourceRange Input) {
-    return Input.Start == Start && Input.End == End;
+  std::vector<ContextInfo> ContextStack;
+  ContextInfo &getCurrentDC() {
+    assert(!ContextStack.empty());
+    return ContextStack.back();
   }
 
-  bool shouldEnter(SourceRange Input) {
+  ResolvedRangeInfo getSingleNodeKind(ASTNode Node) {
+    assert(!Node.isNull());
+    if (Node.is<Expr*>())
+      return ResolvedRangeInfo(RangeKind::SingleExpression,
+                               Node.get<Expr*>()->getType(), Content);
+    else if (Node.is<Stmt*>())
+      return ResolvedRangeInfo(RangeKind::SingleStatement, Type(), Content);
+    else {
+      assert(Node.is<Decl*>());
+      return ResolvedRangeInfo(RangeKind::SingleDecl, Type(), Content);
+    }
+  }
+
+public:
+  Implementation(SourceFile &File, SourceLoc Start, SourceLoc End) :
+    File(File), Start(Start), End(End), Content(getContent()) {}
+  ~Implementation() { assert(ContextStack.empty()); }
+  bool hasResult() { return Result.hasValue(); }
+  void enter(ASTNode Node) { ContextStack.emplace_back(Node); }
+  void leave() { ContextStack.pop_back(); }
+
+  void analyze(ASTNode Node) {
+    auto &DCInfo = getCurrentDC();
+    switch (getRangeMatchKind(Node.getSourceRange())) {
+    case RangeMatchKind::NoneMatch:
+      return;
+    case RangeMatchKind::RangeMatch:
+      Result = getSingleNodeKind(Node);
+      return;
+    case RangeMatchKind::StartMatch:
+      DCInfo.StartMatches.emplace_back(Node);
+      break;
+    case RangeMatchKind::EndMatch:
+      DCInfo.EndMatches.emplace_back(Node);
+      break;
+    }
+    if (!DCInfo.StartMatches.empty() && !DCInfo.EndMatches.empty()) {
+      Result = {RangeKind::MultiStatement, Type(), Content};
+      return;
+    }
+  }
+
+  bool shouldEnter(ASTNode Node) {
     SourceManager &SM = File.getASTContext().SourceMgr;
-    if (SM.isBeforeInBuffer(End, Input.Start))
+    if (SM.isBeforeInBuffer(End, Node.getSourceRange().Start))
       return false;
-    if (SM.isBeforeInBuffer(Input.End, Start))
+    if (SM.isBeforeInBuffer(Node.getSourceRange().End, Start))
       return false;
     return true;
   }
 
-  bool hasResult() {
-    return Result.hasValue();
-  }
-
   ResolvedRangeInfo getResult() {
     if (Result.hasValue())
       return Result.getValue();
     return ResolvedRangeInfo(RangeKind::Invalid, Type(), getContent());
   }
 
+private:
+  RangeMatchKind getRangeMatchKind(SourceRange Input) {
+    bool StartMatch = Input.Start == Start;
+    bool EndMatch = Input.End == End;
+    if (StartMatch && EndMatch)
+      return RangeMatchKind::RangeMatch;
+    else if (StartMatch)
+      return RangeMatchKind::StartMatch;
+    else if (EndMatch)
+      return RangeMatchKind::EndMatch;
+    else
+      return RangeMatchKind::NoneMatch;
+  }
+
   StringRef getContent() {
     SourceManager &SM = File.getASTContext().SourceMgr;
     return CharSourceRange(SM, Start, Lexer::getLocForEndOfToken(SM, End)).str();
@@ -217,45 +284,48 @@
 RangeResolver::~RangeResolver() { delete &Impl; }
 
 bool RangeResolver::walkToExprPre(Expr *E) {
-  if (!Impl.shouldEnter(E->getSourceRange()))
+  if (!Impl.shouldEnter(E))
     return false;
-  if (Impl.isRangeMatch(E->getSourceRange())) {
-    Impl.Result = ResolvedRangeInfo(RangeKind::Expression, E->getType(),
-                                    Impl.getContent());
-  }
+  Impl.analyze(E);
+  Impl.enter(E);
   return !Impl.hasResult();
 }
 
 bool RangeResolver::walkToStmtPre(Stmt *S) {
-  if (!Impl.shouldEnter(S->getSourceRange()))
+  if (!Impl.shouldEnter(S))
     return false;
-  if (Impl.isRangeMatch(S->getSourceRange())) {
-    Impl.Result = ResolvedRangeInfo(RangeKind::SingleStatement, Type(),
-                                    Impl.getContent());
-  }
+  Impl.analyze(S);
+  Impl.enter(S);
   return !Impl.hasResult();
 };
 
-
 bool RangeResolver::walkToDeclPre(Decl *D, CharSourceRange Range) {
-  return Impl.shouldEnter(D->getSourceRange());
+  if (!Impl.shouldEnter(D))
+    return false;
+  Impl.analyze(D);
+  Impl.enter(D);
+  return !Impl.hasResult();
 }
 
 bool RangeResolver::walkToExprPost(Expr *E) {
+  Impl.leave();
   return !Impl.hasResult();
 }
 
 bool RangeResolver::walkToStmtPost(Stmt *S) {
+  Impl.leave();
   return !Impl.hasResult();
 };
 
 bool RangeResolver::walkToDeclPost(Decl *D) {
+  Impl.leave();
   return !Impl.hasResult();
 }
 
-ResolvedRangeInfo
-RangeResolver::resolve() {
+ResolvedRangeInfo RangeResolver::resolve() {
+  Impl.enter(ASTNode());
   walk(Impl.File);
+  Impl.leave();
   return Impl.getResult();
 }
 
diff --git a/test/SourceKit/RangeInfo/basic.swift b/test/SourceKit/RangeInfo/basic.swift
index 95f3cac..425eb4a 100644
--- a/test/SourceKit/RangeInfo/basic.swift
+++ b/test/SourceKit/RangeInfo/basic.swift
@@ -1,37 +1,83 @@
 func foo() -> Int{
   var aaa = 1 + 2
   aaa = aaa + 3
-  if aaa = 3 { aaa = 4 }
+  if aaa == 3 { aaa = 4 }
   return aaa
 }
 
+func foo1() -> Int { return 0 }
+class C { func foo() {} }
+struct S { func foo() {} }
+
 // RUN: %sourcekitd-test -req=range -pos=2:13 -length 5 %s -- %s | %FileCheck %s -check-prefix=CHECK1
 
 // RUN: %sourcekitd-test -req=range -pos=3:3 -length 13 %s -- %s | %FileCheck %s -check-prefix=CHECK2
 // RUN: %sourcekitd-test -req=range -pos=3:1 -length 15 %s -- %s | %FileCheck %s -check-prefix=CHECK2
 
-// RUN: %sourcekitd-test -req=range -pos=4:1 -length 24 %s -- %s | %FileCheck %s -check-prefix=CHECK3
 // RUN: %sourcekitd-test -req=range -pos=4:1 -length 25 %s -- %s | %FileCheck %s -check-prefix=CHECK3
 // RUN: %sourcekitd-test -req=range -pos=4:1 -length 26 %s -- %s | %FileCheck %s -check-prefix=CHECK3
-// RUN: %sourcekitd-test -req=range -pos=4:4 -length 21 %s -- %s | %FileCheck %s -check-prefix=CHECK3
+// RUN: %sourcekitd-test -req=range -pos=4:1 -length 27 %s -- %s | %FileCheck %s -check-prefix=CHECK3
+// RUN: %sourcekitd-test -req=range -pos=4:4 -length 22 %s -- %s | %FileCheck %s -check-prefix=CHECK3
 
 // RUN: %sourcekitd-test -req=range -pos=5:1 -length 12 %s -- %s | %FileCheck %s -check-prefix=CHECK4
 // RUN: %sourcekitd-test -req=range -pos=5:2 -length 11 %s -- %s | %FileCheck %s -check-prefix=CHECK4
 // RUN: %sourcekitd-test -req=range -pos=5:5 -length 8 %s -- %s | %FileCheck %s -check-prefix=CHECK4
 // RUN: %sourcekitd-test -req=range -pos=5:5 -length 9 %s -- %s | %FileCheck %s -check-prefix=CHECK4
 
-// CHECK1-DAG: <kind>source.lang.swift.range.expression</kind>
+// RUN: %sourcekitd-test -req=range -pos=8:1 -length 31 %s -- %s | %FileCheck %s -check-prefix=CHECK5
+// RUN: %sourcekitd-test -req=range -pos=9:1 -length 25 %s -- %s | %FileCheck %s -check-prefix=CHECK6
+// RUN: %sourcekitd-test -req=range -pos=10:1 -length 26 %s -- %s | %FileCheck %s -check-prefix=CHECK7
+// RUN: %sourcekitd-test -req=range -pos=3:1 -length 42 %s -- %s | %FileCheck %s -check-prefix=CHECK8
+// RUN: %sourcekitd-test -req=range -pos=3:1 -length 55 %s -- %s | %FileCheck %s -check-prefix=CHECK9
+// RUN: %sourcekitd-test -req=range -pos=4:1 -length 36 %s -- %s | %FileCheck %s -check-prefix=CHECK10
+
+// RUN: %sourcekitd-test -req=range -pos=8:1 -end-pos 8:32 %s -- %s | %FileCheck %s -check-prefix=CHECK5
+// RUN: %sourcekitd-test -req=range -pos=9:1 -end-pos 9:26 %s -- %s | %FileCheck %s -check-prefix=CHECK6
+// RUN: %sourcekitd-test -req=range -pos=10:1 -end-pos 10:27 %s -- %s | %FileCheck %s -check-prefix=CHECK7
+// RUN: %sourcekitd-test -req=range -pos=3:1 -end-pos=4:26 %s -- %s | %FileCheck %s -check-prefix=CHECK8
+// RUN: %sourcekitd-test -req=range -pos=3:1 -end-pos=5:13 %s -- %s | %FileCheck %s -check-prefix=CHECK9
+// RUN: %sourcekitd-test -req=range -pos=4:1 -end-pos=5:13 %s -- %s | %FileCheck %s -check-prefix=CHECK10
+
+// CHECK1-DAG: <kind>source.lang.swift.range.singleexpression</kind>
 // CHECK1-DAG: <content>1 + 2</content>
 // CHECK1-DAG: <type>Int</type>
 
-// CHECK2-DAG: <kind>source.lang.swift.range.expression</kind>
+// CHECK2-DAG: <kind>source.lang.swift.range.singleexpression</kind>
 // CHECK2-DAG: <content>aaa = aaa + 3</content>
 // CHECK2-DAG: <type>()</type>
 
 // CHECK3-DAG: <kind>source.lang.swift.range.singlestatement</kind>
-// CHECK3-DAG: <content>if aaa = 3 { aaa = 4 }</content>
+// CHECK3-DAG: <content>if aaa == 3 { aaa = 4 }</content>
 // CHECK3-DAG: <type></type>
 
 // CHECK4-DAG: <kind>source.lang.swift.range.singlestatement</kind>
 // CHECK4-DAG: <content>return aaa</content>
 // CHECK4-DAG: <type></type>
+
+// CHECK5-DAG: <kind>source.lang.swift.range.singledeclaration</kind>
+// CHECK5-DAG: <content>func foo1() -> Int { return 0 }</content>
+// CHECK5-DAG: <type></type>
+
+// CHECK6-DAG: <kind>source.lang.swift.range.singledeclaration</kind>
+// CHECK6-DAG: <content>class C { func foo() {} }</content>
+// CHECK6-DAG: <type></type>
+
+// CHECK7-DAG: <kind>source.lang.swift.range.singledeclaration</kind>
+// CHECK7-DAG: <content>struct S { func foo() {} }</content>
+// CHECK7-DAG: <type></type>
+
+// CHECK8-DAG: <kind>source.lang.swift.range.multistatement</kind>
+// CHECK8-DAG: <content>aaa = aaa + 3
+// CHECK8-DAG:   if aaa == 3 { aaa = 4 }</content>
+// CHECK8-DAG: <type></type>
+
+// CHECK9-DAG: <kind>source.lang.swift.range.multistatement</kind>
+// CHECK9-DAG: <content>aaa = aaa + 3
+// CHECK9-DAG:   if aaa == 3 { aaa = 4 }
+// CHECK9-DAG:   return aaa</content>
+// CHECK9-DAG: <type></type>
+
+// CHECK10-DAG: <kind>source.lang.swift.range.multistatement</kind>
+// CHECK10-DAG: <content>if aaa == 3 { aaa = 4 }
+// CHECK10-DAG:   return aaa</content>
+// CHECK10-DAG: <type></type>
diff --git a/test/lit.cfg b/test/lit.cfg
index 70ab538..0459030 100644
--- a/test/lit.cfg
+++ b/test/lit.cfg
@@ -976,8 +976,9 @@
 config.substitutions.append(('%line-directive', config.line_directive))
 config.substitutions.append(('%gyb', config.gyb))
 config.substitutions.append(('%rth', config.rth))
-config.substitutions.append(('%scale-test', config.scale_test))
-
+config.substitutions.append(('%scale-test',
+                             '{} --swiftc-binary={} --tmpdir=%t'.format(
+                                 config.scale_test, config.swiftc)))
 config.substitutions.append(('%target-sil-opt', config.target_sil_opt))
 config.substitutions.append(('%target-sil-extract', config.target_sil_extract))
 config.substitutions.append(
diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp
index f7983a8..9f3a45e 100644
--- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp
+++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp
@@ -154,7 +154,11 @@
 static UIdent KindStructureElemTypeRef("source.lang.swift.structure.elem.typeref");
 
 static UIdent KindRangeSingleStatement("source.lang.swift.range.singlestatement");
-static UIdent KindRangeExpression("source.lang.swift.range.expression");
+static UIdent KindRangeSingleExpression("source.lang.swift.range.singleexpression");
+static UIdent KindRangeSingleDeclaration("source.lang.swift.range.singledeclaration");
+
+static UIdent KindRangeMultiStatement("source.lang.swift.range.multistatement");
+
 static UIdent KindRangeInvalid("source.lang.swift.range.invalid");
 
 std::unique_ptr<LangSupport>
@@ -536,8 +540,10 @@
 SourceKit::UIdent SwiftLangSupport::
 getUIDForRangeKind(swift::ide::RangeKind Kind) {
   switch (Kind) {
-    case swift::ide::RangeKind::Expression: return KindRangeExpression;
+    case swift::ide::RangeKind::SingleExpression: return KindRangeSingleExpression;
     case swift::ide::RangeKind::SingleStatement: return KindRangeSingleStatement;
+    case swift::ide::RangeKind::SingleDecl: return KindRangeSingleDeclaration;
+    case swift::ide::RangeKind::MultiStatement: return KindRangeMultiStatement;
     case swift::ide::RangeKind::Invalid: return KindRangeInvalid;
   }
 }
diff --git a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp
index 1e05e0f..ac30634 100644
--- a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp
+++ b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp
@@ -1097,7 +1097,7 @@
       Result.RangeKind = Lang.getUIDForRangeKind(Info.Kind);
       Result.RangeContent = Info.Content;
       switch (Info.Kind) {
-      case RangeKind::Expression: {
+      case RangeKind::SingleExpression: {
         SmallString<64> SS;
         llvm::raw_svector_ostream OS(SS);
         Info.Ty.print(OS);
@@ -1105,6 +1105,8 @@
         Receiver(Result);
         return;
       }
+      case RangeKind::SingleDecl:
+      case RangeKind::MultiStatement:
       case RangeKind::SingleStatement: {
         Receiver(Result);
         return;
diff --git a/tools/SourceKit/tools/sourcekitd-test/Options.td b/tools/SourceKit/tools/sourcekitd-test/Options.td
index 2d17e78..3c08285 100644
--- a/tools/SourceKit/tools/sourcekitd-test/Options.td
+++ b/tools/SourceKit/tools/sourcekitd-test/Options.td
@@ -87,3 +87,6 @@
 
 def async : Flag<["-"], "async">,
   HelpText<"Perform request asynchronously">;
+
+def end_pos : Separate<["-"], "end-pos">, HelpText<"line:col">;
+def end_pos_EQ : Joined<["-"], "end-pos=">, Alias<end_pos>;
\ No newline at end of file
diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp
index 17741fe..d26fa4d 100644
--- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp
+++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp
@@ -164,6 +164,13 @@
       break;
     }
 
+    case OPT_end_pos: {
+      auto linecol = parseLineCol(InputArg->getValue());
+      EndLine = linecol.first;
+      EndCol = linecol.second;
+      break;
+    }
+
     case OPT_line:
       if (StringRef(InputArg->getValue()).getAsInteger(10, Line)) {
         llvm::errs() << "error: expected integer for 'line'\n";
diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.h b/tools/SourceKit/tools/sourcekitd-test/TestOptions.h
index fed522a..5dc3739 100644
--- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.h
+++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.h
@@ -63,6 +63,8 @@
   std::string InterestedUSR;
   unsigned Line = 0;
   unsigned Col = 0;
+  unsigned EndLine = 0;
+  unsigned EndCol = 0;
   unsigned Offset = 0;
   unsigned Length = 0;
   llvm::Optional<std::string> ReplaceText;
diff --git a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp
index cf8fcde..d98e69c 100644
--- a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp
+++ b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp
@@ -543,12 +543,17 @@
       sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset);
     }
     break;
-  case SourceKitRequest::RangeInfo:
+  case SourceKitRequest::RangeInfo: {
     sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestRangeInfo);
     sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset);
-    sourcekitd_request_dictionary_set_int64(Req, KeyLength, Opts.Length);
+    auto Length = Opts.Length;
+    if (Opts.Length == 0 && Opts.EndLine > 0) {
+      auto EndOff = resolveFromLineCol(Opts.EndLine, Opts.EndCol, SourceFile);
+      Length = EndOff - ByteOffset;
+    }
+    sourcekitd_request_dictionary_set_int64(Req, KeyLength, Length);
     break;
-
+  }
   case SourceKitRequest::RelatedIdents:
     sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestRelatedIdents);
     sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset);
diff --git a/tools/swift-api-digester/swift-api-digester.cpp b/tools/swift-api-digester/swift-api-digester.cpp
index 5c1b265..ebaf953 100644
--- a/tools/swift-api-digester/swift-api-digester.cpp
+++ b/tools/swift-api-digester/swift-api-digester.cpp
@@ -314,6 +314,8 @@
   bool operator==(const SDKNode &Other) const;
   bool operator!=(const SDKNode &Other) const { return !((*this) == Other); }
 
+  ArrayRef<NodeAnnotation>
+    getAnnotations(std::vector<NodeAnnotation> &Scrach) const;
   bool isLeaf() const { return Children.empty(); }
   SDKNodeKind getKind() const { return SDKNodeKind(TheKind); }
   StringRef getName() const { return Name; }
@@ -364,10 +366,10 @@
   bool isObjc() const { return Usr.startswith("c:"); }
   static bool classof(const SDKNode *N);
   DeclKind getDeclKind() const { return DKind; }
-  void printFullyQualifiedName(llvm::raw_ostream &OS);
-  StringRef getFullyQualifiedName();
-  bool isSDKPrivate();
-  bool isDeprecated();
+  void printFullyQualifiedName(llvm::raw_ostream &OS) const;
+  StringRef getFullyQualifiedName() const;
+  bool isSDKPrivate() const;
+  bool isDeprecated() const;
   bool isStatic() const { return IsStatic; };
 };
 
@@ -431,9 +433,9 @@
 }
 
 NodePtr SDKNode::getOnlyChild() const {
-    assert(Children.size() == 1 && "more that one child.");
-    return (*Children.begin()).get();
-  }
+  assert(Children.size() == 1 && "more that one child.");
+  return (*Children.begin()).get();
+}
 
 void SDKNode::addChild(NodeUniquePtr Child) {
   Child->Parent = this;
@@ -468,6 +470,13 @@
   return AnnotateComments.find(Anno)->second;
 }
 
+ArrayRef<NodeAnnotation> SDKNode::
+getAnnotations(std::vector<NodeAnnotation> &Scratch) const {
+  for(auto Ann : Annotations)
+    Scratch.push_back(Ann);
+  return llvm::makeArrayRef(Scratch);
+}
+
 bool SDKNode::isAnnotatedAs(NodeAnnotation Anno) const {
   return Annotations.find(Anno) != Annotations.end();;
 }
@@ -588,11 +597,11 @@
   return Instance.get();
 }
 
-bool SDKNodeDecl::isDeprecated() {
+bool SDKNodeDecl::isDeprecated() const {
   return hasDeclAttribute(SDKDeclAttrKind::DAK_deprecated);
 }
 
-bool SDKNodeDecl::isSDKPrivate() {
+bool SDKNodeDecl::isSDKPrivate() const {
   if (getName().startswith("__"))
     return true;
   if (auto *PD = dyn_cast<SDKNodeDecl>(getParent()))
@@ -600,7 +609,7 @@
   return false;
 }
 
-void SDKNodeDecl::printFullyQualifiedName(llvm::raw_ostream &OS) {
+void SDKNodeDecl::printFullyQualifiedName(llvm::raw_ostream &OS) const {
   std::vector<NodePtr> Parent;
   for (auto *P = getParent(); isa<SDKNodeDecl>(P); P = P->getParent())
     Parent.push_back(P);
@@ -609,7 +618,7 @@
   OS << getPrintedName();
 }
 
-StringRef SDKNodeDecl::getFullyQualifiedName() {
+StringRef SDKNodeDecl::getFullyQualifiedName() const {
   llvm::SmallString<32> Buffer;
   llvm::raw_svector_ostream OS(Buffer);
   printFullyQualifiedName(OS);
@@ -1936,7 +1945,7 @@
     MapImpl.push_back(std::make_pair(Left, Right));
   }
 
-  NodePtr findUpdateCounterpart(NodePtr Node) const {
+  NodePtr findUpdateCounterpart(const SDKNode *Node) const {
     assert(Node->isAnnotatedAs(NodeAnnotation::Updated) && "Not update operation.");
     auto FoundPair = std::find_if(MapImpl.begin(), MapImpl.end(),
                         [&](std::pair<NodePtr, NodePtr> Pair) {
@@ -2688,10 +2697,11 @@
 };
 
 class DiagnosisEmitter : public SDKNodeVisitor {
+  void handle(const SDKNodeDecl *D, NodeAnnotation Anno);
   void visitType(SDKNodeType *T);
   void visitDecl(SDKNodeDecl *D);
   void visit(NodePtr Node) override;
-  SDKNodeDecl *findAddedDecl(SDKNodeDecl *Node);
+  SDKNodeDecl *findAddedDecl(const SDKNodeDecl *Node);
   static StringRef printName(StringRef Name);
   static StringRef printDiagKeyword(StringRef Name);
   static void collectAddedDecls(NodePtr Root, std::set<SDKNodeDecl*> &Results);
@@ -2805,7 +2815,7 @@
     collectAddedDecls(C.get(), Results);
 }
 
-SDKNodeDecl *DiagnosisEmitter::findAddedDecl(SDKNodeDecl *Root) {
+SDKNodeDecl *DiagnosisEmitter::findAddedDecl(const SDKNodeDecl *Root) {
   for (auto *Added : AddedDecls) {
     if (Root->getKind() == Added->getKind() &&
         Root->getPrintedName() == Added->getPrintedName())
@@ -2904,11 +2914,10 @@
   SDKNode::postorderVisit(LeftRoot, Emitter);
 }
 
-void DiagnosisEmitter::visitDecl(SDKNodeDecl *Node) {
-  if (Node->isSDKPrivate())
-    return;
-  if (Node->isAnnotatedAs(NodeAnnotation::Removed) &&
-      !Node->isAnnotatedAs(NodeAnnotation::Rename)) {
+void DiagnosisEmitter::handle(const SDKNodeDecl *Node, NodeAnnotation Anno) {
+  assert(Node->isAnnotatedAs(Anno));
+  switch(Anno) {
+  case NodeAnnotation::Removed: {
     if (auto *Added = findAddedDecl(Node)) {
       MovedDecls.Diags.emplace_back(Node->getDeclKind(),
                                     Added->getDeclKind(),
@@ -2919,29 +2928,34 @@
                                       Node->getFullyQualifiedName(),
                                       Node->isDeprecated());
     }
+    return;
   }
-  if (Node->isAnnotatedAs(NodeAnnotation::Rename)) {
+  case NodeAnnotation::Rename: {
     auto *Count = UpdateMap.findUpdateCounterpart(Node)->getAs<SDKNodeDecl>();
     RenamedDecls.Diags.emplace_back(Node->getDeclKind(), Count->getDeclKind(),
                                     Node->getFullyQualifiedName(),
                                     Count->getFullyQualifiedName());
+    return;
   }
-  if (Node->isAnnotatedAs(NodeAnnotation::NowMutating)) {
+  case NodeAnnotation::NowMutating: {
     AttrChangedDecls.Diags.emplace_back(Node->getDeclKind(),
                                         Node->getFullyQualifiedName(),
                                         InsertToBuffer("mutating"));
+    return;
   }
-  if (Node->isAnnotatedAs(NodeAnnotation::NowThrowing)) {
+  case NodeAnnotation::NowThrowing: {
     AttrChangedDecls.Diags.emplace_back(Node->getDeclKind(),
                                         Node->getFullyQualifiedName(),
                                         InsertToBuffer("throwing"));
+    return;
   }
-  if (Node->isAnnotatedAs(NodeAnnotation::StaticChange)) {
+  case NodeAnnotation::StaticChange: {
     AttrChangedDecls.Diags.emplace_back(Node->getDeclKind(),
                                         Node->getFullyQualifiedName(),
                   InsertToBuffer(Node->isStatic() ? "not static" : "static"));
+    return;
   }
-  if (Node->isAnnotatedAs(NodeAnnotation::OwnershipChange)) {
+  case NodeAnnotation::OwnershipChange: {
     auto getOwnershipDescription = [](swift::Ownership O) {
       switch (O) {
       case Ownership::Strong:    return InsertToBuffer("strong");
@@ -2955,8 +2969,21 @@
                                         Node->getFullyQualifiedName(),
                                   getOwnershipDescription(Node->getOwnership()),
                                 getOwnershipDescription(Count->getOwnership()));
+    return;
+  }
+  default:
+    return;
   }
 }
+
+void DiagnosisEmitter::visitDecl(SDKNodeDecl *Node) {
+  if (Node->isSDKPrivate())
+    return;
+  std::vector<NodeAnnotation> Scratch;
+  for (auto Anno : Node->getAnnotations(Scratch))
+    handle(Node, Anno);
+}
+
 void DiagnosisEmitter::visitType(SDKNodeType *Node) {
   auto *Parent = Node->getParent()->getAs<SDKNodeDecl>();
   if (!Parent || Parent->isSDKPrivate())
diff --git a/utils/scale-test b/utils/scale-test
index 6986ec7..fcba9a5 100755
--- a/utils/scale-test
+++ b/utils/scale-test
@@ -12,15 +12,25 @@
 # values.
 #
 
-import gyb, os, os.path, subprocess
+import argparse
+import json
+import os
+import os.path
+import shutil
+import subprocess
+import sys
+import tempfile
+import gyb
+
 
 def find_which(p):
     for d in os.environ["PATH"].split(os.pathsep):
-        full = os.path.join(d,p)
+        full = os.path.join(d, p)
         if os.path.isfile(full) and os.access(full, os.X_OK):
             return full
     return p
 
+
 # Evidently the debug-symbol reader in dtrace is sufficiently slow and/or buggy
 # that attempting to inject probes into a binary w/ debuginfo is asking for a
 # failed run (possibly racing with probe insertion, or probing the stabs
@@ -28,7 +38,8 @@
 # so we sniff the presence of debug symbols here.
 def has_debuginfo(swiftc):
     swiftc = find_which(swiftc)
-    for line in subprocess.check_output(["dwarfdump", "--file-stats", swiftc]).splitlines():
+    for line in subprocess.check_output(
+            ["dwarfdump", "--file-stats", swiftc]).splitlines():
         if '%' not in line:
             continue
         fields = line.split()
@@ -38,20 +49,22 @@
 
 
 def write_input_file(args, ast, d, n):
-    ifile = os.path.join(d, "in%d.swift" % n)
-    with open(ifile,'w+') as f:
+    fname = "in%d.swift" % n
+    pathname = os.path.join(d, fname)
+    with open(pathname, 'w+') as f:
         f.write(gyb.execute_template(ast, '', N=n))
-    return ifile
+    return fname
 
 
 def run_once_with_primary(args, ast, rng, primary_idx):
-    import sys, shutil, tempfile, json
     r = {}
     try:
-        d = tempfile.mkdtemp()
+        if args.tmpdir is not None and not os.path.exists(args.tmpdir):
+            os.makedirs(args.tmpdir, 0700)
+        d = tempfile.mkdtemp(dir=args.tmpdir)
         inputs = [write_input_file(args, ast, d, i) for i in rng]
         primary = inputs[primary_idx]
-        ofile = os.path.join(d, "out.o")
+        ofile = "out.o"
 
         mode = "-c"
         if args.parse:
@@ -81,31 +94,32 @@
             print "running: " + " ".join(command)
 
         if args.dtrace:
-            trace = os.path.join(d, "trace.txt")
-            script = "pid$target:swiftc:*%s*:entry { @[probefunc] = count() }" % args.select
+            trace = "trace.txt"
+            script = ("pid$target:swiftc:*%s*:entry { @[probefunc] = count() }"
+                      % args.select)
             subprocess.check_call(
                 ["sudo", "dtrace", "-q",
                  "-o", trace,
                  "-b", "256",
                  "-n", script,
-                 "-c", " ".join(command)])
+                 "-c", " ".join(command)], cwd=d)
             r = {fields[0]: int(fields[1]) for fields in
-                 [line.split() for line in open(trace)]
+                 [line.split() for line in open(os.path.join(d, trace))]
                  if len(fields) == 2}
         else:
             if args.debug:
                 command = ["lldb", "--"] + command
-            stats = os.path.join(d, "stats.json")
+            stats = "stats.json"
             argv = command + ["-Xllvm", "-stats",
                               "-Xllvm", "-stats-json",
                               "-Xllvm", "-info-output-file=" + stats]
-            subprocess.check_call(argv)
-            with open(stats) as f:
+            subprocess.check_call(argv, cwd=d)
+            with open(os.path.join(d, stats)) as f:
                 r = json.load(f)
     finally:
         shutil.rmtree(d)
 
-    return {k:v for (k,v) in r.items() if args.select in k}
+    return {k: v for (k, v) in r.items() if args.select in k}
 
 
 def run_once(args, ast, rng):
@@ -122,6 +136,7 @@
     else:
         return run_once_with_primary(args, ast, rng, -1)
 
+
 def run_many(args):
 
     if args.dtrace and has_debuginfo(args.swiftc_binary):
@@ -145,25 +160,24 @@
 
 
 def linear_regression(x, y):
-   # By the book: https://en.wikipedia.org/wiki/Simple_linear_regression
-   n = len(x)
-   assert n == len(y)
-   if n == 0:
-       return 0, 0
-   prod_sum = 0
-   sum_x = sum(x)
-   sum_y = sum(y)
-   sum_prod = sum(a * b for a, b in zip(x, y))
-   sum_x_sq = sum(a ** 2 for a in x)
-   mean_x = sum_x/n
-   mean_y = sum_y/n
-   mean_prod = sum_prod/n
-   mean_x_sq = sum_x_sq/n
-   covar_xy = mean_prod - mean_x * mean_y
-   var_x = mean_x_sq - mean_x**2
-   slope = covar_xy / var_x
-   inter = mean_y - slope * mean_x
-   return slope, inter
+    # By the book: https://en.wikipedia.org/wiki/Simple_linear_regression
+    n = len(x)
+    assert n == len(y)
+    if n == 0:
+        return 0, 0
+    sum_x = sum(x)
+    sum_y = sum(y)
+    sum_prod = sum(a * b for a, b in zip(x, y))
+    sum_x_sq = sum(a ** 2 for a in x)
+    mean_x = sum_x/n
+    mean_y = sum_y/n
+    mean_prod = sum_prod/n
+    mean_x_sq = sum_x_sq/n
+    covar_xy = mean_prod - mean_x * mean_y
+    var_x = mean_x_sq - mean_x**2
+    slope = covar_xy / var_x
+    inter = mean_y - slope * mean_x
+    return slope, inter
 
 
 def report(args, rng, runs):
@@ -196,7 +210,6 @@
 
 
 def main():
-    import argparse, sys
     parser = argparse.ArgumentParser()
     parser.add_argument(
         'file', type=argparse.FileType(),
@@ -242,8 +255,11 @@
         '--swiftc-binary',
         default="swiftc", help='swift binary to execute')
     parser.add_argument(
+        '--tmpdir', type=str,
+        default=None, help='directory to create tempfiles in')
+    parser.add_argument(
         '--select',
-        default="", help='substring of counters/symbols to restrict attention to')
+        default="", help='substring of counters/symbols to limit attention to')
     parser.add_argument(
         '--debug', action='store_true',
         default=False, help='invoke lldb on each scale test')
@@ -273,5 +289,6 @@
         exit(1)
     exit(0)
 
+
 if __name__ == '__main__':
     main()
diff --git a/validation-test/compiler_scale/scale_neighbouring_getset.gyb b/validation-test/compiler_scale/scale_neighbouring_getset.gyb
index 51edbf3..c4317b7 100644
--- a/validation-test/compiler_scale/scale_neighbouring_getset.gyb
+++ b/validation-test/compiler_scale/scale_neighbouring_getset.gyb
@@ -1,9 +1,6 @@
 // RUN: %scale-test --sum-multi --parse --begin 5 --end 16 --step 5 --select typeCheckAbstractFunctionBody %s
 // REQUIRES: OS=macosx, tools-release
 
-// FIXME: this test has been failing in CI
-// REQUIRES: rdar29090287
-
 struct Struct${N} {
 % if int(N) > 1:
     var Field : Struct${int(N)-1}?