Skip to content

Commit 0ab9fd7

Browse files
authored
Merge pull request #31124 from owenv/pretty-printed-diags-source-location-fixes
2 parents 1553c6d + 96696c8 commit 0ab9fd7

File tree

6 files changed

+169
-27
lines changed

6 files changed

+169
-27
lines changed

include/swift/Basic/SourceManager.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,11 @@ class SourceManager {
268268
else
269269
return 0;
270270
}
271+
272+
public:
273+
bool isLocInVirtualFile(SourceLoc Loc) const {
274+
return getVirtualFile(Loc) != nullptr;
275+
}
271276
};
272277

273278
} // end namespace swift

lib/Frontend/PrintingDiagnosticConsumer.cpp

Lines changed: 54 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,9 @@ namespace {
381381
};
382382

383383
unsigned LineNumber;
384+
// The line number displayed to the user. This may differ from the actual
385+
// line number if #sourceLocation is used.
386+
unsigned DisplayLineNumber;
384387
std::string LineText;
385388
SmallVector<Message, 1> Messages;
386389
SmallVector<Highlight, 1> Highlights;
@@ -448,8 +451,10 @@ namespace {
448451
}
449452

450453
public:
451-
AnnotatedLine(unsigned LineNumber, StringRef LineText)
452-
: LineNumber(LineNumber), LineText(LineText) {}
454+
AnnotatedLine(unsigned LineNumber, unsigned DisplayLineNumber,
455+
StringRef LineText)
456+
: LineNumber(LineNumber), DisplayLineNumber(DisplayLineNumber),
457+
LineText(LineText) {}
453458

454459
unsigned getLineNumber() { return LineNumber; }
455460

@@ -469,7 +474,7 @@ namespace {
469474
}
470475

471476
void render(unsigned LineNumberIndent, raw_ostream &Out) {
472-
printNumberedGutter(LineNumber, LineNumberIndent, Out);
477+
printNumberedGutter(DisplayLineNumber, LineNumberIndent, Out);
473478

474479
// Determine if the line is all-ASCII. This will determine a number of
475480
// later formatting decisions.
@@ -628,21 +633,25 @@ namespace {
628633
/// diagnostic message. This is printed alongside the file path so it can be
629634
/// parsed by editors and other tooling.
630635
SourceLoc PrimaryLoc;
636+
/// Whether the excerpt is from a virtual file (e.g. one introduced using
637+
/// #sourceLocation).
638+
bool FromVirtualFile;
631639
std::vector<AnnotatedLine> AnnotatedLines;
632640

633641
/// Return the AnnotatedLine for a given SourceLoc, creating it if it
634642
/// doesn't already exist.
635643
AnnotatedLine &lineForLoc(SourceLoc Loc) {
636-
// FIXME: This call to `getLineAndColumn` is expensive.
637-
unsigned lineNo = SM.getLineAndColumn(Loc).first;
638-
AnnotatedLine newLine(lineNo, "");
644+
// FIXME: This call to `getLineNumber` is expensive.
645+
unsigned lineNo = SM.getLineNumber(Loc);
646+
AnnotatedLine newLine(lineNo, 0, "");
639647
auto iter =
640648
std::lower_bound(AnnotatedLines.begin(), AnnotatedLines.end(),
641649
newLine, [](AnnotatedLine l1, AnnotatedLine l2) {
642650
return l1.getLineNumber() < l2.getLineNumber();
643651
});
644652
if (iter == AnnotatedLines.end() || iter->getLineNumber() != lineNo) {
645653
newLine.LineText = SM.getLineString(BufferID, lineNo);
654+
newLine.DisplayLineNumber = SM.getLineAndColumn(Loc).first;
646655
return *AnnotatedLines.insert(iter, newLine);
647656
} else {
648657
return *iter;
@@ -658,10 +667,8 @@ namespace {
658667

659668
void lineRangesForRange(CharSourceRange Range,
660669
SmallVectorImpl<CharSourceRange> &LineRanges) {
661-
// FIXME: The calls to `getLineAndColumn` and `getLocForLineCol` are
662-
// expensive.
663-
unsigned startLineNo = SM.getLineAndColumn(Range.getStart()).first;
664-
unsigned endLineNo = SM.getLineAndColumn(Range.getEnd()).first;
670+
unsigned startLineNo = SM.getLineNumber(Range.getStart());
671+
unsigned endLineNo = SM.getLineNumber(Range.getEnd());
665672

666673
if (startLineNo == endLineNo) {
667674
LineRanges.push_back(Range);
@@ -687,10 +694,19 @@ namespace {
687694
LineRanges.push_back(CharSourceRange(SM, lastLineStart, Range.getEnd()));
688695
}
689696

697+
void printLineEllipsis(raw_ostream &Out) {
698+
Out.changeColor(ColoredStream::Colors::CYAN, true);
699+
Out << llvm::formatv("{0}...\n",
700+
llvm::fmt_repeat(" ", getPreferredLineNumberIndent()));
701+
Out.resetColor();
702+
}
703+
690704
public:
691705
AnnotatedFileExcerpt(SourceManager &SM, unsigned BufferID,
692706
SourceLoc PrimaryLoc)
693-
: SM(SM), BufferID(BufferID), PrimaryLoc(PrimaryLoc) {}
707+
: SM(SM), BufferID(BufferID), PrimaryLoc(PrimaryLoc) {
708+
FromVirtualFile = SM.isLocInVirtualFile(PrimaryLoc);
709+
}
694710

695711
unsigned getPreferredLineNumberIndent() {
696712
// The lines are already in sorted ascending order, and we render one line
@@ -734,13 +750,14 @@ namespace {
734750
auto primaryLineAndColumn = SM.getLineAndColumn(PrimaryLoc);
735751
Out.changeColor(ColoredStream::Colors::CYAN);
736752
Out << std::string(lineNumberIndent + 1, '=') << " "
737-
<< SM.getIdentifierForBuffer(BufferID) << ":"
753+
<< SM.getDisplayNameForLoc(PrimaryLoc) << ":"
738754
<< primaryLineAndColumn.first << ":" << primaryLineAndColumn.second
739755
<< " " << std::string(lineNumberIndent + 1, '=') << "\n";
740756
Out.resetColor();
741757

742-
// Print one extra line at the top for context.
743-
if (AnnotatedLines.front().getLineNumber() > 1)
758+
// Print one extra line at the top for context, so long as this isn't an
759+
// excerpt from a virtual file.
760+
if (AnnotatedLines.front().getLineNumber() > 1 && !FromVirtualFile)
744761
printNumberedLine(SM, BufferID,
745762
AnnotatedLines.front().getLineNumber() - 1,
746763
lineNumberIndent, Out);
@@ -754,14 +771,18 @@ namespace {
754771
for (auto line = AnnotatedLines.begin() + 1; line != AnnotatedLines.end();
755772
++line) {
756773
unsigned lineNumber = line->getLineNumber();
757-
if (lineNumber - lastLineNumber > maxIntermediateLines) {
774+
if (FromVirtualFile) {
775+
// Don't print intermediate lines in virtual files, as they may not
776+
// make sense in context. Instead, just print an ellipsis between them
777+
// if they're not consecutive in the actual source file.
778+
if (lineNumber - lastLineNumber > 1) {
779+
printLineEllipsis(Out);
780+
}
781+
} else if (lineNumber - lastLineNumber > maxIntermediateLines) {
758782
// Use an ellipsis to denote an ommitted part of the file.
759783
printNumberedLine(SM, BufferID, lastLineNumber + 1, lineNumberIndent,
760784
Out);
761-
Out.changeColor(ColoredStream::Colors::CYAN);
762-
Out << llvm::formatv("{0}...\n",
763-
llvm::fmt_repeat(" ", lineNumberIndent));
764-
Out.resetColor();
785+
printLineEllipsis(Out);
765786
printNumberedLine(SM, BufferID, lineNumber - 1, lineNumberIndent,
766787
Out);
767788
} else {
@@ -774,11 +795,14 @@ namespace {
774795
line->render(lineNumberIndent, Out);
775796
lastLineNumber = lineNumber;
776797
}
777-
// Print one extra line at the bottom for context.
778-
printNumberedLine(
779-
SM, BufferID,
780-
AnnotatedLines[AnnotatedLines.size() - 1].getLineNumber() + 1,
781-
lineNumberIndent, Out);
798+
// Print one extra line at the bottom for context, so long as the excerpt
799+
// isn't from a virtual file.
800+
if (!FromVirtualFile) {
801+
printNumberedLine(
802+
SM, BufferID,
803+
AnnotatedLines[AnnotatedLines.size() - 1].getLineNumber() + 1,
804+
lineNumberIndent, Out);
805+
}
782806
}
783807
};
784808
} // end anonymous namespace
@@ -788,14 +812,17 @@ namespace swift {
788812
/// complete diagnostic message.
789813
class AnnotatedSourceSnippet {
790814
SourceManager &SM;
791-
std::map<unsigned, AnnotatedFileExcerpt> FileExcerpts;
815+
std::map<StringRef, AnnotatedFileExcerpt> FileExcerpts;
792816
SmallVector<std::pair<DiagnosticKind, std::string>, 1>
793817
UnknownLocationMessages;
794818

795819
AnnotatedFileExcerpt &excerptForLoc(SourceLoc Loc) {
820+
StringRef bufName = SM.getDisplayNameForLoc(Loc);
796821
unsigned bufID = SM.findBufferContainingLoc(Loc);
797-
FileExcerpts.emplace(bufID, AnnotatedFileExcerpt(SM, bufID, Loc));
798-
return FileExcerpts.find(bufID)->second;
822+
// Use the buffer display name as the key in the excerpt map instead of the
823+
// buffer identifier to respect #sourceLocation directives.
824+
FileExcerpts.emplace(bufName, AnnotatedFileExcerpt(SM, bufID, Loc));
825+
return FileExcerpts.find(bufName)->second;
799826
}
800827

801828
public:

test/diagnostics/Inputs/RenamedObjc.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
@import Foundation;
2+
3+
#define SWIFT_NAME(X) __attribute__((swift_name(#X)))
4+
5+
#pragma clang assume_nonnull begin
6+
@interface SwiftNameTest : NSObject
7+
+ (instancetype)g:(id)x outParam:(int *)foo SWIFT_NAME(init(g:));
8+
@end
9+
#pragma clang assume_nonnull end

test/diagnostics/Inputs/module.map

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module RenamedObjc {
2+
header "RenamedObjc.h"
3+
export *
4+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %empty-directory(%t.mcp)
2+
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-experimental-diagnostic-formatting -I %S/Inputs/ -typecheck %s -module-cache-path %t.mcp 2>&1 | %FileCheck %s
3+
4+
// REQUIRES: objc_interop
5+
6+
import RenamedObjc
7+
8+
let foo = SwiftNameTest(g: "")
9+
10+
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}Inputs{{[/\]+}}RenamedObjc.h:7:1
11+
// CHECK: 7 | + (instancetype)g:(id)x outParam:(int *)foo SWIFT_NAME(init(g:));
12+
// CHECK: | ^ warning: too few parameters in swift_name attribute (expected 2; got 1)
13+
// CHECK: | ^ note: please report this issue to the owners of 'RenamedObjc'
14+
15+
16+
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diags-in-clang-buffer.swift:8:28
17+
// CHECK: 7 |
18+
// CHECK: 8 | let foo = SwiftNameTest(g: "")
19+
// CHECK: | ~~
20+
// CHECK: | ^ error: argument passed to call that takes no arguments
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// RUN: not %target-swift-frontend -enable-experimental-diagnostic-formatting -typecheck %s 2>&1 | %FileCheck %s
2+
3+
// Error split between the real file and a virtual one.
4+
#sourceLocation(file: "abc.swift", line: 9)
5+
let x = 1
6+
#sourceLocation()
7+
let x = 2
8+
9+
// Error split between two virtual files.
10+
#sourceLocation(file: "abc.swift", line: 4)
11+
let y = 1
12+
#sourceLocation(file: "xyz.swift", line: 18)
13+
let y = 2
14+
#sourceLocation()
15+
16+
// Error within a virtual file on non-consecutive lines.
17+
#sourceLocation(file: "abc.swift", line: 1)
18+
let z = 1
19+
// space
20+
let z = 2
21+
#sourceLocation()
22+
23+
// Error with note location placed in the same virtual file via a separate #sourceLocation block.
24+
#sourceLocation(file: "abc.swift", line: 1)
25+
let a = 1
26+
#sourceLocation()
27+
28+
29+
#sourceLocation(file: "abc.swift", line: 10)
30+
let a = 2
31+
#sourceLocation()
32+
33+
// Error at the beginning of a virtual file.
34+
#sourceLocation(file: "abc.swift", line: 1)
35+
let any: Any = ""
36+
let zz: Int = any
37+
#sourceLocation()
38+
39+
// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-source-loc-directive-diags.swift:[[#LINE:]]:5
40+
// CHECK: [[#LINE-1]] | #sourceLocation()
41+
// CHECK: [[#LINE]] | let x = 2
42+
// CHECK: | ^ error: invalid redeclaration of 'x'
43+
// CHECK: [[#LINE+1]] |
44+
// CHECK: abc.swift:9:5
45+
// CHECK: 9 | let x = 1
46+
// CHECK: | ^ note: 'x' previously declared here
47+
48+
49+
// CHECK: abc.swift:4:5
50+
// CHECK: 4 | let y = 1
51+
// CHECK: | ^ note: 'y' previously declared here
52+
// CHECK: xyz.swift:18:5
53+
// CHECK: 18 | let y = 2
54+
// CHECK: | ^ error: invalid redeclaration of 'y'
55+
56+
57+
// CHECK: abc.swift:3:5
58+
// CHECK: 1 | let z = 1
59+
// CHECK: | ^ note: 'z' previously declared here
60+
// CHECK: ...
61+
// CHECK: 3 | let z = 2
62+
// CHECK: | ^ error: invalid redeclaration of 'z'
63+
64+
65+
// CHECK: abc.swift:10:5
66+
// CHECK: 1 | let a = 1
67+
// CHECK: | ^ note: 'a' previously declared here
68+
// CHECK: ...
69+
// CHECK: 10 | let a = 2
70+
// CHECK: | ^ error: invalid redeclaration of 'a'
71+
72+
73+
// CHECK: abc.swift:2:15
74+
// CHECK: 2 | let zz: Int = any as! Int
75+
// CHECK: | ~~~++++++++
76+
// CHECK: | ^ error: cannot convert value of type 'Any' to specified type 'Int' [insert ' as! Int']
77+

0 commit comments

Comments
 (0)