Index: llvm/test/FileCheck/dump-input/filter-error-neighbor/check-dag.txt
===================================================================
--- /dev/null
+++ llvm/test/FileCheck/dump-input/filter-error-neighbor/check-dag.txt
@@ -0,0 +1,62 @@
+; The neighboring behavior of -dump-input-filter=error does not reveal lines
+; that are within an error annotation.  CHECK-DAG is the reason.  In the example
+; below, it would cause all the CHECK-DAG matches to be revealed.
+;
+; Moreover, the neighboring implementation has special handling for CHECK-LABEL
+; in that its match is revealed if it precedes the last line of an error.  That
+; special handling shouldn't be applied to other directives.  Otherwise, the
+; successful CHECK-DAG match at the end of the CHECK-DAG error here would be
+; revealed accidentally.
+
+     CHECK:<<<<<<
+CHECK-NEXT:        1: loop iter 3
+CHECK-NEXT:dag:4      ^~~~~~~~~~~
+CHECK-NEXT:dag:10     X~~~~~~~~~~ error: no match found
+CHECK-NEXT:        2: loop iter 0
+CHECK-NEXT:dag:1      ^~~~~~~~~~~
+CHECK-NEXT:dag:10     ~~~~~~~~~~~
+CHECK-NEXT:        3: loop iter 4
+CHECK-NEXT:dag:5      ^~~~~~~~~~~
+CHECK-NEXT:dag:10     ~~~~~~~~~~~
+CHECK-NEXT:        .
+CHECK-NEXT:        .
+CHECK-NEXT:        .
+CHECK-NEXT:>>>>>>
+
+RUN: echo 'CHECK-DAG: loop iter 0'  >  %t.chk
+RUN: echo 'CHECK-DAG: loop iter 1'  >> %t.chk
+RUN: echo 'CHECK-DAG: loop iter 2'  >> %t.chk
+RUN: echo 'CHECK-DAG: loop iter 3'  >> %t.chk
+RUN: echo 'CHECK-DAG: loop iter 4'  >> %t.chk
+RUN: echo 'CHECK-DAG: loop iter 5'  >> %t.chk
+RUN: echo 'CHECK-DAG: loop iter 6'  >> %t.chk
+RUN: echo 'CHECK-DAG: loop iter 7'  >> %t.chk
+RUN: echo 'CHECK-DAG: loop iter 8'  >> %t.chk
+RUN: echo 'CHECK-DAG: loop iter 9'  >> %t.chk
+RUN: echo 'CHECK-DAG: loop iter 10' >> %t.chk
+RUN: echo 'CHECK-DAG: loop iter 11' >> %t.chk
+RUN: echo 'CHECK-DAG: loop iter 12' >> %t.chk
+RUN: echo 'CHECK-DAG: loop iter 13' >> %t.chk
+RUN: echo 'CHECK-DAG: loop iter 14' >> %t.chk
+RUN: echo 'CHECK-DAG: loop iter 15' >> %t.chk
+
+RUN: echo 'loop iter 3'  >  %t.in
+RUN: echo 'loop iter 0'  >> %t.in
+RUN: echo 'loop iter 4'  >> %t.in
+RUN: echo 'loop iter 6'  >> %t.in
+RUN: echo 'loop iter 12' >> %t.in
+RUN: echo 'loop iter 7'  >> %t.in
+RUN: echo 'loop iter 8'  >> %t.in
+RUN: echo 'loop iter 14' >> %t.in
+RUN: echo 'loop iter 2'  >> %t.in
+RUN: echo 'loop iter 11' >> %t.in
+RUN: echo 'loop iter 1'  >> %t.in
+RUN: echo 'loop iter 15' >> %t.in
+RUN: echo 'loop iter 13' >> %t.in
+RUN: echo 'loop iter 10' >> %t.in
+RUN: echo 'loop iter 5'  >> %t.in
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -dump-input-context=2 -v %t.chk < %t.in 2>&1 \
+RUN:               -dump-input-filter=error \
+RUN: | FileCheck %s -match-full-lines
Index: llvm/test/FileCheck/dump-input/filter-error-neighbor/check-label-follows.txt
===================================================================
--- /dev/null
+++ llvm/test/FileCheck/dump-input/filter-error-neighbor/check-label-follows.txt
@@ -0,0 +1,70 @@
+; The neighboring behavior of -dump-input-filter=error reveals input lines 12-16
+; below (neighbor of an error).  Those lines are where the actual problem is:
+; the "host triples:" header is off by one.  Sometimes the key to understanding
+; a directive's failure is where the following CHECK-LABEL matched because it
+; limits the search range.
+
+     CHECK:<<<<<<
+CHECK-NEXT:           1: offload triples:
+CHECK-NEXT:label:1'0     ^~~~~~~~~~~~~~~~
+CHECK-NEXT:label:1'1     ^~~~~~~~~~~~~~~~
+CHECK-NEXT:           2: - x86_64-linux-gnu
+CHECK-NEXT:check:2'0     X~~~~~~~~~~~~~~~~~ error: no match found
+CHECK-NEXT:check:2'1       ?                possible intended match
+CHECK-NEXT:           3: - x86_64-unknown-linux-gnu
+CHECK-NEXT:check:2'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~
+CHECK-NEXT:           4: - x86_64-pc-linux-gnu
+CHECK-NEXT:check:2'0     ~~~~~~~~~~~~~~~~~~~~~
+CHECK-NEXT:           .
+CHECK-NEXT:           .
+CHECK-NEXT:           .
+CHECK-NEXT:          12: - x86_64-amazon-linux
+CHECK-NEXT:check:2'0     ~~~~~~~~~~~~~~~~~~~~~
+CHECK-NEXT:          13: - x86_64-linux-android
+CHECK-NEXT:check:2'0     ~~~~~~~~~~~~~~~~~~~~~~
+CHECK-NEXT:          14: host triples:
+CHECK-NEXT:label:3       ^~~~~~~~~~~~~
+CHECK-NEXT:check:2'0     ~~~~~~~~~~~~~
+CHECK-NEXT:          15: - nvptx64-nvidia-cuda
+CHECK-NEXT:          16: - x86_64-linux-gnu
+CHECK-NEXT:           .
+CHECK-NEXT:           .
+CHECK-NEXT:           .
+CHECK-NEXT:>>>>>>
+
+RUN: echo 'CHECK-LABEL: offload triples:' >  %t.chk
+RUN: echo 'CHECK: nvptx64-nvidia-cuda'    >> %t.chk
+RUN: echo 'CHECK-LABEL: host triples:'    >> %t.chk
+
+RUN: echo 'offload triples:'           >  %t.in
+RUN: echo '- x86_64-linux-gnu'         >> %t.in
+RUN: echo '- x86_64-unknown-linux-gnu' >> %t.in
+RUN: echo '- x86_64-pc-linux-gnu'      >> %t.in
+RUN: echo '- x86_64-redhat-linux6E'    >> %t.in
+RUN: echo '- x86_64-redhat-linux'      >> %t.in
+RUN: echo '- x86_64-suse-linux'        >> %t.in
+RUN: echo '- x86_64-manbo-linux-gnu'   >> %t.in
+RUN: echo '- x86_64-linux-gnu'         >> %t.in
+RUN: echo '- x86_64-slackware-linux'   >> %t.in
+RUN: echo '- x86_64-unknown-linux'     >> %t.in
+RUN: echo '- x86_64-amazon-linux'      >> %t.in
+RUN: echo '- x86_64-linux-android'     >> %t.in
+RUN: echo 'host triples:'              >> %t.in
+RUN: echo '- nvptx64-nvidia-cuda'      >> %t.in
+RUN: echo '- x86_64-linux-gnu'         >> %t.in
+RUN: echo '- x86_64-unknown-linux-gnu' >> %t.in
+RUN: echo '- x86_64-pc-linux-gnu'      >> %t.in
+RUN: echo '- x86_64-redhat-linux6E'    >> %t.in
+RUN: echo '- x86_64-redhat-linux'      >> %t.in
+RUN: echo '- x86_64-suse-linux'        >> %t.in
+RUN: echo '- x86_64-manbo-linux-gnu'   >> %t.in
+RUN: echo '- x86_64-linux-gnu'         >> %t.in
+RUN: echo '- x86_64-slackware-linux'   >> %t.in
+RUN: echo '- x86_64-unknown-linux'     >> %t.in
+RUN: echo '- x86_64-amazon-linux'      >> %t.in
+RUN: echo '- x86_64-linux-android'     >> %t.in
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -dump-input-context=2 -v %t.chk < %t.in 2>&1 \
+RUN:               -dump-input-filter=error \
+RUN: | FileCheck %s -match-full-lines
Index: llvm/test/FileCheck/dump-input/filter-error-neighbor/check-next-same.txt
===================================================================
--- /dev/null
+++ llvm/test/FileCheck/dump-input/filter-error-neighbor/check-next-same.txt
@@ -0,0 +1,50 @@
+; The neighboring behavior of -dump-input-filter=error reveals input lines 1-3
+; below (neighbor preceding an error).  Those lines are where the actual problem
+; is because that's where the CHECK-NEXT/CHECK-SAME directive was expected to
+; match but didn't.  Seeing the line where CHECK-NEXT/CHECK-SAME actually
+; matched, line 13, is typically less useful information.
+
+     CHECK:<<<<<<
+CHECK-NEXT:         1: start
+CHECK-NEXT:check:1     ^~~~~
+CHECK-NEXT:         2: foo0
+CHECK-NEXT:         3: foo1
+CHECK-NEXT:         .
+CHECK-NEXT:         .
+CHECK-NEXT:         .
+CHECK-NEXT:        11: foo9
+CHECK-NEXT:        12: foo10
+CHECK-NEXT:        13: end
+ NEXT-NEXT:next:2      !~~ error: match on wrong line
+ SAME-NEXT:same:2      !~~ error: match on wrong line
+CHECK-NEXT:>>>>>>
+
+RUN: echo 'CHECK: start'    >  %t.next.chk
+RUN: echo 'CHECK-NEXT: end' >> %t.next.chk
+
+RUN: echo 'CHECK: start'    >  %t.same.chk
+RUN: echo 'CHECK-SAME: end' >> %t.same.chk
+
+RUN: echo start >  %t.in
+RUN: echo foo0  >> %t.in
+RUN: echo foo1  >> %t.in
+RUN: echo foo2  >> %t.in
+RUN: echo foo3  >> %t.in
+RUN: echo foo4  >> %t.in
+RUN: echo foo5  >> %t.in
+RUN: echo foo6  >> %t.in
+RUN: echo foo7  >> %t.in
+RUN: echo foo8  >> %t.in
+RUN: echo foo9  >> %t.in
+RUN: echo foo10 >> %t.in
+RUN: echo end   >> %t.in
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -dump-input-context=2 -v %t.next.chk < %t.in 2>&1 \
+RUN:               -dump-input-filter=error \
+RUN: | FileCheck %s -match-full-lines -check-prefixes=CHECK,NEXT
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -dump-input-context=2 -v %t.same.chk < %t.in 2>&1 \
+RUN:               -dump-input-filter=error \
+RUN: | FileCheck %s -match-full-lines -check-prefixes=CHECK,SAME
Index: llvm/test/FileCheck/dump-input/filter-error-neighbor/check-not.txt
===================================================================
--- /dev/null
+++ llvm/test/FileCheck/dump-input/filter-error-neighbor/check-not.txt
@@ -0,0 +1,103 @@
+; The neighboring behavior of -dump-input-filter=error reveals input lines 1-3
+; and 60-63 below.  With only the former (neighbor preceding an error), it looks
+; like the host list includes a GPU triple.  The latter (neighbor following an
+; error) reveals we have an accidentally nested list.  Often the key to
+; understanding a CHECK-NOT failure is where the directives before and after it
+; matched because those establish the search range.
+
+     CHECK:<<<<<<
+CHECK-NEXT:         1: 
+CHECK-NEXT:check:1     ^~~~~~~~~~~~~~~~~~~~~
+CHECK-NEXT:         2:  x86_64-linux-gnu
+CHECK-NEXT:         3:  x86_64-unknown-linux-gnu
+CHECK-NEXT:         .
+CHECK-NEXT:         .
+CHECK-NEXT:         .
+CHECK-NEXT:        42:  x86_64-amazon-linux
+CHECK-NEXT:        43:  x86_64-linux-android
+CHECK-NEXT:        44:  nvptx64-nvidia-cuda
+CHECK-NEXT:not:2        !~~~~~~~~~~~~~~~~~~ error: no match expected
+CHECK-NEXT:        45:  i686-linux-gnu
+CHECK-NEXT:        46:  i686-pc-linux-gnu
+CHECK-NEXT:         .
+CHECK-NEXT:         .
+CHECK-NEXT:         .
+CHECK-NEXT:        60:  i586-gnu
+CHECK-NEXT:        61:  i686-gnu
+CHECK-NEXT:        62: 
+CHECK-NEXT:check:3     ^~~~~~~~~~
+CHECK-NEXT:        63: 
+CHECK-NEXT:>>>>>>
+
+RUN: echo 'CHECK: '   >  %t.chk
+RUN: echo 'CHECK-NOT: nvptx64-nvidia-cuda' >> %t.chk
+RUN: echo 'CHECK: '              >> %t.chk
+
+RUN: echo ''      >  %t.in
+RUN: echo '  x86_64-linux-gnu'         >> %t.in
+RUN: echo '  x86_64-unknown-linux-gnu' >> %t.in
+RUN: echo '  x86_64-pc-linux-gnu'      >> %t.in
+RUN: echo '  x86_64-redhat-linux6E'    >> %t.in
+RUN: echo '  x86_64-redhat-linux'      >> %t.in
+RUN: echo '  x86_64-suse-linux'        >> %t.in
+RUN: echo '  x86_64-manbo-linux-gnu'   >> %t.in
+RUN: echo '  x86_64-linux-gnu'         >> %t.in
+RUN: echo '  x86_64-slackware-linux'   >> %t.in
+RUN: echo '  x86_64-unknown-linux'     >> %t.in
+RUN: echo '  x86_64-amazon-linux'      >> %t.in
+RUN: echo '  x86_64-linux-android'     >> %t.in
+RUN: echo '  i686-linux-gnu'           >> %t.in
+RUN: echo '  i686-pc-linux-gnu'        >> %t.in
+RUN: echo '  i486-linux-gnu'           >> %t.in
+RUN: echo '  i386-linux-gnu'           >> %t.in
+RUN: echo '  i386-redhat-linux6E'      >> %t.in
+RUN: echo '  i686-redhat-linux'        >> %t.in
+RUN: echo '  i586-redhat-linux'        >> %t.in
+RUN: echo '  i386-redhat-linux'        >> %t.in
+RUN: echo '  i586-suse-linux'          >> %t.in
+RUN: echo '  i486-slackware-linux'     >> %t.in
+RUN: echo '  i686-montavista-linux'    >> %t.in
+RUN: echo '  i586-linux-gnu'           >> %t.in
+RUN: echo '  i686-linux-android'       >> %t.in
+RUN: echo '  i386-gnu'                 >> %t.in
+RUN: echo '  i486-gnu'                 >> %t.in
+RUN: echo '  i586-gnu'                 >> %t.in
+RUN: echo '  i686-gnu'                 >> %t.in
+RUN: echo ''   >> %t.in
+RUN: echo '  x86_64-linux-gnu'         >> %t.in
+RUN: echo '  x86_64-unknown-linux-gnu' >> %t.in
+RUN: echo '  x86_64-pc-linux-gnu'      >> %t.in
+RUN: echo '  x86_64-redhat-linux6E'    >> %t.in
+RUN: echo '  x86_64-redhat-linux'      >> %t.in
+RUN: echo '  x86_64-suse-linux'        >> %t.in
+RUN: echo '  x86_64-manbo-linux-gnu'   >> %t.in
+RUN: echo '  x86_64-linux-gnu'         >> %t.in
+RUN: echo '  x86_64-slackware-linux'   >> %t.in
+RUN: echo '  x86_64-unknown-linux'     >> %t.in
+RUN: echo '  x86_64-amazon-linux'      >> %t.in
+RUN: echo '  x86_64-linux-android'     >> %t.in
+RUN: echo '  nvptx64-nvidia-cuda'      >> %t.in
+RUN: echo '  i686-linux-gnu'           >> %t.in
+RUN: echo '  i686-pc-linux-gnu'        >> %t.in
+RUN: echo '  i486-linux-gnu'           >> %t.in
+RUN: echo '  i386-linux-gnu'           >> %t.in
+RUN: echo '  i386-redhat-linux6E'      >> %t.in
+RUN: echo '  i686-redhat-linux'        >> %t.in
+RUN: echo '  i586-redhat-linux'        >> %t.in
+RUN: echo '  i386-redhat-linux'        >> %t.in
+RUN: echo '  i586-suse-linux'          >> %t.in
+RUN: echo '  i486-slackware-linux'     >> %t.in
+RUN: echo '  i686-montavista-linux'    >> %t.in
+RUN: echo '  i586-linux-gnu'           >> %t.in
+RUN: echo '  i686-linux-android'       >> %t.in
+RUN: echo '  i386-gnu'                 >> %t.in
+RUN: echo '  i486-gnu'                 >> %t.in
+RUN: echo '  i586-gnu'                 >> %t.in
+RUN: echo '  i686-gnu'                 >> %t.in
+RUN: echo ''                 >> %t.in
+RUN: echo ''                 >> %t.in
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -dump-input-context=2 -v %t.chk < %t.in 2>&1 \
+RUN:               -dump-input-filter=error \
+RUN: | FileCheck %s -match-full-lines
Index: llvm/test/FileCheck/dump-input/filter-error-neighbor/multiline.txt
===================================================================
--- /dev/null
+++ llvm/test/FileCheck/dump-input/filter-error-neighbor/multiline.txt
@@ -0,0 +1,112 @@
+; -dump-input-filter=error reveals only the first or last line of an annotation
+; neighboring an error, depending on whether it precedes or follows the error.
+; This test checks that we don't mix up first and last.  Otherwise, a preceding
+; or following annotation can accidentally be omitted altogether.  Of course,
+; we must have multiline annotations so that first != last.
+;
+; This test also checks that CHECK-LABEL is handled in the same way.  That is,
+; CHECK-LABEL has additional special handling in that its match is revealed if
+; it precedes the last line of an error, but that special handling alone
+; wouldn't be sufficient for CHECK-LABEL here: it wouldn't reveal the first
+; CHECK-LABEL's match because the CHECK-NOT match has multiple lines.
+
+     CHECK:<<<<<<
+CHECK-NEXT:           .
+CHECK-NEXT:           .
+CHECK-NEXT:           .
+CHECK-NEXT:           3: start
+  CHK-NEXT:check:1       ~~~~~
+  LAB-NEXT:label:1'0     ~~~~~
+  LAB-NEXT:label:1'1     ~~~~~
+CHECK-NEXT:           4: start
+  CHK-NEXT:check:1       ~~~~~
+  LAB-NEXT:label:1'0     ~~~~~
+  LAB-NEXT:label:1'1     ~~~~~
+CHECK-NEXT:           5: start
+  CHK-NEXT:check:1       ~~~~~
+  LAB-NEXT:label:1'0     ~~~~~
+  LAB-NEXT:label:1'1     ~~~~~
+CHECK-NEXT:           6: bar
+CHECK-NEXT:           7: bar
+CHECK-NEXT:           .
+CHECK-NEXT:           .
+CHECK-NEXT:           .
+CHECK-NEXT:          14: bar
+CHECK-NEXT:          15: bar
+CHECK-NEXT:          16: foo
+CHECK-NEXT:not:2         !~~ error: no match expected
+CHECK-NEXT:          17: foo
+CHECK-NEXT:not:2         ~~~
+CHECK-NEXT:          18: bar
+CHECK-NEXT:           .
+CHECK-NEXT:           .
+CHECK-NEXT:           .
+CHECK-NEXT:          26: bar
+CHECK-NEXT:          27: bar
+CHECK-NEXT:          28: end
+  CHK-NEXT:check:3       ^~~
+  LAB-NEXT:label:3'0     ^~~
+  LAB-NEXT:label:3'1     ^~~
+CHECK-NEXT:          29: end
+  CHK-NEXT:check:3       ~~~
+  LAB-NEXT:label:3'0     ~~~
+  LAB-NEXT:label:3'1     ~~~
+CHECK-NEXT:          30: end
+  CHK-NEXT:check:3       ~~~
+  LAB-NEXT:label:3'0     ~~~
+  LAB-NEXT:label:3'1     ~~~
+CHECK-NEXT:           .
+CHECK-NEXT:           .
+CHECK-NEXT:           .
+CHECK-NEXT:>>>>>>
+
+RUN: echo 'CHECK: start{{([[:space:]]*start)+}}' >  %t.chk.chk
+RUN: echo 'CHECK-NOT: foo{{([[:space:]]*foo)*}}' >> %t.chk.chk
+RUN: echo 'CHECK: end{{([[:space:]]*end)+}}'     >> %t.chk.chk
+
+RUN: echo 'CHECK-LABEL: start{{([[:space:]]*start)+}}' >  %t.lab.chk
+RUN: echo 'CHECK-NOT: foo{{([[:space:]]*foo)*}}'       >> %t.lab.chk
+RUN: echo 'CHECK-LABEL: end{{([[:space:]]*end)+}}'     >> %t.lab.chk
+
+RUN: echo 'start' >  %t.in
+RUN: echo 'start' >> %t.in
+RUN: echo 'start' >> %t.in
+RUN: echo 'start' >> %t.in
+RUN: echo 'start' >> %t.in
+RUN: echo 'bar'   >> %t.in
+RUN: echo 'bar'   >> %t.in
+RUN: echo 'bar'   >> %t.in
+RUN: echo 'bar'   >> %t.in
+RUN: echo 'bar'   >> %t.in
+RUN: echo 'bar'   >> %t.in
+RUN: echo 'bar'   >> %t.in
+RUN: echo 'bar'   >> %t.in
+RUN: echo 'bar'   >> %t.in
+RUN: echo 'bar'   >> %t.in
+RUN: echo 'foo'   >> %t.in
+RUN: echo 'foo'   >> %t.in
+RUN: echo 'bar'   >> %t.in
+RUN: echo 'bar'   >> %t.in
+RUN: echo 'bar'   >> %t.in
+RUN: echo 'bar'   >> %t.in
+RUN: echo 'bar'   >> %t.in
+RUN: echo 'bar'   >> %t.in
+RUN: echo 'bar'   >> %t.in
+RUN: echo 'bar'   >> %t.in
+RUN: echo 'bar'   >> %t.in
+RUN: echo 'bar'   >> %t.in
+RUN: echo 'end'   >> %t.in
+RUN: echo 'end'   >> %t.in
+RUN: echo 'end'   >> %t.in
+RUN: echo 'end'   >> %t.in
+RUN: echo 'end'   >> %t.in
+
+XUN: %ProtectFileCheckOutput \
+XUN: not FileCheck -dump-input-context=2 -v %t.chk.chk < %t.in 2>&1 \
+XUN:               -dump-input-filter=error \
+XUN: | FileCheck %s -match-full-lines -check-prefixes=CHECK,CHK
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -dump-input-context=2 -v %t.lab.chk < %t.in 2>&1 \
+RUN:               -dump-input-filter=error \
+RUN: | FileCheck %s -match-full-lines -check-prefixes=CHECK,LAB
Index: llvm/test/FileCheck/dump-input/filter.txt
===================================================================
--- llvm/test/FileCheck/dump-input/filter.txt
+++ llvm/test/FileCheck/dump-input/filter.txt
@@ -215,6 +215,7 @@
 ; ERROR-NEXT:            .
 ; ERROR-NEXT:            .
 ; ERROR-NEXT:            .
+; ERROR-NEXT:           10: foo8
 ; ERROR-NEXT:           11: foo9
 ; ERROR-NEXT:           12: hello
 ; ERROR-NEXT: check:2       ^~~~~
Index: llvm/utils/FileCheck/FileCheck.cpp
===================================================================
--- llvm/utils/FileCheck/FileCheck.cpp
+++ llvm/utils/FileCheck/FileCheck.cpp
@@ -156,6 +156,7 @@
                           "Input lines with starting points of annotations"),
                clEnumValN(DumpInputFilterError, "error",
                           "Input lines with starting points of error "
+                          "annotations or with annotations that neighbor error "
                           "annotations")));
 
 static cl::list DumpInputContexts(
@@ -306,13 +307,18 @@
 
 /// An annotation for a single input line.
 struct InputAnnotation {
-  /// The index of the match result across all checks
+  /// The index of the match result across all checks.
   unsigned DiagIndex;
+  /// The kind of check that produced the match result.
+  Check::FileCheckType CheckTy;
   /// The label for this annotation.
   std::string Label;
-  /// Is this the initial fragment of a diagnostic that has been broken across
-  /// multiple lines?
+  /// Is this the initial annotation for a diagnostic, which might be broken
+  /// across multiple lines?
   bool IsFirstLine;
+  /// Is this the last annotation for a diagnostic, which might be broken across
+  /// multiple lines?
+  bool IsLastLine;
   /// What input line (one-origin indexing) this annotation marks.  This might
   /// be different from the starting line of the original diagnostic if
   /// !IsFirstLine.
@@ -376,6 +382,7 @@
        ++DiagItr) {
     InputAnnotation A;
     A.DiagIndex = DiagCount++;
+    A.CheckTy = DiagItr->CheckTy;
 
     // Build label, which uniquely identifies this check result.
     unsigned CheckBufferID = SM.FindBufferContainingLoc(DiagItr->CheckLoc);
@@ -424,6 +431,7 @@
     // Compute the mark location, and break annotation into multiple
     // annotations if it spans multiple lines.
     A.IsFirstLine = true;
+    A.IsLastLine = false;
     A.InputLine = DiagItr->InputStartLine;
     A.InputStartCol = DiagItr->InputStartCol;
     if (DiagItr->InputStartLine == DiagItr->InputEndLine) {
@@ -446,8 +454,10 @@
           break;
         InputAnnotation B;
         B.DiagIndex = A.DiagIndex;
+        B.CheckTy = A.CheckTy;
         B.Label = A.Label;
         B.IsFirstLine = false;
+        B.IsLastLine = false;
         B.InputLine = L;
         B.Marker = A.Marker;
         B.Marker.Lead = '~';
@@ -461,16 +471,19 @@
         Annotations.push_back(B);
       }
     }
+    Annotations.back().IsLastLine = true;
   }
 }
 
 static unsigned FindInputLineInFilter(
     DumpInputFilterValue DumpInputFilter, unsigned CurInputLine,
-    const std::vector::iterator &AnnotationBeg,
-    const std::vector::iterator &AnnotationEnd) {
+    const std::vector::iterator &AnnotationCur,
+    const std::vector &Annotations) {
   if (DumpInputFilter == DumpInputFilterAll)
     return CurInputLine;
-  for (auto AnnotationItr = AnnotationBeg; AnnotationItr != AnnotationEnd;
+  auto AnnotationBeg = Annotations.begin();
+  auto AnnotationEnd = Annotations.end();
+  for (auto AnnotationItr = AnnotationCur; AnnotationItr != AnnotationEnd;
        ++AnnotationItr) {
     switch (DumpInputFilter) {
     case DumpInputFilterAll:
@@ -483,8 +496,50 @@
         return AnnotationItr->InputLine;
       break;
     case DumpInputFilterError:
+      // If it's the first line of an error, include it.
       if (AnnotationItr->IsFirstLine && AnnotationItr->Marker.FiltersAsError)
         return AnnotationItr->InputLine;
+      // If it's a diagnostic's first line and it's after the last line of an
+      // error, include it.  This might reveal an incorrect end to a search
+      // range.
+      if (AnnotationItr->IsFirstLine && AnnotationItr != AnnotationBeg) {
+        auto AnnotationPrev = std::prev(AnnotationItr);
+        if (AnnotationPrev->Marker.FiltersAsError && AnnotationPrev->IsLastLine)
+          return AnnotationItr->InputLine;
+      }
+      // If it's a diagnostic's last line and it's before the first line of an
+      // error, include it.  This might reveal an incorrect start to a search
+      // range.  It might reveal the incorrect text where a CHECK-NEXT or
+      // CHECK-SAME was expected to match but instead matched on a later line.
+      //
+      // As a special exception, if it's the last line of a CHECK-LABEL match
+      // before the last line of an error, include it.  A CHECK-LABEL directive
+      // is processed before all other directives, so its annotation precedes
+      // the last line of an error that precedes it.  Don't apply that exception
+      // generally, or a CHECK-DAG error can produce a spurious inclusion due to
+      // a successful CHECK-DAG at the end of the search range.
+      //
+      // Because we include last lines sometimes, input lines from
+      // DumpInputFilterError are not always a subset of input lines from
+      // DumpInputFilterAnnotation.
+      if (AnnotationItr->IsLastLine) {
+        auto AnnotationNext = std::next(AnnotationItr);
+        if (AnnotationNext != AnnotationEnd &&
+            AnnotationNext->Marker.FiltersAsError &&
+            (AnnotationNext->IsFirstLine ||
+             (AnnotationItr->CheckTy == Check::CheckLabel &&
+              AnnotationNext->IsLastLine)))
+          return AnnotationItr->InputLine;
+      }
+      // We have included only first and last lines of diagnostics so that we
+      // don't include a line within an error just because it's next to a
+      // "possible intended match" diagnostic.  The "possible intended match"
+      // diagnostic is included on its own merits anyway, but the amount of
+      // context could be unexpectedly augmented.
+      //
+      // We have not included lines just because they're next to non-first and
+      // non-last error lines.  Otherwise, a CHECK-DAG error could include many
+      // lines for successful matches from the same CHECK-DAG block.
       break;
     }
   }
@@ -603,7 +658,7 @@
     // Compute the previous and next line included by the filter.
     if (NextLineInFilter < Line)
       NextLineInFilter = FindInputLineInFilter(DumpInputFilter, Line,
-                                               AnnotationItr, AnnotationEnd);
+                                               AnnotationItr, Annotations);
     assert(NextLineInFilter && "expected NextLineInFilter to be computed");
     if (NextLineInFilter == Line)
       PrevLineInFilter = Line;