Skip to content

Commit 91fedd9

Browse files
committed
Verifier: Support line numbers in fix-it verification
1 parent d5087ee commit 91fedd9

File tree

4 files changed

+145
-28
lines changed

4 files changed

+145
-28
lines changed

include/swift/Frontend/DiagnosticVerifier.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,12 @@ struct ExpectedFixIt;
4040
struct LineColumnRange {
4141
static constexpr unsigned NoValue = ~0u;
4242

43-
unsigned StartCol, EndCol;
43+
unsigned StartLine, StartCol;
44+
unsigned EndLine, EndCol;
4445

45-
LineColumnRange() : StartCol(NoValue), EndCol(NoValue) {}
46+
LineColumnRange()
47+
: StartLine(NoValue), StartCol(NoValue), EndLine(NoValue),
48+
EndCol(NoValue) {}
4649
};
4750

4851
class CapturedFixItInfo final {
@@ -60,7 +63,9 @@ class CapturedFixItInfo final {
6063
/// Obtain the line-column range corresponding to the fix-it's
6164
/// replacement range.
6265
const LineColumnRange &getLineColumnRange(const SourceManager &SM,
63-
unsigned BufferID) const;
66+
unsigned BufferID,
67+
bool ComputeStartLocLine,
68+
bool ComputeEndLocLine) const;
6469
};
6570

6671
struct CapturedDiagnosticInfo {
@@ -132,7 +137,7 @@ class DiagnosticVerifier : public DiagnosticConsumer {
132137

133138
// Render the verifier syntax for a given set of fix-its.
134139
std::string renderFixits(ArrayRef<CapturedFixItInfo> ActualFixIts,
135-
unsigned BufferID) const;
140+
unsigned BufferID, unsigned DiagnosticLineNo) const;
136141

137142
void printRemainingDiagnostics() const;
138143
};

lib/Frontend/DiagnosticVerifier.cpp

Lines changed: 88 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,23 @@ struct ExpectedFixIt {
3737

3838
const LineColumnRange &
3939
CapturedFixItInfo::getLineColumnRange(const SourceManager &SM,
40-
unsigned BufferID) const {
41-
if (LineColRange.StartCol == LineColumnRange::NoValue) {
40+
unsigned BufferID,
41+
bool ComputeStartLocLine,
42+
bool ComputeEndLocLine) const {
43+
if (LineColRange.StartLine == LineColumnRange::NoValue &&
44+
ComputeStartLocLine) {
45+
std::tie(LineColRange.StartLine, LineColRange.StartCol) =
46+
SM.getPresumedLineAndColumnForLoc(getSourceRange().getStart(),
47+
BufferID);
48+
} else if (LineColRange.StartCol == LineColumnRange::NoValue) {
4249
LineColRange.StartCol =
4350
SM.getColumnInBuffer(getSourceRange().getStart(), BufferID);
4451
}
4552

46-
if (LineColRange.EndCol == LineColumnRange::NoValue) {
53+
if (LineColRange.EndLine == LineColumnRange::NoValue && ComputeEndLocLine) {
54+
std::tie(LineColRange.EndLine, LineColRange.EndCol) =
55+
SM.getPresumedLineAndColumnForLoc(FixIt.getRange().getEnd(), BufferID);
56+
} else if (LineColRange.EndCol == LineColumnRange::NoValue) {
4757
LineColRange.EndCol =
4858
SM.getColumnInBuffer(getSourceRange().getEnd(), BufferID);
4959
}
@@ -262,12 +272,26 @@ bool DiagnosticVerifier::checkForFixIt(const ExpectedFixIt &Expected,
262272
if (ActualFixIt.getText() != Expected.Text)
263273
continue;
264274

265-
LineColumnRange ActualRange = ActualFixIt.getLineColumnRange(SM, BufferID);
266-
if (ActualRange.StartCol != Expected.Range.StartCol)
275+
LineColumnRange ActualRange = ActualFixIt.getLineColumnRange(
276+
SM, BufferID,
277+
// Don't compute line numbers unless we have to.
278+
/*ComputeStartLocLine=*/Expected.Range.StartLine !=
279+
LineColumnRange::NoValue,
280+
/*ComputeEndLocLine=*/Expected.Range.EndLine !=
281+
LineColumnRange::NoValue);
282+
283+
if (Expected.Range.StartCol != ActualRange.StartCol ||
284+
Expected.Range.EndCol != ActualRange.EndCol) {
267285
continue;
268-
if (ActualRange.EndCol != Expected.Range.EndCol)
286+
}
287+
if (Expected.Range.StartLine != LineColumnRange::NoValue &&
288+
Expected.Range.StartLine != ActualRange.StartLine) {
269289
continue;
270-
290+
}
291+
if (Expected.Range.EndLine != LineColumnRange::NoValue &&
292+
Expected.Range.EndLine != ActualRange.EndLine) {
293+
continue;
294+
}
271295
return true;
272296
}
273297

@@ -276,16 +300,30 @@ bool DiagnosticVerifier::checkForFixIt(const ExpectedFixIt &Expected,
276300

277301
std::string
278302
DiagnosticVerifier::renderFixits(ArrayRef<CapturedFixItInfo> ActualFixIts,
279-
unsigned BufferID) const {
303+
unsigned BufferID,
304+
unsigned DiagnosticLineNo) const {
280305
std::string Result;
281306
llvm::raw_string_ostream OS(Result);
282307
interleave(
283308
ActualFixIts,
284309
[&](const CapturedFixItInfo &ActualFixIt) {
285310
LineColumnRange ActualRange =
286-
ActualFixIt.getLineColumnRange(SM, BufferID);
311+
ActualFixIt.getLineColumnRange(SM, BufferID,
312+
/*ComputeStartLocLine=*/true,
313+
/*ComputeEndLocLine=*/true);
314+
OS << "{{";
287315

288-
OS << "{{" << ActualRange.StartCol << '-' << ActualRange.EndCol << '=';
316+
if (ActualRange.StartLine != DiagnosticLineNo)
317+
OS << ActualRange.StartLine << ':';
318+
OS << ActualRange.StartCol;
319+
320+
OS << '-';
321+
322+
if (ActualRange.EndLine != ActualRange.StartLine)
323+
OS << ActualRange.EndLine << ':';
324+
OS << ActualRange.EndCol;
325+
326+
OS << '=';
289327

290328
for (auto C : ActualFixIt.getText()) {
291329
if (C == '\n')
@@ -537,40 +575,68 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
537575
break;
538576
}
539577

540-
// Parse the pieces of the fix-it.
541-
size_t MinusLoc = CheckStr.find('-');
578+
// Parse the pieces of the fix-it: (L:)?C-(L:)?C=text
579+
const size_t MinusLoc = CheckStr.find('-');
542580
if (MinusLoc == StringRef::npos) {
543581
addError(CheckStr.data(), "expected '-' in fix-it verification");
544582
continue;
545583
}
546-
StringRef StartColStr = CheckStr.slice(0, MinusLoc);
547-
StringRef AfterMinus = CheckStr.substr(MinusLoc + 1);
548584

549-
size_t EqualLoc = AfterMinus.find('=');
585+
const size_t EqualLoc = CheckStr.find('=', /*From=*/MinusLoc + 1);
550586
if (EqualLoc == StringRef::npos) {
551-
addError(AfterMinus.data(),
587+
addError(CheckStr.data(),
552588
"expected '=' after '-' in fix-it verification");
553589
continue;
554590
}
555-
StringRef EndColStr = AfterMinus.slice(0, EqualLoc);
556-
StringRef AfterEqual = AfterMinus.substr(EqualLoc+1);
557-
591+
592+
const size_t FirstColonLoc = CheckStr.take_front(MinusLoc).find(':');
593+
const size_t SecondColonLoc =
594+
CheckStr.take_front(EqualLoc).find(':', /*From=*/MinusLoc + 1);
595+
558596
ExpectedFixIt FixIt;
559597
FixIt.StartLoc = OpenLoc;
560598
FixIt.EndLoc = CloseLoc;
599+
if (FirstColonLoc != StringRef::npos) {
600+
const StringRef StartLineStr = CheckStr.take_front(FirstColonLoc);
601+
if (StartLineStr.getAsInteger(10, FixIt.Range.StartLine)) {
602+
addError(StartLineStr.data(),
603+
"invalid line number in fix-it verification");
604+
continue;
605+
}
606+
}
607+
608+
const StringRef StartColStr =
609+
(FirstColonLoc != StringRef::npos)
610+
? CheckStr.slice(FirstColonLoc + 1, MinusLoc)
611+
: CheckStr.take_front(MinusLoc);
561612
if (StartColStr.getAsInteger(10, FixIt.Range.StartCol)) {
562613
addError(StartColStr.data(),
563614
"invalid column number in fix-it verification");
564615
continue;
565616
}
617+
618+
if (SecondColonLoc != StringRef::npos) {
619+
const StringRef EndLineStr =
620+
CheckStr.slice(MinusLoc + 1, SecondColonLoc);
621+
if (EndLineStr.getAsInteger(10, FixIt.Range.EndLine)) {
622+
addError(EndLineStr.data(),
623+
"invalid line number in fix-it verification");
624+
continue;
625+
}
626+
}
627+
628+
const StringRef EndColStr =
629+
(SecondColonLoc != StringRef::npos)
630+
? CheckStr.slice(SecondColonLoc + 1, EqualLoc)
631+
: CheckStr.slice(MinusLoc + 1, EqualLoc);
566632
if (EndColStr.getAsInteger(10, FixIt.Range.EndCol)) {
567633
addError(EndColStr.data(),
568634
"invalid column number in fix-it verification");
569635
continue;
570636
}
571637

572638
// Translate literal "\\n" into '\n', inefficiently.
573-
StringRef fixItText = AfterEqual.slice(0, EndIndex);
639+
const StringRef fixItText = CheckStr.substr(EqualLoc + 1);
574640
for (const char *current = fixItText.begin(), *end = fixItText.end();
575641
current != end; /* in loop */) {
576642
if (*current == '\\' && current + 1 < end) {
@@ -642,7 +708,8 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
642708

643709
auto makeActualFixitsPhrase =
644710
[&](ArrayRef<CapturedFixItInfo> actualFixits) -> ActualFixitsPhrase {
645-
std::string actualFixitsStr = renderFixits(actualFixits, BufferID);
711+
std::string actualFixitsStr =
712+
renderFixits(actualFixits, BufferID, expected.LineNo);
646713

647714
return ActualFixitsPhrase{(Twine("actual fix-it") +
648715
(actualFixits.size() >= 2 ? "s" : "") +

test/Frontend/verify-fixits.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,42 @@ func test0Fixits() {
1818
// CHECK: [[@LINE+1]]:78: error: expected fix-it not seen
1919
undefinedFunc() // expected-error {{cannot find 'undefinedFunc' in scope}} {{1-1=a}}
2020

21+
// CHECK: [[@LINE+1]]:80: error: invalid column number in fix-it verification
22+
undefinedFunc() // expected-error {{cannot find 'undefinedFunc' in scope}} {{x-1=a}}
23+
24+
// CHECK: [[@LINE+1]]:82: error: invalid column number in fix-it verification
25+
undefinedFunc() // expected-error {{cannot find 'undefinedFunc' in scope}} {{1-x=a}}
26+
27+
// CHECK: [[@LINE+1]]:82: error: invalid column number in fix-it verification
28+
undefinedFunc() // expected-error {{cannot find 'undefinedFunc' in scope}} {{1:x-1=a}}
29+
30+
// CHECK: [[@LINE+1]]:80: error: invalid line number in fix-it verification
31+
undefinedFunc() // expected-error {{cannot find 'undefinedFunc' in scope}} {{x:1-1=a}}
32+
33+
// CHECK: [[@LINE+1]]:84: error: invalid column number in fix-it verification
34+
undefinedFunc() // expected-error {{cannot find 'undefinedFunc' in scope}} {{1-1:x=a}}
35+
36+
// CHECK: [[@LINE+1]]:82: error: invalid line number in fix-it verification
37+
undefinedFunc() // expected-error {{cannot find 'undefinedFunc' in scope}} {{1-x:1=a}}
38+
39+
// CHECK: [[@LINE+1]]:82: error: invalid column number in fix-it verification
40+
undefinedFunc() // expected-error {{cannot find 'undefinedFunc' in scope}} {{1:x-1:x=a}}
41+
42+
// CHECK: [[@LINE+1]]:80: error: invalid line number in fix-it verification
43+
undefinedFunc() // expected-error {{cannot find 'undefinedFunc' in scope}} {{x:1-1:x=a}}
44+
45+
// CHECK: [[@LINE+1]]:82: error: invalid column number in fix-it verification
46+
undefinedFunc() // expected-error {{cannot find 'undefinedFunc' in scope}} {{1:x-x:1=a}}
47+
48+
// CHECK: [[@LINE+1]]:78: error: expected fix-it not seen
49+
undefinedFunc() // expected-error {{cannot find 'undefinedFunc' in scope}} {{1:1-1:1=a}}
50+
51+
// CHECK: [[@LINE+1]]:78: error: expected fix-it not seen
52+
undefinedFunc() // expected-error {{cannot find 'undefinedFunc' in scope}} {{1-1:1=a}}
53+
54+
// CHECK: [[@LINE+1]]:78: error: expected fix-it not seen
55+
undefinedFunc() // expected-error {{cannot find 'undefinedFunc' in scope}} {{1:1-1=a}}
56+
2157
// CHECK: [[@LINE+1]]:78: error: expected fix-it not seen
2258
undefinedFunc() // expected-error {{cannot find 'undefinedFunc' in scope}} {{1-1=a}} {{2-2=b}}
2359

@@ -57,6 +93,15 @@ func test1Fixits() {
5793

5894
// CHECK: [[@LINE+1]]:121: error: expected fix-it not seen; actual fix-it seen: {{{{}}15-18=aa}}
5995
labeledFunc(aax: 0, bb: 1) // expected-error {{incorrect argument label in call (have 'aax:bb:', expected 'aa:bb:')}} {{15-18=xx}} {{15-18=aa}} {{none}}
96+
97+
// CHECK-NOT: [[@LINE+1]]:{{[0-9]+}}: error:
98+
labeledFunc(aax: 0, bb: 1) // expected-error {{incorrect argument label in call (have 'aax:bb:', expected 'aa:bb:')}} {{98:15-98:18=aa}}
99+
// CHECK-NOT: [[@LINE+1]]:{{[0-9]+}}: error:
100+
labeledFunc(aax: 0, bb: 1) // expected-error {{incorrect argument label in call (have 'aax:bb:', expected 'aa:bb:')}} {{100:15-18=aa}}
101+
// CHECK-NOT: [[@LINE+1]]:{{[0-9]+}}: error:
102+
labeledFunc(aax: 0, bb: 1) // expected-error {{incorrect argument label in call (have 'aax:bb:', expected 'aa:bb:')}} {{15-102:18=aa}}
103+
// CHECK: [[@LINE+1]]:121: error: expected fix-it not seen; actual fix-it seen: {{{{}}15-18=aa}}
104+
labeledFunc(aax: 0, bb: 1) // expected-error {{incorrect argument label in call (have 'aax:bb:', expected 'aa:bb:')}} {{61:15-18=aa}}
60105
}
61106

62107
func test2Fixits() {

test/Generics/associated_type_where_clause_hints.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ protocol P2 : P1 {
2323
// A redeclaration of an associated type that adds type/layout requirements
2424
// should be written via a where clause.
2525
protocol P3a : P1 {
26-
associatedtype A: P0, P0b // expected-warning{{redeclaration of associated type 'A' from protocol 'P1' is better expressed as a 'where' clause on the protocol}}{{18-18= where A: P0, A: P0b}}{{3-29=}}
26+
associatedtype A: P0, P0b // expected-warning{{redeclaration of associated type 'A' from protocol 'P1' is better expressed as a 'where' clause on the protocol}}{{25:18-25:18= where A: P0, A: P0b}}{{26:3-26:29=}}
2727
associatedtype A2: P0, P0b where A2.A == Never, A2: P1 // expected-warning{{redeclaration of associated type 'A2' from protocol 'P1' is better expressed as a 'where' clause on the protocol}}{{18-18= where A2: P0, A2: P0b, A2.A == Never, A2 : P1}}{{3-58=}}
2828
associatedtype A3 where A3: P0 // expected-warning{{redeclaration of associated type 'A3' from protocol 'P1' is better expressed as a 'where' clause on the protocol}}{{18-18= where A3 : P0}}{{3-34=}}
2929

30-
// expected-warning@+1 {{redeclaration of associated type 'A4' from protocol 'P1' is better expressed as a 'where' clause on the protocol}}{{18-18= where A4: P0, A4 : Collection, A4.Element == A4.Index, A4.SubSequence == A4}}{{3-52=}}
30+
// expected-warning@+1 {{redeclaration of associated type 'A4' from protocol 'P1' is better expressed as a 'where' clause on the protocol}}{{25:18-25:18= where A4: P0, A4 : Collection, A4.Element == A4.Index, A4.SubSequence == A4}}{{31:3-33:51=}}
3131
associatedtype A4: P0 where A4: Collection,
3232
A4.Element == A4.Index,
33-
A4.SubSequence == A4 // {{3-52=}} is this line, so it is correct.
33+
A4.SubSequence == A4
3434
}
3535

3636
// ... unless it has adds a default type witness

0 commit comments

Comments
 (0)