return A;
}
-/// Highlight a SourceRange (with ~'s) for any characters on LineNo.
-static void highlightRange(const CharSourceRange &R,
- unsigned LineNo, FileID FID,
- const SourceColumnMap &map,
- std::string &CaretLine,
- const SourceManager &SM,
- const LangOptions &LangOpts) {
- if (!R.isValid()) return;
-
- SourceLocation Begin = R.getBegin();
- SourceLocation End = R.getEnd();
-
- unsigned StartLineNo = SM.getExpansionLineNumber(Begin);
- if (StartLineNo > LineNo || SM.getFileID(Begin) != FID)
- return; // No intersection.
-
- unsigned EndLineNo = SM.getExpansionLineNumber(End);
- if (EndLineNo < LineNo || SM.getFileID(End) != FID)
- return; // No intersection.
-
- // Compute the column number of the start.
- unsigned StartColNo = 0;
- if (StartLineNo == LineNo) {
- StartColNo = SM.getExpansionColumnNumber(Begin);
- if (StartColNo) --StartColNo; // Zero base the col #.
- }
-
- // Compute the column number of the end.
- unsigned EndColNo = map.getSourceLine().size();
- if (EndLineNo == LineNo) {
- EndColNo = SM.getExpansionColumnNumber(End);
- if (EndColNo) {
- --EndColNo; // Zero base the col #.
-
- // Add in the length of the token, so that we cover multi-char tokens if
- // this is a token range.
- if (R.isTokenRange())
- EndColNo += Lexer::MeasureTokenLength(End, SM, LangOpts);
- } else {
- EndColNo = CaretLine.size();
- }
- }
-
- assert(StartColNo <= EndColNo && "Invalid range!");
-
- // Check that a token range does not highlight only whitespace.
- if (R.isTokenRange()) {
- // Pick the first non-whitespace column.
- while (StartColNo < map.getSourceLine().size() &&
- (map.getSourceLine()[StartColNo] == ' ' ||
- map.getSourceLine()[StartColNo] == '\t'))
- StartColNo = map.startOfNextColumn(StartColNo);
-
- // Pick the last non-whitespace column.
- if (EndColNo > map.getSourceLine().size())
- EndColNo = map.getSourceLine().size();
- while (EndColNo &&
- (map.getSourceLine()[EndColNo-1] == ' ' ||
- map.getSourceLine()[EndColNo-1] == '\t'))
- EndColNo = map.startOfPreviousColumn(EndColNo);
-
- // If the start/end passed each other, then we are trying to highlight a
- // range that just exists in whitespace. That most likely means we have
- // a multi-line highlighting range that covers a blank line.
- if (StartColNo > EndColNo) {
- assert(StartLineNo != EndLineNo && "trying to highlight whitespace");
- StartColNo = EndColNo;
- }
- }
+struct LineRange {
+ unsigned LineNo;
+ unsigned StartCol;
+ unsigned EndCol;
+};
- assert(StartColNo <= map.getSourceLine().size() && "Invalid range!");
- assert(EndColNo <= map.getSourceLine().size() && "Invalid range!");
+/// Highlight \p R (with ~'s) on the current source line.
+static void highlightRange(const LineRange &R, const SourceColumnMap &Map,
+ std::string &CaretLine) {
+ // Pick the first non-whitespace column.
+ unsigned StartColNo = R.StartCol;
+ while (StartColNo < Map.getSourceLine().size() &&
+ (Map.getSourceLine()[StartColNo] == ' ' ||
+ Map.getSourceLine()[StartColNo] == '\t'))
+ StartColNo = Map.startOfNextColumn(StartColNo);
+
+ // Pick the last non-whitespace column.
+ unsigned EndColNo =
+ std::min(static_cast<size_t>(R.EndCol), Map.getSourceLine().size());
+ while (EndColNo && (Map.getSourceLine()[EndColNo - 1] == ' ' ||
+ Map.getSourceLine()[EndColNo - 1] == '\t'))
+ EndColNo = Map.startOfPreviousColumn(EndColNo);
+
+ // If the start/end passed each other, then we are trying to highlight a
+ // range that just exists in whitespace. That most likely means we have
+ // a multi-line highlighting range that covers a blank line.
+ if (StartColNo > EndColNo)
+ return;
// Fill the range with ~'s.
- StartColNo = map.byteToContainingColumn(StartColNo);
- EndColNo = map.byteToContainingColumn(EndColNo);
+ StartColNo = Map.byteToContainingColumn(StartColNo);
+ EndColNo = Map.byteToContainingColumn(EndColNo);
assert(StartColNo <= EndColNo && "Invalid range!");
if (CaretLine.size() < EndColNo)
- CaretLine.resize(EndColNo,' ');
- std::fill(CaretLine.begin()+StartColNo,CaretLine.begin()+EndColNo,'~');
+ CaretLine.resize(EndColNo, ' ');
+ std::fill(CaretLine.begin() + StartColNo, CaretLine.begin() + EndColNo, '~');
}
static std::string buildFixItInsertionLine(FileID FID,
return L;
}
+/// Filter out invalid ranges, ranges that don't fit into the window of
+/// source lines we will print, and ranges from other files.
+///
+/// For the remaining ranges, convert them to simple LineRange structs,
+/// which only cover one line at a time.
+static SmallVector<LineRange>
+prepareAndFilterRanges(const SmallVectorImpl<CharSourceRange> &Ranges,
+ const SourceManager &SM,
+ const std::pair<unsigned, unsigned> &Lines, FileID FID,
+ const LangOptions &LangOpts) {
+ SmallVector<LineRange> LineRanges;
+
+ for (const CharSourceRange &R : Ranges) {
+ if (R.isInvalid())
+ continue;
+ SourceLocation Begin = R.getBegin();
+ SourceLocation End = R.getEnd();
+
+ unsigned StartLineNo = SM.getExpansionLineNumber(Begin);
+ if (StartLineNo > Lines.second || SM.getFileID(Begin) != FID)
+ continue;
+
+ unsigned EndLineNo = SM.getExpansionLineNumber(End);
+ if (EndLineNo < Lines.first || SM.getFileID(End) != FID)
+ continue;
+
+ unsigned StartColumn = SM.getExpansionColumnNumber(Begin);
+ unsigned EndColumn = SM.getExpansionColumnNumber(End);
+ if (R.isTokenRange())
+ EndColumn += Lexer::MeasureTokenLength(End, SM, LangOpts);
+
+ // Only a single line.
+ if (StartLineNo == EndLineNo) {
+ LineRanges.push_back({StartLineNo, StartColumn - 1, EndColumn - 1});
+ continue;
+ }
+
+ // Start line.
+ LineRanges.push_back({StartLineNo, StartColumn - 1, ~0u});
+
+ // Middle lines.
+ for (unsigned S = StartLineNo + 1; S != EndLineNo; ++S)
+ LineRanges.push_back({S, 0, ~0u});
+
+ // End line.
+ LineRanges.push_back({EndLineNo, 0, EndColumn - 1});
+ }
+
+ return LineRanges;
+}
+
/// Emit a code snippet and caret line.
///
/// This routine emits a single line's code snippet and caret line..
OS.indent(MaxLineNoDisplayWidth + 2) << "| ";
};
+ SmallVector<LineRange> LineRanges =
+ prepareAndFilterRanges(Ranges, SM, Lines, FID, LangOpts);
+
for (unsigned LineNo = Lines.first; LineNo != Lines.second + 1;
++LineNo, ++DisplayLineNo) {
// Rewind from the current position to the start of the line.
std::string CaretLine;
// Highlight all of the characters covered by Ranges with ~ characters.
- for (const auto &I : Ranges)
- highlightRange(I, LineNo, FID, sourceColMap, CaretLine, SM, LangOpts);
+ for (const auto &LR : LineRanges) {
+ if (LR.LineNo == LineNo)
+ highlightRange(LR, sourceColMap, CaretLine);
+ }
// Next, insert the caret itself.
if (CaretLineNo == LineNo) {
// CHECK-NEXT: {{^}} if (cond) {
// CHECK-NEXT: {{^}} ^~~~~~~~~~~{{$}}
// CHECK-NEXT: {{^}} line(1);
-// CHECK-NEXT: {{^}}~~~~~~~~~~~~{{$}}
+// CHECK-NEXT: {{^}} ~~~~~~~~{{$}}
// CHECK-NEXT: {{^}} } else {
-// CHECK-NEXT: {{^}}~~~~~~~~~{{$}}
+// CHECK-NEXT: {{^}} ~~~~~~{{$}}
// CHECK-NEXT: note: initialize the variable
int f1(int cond) {
int a;
// CHECK-NEXT: {{^}} if (cond) {
// CHECK-NEXT: {{^}} ^~~~~~~~~~~{{$}}
// CHECK-NEXT: {{^}} line(1);
-// CHECK-NEXT: {{^}}~~~~~~~~~~~~{{$}}
+// CHECK-NEXT: {{^}} ~~~~~~~~{{$}}
// CHECK-NEXT: {{^}} line(2);
-// CHECK-NEXT: {{^}}~~~~~~~~~~~~{{$}}
+// CHECK-NEXT: {{^}} ~~~~~~~~{{$}}
// CHECK-NEXT: {{^}} } else {
-// CHECK-NEXT: {{^}}~~~~~~~~~{{$}}
+// CHECK-NEXT: {{^}} ~~~~~~{{$}}
// CHECK-NEXT: note: initialize the variable
int f2(int cond) {
int a;
// CHECK-NEXT: {{^}} if (cond) {
// CHECK-NEXT: {{^}} ^~~~~~~~~~~{{$}}
// CHECK-NEXT: {{^}} line(1);
-// CHECK-NEXT: {{^}}~~~~~~~~~~~~{{$}}
+// CHECK-NEXT: {{^}} ~~~~~~~~{{$}}
// CHECK-NEXT: {{^}} line(2);
-// CHECK-NEXT: {{^}}~~~~~~~~~~~~{{$}}
+// CHECK-NEXT: {{^}} ~~~~~~~~{{$}}
// CHECK-NEXT: {{^}} line(3);
-// CHECK-NEXT: {{^}}~~~~~~~~~~~~{{$}}
+// CHECK-NEXT: {{^}} ~~~~~~~~{{$}}
// CHECK-NEXT: {{^}} } else {
-// CHECK-NEXT: {{^}}~~~~~~~~~{{$}}
+// CHECK-NEXT: {{^}} ~~~~~~{{$}}
// CHECK-NEXT: note: initialize the variable
int f3(int cond) {
int a;
// CHECK-NEXT: {{^}} if (cond) {
// CHECK-NEXT: {{^}} ^~~~~~~~~~~{{$}}
// CHECK-NEXT: {{^}} line(1);
-// CHECK-NEXT: {{^}}~~~~~~~~~~~~{{$}}
+// CHECK-NEXT: {{^}} ~~~~~~~~{{$}}
// CHECK-NEXT: {{^}} line(2);
-// CHECK-NEXT: {{^}}~~~~~~~~~~~~{{$}}
+// CHECK-NEXT: {{^}} ~~~~~~~~{{$}}
// CHECK-NEXT: {{^}} line(3);
-// CHECK-NEXT: {{^}}~~~~~~~~~~~~{{$}}
+// CHECK-NEXT: {{^}} ~~~~~~~~{{$}}
// CHECK-NEXT: {{^}} line(4);
-// CHECK-NEXT: {{^}}~~~~~~~~~~~~{{$}}
+// CHECK-NEXT: {{^}} ~~~~~~~~{{$}}
// CHECK-NEXT: note: initialize the variable
int f4(int cond) {
int a;
// CHECK-NEXT: {{^}} if (cond) {
// CHECK-NEXT: {{^}} ^~~~~~~~~~~{{$}}
// CHECK-NEXT: {{^}} line(1);
-// CHECK-NEXT: {{^}}~~~~~~~~~~~~{{$}}
+// CHECK-NEXT: {{^}} ~~~~~~~~{{$}}
// CHECK-NEXT: {{^}} line(2);
-// CHECK-NEXT: {{^}}~~~~~~~~~~~~{{$}}
+// CHECK-NEXT: {{^}} ~~~~~~~~{{$}}
// CHECK-NEXT: {{^}} line(3);
-// CHECK-NEXT: {{^}}~~~~~~~~~~~~{{$}}
+// CHECK-NEXT: {{^}} ~~~~~~~~{{$}}
// CHECK-NEXT: {{^}} line(4);
-// CHECK-NEXT: {{^}}~~~~~~~~~~~~{{$}}
+// CHECK-NEXT: {{^}} ~~~~~~~~{{$}}
// CHECK-NEXT: note: initialize the variable
int f5(int cond) {
int a;
int [3] (*a) = 0;
// expected-error@-1{{brackets are not allowed here; to declare an array, place the brackets after the name}}
// CHECK: {{^}} int [3] (*a) = 0;
- // CHECK: {{^}} ~~~~ ^
+ // CHECK: {{^}} ~~~ ^
// CHECK: {{^}} [3]
// CHECK: fix-it:{{.*}}:{[[@LINE-5]]:7-[[@LINE-5]]:11}:""
// CHECK: fix-it:{{.*}}:{[[@LINE-6]]:15-[[@LINE-6]]:15}:"[3]"
int [5] *B::x = 0;
// expected-error@-1{{brackets are not allowed here; to declare an array, place the brackets after the name}}
// CHECK: {{^}}int [5] *B::x = 0;
-// CHECK: {{^}} ~~~~ ^
+// CHECK: {{^}} ~~~ ^
// CHECK: {{^}} ( )[5]
// CHECK: fix-it:{{.*}}:{[[@LINE-5]]:5-[[@LINE-5]]:9}:""
// CHECK: fix-it:{{.*}}:{[[@LINE-6]]:9-[[@LINE-6]]:9}:"("
int [3] *a;
// expected-error@-1{{brackets are not allowed here; to declare an array, place the brackets after the name}}
// CHECK: {{^}} int [3] *a;
- // CHECK: {{^}} ~~~~ ^
+ // CHECK: {{^}} ~~~ ^
// CHECK: {{^}} ( )[3]
// CHECK: fix-it:{{.*}}:{[[@LINE-5]]:7-[[@LINE-5]]:11}:""
// CHECK: fix-it:{{.*}}:{[[@LINE-6]]:11-[[@LINE-6]]:11}:"("
int [2] a;
// expected-error@-1{{brackets are not allowed here; to declare an array, place the brackets after the name}}
// CHECK: {{^}} int [2] a;
- // CHECK: {{^}} ~~~~ ^
+ // CHECK: {{^}} ~~~ ^
// CHECK: {{^}} [2]
// CHECK: fix-it:{{.*}}:{[[@LINE-5]]:7-[[@LINE-5]]:11}:""
// CHECK: fix-it:{{.*}}:{[[@LINE-6]]:12-[[@LINE-6]]:12}:"[2]"
int [2] &b = a;
// expected-error@-1{{brackets are not allowed here; to declare an array, place the brackets after the name}}
// CHECK: {{^}} int [2] &b = a;
- // CHECK: {{^}} ~~~~ ^
+ // CHECK: {{^}} ~~~ ^
// CHECK: {{^}} ( )[2]
// CHECK: fix-it:{{.*}}:{[[@LINE-5]]:7-[[@LINE-5]]:11}:""
// CHECK: fix-it:{{.*}}:{[[@LINE-6]]:11-[[@LINE-6]]:11}:"("
int [3] ::test6::A::arr = {1,2,3};
// expected-error@-1{{brackets are not allowed here; to declare an array, place the brackets after the name}}
// CHECK: {{^}}int [3] ::test6::A::arr = {1,2,3};
-// CHECK: {{^}} ~~~~ ^
+// CHECK: {{^}} ~~~ ^
// CHECK: {{^}} [3]
// CHECK: fix-it:{{.*}}:{[[@LINE-5]]:5-[[@LINE-5]]:9}:""
// CHECK: fix-it:{{.*}}:{[[@LINE-6]]:24-[[@LINE-6]]:24}:"[3]"
int [3] A::*a;
// expected-error@-1{{brackets are not allowed here; to declare an array, place the brackets after the name}}
// CHECK: {{^}} int [3] A::*a;
- // CHECK: {{^}} ~~~~ ^
+ // CHECK: {{^}} ~~~ ^
// CHECK: {{^}} ( )[3]
// CHECK: fix-it:{{.*}}:{[[@LINE-5]]:7-[[@LINE-5]]:11}:""
// CHECK: fix-it:{{.*}}:{[[@LINE-6]]:11-[[@LINE-6]]:11}:"("